/* ============================================================
   TRILLNOVO  site.css
   One stylesheet, two historical layers kept in cascade order:

     PART 1  the original 2026 base (formerly styles.css)
     PART 2  the Aug 2026 redesign layer, which overrides PART 1

   The parts were merged 24 Aug 2026; load order inside this file
   preserves the exact cascade the two-file version had. When
   editing, prefer PART 2. If you touch PART 1, remember PART 2
   may override it, and that an unconditional rule in PART 2 beats
   a @media rule in PART 1 (media queries add no specificity).
   ============================================================ */

/* ############################################################
   PART 0: SELF-HOSTED FONTS
   The same latin woff2 files Google Fonts served, hosted from
   /fonts so the page has no third-party requests. Source Sans 3
   arrives as a variable font: one file spans weights 400-700,
   which covers the 400/500/600 the site uses. All faces carry
   font-display: swap.
   ############################################################ */

@font-face {
  font-family: 'Libre Baskerville';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/libre-baskerville-400-italic.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Libre Baskerville';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fonts/libre-baskerville-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Libre Baskerville';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('fonts/libre-baskerville-700.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: italic;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/source-sans-3-400-700-italic.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Source Sans 3';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/source-sans-3-400-700.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ############################################################
   PART 1: BASE  (formerly styles.css)
   ############################################################ */

:root {
  --navy: #1B2A4A;
  --navy-deep: #111D35;
  --blue: #2B4C7E;
  --teal: #3DA5A0;
  --teal-soft: #4DB8B3;
  --teal-ink: #2E7D78;
  --warm-white: #FAFAF8;
  --cream: #F5F3EE;
  --text: #1E1E1E;
  --text-light: #3D3D3D;
  --border: #DDD9D0;
  --border-light: #EDEAE4;

  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
}

* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }

body {
  font-family: 'Source Sans 3', 'Segoe UI', sans-serif;
  color: var(--text);
  background: var(--warm-white);
  line-height: 1.7;
  font-size: 20px;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

h1, h2, h3 { text-wrap: balance; }

/* ============================================
   SKIP LINK
   ============================================ */
.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  background: var(--navy);
  color: #fff;
  padding: 12px 20px;
  border-radius: 0 0 8px 0;
  font-size: 16px;
  text-decoration: none;
}

.skip-link:focus { left: 0; }

/* ============================================
   GRAIN OVERLAY — adds tactile depth
   Disabled on mobile for scroll performance
   ============================================ */
body::after {
  content: '';
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  pointer-events: none;
  z-index: 9999;
  opacity: 0.025;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
  background-repeat: repeat;
  background-size: 200px 200px;
}

/* ============================================
   HEADER
   Solid navy by default so it stays readable
   without JS; transparent-at-top is a JS-gated
   enhancement (main.js adds .js + .scrolled).
   ============================================ */
header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  padding: 20px 0;
  background: rgba(27, 42, 74, 0.95);
  transition: padding 0.4s var(--ease-out-quart),
              background 0.4s var(--ease-out-quart),
              box-shadow 0.4s var(--ease-out-quart);
}

.js header { background: transparent; }

.js header.scrolled {
  padding: 12px 0;
  background: rgba(27, 42, 74, 0.97);
  box-shadow: 0 1px 30px rgba(0,0,0,0.15);
}

.header-inner {
  max-width: 1040px;
  margin: 0 auto;
  padding: 0 32px;
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.logo-area {
  display: flex;
  align-items: center;
}

.logo-area img {
  height: 26px;
  width: auto;
  transition: opacity 0.3s;
}

.header-phone {
  color: rgba(255,255,255,0.85);
  text-decoration: none;
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0.3px;
  padding: 8px 18px;
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: 100px;
  white-space: nowrap;
  transition: all 0.3s var(--ease-out-quart);
}

.header-phone:hover {
  color: #fff;
  border-color: var(--teal);
  background: rgba(61, 165, 160, 0.1);
}

/* ============================================
   HERO
   ============================================ */
.hero {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    linear-gradient(rgba(17, 29, 53, 0.87), rgba(17, 29, 53, 0.87)),
    url('hero.jpg') center / cover no-repeat;
  position: relative;
  overflow: hidden;
  padding: 120px 32px 80px;
}

/* Ambient gradient orbs */
.hero::before,
.hero::after {
  content: '';
  position: absolute;
  border-radius: 50%;
  filter: blur(80px);
  opacity: 0;
  animation: orbReveal 2s var(--ease-out-expo) forwards;
}

.hero::before {
  width: 600px;
  height: 600px;
  background: radial-gradient(circle, rgba(43, 76, 126, 0.55) 0%, transparent 70%);
  top: -15%;
  right: -10%;
  animation-delay: 0.5s;
}

.hero::after {
  width: 500px;
  height: 500px;
  background: radial-gradient(circle, rgba(61, 165, 160, 0.25) 0%, transparent 70%);
  bottom: -10%;
  left: -5%;
  animation-delay: 0.8s;
}

@keyframes orbReveal {
  from { opacity: 0; transform: scale(0.8); }
  to { opacity: 1; transform: scale(1); }
}

.hero-content {
  max-width: 680px;
  text-align: center;
  position: relative;
  z-index: 1;
}

/* Decorative line above tagline */
.hero-rule {
  width: 48px;
  height: 2px;
  background: var(--teal);
  margin: 0 auto 28px;
  opacity: 0;
  animation: ruleReveal 0.8s var(--ease-out-expo) 0.3s forwards;
}

@keyframes ruleReveal {
  /* The final keyframe pins width, and an animated value outranks any
     normal declaration, so this MUST match the .hero-rule width below
     (116px, the tapered rule) or the rule silently renders short. */
  from { opacity: 0; width: 0; }
  to { opacity: 1; width: 116px; }
}

.hero .tagline {
  font-size: 18px;
  color: var(--teal-soft);
  font-weight: 600;
  letter-spacing: 3px;
  text-transform: uppercase;
  margin-bottom: 28px;
  opacity: 0;
  animation: heroFadeUp 0.9s var(--ease-out-expo) 0.4s forwards;
}

.hero h1 {
  font-family: 'Libre Baskerville', Georgia, serif;
  font-size: 54px;
  color: #fff;
  line-height: 1.25;
  margin-bottom: 28px;
  font-weight: 700;
  letter-spacing: -0.5px;
  opacity: 0;
  animation: heroFadeUp 1s var(--ease-out-expo) 0.55s forwards;
}

.hero .hero-sub {
  font-size: 22px;
  color: rgba(255,255,255,0.78);
  max-width: 460px;
  margin: 0 auto 48px;
  line-height: 1.75;
  font-weight: 400;
  opacity: 0;
  animation: heroFadeUp 1s var(--ease-out-expo) 0.7s forwards;
}

.hero-actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
  opacity: 0;
  animation: heroFadeUp 1s var(--ease-out-expo) 0.85s forwards;
}

@keyframes heroFadeUp {
  from { opacity: 0; transform: translateY(24px); }
  to { opacity: 1; transform: translateY(0); }
}

.btn-primary {
  display: inline-block;
  background: var(--teal);
  color: #fff;
  padding: 16px 36px;
  border-radius: 100px;
  font-size: 18px;
  font-weight: 600;
  text-decoration: none;
  letter-spacing: 0.3px;
  transition: all 0.35s var(--ease-out-quart);
  box-shadow: 0 4px 24px rgba(61, 165, 160, 0.25),
              inset 0 1px 0 rgba(255,255,255,0.15);
  position: relative;
  overflow: hidden;
}

.btn-primary::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(255,255,255,0.1) 0%, transparent 50%);
  border-radius: inherit;
}

.btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 32px rgba(61, 165, 160, 0.35),
              inset 0 1px 0 rgba(255,255,255,0.2);
  background: var(--teal-soft);
}

.btn-secondary {
  display: inline-block;
  color: rgba(255,255,255,0.7);
  padding: 16px 28px;
  font-size: 18px;
  font-weight: 500;
  text-decoration: none;
  letter-spacing: 0.3px;
  transition: color 0.3s;
}

.btn-secondary:hover { color: rgba(255,255,255,0.85); }

/* ============================================
   REVEAL ANIMATION SYSTEM
   Hidden state only applies when main.js has
   confirmed it can animate (html.anim). With
   JS blocked or reduced motion, content is
   simply visible.
   ============================================ */
.anim .reveal {
  opacity: 0;
  transform: translateY(32px);
  /* Longhand on purpose. The `transition` shorthand also writes
     transition-delay: 0s at (0,2,0), which outranks the bare
     .reveal-delay-N rules at (0,1,0) and killed every stagger on the
     old live site for weeks before anyone noticed. */
  transition-property: opacity, transform;
  transition-duration: 0.8s;
  transition-timing-function: var(--ease-out-expo);
}

.anim .reveal.visible {
  opacity: 1;
  transform: translateY(0);
}

.reveal-delay-1 { transition-delay: 0.1s; }
.reveal-delay-2 { transition-delay: 0.2s; }
.reveal-delay-3 { transition-delay: 0.3s; }
.reveal-delay-4 { transition-delay: 0.4s; }
.reveal-delay-5 { transition-delay: 0.5s; }

/* ============================================
   SECTION FUNDAMENTALS
   ============================================ */
section {
  padding: 96px 32px;
  position: relative;
}

.section-rule {
  width: 32px;
  height: 2px;
  background: var(--teal);
  margin-bottom: 24px;
}

.section-rule.center { margin-left: auto; margin-right: auto; }

.section-rule.wide {
  max-width: 960px;
  margin-left: auto;
  margin-right: auto;
  margin-bottom: 40px;
}

.section-rule.on-dark { background: var(--teal-soft); }

/* ============================================
   EMPATHY SECTION
   ============================================ */
.empathy {
  background: var(--cream);
  position: relative;
}

.empathy-inner {
  max-width: 960px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 64px;
  align-items: center;
  position: relative;
}

.empathy-text {
  text-align: left;
}

.empathy-image {
  align-self: stretch;
  -webkit-mask-image: linear-gradient(to right, transparent 0%, rgba(0,0,0,0.6) 25%, black 50%);
  mask-image: linear-gradient(to right, transparent 0%, rgba(0,0,0,0.6) 25%, black 50%);
}

.empathy-image picture {
  display: block;
  height: 100%;
}

.empathy-image img {
  width: 100%;
  height: 100%;
  min-height: 420px;
  object-fit: cover;
  object-position: center;
  display: block;
}

.empathy-inner h2 {
  font-family: 'Libre Baskerville', Georgia, serif;
  font-size: 36px;
  color: var(--navy);
  margin-bottom: 16px;
  letter-spacing: -0.3px;
}

.empathy-inner .lead {
  font-size: 20px;
  color: var(--text-light);
  margin-bottom: 44px;
  line-height: 1.75;
  font-weight: 400;
}

.pain-points {
  list-style: none;
  text-align: left;
  max-width: 500px;
  margin: 0 auto 44px;
}

.pain-points li {
  padding: 16px 0;
  border-bottom: 1px solid var(--border);
  font-size: 18px;
  color: var(--text);
  line-height: 1.6;
}

.pain-points li:last-child { border-bottom: none; }

.empathy-close {
  font-family: 'Libre Baskerville', Georgia, serif;
  font-size: 22px;
  color: var(--navy);
  font-style: italic;
  line-height: 1.6;
  text-align: center;
  max-width: 960px;
  margin: 48px auto 0;
  padding-top: 40px;
  border-top: 1px solid var(--border-light);
}

/* ============================================
   SERVICES
   ============================================ */
.services {
  background: var(--warm-white);
}

.services-heading {
  text-align: center;
  margin-bottom: 12px;
}

.services-heading h2 {
  font-family: 'Libre Baskerville', Georgia, serif;
  font-size: 34px;
  color: var(--navy);
  letter-spacing: -0.3px;
}

.services-lead {
  text-align: center;
  font-size: 20px;
  color: var(--text-light);
  max-width: 520px;
  margin: 0 auto 52px;
  line-height: 1.75;
  font-weight: 400;
}

.svc-icon {
  width: 48px;
  height: 48px;
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 20px;
  transition: transform 0.4s var(--ease-out-quart);
}

.svc-icon svg {
  width: 24px;
  height: 24px;
}

.services-cta {
  text-align: center;
  margin-top: 56px;
}

.services-cta p {
  font-size: 19px;
  color: var(--text-light);
  max-width: 460px;
  margin: 0 auto 24px;
  line-height: 1.7;
}

/* ============================================
   WHAT TO EXPECT — TIMELINE
   ============================================ */
.expect {
  background: var(--navy-deep);
  color: #fff;
  overflow: hidden;
  position: relative;
}

.expect::before {
  content: '';
  position: absolute;
  top: 0; right: 0;
  width: 400px;
  height: 400px;
  background: radial-gradient(circle, rgba(43, 76, 126, 0.2) 0%, transparent 70%);
  border-radius: 50%;
}

.timeline {
  display: flex;
  gap: 0;
  position: relative;
}

/* Connecting line */
.timeline::before {
  content: '';
  position: absolute;
  top: 24px;
  left: 16.66%;
  right: 16.66%;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(61,165,160,0.3) 20%, rgba(61,165,160,0.3) 80%, transparent);
}

.step {
  flex: 1;
  text-align: center;
  position: relative;
  padding: 0 16px;
}

.step-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: rgba(61, 165, 160, 0.1);
  border: 1px solid rgba(61, 165, 160, 0.25);
  color: var(--teal-soft);
  font-family: 'Libre Baskerville', Georgia, serif;
  font-size: 18px;
  font-weight: 700;
  margin-bottom: 24px;
  position: relative;
  z-index: 1;
  transition: all 0.4s var(--ease-out-quart);
}

.step:hover .step-num {
  background: rgba(61, 165, 160, 0.2);
  transform: scale(1.1);
}

.step h3 {
  font-family: 'Libre Baskerville', Georgia, serif;
  font-size: 19px;
  margin-bottom: 10px;
}

.step p {
  font-size: 17px;
  color: rgba(255,255,255,0.75);
  line-height: 1.65;
  font-weight: 400;
}/* ============================================
   TRUST STRIP
   ============================================ */

/* ============================================
   CONTACT
   ============================================ */
.contact {
  background: var(--warm-white);
  text-align: center;
  padding: 104px 32px;
}

.contact h2 {
  font-family: 'Libre Baskerville', Georgia, serif;
  font-size: 36px;
  color: var(--navy);
  margin-bottom: 14px;
  letter-spacing: -0.3px;
}

.contact .lead {
  font-size: 20px;
  color: var(--text-light);
  max-width: 400px;
  margin: 0 auto 40px;
  line-height: 1.75;
  font-weight: 400;
}

.contact-family {
  font-size: 16px;
  color: var(--text-light);
  margin-top: 12px;
  line-height: 1.6;
}

/* ============================================
   FOOTER
   ============================================ */
footer {
  background: var(--navy-deep);
  padding: 56px 32px 40px;
  text-align: center;
  border-top: 1px solid rgba(255,255,255,0.05);
}

footer img {
  height: 22px;
  width: auto;
  opacity: 0.9;
  margin-bottom: 16px;
}

.footer-tagline {
  font-family: 'Libre Baskerville', Georgia, serif;
  font-style: italic;
  font-size: 17px;
  color: rgba(255,255,255,0.75);
  margin-bottom: 20px;
}

/* ============================================
   RESPONSIVE
   ============================================ */
@media (max-width: 768px) {
  /* Kill the grain overlay on mobile — major scroll perf win */
  body::after { display: none; }

  /* Disable hero orbs on mobile — eliminates top-of-page lag */
  .hero::before,
  .hero::after {
    display: none;
  }

  /* Larger tap targets for older adults */
  .btn-primary { padding: 18px 40px; font-size: 19px; }
  .header-phone { padding: 10px 20px; }
  .contact-phone { padding: 8px 0; }

  .hero {
    padding: 100px 24px 60px;
    min-height: 90vh;
    background:
      linear-gradient(rgba(17, 29, 53, 0.87), rgba(17, 29, 53, 0.87)),
      url('hero-mobile.jpg') center / cover no-repeat;
  }
  .hero h1 { font-size: 30px; line-height: 1.3; }
  .hero .hero-sub { font-size: 18px; }
  .hero-actions { flex-direction: column; gap: 12px; }
  .empathy-inner {
    grid-template-columns: 1fr;
    gap: 36px;
  }
  .empathy-image { display: none; }
  .empathy-inner h2 { font-size: 28px; }
  .services-grid { grid-template-columns: 1fr; }
  .services-heading h2 { font-size: 28px; }
  .timeline {
    flex-direction: column;
    gap: 36px;
    max-width: 320px;
    margin: 0 auto;
  }
  .timeline::before {
    top: 0;
    bottom: 0;
    left: 23px;
    right: auto;
    width: 1px;
    height: auto;
    background: linear-gradient(to bottom, transparent, rgba(61,165,160,0.3) 20%, rgba(61,165,160,0.3) 80%, transparent);
  }
  .step { text-align: left; padding-left: 64px; }
  .step-num {
    position: absolute;
    left: 0;
    top: 0;
  }
  .contact-phone { font-size: 24px; }
  .contact-card { padding: 36px 28px 32px; }
  .trust-inner {
    flex-wrap: wrap;
    gap: 24px;
  }
  .trust-divider { display: none; }
  section { padding: 72px 24px; }
}

@media (max-width: 480px) {
  .hero h1 { font-size: 28px; }
  .svc { padding: 28px 22px; }
}

/* ============================================
   SMOOTH SCROLL OFFSET
   ============================================ */
[id] { scroll-margin-top: 80px; }

/* ============================================
   SELECTION STYLE
   ============================================ */
::selection {
  background: rgba(61, 165, 160, 0.2);
  color: var(--navy);
}

/* ============================================
   ACCESSIBLE FOCUS STYLE
   Blue passes 3:1 on the light sections; white
   takes over on dark backgrounds.
   ============================================ */
a:focus-visible, button:focus-visible {
  outline: 3px solid var(--blue);
  outline-offset: 3px;
  border-radius: 4px;
}

header a:focus-visible,
.hero a:focus-visible,
.expect a:focus-visible,
footer a:focus-visible {
  outline-color: #fff;
}

/* ============================================
   REDUCED MOTION
   ============================================ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  .hero-rule,
  .hero .tagline,
  .hero h1,
  .hero .hero-sub,
  .hero-actions,
  .hero::before,
  .hero::after {
    animation: none;
    opacity: 1;
  }

  .anim .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }

  header, .svc, .svc-icon, .step-num, .btn-primary, .contact-card {
    transition: none;
  }
}


/* ############################################################
   PART 2: REDESIGN LAYER  (formerly the separate site.css)
   ############################################################ */

/* ============================================================
   SITE LAYER
   Loaded after styles.css and overrides it. styles.css is the
   original 2026 stylesheet and is still the base; everything
   here is the Aug 2026 redesign layered on top.

   IMPORTANT: an unconditional rule here beats a @media rule in
   styles.css, because media queries add no specificity. When you
   override something that styles.css only sets inside a
   breakpoint, restate the mobile value in this file's own
   breakpoints too, or phones silently get the desktop value.

   Section map:
     W   content width 960 -> 1200
     T   heading scale up; body copy sizes untouched
     H   hero left-aligned with a directional gradient
     P   trust panel overlapping the hero
     E   empathy as a wider editorial split, stats folded in
     S   the tiered services section
     X   What to Expect, more spacious
     C   contact at 50/50 with an image
     F   three-column footer
     M1-M4  scroll and ambient motion
   ============================================================ */

/* ------------------------------------------------------------
   W: the shared container
   ------------------------------------------------------------ */
.wide {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 40px;
}

.header-inner { max-width: 1200px; padding: 0 40px; }

section { padding: 110px 0; }

/* ------------------------------------------------------------
   M3: refined reveal
   ------------------------------------------------------------ */
.anim .reveal {
  transform: translateY(14px);
  filter: blur(2px);
  /* Longhand on purpose. The `transition` shorthand also sets
     transition-delay:0, which silently killed every .reveal-delay-N
     stagger in styles.css and fired all 33 elements at once. */
  transition-property: opacity, transform, filter;
  transition-duration: 0.7s, 0.9s, 0.6s;
  transition-timing-function: ease, cubic-bezier(0.16, 1, 0.3, 1), ease;
}

.anim .reveal.visible { transform: none; filter: none; }


/* ------------------------------------------------------------
   M1: icons draw themselves in
   ------------------------------------------------------------ */
.anim .svc-icon svg > *,
.anim .annual-icon svg > *,
.anim .project-icon svg > *,
.anim .kit-ico svg > *,
.anim .tp-ico svg > * {
  stroke-dasharray: var(--len, 120);
  stroke-dashoffset: var(--len, 120);
}

.anim .svc-icon.drawn svg > *,
.anim .annual-icon.drawn svg > *,
.anim .project-icon.drawn svg > *,
.anim .kit-ico.drawn svg > *,
.anim .tp-ico.drawn svg > * {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 1.05s cubic-bezier(0.16, 1, 0.3, 1);
}

/* ------------------------------------------------------------
   M2: the process connector draws across
   ------------------------------------------------------------ */
.anim .timeline::before {
  transform: scaleX(0);
  transform-origin: left center;
}

.anim .timeline.drawn::before {
  transform: scaleX(1);
  transition: transform 1.5s cubic-bezier(0.16, 1, 0.3, 1) 0.2s;
}

@keyframes numArrive {
  from { opacity: 0; transform: scale(0.82); }
  to   { opacity: 1; transform: scale(1); }
}

.anim .step .step-num { opacity: 0; }
.anim .step.visible .step-num { animation: numArrive 0.7s cubic-bezier(0.16, 1, 0.3, 1) forwards; }

/* ------------------------------------------------------------
   M4: ambient drift. Mobile hides the orbs, so no battery cost.
   ------------------------------------------------------------ */
@keyframes orbFade { from { opacity: 0; } to { opacity: 1; } }

@keyframes orbDriftA {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(-58px, 38px, 0) scale(1.12); }
}

@keyframes orbDriftB {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(48px, -32px, 0) scale(1.1); }
}

.hero::before {
  animation: orbFade 1.6s ease 0.5s forwards,
             orbDriftA 26s ease-in-out 2.1s infinite alternate;
}

.hero::after {
  animation: orbFade 1.6s ease 0.8s forwards,
             orbDriftB 34s ease-in-out 2.4s infinite alternate;
}

/* ------------------------------------------------------------
   The rules
   Treatment B, the tapered rule: a wider line fading to nothing at
   both ends. Picked over the wave, which read as hand-drawn when
   nothing else on the site does.

   .hero-rule and .section-rule are separate classes, and only the
   second one had ever been restyled, which is why the hero divider
   looked unrelated to the rest. Both carry the mark now.

   The hero is left-aligned, so its rule gets a negative left margin
   equal to the fade: the gradient is solid from 22% of 116px, i.e.
   25px in, and without the offset the visible mass would start 25px
   right of the tagline beneath it and read as indented.

   The .on-dark rule below is required, not tidiness. styles.css sets
   `.section-rule.on-dark { background: var(--teal-soft) }` at
   (0,2,0), which outranks a bare `.section-rule` here no matter how
   late this file loads, so the navy section would keep a solid slab.
   ------------------------------------------------------------ */
.hero-rule,
.section-rule {
  width: 116px;
  height: 2px;
  background: linear-gradient(90deg,
      transparent 0%,
      var(--teal) 22%,
      var(--teal) 78%,
      transparent 100%);
}

.section-rule {
  margin: 0 auto 26px;
}

.hero-rule {
  margin: 0 0 26px -25px;
}

.section-rule.on-dark {
  background: linear-gradient(90deg,
      transparent 0%,
      var(--teal-soft) 22%,
      var(--teal-soft) 78%,
      transparent 100%);
}

/* ============================================================
   H: HERO
   Directional gradient: heavy behind the words on the left,
   lifting to 30% on the right so the photograph finally reads.
   ============================================================ */
.hero {
  display: block;
  min-height: 0;
  padding: 190px 0 172px;
  text-align: left;
  background:
    linear-gradient(100deg,
      rgba(17, 29, 53, 0.94) 0%,
      rgba(17, 29, 53, 0.80) 44%,
      rgba(17, 29, 53, 0.44) 70%,
      rgba(17, 29, 53, 0.30) 100%),
    /* `center` loses the table: at 1920 the hero box is 2.16 wide against a
       1.5 image, so cover keeps only the middle 70% of the height and the
       tablet sits low in the frame. 68% drops the visible band far enough to
       hold the whole table with margin under it. */
    url('hero.jpg?v=aug26') center 68% / cover no-repeat;
}

.hero-inner { max-width: 1200px; margin: 0 auto; padding: 0 40px; }

.hero-content {
  max-width: 730px;
  margin: 0;
  text-align: left;
}


.hero .tagline {
  font-size: 15px;
  letter-spacing: 2.6px;
  margin-bottom: 24px;
}

/* Heading scale up. Body copy sizes are deliberately untouched. */
.hero h1 {
  font-size: 58px;
  line-height: 1.18;
  letter-spacing: -0.8px;
  margin-bottom: 26px;
}

.hero .hero-sub {
  font-size: 21px;
  max-width: 510px;
  margin: 0 0 38px;
}

.hero-actions { justify-content: flex-start; gap: 18px; }

.btn-primary { border-radius: 8px; padding: 17px 34px; }

.btn-secondary {
  border: 1px solid rgba(255,255,255,0.3);
  border-radius: 8px;
  padding: 16px 30px;
  color: rgba(255,255,255,0.9);
  transition: color 0.3s, border-color 0.3s;
}

.btn-secondary:hover { color: #fff; border-color: rgba(255,255,255,0.55); }

/* ============================================================
   P: TRUST PANEL, overlapping the hero
   ============================================================ */
.trustpanel-wrap {
  max-width: none;
  margin: -80px auto 0;
  padding: 0 40px;
  position: relative;
  z-index: 5;
  /* The first 80px is the hero overlap and must stay transparent. Below
     that the strip becomes cream so it reads as the start of the section
     underneath, instead of a white band behind a white card. */
  background: linear-gradient(to bottom, transparent 0, transparent 80px, var(--cream) 80px);
}

.trustpanel { max-width: 1120px; margin: 0 auto; }

/* White, not cream: the section below is cream, so a cream panel loses
   its bottom edge entirely. */
.trustpanel {
  /* Carries the accent hairline like the other card surfaces; teal-ink to
     match its own icon tiles. */
  border: 2px solid transparent;
  background:
    linear-gradient(#fff, #fff) padding-box,
    linear-gradient(135deg, var(--r, var(--teal-ink)) 0%,
        rgba(var(--rgb, 46,125,120), 0.38) 45%,
        rgba(var(--rgb, 46,125,120), 0) 80%) border-box;
  border-radius: 14px;
  position: relative;
  --r: var(--teal-ink);
  --rgb: 46,125,120;
  padding: 30px 36px;
  display: grid;
  /* Lead across the top, three items beneath. In a 4-up row the lead needed
     ~360px to break at two lines while "Neighbors who tell neighbors." needed
     ~266px to stay on one, and 1046px of panel will not pay for both: the lead
     ran to three lines, set the row height by itself, and left the two short
     items sitting above ~40px of nothing. Full width, the lead fits on one
     line and so does every description, so the row has no slack to leave.
     This is also what the <=1040px breakpoint already did by hand, so the two
     layouts now agree, and it matches the compatibility strip lower down. */
  grid-template-columns: repeat(3, 1fr);
  gap: 22px 30px;
  align-items: start;
  box-shadow: 0 18px 48px rgba(17, 29, 53, 0.10);
}

.tp-lead {
  font-size: 18.5px;
  line-height: 1.6;
  color: var(--text-light);
  grid-column: 1 / -1;
  border-bottom: 1px solid var(--border);
  padding-bottom: 20px;
}

.tp-item { display: flex; gap: 13px; align-items: flex-start; }

.tp-ico {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  border-radius: 10px;
  background: rgba(61,165,160,0.12);
  color: var(--teal-ink);
  display: flex;
  align-items: center;
  justify-content: center;
}

.tp-ico svg { width: 19px; height: 19px; }

/* Not a heading. These are card labels sitting between the hero h1 and
   the first real section heading, and three h2s there wrecked the outline. */
.tp-title {
  font-size: 17px;
  font-weight: 650;
  color: var(--navy);
  margin-bottom: 2px;
  line-height: 1.35;
}

/* :not(.tp-title) matters. A bare `.tp-item p` is (0,1,1) and outranks the
   `.tp-title` rule above at (0,1,0), so the label size set there never
   applied and both lines rendered at the body size. */
.tp-item p:not(.tp-title) { font-size: 16px; line-height: 1.55; color: var(--text-light); }

/* ============================================================
   E: EMPATHY, wider editorial split with the stats folded in
   ============================================================ */
.empathy { padding: 84px 0 104px; }

.empathy-inner {
  max-width: none;
  grid-template-columns: 0.92fr 1.08fr;
  gap: 72px;
  align-items: center;
}

.empathy-text { text-align: left; }

.empathy-inner h2 { font-size: 44px; letter-spacing: -0.6px; margin-bottom: 18px; }

.empathy-inner .lead { max-width: 460px; margin-bottom: 38px; }

.pain-points { max-width: none; margin: 0; }

.empathy-image {
  border-radius: 18px;
  overflow: hidden;
  align-self: center;
  max-height: 560px;
}

.empathy-image img { min-height: 420px; height: 100%; object-fit: cover; }

.empathy-close {
  max-width: 780px;
  margin: 72px auto 0;
  padding-top: 0;
  border-top: none;
  font-size: 24px;
}

/* ============================================================
   S: SERVICES, SPECIAL PROJECTS, CALL BAND
   Was one long section. Split into three so the middle of the page
   has a rhythm instead of running as a single column of blocks:
   warm-white Services (four cards, the Annual Maintenance bar, then
   the compatibility strip), cream Special Projects, then a slim
   warm-white call band before the navy What to Expect.
   ============================================================ */
.services { padding: 110px 0 104px; }

.projects-band {
  padding: 96px 0 100px;
  background: var(--cream);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.projects-band .services-heading { margin-bottom: 44px; }

/* One job: the ask. The compatibility strip used to share this band and
   duplicated the CTA copy's work, which already covers the unsure reader. */
.ctaband { padding: 72px 0; }

/* Paired copy variants. The long form is the default; the phone breakpoint
   swaps them. Both are in the markup, so this survives with JS off. */
.narrow-only { display: none; }
.services-heading h2 { font-size: 40px; letter-spacing: -0.5px; }
.services-lead { max-width: 580px; margin-bottom: 56px; }

/* ---- Four service cards ---- */
.svc-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 20px;
  align-items: start;
}

/* --border-light (#EDEAE4) on a #FAFAF8 section is a 2% step and the cards
   dissolved into the page. A full --border plus a two-stage shadow lifts
   them without turning them into heavy tiles. */
.svc-card {
  border: 2px solid transparent;
  border-radius: 14px;
  background:
    linear-gradient(var(--c, #fff), var(--c, #fff)) padding-box,
    linear-gradient(135deg, var(--r, var(--teal-ink)) 0%,
        rgba(var(--rgb, 46,125,120), 0.38) 45%,
        rgba(var(--rgb, 46,125,120), 0) 80%) border-box;
  padding: 30px 26px 28px;
  height: 100%;
  position: relative;
  box-shadow: 0 1px 2px rgba(17,29,53,0.05), 0 12px 28px rgba(17,29,53,0.07);
}

/* Barely-there tints so the four read as a set without becoming
   coloured tiles. Each pairs with its own icon tone. */
.svc-card:nth-child(1) { --c: #FCFCFB; --r: var(--blue);     --rgb: 43,76,126;  --i: rgba(43,76,126,0.09); }
.svc-card:nth-child(2) { --c: #FAFAFC; --r: var(--navy);     --rgb: 27,42,74;   --i: rgba(27,42,74,0.08); }
.svc-card:nth-child(3) { --c: #FBFAF7; --r: var(--teal-ink); --rgb: 46,125,120; --i: rgba(61,165,160,0.13); }
.svc-card:nth-child(4) { --c: #FAFBFA; --r: var(--teal-ink); --rgb: 46,125,120; --i: rgba(61,165,160,0.10); }

.svc-top { text-align: center; }

.svc-icon {
  width: 58px;
  height: 58px;
  margin: 0 auto 18px;
  border-radius: 50%;
  background: var(--i);
  color: var(--r);
  display: flex;
  align-items: center;
  justify-content: center;
}

.svc-icon svg { width: 26px; height: 26px; }

.svc-card h3 {
  font-family: 'Libre Baskerville', Georgia, serif;
  font-size: 19px;
  color: var(--navy);
  line-height: 1.3;
  margin-bottom: 10px;
}

.svc-top p {
  font-size: 16px;
  line-height: 1.55;
  color: var(--text-light);
  padding-bottom: 20px;
  margin-bottom: 20px;
  border-bottom: 1px solid #D0C9BB;
}

/* ---- Shared checkmark list ---- */
/* Two-up inside the full-width cards, but only once the columns can hold a
   bullet on one line. The longest one, "Hands-free calls to family or for
   help", needs about 280px, which the body only clears past a 1180px
   viewport; below that it goes single column rather than wrapping every
   other item. Gated by min-width so it sits after the max-width blocks and
   wins on its own terms. The service grid above uses .svc-list too, hence
   the .project scope. */
.svc-list { list-style: none; }
.project .svc-list { display: grid; grid-template-columns: 1fr; column-gap: 40px; }
/* Roomier than the four service cards above: these bullets have a 286px
   panel to sit level with, and spreading them is better than pooling the
   difference as a gap. */
.project .svc-list li { padding: 13px 0; }

.svc-list li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 6px 0;
  font-size: 16px;
  line-height: 1.5;
  color: var(--text-light);
}

/* These were 28 identical 15px ticks at stroke 2.4. They carry a different
   icon per item now, which needs a little more room and a lighter stroke to
   stay legible at this size. */
.svc-list .chk {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  margin-top: 2px;
  color: var(--r, var(--teal-ink));
}

/* ---- Annual Maintenance ---- */
.annual {
  margin-top: 22px;
  background: var(--navy-deep);
  border-radius: 16px;
  padding: 30px 34px;
  display: grid;
  grid-template-columns: 1fr 1.35fr;
  gap: 40px;
  align-items: center;
  position: relative;
  overflow: hidden;
  --r: var(--teal-soft);
}

.annual::before {
  content: '';
  position: absolute;
  top: -150px;
  left: -70px;
  width: 400px;
  height: 400px;
  border-radius: 50%;
  background: rgba(61,165,160,0.26);
  filter: blur(70px);
  pointer-events: none;
}

.annual > * { position: relative; }

.annual-lead { display: flex; align-items: flex-start; gap: 18px; }

.annual-icon {
  flex: 0 0 auto;
  width: 52px;
  height: 52px;
  border-radius: 13px;
  background: rgba(61,165,160,0.16);
  color: var(--teal-soft);
  display: flex;
  align-items: center;
  justify-content: center;
}

.annual-icon svg { width: 25px; height: 25px; }

.annual h3 {
  font-family: 'Libre Baskerville', Georgia, serif;
  font-size: 21px;
  color: #fff;
  margin-bottom: 4px;
}

.annual-sub {
  font-family: 'Libre Baskerville', Georgia, serif;
  font-style: italic;
  font-size: 16px;
  color: var(--teal-soft);
  line-height: 1.5;
}

.annual-list {
  list-style: none;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px 28px;
  border-left: 1px solid rgba(255,255,255,0.14);
  padding-left: 36px;
}

.annual-list li {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  font-size: 16px;
  line-height: 1.5;
  color: rgba(255,255,255,0.84);
  padding: 4px 0;
}

.annual-list .chk { flex: 0 0 auto; width: 20px; height: 20px; margin-top: 2px; color: var(--teal-soft); }

/* ---- Two project cards with side imagery ---- */
/* One card per row. Two-up gave each photo a 214px strip and squeezed the
   text to 334px; full width lets the photo run 400px with the copy beside
   it, at the cost of about 420px of extra section height. */
.projects { display: grid; grid-template-columns: 1fr; gap: 20px; margin-top: 22px; }

.project {
  display: grid;
  grid-template-columns: 400px 1fr;
  /* Same ring as the service cards. These now sit on a cream band, where
     a plain grey border had no edge at all. */
  border: 2px solid transparent;
  background:
    linear-gradient(#fff, #fff) padding-box,
    linear-gradient(135deg, var(--r, var(--teal-ink)) 0%,
        rgba(var(--rgb, 46,125,120), 0.38) 45%,
        rgba(var(--rgb, 46,125,120), 0) 80%) border-box;
  border-radius: 16px;
  overflow: hidden;
  position: relative;
  box-shadow: 0 1px 2px rgba(17,29,53,0.05), 0 12px 28px rgba(17,29,53,0.07);
  --r: var(--teal-ink);
  --rgb: 46,125,120;
}

.project--flip { grid-template-columns: 1fr 400px; --r: var(--blue); --rgb: 43,76,126; }
.project--flip .project-media { order: 2; }

/* The picture is taken out of flow so the photo never sets the card's
   height. With it in flow, `height:100%` against an auto-height grid row is
   indefinite, so the img fell back to its own 700x1000 ratio and stretched
   the row to 343px, leaving a dead band under the last list item. Absolute
   positioning makes the row height come from the text, and the photo fills
   whatever that turns out to be. */
/* 400 wide x 286 tall is the 1.4 ratio the masters are cropped to, so the
   photo is uncropped and the card is only as tall as it needs to be. The
   360px floor it replaces left ~150px of slack the bullets could not fill,
   which is what read as dead white space. */
.project-media { overflow: hidden; position: relative; min-height: 286px; }
.project-media picture { position: absolute; inset: 0; display: block; }

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

/* Centred, because the copy runs about 280px against a 360px photo and a
   flush-top block would leave the slack pooled under the last bullet. */
.project-body {
  padding: 30px 44px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.project-head { display: flex; align-items: center; gap: 14px; margin-bottom: 18px; }

.project-icon {
  flex: 0 0 auto;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: rgba(27,42,74,0.06);
  color: var(--r);
  display: flex;
  align-items: center;
  justify-content: center;
}

.project-icon svg { width: 21px; height: 21px; }

.project h3 {
  font-family: 'Libre Baskerville', Georgia, serif;
  font-size: 20px;
  color: var(--navy);
  margin-bottom: 3px;
  line-height: 1.3;
}

.project-sub {
  font-family: 'Libre Baskerville', Georgia, serif;
  font-style: italic;
  font-size: 16px;
  color: var(--r);
  line-height: 1.45;
}

/* ---- Compatibility strip ---- */
/* Closes the Services section, directly under the Annual Maintenance bar.
   It answers "do you handle my thing?", which is a question about the four
   service categories above it, not about Special Projects. Kept white with
   a full border rather than cream: it sits on warm-white, a 2% step, and a
   cream panel there had no edge at all. */
.kitstrip {
  margin-top: 22px;
  box-shadow: 0 1px 2px rgba(17,29,53,0.05), 0 12px 28px rgba(17,29,53,0.07);
  border: 2px solid transparent;
  background:
    linear-gradient(#fff, #fff) padding-box,
    linear-gradient(135deg, var(--r, var(--teal-ink)) 0%,
        rgba(var(--rgb, 46,125,120), 0.38) 45%,
        rgba(var(--rgb, 46,125,120), 0) 80%) border-box;
  border-radius: 16px;
  position: relative;
  padding: 26px 32px 28px;
  display: grid;
  /* The label used to sit in an `auto` first column, which left it 103px of
     text and broke "What We Work With" over three lines while squeezing the
     four items to 201px. Spanning it across the top gives the items the whole
     width and lets each description settle on two lines. */
  grid-template-columns: repeat(4, 1fr);
  gap: 24px 30px;
  align-items: start;
}

.kitstrip h3 {
  font-family: 'Libre Baskerville', Georgia, serif;
  font-size: 23px;
  color: var(--navy);
  line-height: 1.25;
  grid-column: 1 / -1;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--border);
}

.kit-item { display: flex; align-items: flex-start; gap: 13px; }

.kit-ico {
  flex: 0 0 auto;
  width: 38px;
  height: 38px;
  color: var(--teal-ink);
  display: flex;
  align-items: center;
  justify-content: center;
}

.kit-ico svg { width: 26px; height: 26px; }

.kit-item h4 {
  font-size: 17px;
  font-weight: 650;
  color: var(--navy);
  margin-bottom: 4px;
}

.kit-item p { font-size: 16px; line-height: 1.55; color: var(--text-light); }

/* No top margin: this is the call band's only child, so the band's own
   padding does the spacing. */
.services-cta { margin-top: 0; }

/* ============================================================
   B: ACCENT HAIRLINE
   A 2px ring on each card's border, tinted with that card's own
   --r and fading out toward the bottom right. The four service
   cards, the two project cards, the compatibility strip and the
   trust panel all carry it.

   Chosen over corner brackets because it reads the same at any
   card shape. Brackets framed the square service cards nicely
   but landed a metre apart on the 1120px-wide strip, where they
   stopped reading as a frame at all.

   This used to be a gradient masked into a ring via
   mask-composite: exclude, layered as a ::before over a separate
   1px grey border. That technique composites two independently
   anti-aliased gradient masks along the same curve, and at a
   small radius with a thin ring the two edges don't cancel
   perfectly: a hairline of whatever sits behind the card shows
   through, worst exactly at the tightest part of the curve (the
   corner) and most visible where the ring is most opaque
   (top-left, where the gradient starts solid).

   Replaced with the standard double-background border instead:
   the border itself is transparent, filled by two background
   layers clipped to the padding-box and border-box respectively.
   No masking, so no seam is possible, and background-clip has
   been supported everywhere for long enough that no @supports
   gate or fallback is needed. The border width step from 1px to
   2px is absorbed by the global `box-sizing: border-box` reset,
   so nothing shifts.
   ============================================================ */

/* The strip has no accent of its own; navy keeps it reading as the
   neutral summary panel it is, rather than competing with the teal CTA
   directly beneath it. */
.kitstrip { --r: var(--navy); --rgb: 27,42,74; }

/* One-shot bloom as a card arrives, on the two Special Projects cards
   only. The page carries seven of these surfaces; blooming all of them
   on the way down is a light show, blooming the two feature cards is an
   accent. Gated behind .anim, which main.js only sets when the visitor
   has not asked for reduced motion. */
.anim .project.visible {
  animation: cardbloom 1.6s var(--ease-out-quart) 0.15s 1;
}

@keyframes cardbloom {
  0%   { box-shadow: 0 1px 2px rgba(17,29,53,0.05), 0 12px 28px rgba(17,29,53,0.07), 0 0 0 0 rgba(var(--rgb), 0); }
  30%  { box-shadow: 0 1px 2px rgba(17,29,53,0.05), 0 12px 28px rgba(17,29,53,0.07), 0 0 30px 6px rgba(var(--rgb), 0.30); }
  100% { box-shadow: 0 1px 2px rgba(17,29,53,0.05), 0 12px 28px rgba(17,29,53,0.07), 0 0 0 0 rgba(var(--rgb), 0); }
}


/* ============================================================
   I: IMAGE EDGES
   Treatment A of five mocked: the edge facing the copy dissolves,
   so a photo bleeds into the card or the page instead of stopping
   on a hard line. Outer edges keep their crisp rounded corner, so
   a card still reads as a card.

   Guarded by @supports because a mask that fails to composite is
   not a soft edge, it is the browser painting the entire box. The
   fallback is simply today's hard edge.

   The photo changes side twice as the layout collapses, so the
   fade direction has to follow it. Overrides live in the 940 and
   680 blocks further down:
     > 940   photo left in .project, right in .project--flip
     <= 940  .project--flip .project-media gets order:0, so both
             sit on the left and both fade right
     <= 680  cards stack, photo on top, so both fade downward
   ============================================================ */
@supports ((-webkit-mask-image: linear-gradient(#000, #000)) or (mask-image: linear-gradient(#000, #000))) {
  .project .project-media {
    -webkit-mask-image: linear-gradient(to right, #000 0%, #000 60%, transparent 100%);
            mask-image: linear-gradient(to right, #000 0%, #000 60%, transparent 100%);
  }

  .project--flip .project-media {
    -webkit-mask-image: linear-gradient(to left, #000 0%, #000 60%, transparent 100%);
            mask-image: linear-gradient(to left, #000 0%, #000 60%, transparent 100%);
  }

  /* Both standalone photos sit to the right of their copy, so both
     dissolve on the left. Shorter fade than the cards: these are wider
     and a 40% fade would eat the subject. */
  .empathy-image,
  .contact-image {
    -webkit-mask-image: linear-gradient(to left, #000 0%, #000 72%, transparent 100%);
            mask-image: linear-gradient(to left, #000 0%, #000 72%, transparent 100%);
  }
}

/* ============================================================
   X: WHAT TO EXPECT
   ============================================================ */
.expect { padding: 116px 0; }
/* styles.css scoped the serif and the muted lead to .expect-inner, which
   this rebuild replaced with .wide. Both have to be restated here. */
.expect-head { text-align: center; margin-bottom: 72px; position: relative; }

.expect-head h2 {
  font-family: 'Libre Baskerville', Georgia, serif;
  font-size: 40px;
  letter-spacing: -0.5px;
  margin-bottom: 14px;
}

.expect-head .lead {
  font-size: 20px;
  color: rgba(255,255,255,0.78);
  line-height: 1.75;
  font-weight: 400;
  /* 660, not 620. The sentence measures 638px at this size and the old cap
     broke it across two lines for the sake of 18px. It still wraps naturally
     once the container itself is narrower, which is what should happen. */
  max-width: 660px;
  margin: 0 auto;
}

.expect .wide { position: relative; }

.timeline { gap: 16px; max-width: 1000px; margin: 0 auto; }
.timeline::before { top: 36px; left: 16.66%; right: 16.66%; }
.step { padding: 0 26px; }

.step-num {
  width: 72px;
  height: 72px;
  font-size: 27px;
  margin-bottom: 32px;
  background: rgba(61, 165, 160, 0.12);
  border-color: rgba(61, 165, 160, 0.32);
}

.step h3 { font-size: 21px; margin-bottom: 12px; }

/* ============================================================
   C: CONTACT at 50/50
   ============================================================ */
.contact { padding: 110px 0; text-align: left; }

.contact-inner {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 72px;
  align-items: center;
}

.contact h2 { font-size: 42px; letter-spacing: -0.6px; margin-bottom: 16px; }

.contact .lead {
  font-size: 20px;
  max-width: 440px;
  margin: 0 0 36px;
  text-align: left;
}

.creach { display: flex; flex-direction: column; gap: 12px; max-width: 460px; }

/* Only the two links get the card treatment. The location line is not
   tappable and should not look like it is. */
.creach a {
  display: flex;
  align-items: center;
  gap: 16px;
  background: #fff;
  border: 1px solid var(--border-light);
  border-radius: 10px;
  padding: 18px 24px;
  text-decoration: none;
  transition: border-color 0.3s, box-shadow 0.3s;
}

.creach .cwhere {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 10px 24px 0;
}

.creach a:hover { border-color: var(--teal); box-shadow: 0 4px 18px rgba(27,42,74,0.07); }

.creach .ci { flex: 0 0 auto; width: 22px; height: 22px; color: var(--teal-ink); }
.creach .ci svg { width: 22px; height: 22px; display: block; }

.creach .cphone {
  font-family: 'Libre Baskerville', Georgia, serif;
  font-size: 31px;
  color: var(--navy);
  letter-spacing: -0.4px;
}

.creach-phone:hover .cphone { color: var(--teal-ink); }
.creach .ctext { font-size: 18px; color: var(--blue); }
.creach .csmall { font-size: 16px; color: var(--text-light); }

.contact .contact-family {
  margin-top: 28px;
  font-size: 16px;
  max-width: 460px;
  text-align: left;
}

/* align-self:center + a cap. Stretching to match the copy column made
   this 953px tall at 900px wide. */
.contact-image {
  border-radius: 18px;
  overflow: hidden;
  align-self: center;
  max-height: 620px;
  display: flex;
}

.contact-image picture { display: block; width: 100%; }

.contact-image img {
  width: 100%;
  height: 100%;
  min-height: 460px;
  object-fit: cover;
  display: block;
}

/* ============================================================
   F: FOOTER
   ============================================================ */
footer { padding: 70px 0 0; text-align: left; }

.footer-top {
  display: grid;
  grid-template-columns: 1.15fr 1fr 1fr;
  gap: 60px;
  padding-bottom: 48px;
}

.footer-brand img { height: 24px; width: auto; margin-bottom: 18px; opacity: 0.95; }

.footer-brand p {
  font-size: 16px;
  line-height: 1.65;
  color: rgba(255,255,255,0.68);
  max-width: 330px;
}

/* A styled label, not a heading. The document outline ends with the
   contact section's h2; a second heading in the footer added nothing
   but noise for screen-reader users navigating by headings. */
.footer-heading {
  font-family: 'Source Sans 3', 'Segoe UI', sans-serif;
  font-size: 13px;
  font-weight: 650;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--teal-soft);
  margin-bottom: 18px;
}

.footer-col ul { list-style: none; }
.footer-col li { margin-bottom: 12px; line-height: 1.5; }

.footer-col a, .footer-col span {
  font-size: 16px;
  color: rgba(255,255,255,0.78);
  text-decoration: none;
  transition: color 0.3s;
}

.footer-col a:hover { color: #fff; }

.footer-bottom {
  border-top: 1px solid rgba(255,255,255,0.09);
  padding: 22px 0 26px;
  display: flex;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
  font-size: 15px;
  color: rgba(255,255,255,0.6);
}

.footer-bottom .footer-tagline {
  font-family: 'Libre Baskerville', Georgia, serif;
  font-style: italic;
  font-size: 15px;
  color: rgba(255,255,255,0.6);
  margin: 0;
}

/* ============================================================
   RESPONSIVE
   ============================================================ */
@media (max-width: 1120px) {
  .svc-grid { grid-template-columns: 1fr 1fr; gap: 18px; }
  .annual { grid-template-columns: 1fr 1.25fr; gap: 26px 32px; }
  .kitstrip { grid-template-columns: repeat(2, 1fr); gap: 24px 30px; }
  .kitstrip h3 { grid-column: 1 / -1; max-width: none; padding-right: 0; padding-bottom: 18px;
                 border-right: none; border-bottom: 1px solid var(--border); }
}

@media (max-width: 1080px) {

  .empathy-inner { gap: 48px; }
}

@media (max-width: 1040px) {
  /* Three across squeezes each item at this width; go two-up early. The lead
     spans the full width at every size now, so it needs nothing here. */
  .trustpanel { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 940px) {
  .projects { grid-template-columns: 1fr; }
  .project, .project--flip { grid-template-columns: 0.72fr 1fr; }
  .project--flip .project-media { order: 0; }
  /* order:0 puts the flipped card's photo on the left too, so it has to
     fade the same way as its sibling. */
  .project--flip .project-media {
    -webkit-mask-image: linear-gradient(to right, #000 0%, #000 60%, transparent 100%);
            mask-image: linear-gradient(to right, #000 0%, #000 60%, transparent 100%);
  }
  /* Both of these stack under their copy at this width and run full bleed,
     so a side fade would just clip a third of the picture off. */
  .empathy-image, .contact-image {
    -webkit-mask-image: none;
            mask-image: none;
  }
  .project-body { padding: 28px; }
  .wide, .hero-inner, .trustpanel-wrap, .header-inner { padding-left: 28px; padding-right: 28px; }
  .empathy-inner { grid-template-columns: 1fr; gap: 40px; }
  .empathy-image img { min-height: 340px; }


  .projects { grid-template-columns: 1fr; }


  .contact-inner { grid-template-columns: 1fr; gap: 44px; }
  .contact-image img { min-height: 340px; }

  /* Four across is ~150px per item at this width, which wraps every
     description to four lines. */
  .kitstrip { grid-template-columns: 1fr 1fr; }

  .footer-top { grid-template-columns: 1fr 1fr; gap: 40px; }
  .footer-brand { grid-column: 1 / -1; }

}

@media (max-width: 768px) {
  section { padding: 76px 0; }

  /* The base .hero rule is unconditional and loads after styles.css, so it
     wins at every width. Both the mobile image and a flat scrim have to be
     restated here or phones get the desktop photo under a 0.30 gradient. */
  .hero {
    padding: 132px 0 150px;
    background:
      linear-gradient(180deg,
        rgba(17, 29, 53, 0.90) 0%,
        rgba(17, 29, 53, 0.86) 55%,
        rgba(17, 29, 53, 0.90) 100%),
      url('hero-mobile.jpg?v=aug26') center / cover no-repeat;
  }
  .hero h1 { font-size: 34px; }
  .hero .tagline { font-size: 13px; letter-spacing: 2.2px; }
  .hero .hero-sub { font-size: 18px; }
  .hero-actions { flex-direction: column; align-items: stretch; gap: 12px; }
  .hero-actions a { text-align: center; }

  .trustpanel-wrap { margin-top: -60px; }

  .empathy-inner h2 { font-size: 30px; }
  .empathy-close { font-size: 20px; margin-top: 48px; }

  .services-heading h2, .expect-head h2 { font-size: 29px; }
  .contact h2 { font-size: 30px; }

  .services-cta { margin-top: 48px; }

  /* The mobile rail is vertical, so it draws downward instead */
  .anim .timeline::before { transform: scaleY(0); transform-origin: center top; }
  .anim .timeline.drawn::before { transform: scaleY(1); }

  /* styles.css absolutely positions .step-num at left:0 and clears it with
     padding-left:64px on .step. The base rules above set padding to 0 26px
     and grew the badge to 72px, so the headings printed across the circles.
     88px = 72px badge + 16px gutter. */
  .timeline { gap: 36px; max-width: 340px; margin: 0 auto; }
  .step { padding: 0 0 0 88px; }
  .step-num { width: 64px; height: 64px; font-size: 24px; margin-bottom: 0; }
  .timeline::before { left: 31px; }
}

/* ============================================================
   M: PHONES
   Not a separate mobile site, one responsive page tuned for it:
   fewer decorative photos, a shorter services lead, the service
   card heads turned sideways so four of them do not run to
   2500px, and a persistent call button.
   ============================================================ */
@media (max-width: 680px) {
  .wide-only { display: none; }
  .narrow-only { display: inline; }

  /* Icon above a centred title costs ~76px a card, and there are four of
     them stacked here. Turning the head sideways reads the same and saves
     ~300px. flex-wrap does it without touching the markup: the icon and h3
     share row one, and flex-basis:100% pushes the subtitle to row two. */
  .svc-top {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    text-align: left;
  }
  .svc-icon { width: 46px; height: 46px; margin: 0 14px 0 0; }
  .svc-icon svg { width: 22px; height: 22px; }
  .svc-card h3 { flex: 1; margin-bottom: 0; }
  .svc-top p { flex-basis: 100%; margin: 14px 0 16px; padding-bottom: 16px; }
  .svc-card { padding: 24px 22px 22px; }

  /* Decoration, and it sits between the reader and the phone number. */
  .contact-image { display: none; }
  .contact-inner { gap: 0; }

  .project-media { min-height: 180px; }
  /* 13px was to fill a 286px photo panel the cards no longer sit beside. */
  .project .svc-list li { padding: 7px 0; }
  .projects-band { padding: 64px 0 68px; }
  .projects-band .services-heading { margin-bottom: 28px; }
  .ctaband { padding: 52px 0; }

  /* Clear the fixed call button so it never covers the footer. */
  body { padding-bottom: 74px; }


  .svc-grid { grid-template-columns: 1fr; }
  .annual { grid-template-columns: 1fr; padding: 26px 24px; }
  .annual-list { grid-template-columns: 1fr; border-left: none; padding-left: 0;
                 padding-top: 20px; border-top: 1px solid rgba(255,255,255,0.14); }
  .project, .project--flip { grid-template-columns: 1fr; }
  .project-body { padding: 26px 24px; }
  /* min-height moved off the img with the absolute-fill change above, so
     the stacked photo band has to be sized on .project-media instead. */
  .project-media { min-height: 220px; }
  /* Photo sits above the copy now, so the dissolve runs downward. */
  .project .project-media,
  .project--flip .project-media {
    -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 66%, transparent 100%);
            mask-image: linear-gradient(to bottom, #000 0%, #000 66%, transparent 100%);
  }
  .kitstrip { grid-template-columns: 1fr; padding: 24px 22px; }
  .wide, .hero-inner, .trustpanel-wrap, .header-inner { padding-left: 22px; padding-right: 22px; }

  .trustpanel { grid-template-columns: 1fr; padding: 24px 24px; }


  .creach .cphone { font-size: 26px; }

  .footer-top { grid-template-columns: 1fr; gap: 34px; }
  .footer-bottom { flex-direction: column; gap: 8px; }
}

/* ============================================================
   REDUCED MOTION
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .anim .reveal,
  .anim .reveal.visible {
    opacity: 1;
    transform: none;
    filter: none;
    transition: none;
  }

  .anim .band-icon svg > *,
  .anim .project-icon svg > *,
    .anim .tp-ico svg > * { stroke-dashoffset: 0; transition: none; }

  .anim .timeline::before,
  .anim .timeline.drawn::before { transform: none; transition: none; }

  .anim .step .step-num { opacity: 1; }
  .anim .step.visible .step-num { animation: none; }

  .hero::before,
  .hero::after { animation: none; opacity: 1; }
}

@media (min-width: 1180px) {
  .project .svc-list { grid-template-columns: 1fr 1fr; }
}

/* ---- Persistent call button, phones only ----
   The header phone pill scrolls away with the hero, and every conversion on
   this site is a phone call. Remove this rule and the .callbar element in
   index.html together.

   Was unconditionally visible from first paint, with nothing in main.js
   controlling it. Two bugs came from that: it doubled up with the hero's
   own Call button on arrival, and its height visibly grew shortly after
   load or mid-scroll on iOS, because env(safe-area-inset-bottom) is a
   documented case of reporting 0 on first paint and updating once layout
   settles -- anything sized off it grows right after you land on the page
   or during the viewport changes a scroll-driven address-bar hide/show
   causes. Both are fixed below: main.js now toggles .callbar-visible once
   the hero has scrolled fully past, and the toggle only ever changes
   opacity/transform, never anything that touches the box's own height, so
   even if the safe-area inset does change late, it can't read as a jump
   in visible content. min-height keeps the box a fixed size regardless.

   Fails open without JS: the base rule below is fully visible, and it
   takes html.js to install the hidden starting state, so a reader with
   JS blocked always has the phone number. */
/* display:none is the real gate, unconditionally, at every width. Everything
   fade-related lives ONLY inside the mobile breakpoint below it, so there is
   no path -- JS state, scroll position, viewport width -- that can make this
   render outside a phone. An earlier version of this rule set opacity here
   at the top level with no width scoping, which meant that once main.js
   added .callbar-visible on scroll, a desktop visitor scrolling down would
   fade in a stray inline "Call" link at the very bottom of the page, below
   the footer, in normal document flow (opacity was overriding visibility,
   but nothing was left to keep it out of layout and off desktop). */
.callbar { display: none; }

@media (max-width: 680px) {
  /* The min-height floor from the last pass stopped the bar shrinking but
     not growing, and it turns out env(safe-area-inset-bottom) does not just
     read once: Safari updates it live as its own chrome (the URL bar)
     hides and reappears, and reappearing is exactly what scrolling up from
     the bottom of the page triggers. Any padding built from that value
     grows the instant it does, which is the turquoise box "expanding".

     Fixed by decoupling the two things that were tangled into one box.
     .callbar itself is now a fixed 60px pill -- the phone icon and number,
     never resized by anything -- positioned by `bottom: env(...)` so it
     sits a constant distance above the true edge. The env() value can
     still change live, but now that only slides the whole rigid pill up
     or down a few px; nothing about its own size changes, so there is
     nothing left for the eye to read as growth.

     The ::after pseudo-element carries the color the rest of the way down
     to the true edge, so the turquoise still bleeds under the home
     indicator the way a native app's would. It is a plain rectangle with
     no content in it, so it can resize freely below the pill without
     anything visibly distorting. */
  .callbar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: env(safe-area-inset-bottom, 0px);
    z-index: 60;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    height: 60px;
    padding: 0 20px;
    background: var(--teal-ink);
    color: #fff;
    font-size: 18px;
    font-weight: 600;
    letter-spacing: 0.2px;
    text-decoration: none;
    box-shadow: 0 -6px 20px rgba(17, 29, 53, 0.18);
    /* No-JS fallback: fully visible and static. html.js below overrides
       this into the fade-in-on-scroll behaviour once JS confirms it can
       run the toggle that makes hiding it meaningful. */
    opacity: 1;
    transform: translateY(0);
    /* Belt and suspenders against the same symptom from a second,
       unrelated cause: iOS Safari is documented to visibly glitch
       position:fixed elements -- jump, stretch, momentarily resize --
       during a scroll-driven toolbar transition, independent of any CSS
       value on the element itself, because the main thread ends up
       relaying it out mid-transition instead of the compositor just
       repositioning an existing layer. Forcing this onto its own
       compositor layer ahead of time is the standard mitigation. */
    will-change: transform;
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
  }
  .callbar::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    height: env(safe-area-inset-bottom, 0px);
    background: inherit;
  }
  .callbar svg { width: 20px; height: 20px; }

  html.js .callbar {
    opacity: 0;
    transform: translateY(14px);
    pointer-events: none;
  }
  html.js .callbar.callbar-visible {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
  }
}

@media (max-width: 680px) and (prefers-reduced-motion: no-preference) {
  /* `bottom` transitions too: if the safe-area value does change live, the
     rigid pill eases to its new offset instead of snapping. */
  .callbar { transition: background 0.3s, opacity 0.3s ease, transform 0.3s ease, bottom 0.3s ease; }
}
