/* ============================================================
   Ian Sheldon, redesign prototype
   Personal web designer/developer/consultant site.
   Inter Tight for headings + mobile nav, Open Sans for body copy
   and desktop nav. Off-blacks, white, grays. Consistent radii.
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap');

/* Inter Tight's comma/apostrophe/quote glyphs read as flat ticks rather than
   a curl. Onest's are properly curled, so this face is used for punctuation
   only, layered in front of Inter Tight by unicode-range. Pinned to Onest's
   own 800 weight (not the variable file) so the curl reads as bold and
   deliberate rather than a thin mark borrowed from a lighter cut.

   The two ranges below must not overlap. They did in an earlier version
   (Inter Tight's range included the same punctuation codepoints as a
   subset), and a browser is free to resolve that overlap however it likes -
   here it silently chose Inter Tight for the comma every time, so the Onest
   face never even loaded. Carving the punctuation codepoints out of Inter
   Tight's range makes the choice unambiguous instead of hoping the browser
   guesses the way this file is ordered. */
@font-face{
  font-family:'Heading Sans';
  font-style:normal;
  font-weight:100 900;
  font-display:swap;
  src:url('https://fonts.gstatic.com/s/onest/v11/gNMZW3F-SZuj7zOT0IfSjTS16cPhdRiptRtN.woff2') format('woff2');
  unicode-range:U+0027,U+002C,U+2018,U+2019,U+201C,U+201D;
}
@font-face{
  font-family:'Heading Sans';
  font-style:normal;
  font-weight:100 900;
  font-display:swap;
  src:url('https://fonts.gstatic.com/s/intertight/v9/NGSwv5HMAFg6IuGlBNMjxLsH8ag.woff2') format('woff2');
  unicode-range:U+0000-0026, U+0028-002B, U+002D-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-2017, U+201A-201B, U+201E-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root{
  /* ---- color ---- */
  --white:      #ffffff;
  --paper:      #fbfbfa;
  --ink:        #17171a;
  /* Grey-blue instead of neutral grey - a cool, slightly slate cast on all
     the secondary text/border tones rather than true neutral. --ink, --white
     and --paper are untouched. */
  --ink-soft:   #383c46;
  --gray-700:   #52565f;
  --gray-500:   #82868f;
  --gray-400:   #a2a7b0;
  --gray-300:   #cfd3da;
  --gray-200:   #e3e6ec;
  --gray-100:   #eff1f5;
  --line:       rgba(23,23,26,.10);
  --line-soft:  rgba(23,23,26,.06);

  /* ---- type ---- */
  --font-ui:      'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-heading: 'Heading Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-nav:     var(--font-ui);

  /* ---- radius: one consistent scale everywhere ---- */
  --r-sm:   10px;
  --r-md:   18px;
  --r-lg:   28px;
  --r-xl:   36px;
  --r-pill: 999px;

  /* ---- space ---- */
  --sp-1: 8px;
  --sp-2: 12px;
  --sp-3: 16px;
  --sp-4: 24px;
  --sp-5: 32px;
  --sp-6: 48px;
  --sp-7: 64px;
  --sp-8: 96px;
  --sp-9: 132px;

  --container: 1160px;
  --shadow-sm: 0 1px 2px rgba(23,23,26,.05), 0 1px 1px rgba(23,23,26,.04);
  --shadow-md: 0 12px 32px -8px rgba(23,23,26,.14);
  --shadow-lg: 0 24px 64px -16px rgba(23,23,26,.18);

  --ease: cubic-bezier(.16,.84,.36,1);

  /* the angled top edge on .contact, matching the live site's footer cut */
  --cutA: 7vw;
}

/* ---------------------------------------------------------- reset */
*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
html,body{margin:0;padding:0}
body{
  background:var(--paper);
  color:var(--ink);
  font-family:var(--font-ui);
  font-size:16px;
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
}
img{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
ul{list-style:none;margin:0;padding:0}
button{font:inherit;background:none;border:0;color:inherit;cursor:pointer}
h1,h2,h3,h4,p{margin:0}
h1,h2,h3,h4{font-family:var(--font-heading);font-weight:700;letter-spacing:-.008em;line-height:1.08;color:var(--ink)}
h2{line-height:1}
input,textarea,select{font:inherit;color:inherit}

.wrap{max-width:var(--container);margin:0 auto;padding:0 var(--sp-4)}
@media (min-width:900px){ .wrap{padding:0 var(--sp-6)} }

/* Bottom padding is a notch tighter than top on every section, so the gap
   between any two adjacent sections reads the same everywhere - equal
   top+bottom padding here made back-to-back sections feel like double
   the empty space of what's above each individual heading. */
section{padding:var(--sp-8) 0 var(--sp-6)}
@media (max-width:760px){ section{padding:var(--sp-7) 0 var(--sp-5)} }

.eyebrow{
  display:inline-block;
  font-family:var(--font-ui);
  font-weight:700;
  font-size:12.5px;
  letter-spacing:.14em;
  text-transform:uppercase;
  color:var(--gray-500);
  margin:0 0 var(--sp-2);
}

/* buttons -------------------------------------------------- */
.btn{
  display:inline-flex;
  align-items:center;
  gap:8px;
  padding:18px 34px;
  border-radius:var(--r-pill);
  font-family:var(--font-ui);
  font-weight:500;
  font-size:11.5px;
  letter-spacing:.05em;
  text-transform:uppercase;
  line-height:1;
  white-space:nowrap;
}
.btn--primary{background:var(--ink);color:var(--white)}
.btn--ghost{background:transparent;color:var(--ink);box-shadow:inset 0 0 0 1.5px var(--line)}
.btn--sm{padding:13px 24px;font-size:10px}

/* ---------------------------------------------------------- nav */
/* The bar itself runs edge to edge - full width, so scrolled content never
   peeks out on either side of it - while .nav__inner below constrains just
   its contents (logo, links, mail, toggle) to the same width as everything
   else on the page. */
.nav{
  position:fixed;top:0;left:0;right:0;z-index:100;
  font-family:var(--font-nav);
  background:var(--paper);
  border-bottom:1px solid transparent;
  transition:border-color .3s var(--ease), background-color .3s var(--ease), transform .35s var(--ease);
}
.nav.is-scrolled{border-bottom-color:var(--line)}
/* Hidden by scrolling down, and js/main.js only ever adds this past the
   nav's own height - see the note there for the full up/down/pin logic. */
.nav.is-hidden{transform:translateY(-100%)}
/* When the mobile menu opens it's meant to read as one solid black screen,
   but the nav bar is fixed above it (higher z-index, so its toggle button
   stays clickable to close the menu) with its own light, blurred background -
   left alone that shows as a pale strip across the top instead of the menu
   going edge to edge. Matching the bar's colors to the menu here is what
   makes the two read as a single surface. */
.nav.menu-open{background:var(--ink);border-bottom-color:transparent;color:var(--white)}
.nav.menu-open .nav__toggle{color:var(--white)}

.nav__inner{
  max-width:var(--container);margin:0 auto;
  display:flex;align-items:center;justify-content:space-between;
  padding:20px var(--sp-4);
}
@media (min-width:900px){ .nav__inner{padding:22px var(--sp-6)} }

.nav__mark{
  display:flex;align-items:center;gap:10px;
  font-weight:600;font-size:12.5px;
  text-transform:uppercase;letter-spacing:.05em;
}
.nav__markbadge{
  width:32px;height:32px;border-radius:var(--r-sm);
  background:var(--ink);color:var(--white);
  display:flex;align-items:center;justify-content:center;
  font-family:var(--font-heading);font-weight:700;font-size:13px;
  flex:none;
}

/* No box around it - a boxed icon here read as a generic, off-the-shelf
   navbar toggle rather than something drawn for this site. Just the icon,
   sized and weighted to hold its own against the wordmark next to it.
   Visible at every width now - the inline link list and the email address
   that used to sit here on desktop were dropped in favour of one nav
   treatment everywhere: wordmark, toggle, the same full-screen menu either
   way. Fewer competing things in the bar. */
.nav__toggle{
  display:flex;align-items:center;justify-content:center;
  width:40px;height:40px;
  color:var(--ink);
}
.nav__toggle svg{width:26px;height:26px}
.nav__toggle .icon-close{display:none}
.nav.menu-open .icon-menu{display:none}
.nav.menu-open .icon-close{display:block}

.nav__space{height:78px}

/* full-screen menu - the site's only nav now, at every width, not just
   mobile. align-items:center plus the max-width on .menu__nav keep it from
   stretching hairline-thin across a wide desktop screen: the whole thing
   stays one centered column regardless of viewport. ---------------------- */
.menu{
  position:fixed;inset:0;z-index:99;
  background:var(--ink);color:var(--white);
  display:flex;flex-direction:column;align-items:center;justify-content:space-between;
  padding:118px var(--sp-4) var(--sp-6);
  font-family:var(--font-heading);
  opacity:0;visibility:hidden;transform:translateY(-8px);
  transition:opacity .35s var(--ease), transform .35s var(--ease), visibility 0s linear .35s;
}
.menu.is-open{opacity:1;visibility:visible;transform:translateY(0);transition:opacity .35s var(--ease), transform .35s var(--ease)}
.menu__nav{width:100%;max-width:480px}
.menu__nav a{
  display:block;font-size:clamp(36px,11.5vw,50px);font-weight:700;
  padding:14px 0;letter-spacing:-.01em;text-align:center;
  border-bottom:1px solid rgba(255,255,255,.14);
}
.menu__foot{display:flex;flex-direction:column;align-items:center;gap:16px;text-align:center;font-family:var(--font-heading);font-size:15px;color:rgba(255,255,255,.6)}
.menu__foot a{color:var(--white);font-weight:600}
.menu__mail{
  display:inline-flex;align-items:center;gap:8px;
  padding:11px 22px;
  border-radius:var(--r-pill);
  background:rgba(255,255,255,.08);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.16);
}
.menu__mail svg{width:16px;height:16px;flex:none}

/* ---------------------------------------------------------- hero */
.hero{
  min-height:calc(100svh - 78px);
  display:flex;
  flex-direction:column;
  justify-content:center;
  /* extra bottom padding is clearance for the pinned .hero__meta bar below,
     not spacing for anything in normal flow - see the note on .hero__meta */
  padding:var(--sp-7) 0 calc(var(--sp-7) + 110px);
  position:relative;
  overflow:hidden;
}
@media (max-width:760px){ .hero{padding:var(--sp-5) 0 calc(var(--sp-5) + 100px)} }

.hero__inner{
  /* width:100% matters here in a way it wouldn't outside a flex context:
     .hero is display:flex (for the vertical centering below), and a flex
     item with auto side-margins sizes to its content instead of stretching
     to fill - so without this, hero__inner quietly shrank to its widest
     line's width and centered *that*, landing its left edge ~50px right of
     every .wrap elsewhere on the page instead of matching it. */
  width:100%;max-width:var(--container);margin:0 auto;padding:0 var(--sp-4);
  display:flex;flex-direction:column;align-items:flex-start;gap:var(--sp-5);
}
@media (min-width:900px){ .hero__inner{padding:0 var(--sp-6)} }

/* Sits above the headline at every width now - smaller here since desktop
   has the headline's own left edge to line up against, sized back up in the
   mobile block below once everything centers instead. background-color is a
   fallback in case the image is ever missing or slow, not a designed
   placeholder look. */
.hero__photo{
  display:block;
  width:110px;height:110px;border-radius:50%;flex:none;
  overflow:hidden;
  background:var(--gray-200);
  box-shadow:var(--shadow-sm);
}
/* object-position biased well above center: it's a tall portrait crop with
   the head in the top third and crossed arms filling the bottom two thirds,
   so a plain center crop on an 88px circle would land on the chest, not
   the face. */
.hero__photo img{width:100%;height:100%;object-fit:cover;object-position:50% 18%;display:block}

.hero__title{
  font-weight:800;
  font-size:clamp(46px,6.2vw,74px);
  line-height:.98;
  letter-spacing:-.022em;
  max-width:19ch;
}
.hero__title .hero__title--muted{
  font-style:normal;
  font-weight:800;
  color:var(--gray-400);
}

.hero__sub{
  font-family:var(--font-ui);
  font-size:clamp(14.5px,1.6vw,17px);
  color:var(--gray-700);
  max-width:46ch;
  line-height:1.6;
}

.hero__actions{display:flex;flex-wrap:wrap;gap:14px;margin-top:var(--sp-1)}

@media (max-width:760px){
  .hero__inner{align-items:center;text-align:center}
  .hero__title{font-size:clamp(40px,6.2vw,74px)}
  .hero__sub{max-width:38ch}
  .hero__actions{justify-content:center}
  .hero__photo{width:132px;height:132px}
}

/* Pinned to the bottom of the viewport, independent of the flow above, so
   the headline centers in the full hero height regardless of this bar's
   height. It sizes to content and reserves nothing in .hero's own padding -
   see the extra bottom padding on .hero__inner below, which exists purely
   as clearance so a long wrapped headline never runs under this bar. */
/* js/main.js toggles is-inverted the moment the page scrolls away from the
   very top (and removes it the moment it's back at 0) - so the solid
   black bar fades to blend into the white page behind it as soon as you
   start scrolling, and fades back to black the instant you're back at
   the top. border-color is reserved at 0-width transparent even at rest
   so the inverted state's hairline doesn't shift layout by appearing. */
.hero__meta{
  position:absolute;left:0;right:0;bottom:0;
  display:flex;align-items:center;gap:var(--sp-2);
  background:var(--ink);
  border-top:1px solid transparent;border-bottom:1px solid transparent;
  padding:var(--sp-4) max(var(--sp-4), calc((100vw - var(--container)) / 2 + var(--sp-4))) max(var(--sp-4), env(safe-area-inset-bottom));
  transition:background-color .5s var(--ease), border-color .5s var(--ease);
}
.hero__meta.is-inverted{background:var(--white);border-color:var(--line)}
@media (min-width:900px){
  .hero__meta{
    padding-left:max(var(--sp-6), calc((100vw - var(--container)) / 2 + var(--sp-6)));
    padding-right:max(var(--sp-6), calc((100vw - var(--container)) / 2 + var(--sp-6)));
  }
}

/* The 3 items used to be direct flex children of .hero__meta itself; now
   they're wrapped in their own track so the prev/next buttons (mobile
   only, see below) can sit either side of it as ordinary flex items
   rather than needing to be absolutely positioned over a scrolling
   element - a child with position:absolute still scrolls away with an
   overflow:auto ancestor if that ancestor is its containing block, so
   the buttons have to live outside the thing that scrolls, not on top
   of it. */
.hero__metatrack{display:flex;justify-content:space-between;gap:var(--sp-4);flex:1;min-width:0}

/* Hidden on desktop - three items fit without needing a swipe there at
   all. .hero__meta itself fades between the dark and inverted colour
   states, so the buttons follow the same transition and palette. */
.hero__navbtn{
  display:none;flex:none;align-items:center;justify-content:center;
  width:32px;height:32px;
  border:0;border-radius:var(--r-pill);
  background:rgba(255,255,255,.14);
  color:var(--white);
  cursor:pointer;
  -webkit-user-select:none;user-select:none;
  -webkit-tap-highlight-color:transparent;
  transition:background-color .5s var(--ease), color .5s var(--ease), opacity .3s var(--ease);
}
.hero__navbtn svg{width:15px;height:15px}
.hero__navbtn svg path{fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.hero__navbtn--next svg{transform:scaleX(-1)}
.hero__navbtn:disabled{opacity:.35;cursor:default;pointer-events:none}
.hero__meta.is-inverted .hero__navbtn{background:var(--gray-100);color:var(--ink)}

.hero__metaitem{display:flex;flex-direction:column;align-items:center;gap:4px;flex:1;text-align:center}
.hero__metaitem b{font-family:var(--font-heading);font-size:15px;color:var(--white);transition:color .5s var(--ease)}
.hero__metaitem span{font-size:13.5px;color:rgba(255,255,255,.5);transition:color .5s var(--ease)}
.hero__meta.is-inverted .hero__metaitem b{color:var(--ink)}
.hero__meta.is-inverted .hero__metaitem span{color:var(--gray-500)}

/* The clone slide only exists to make the mobile autoplay loop seamless
   (see index.html and js/main.js) - aria-hidden keeps it out of the a11y
   tree, but that alone does nothing visually, so outside the carousel
   breakpoint it has to be hidden explicitly or it shows up as a genuine
   fourth, repeated block. */
[data-clone]{display:none}

/* Three stats don't need a swipe interaction at desktop widths, but wrapping
   them wastes the one thing a phone doesn't have, vertical space, so below
   760px they become a snap-scroll strip instead of wrapping to extra rows.
   justify-content:flex-start overrides the space-between set above - with
   space-between still active the items fought the snap points for their
   starting position instead of the first one sitting flush at scrollLeft 0,
   which is what read as "a mess". js/main.js drives the autoplay/loop and
   the prev/next buttons alike. */
@media (max-width:760px){
  .hero__navbtn{display:flex}
  .hero__metatrack{
    flex-wrap:nowrap;
    justify-content:flex-start;
    gap:0;
    /* Padding removed from the track and moved onto each item below. With
       it here, every item was 100% of a box already narrowed by this
       padding - narrower than the track itself - and since a scroll
       container only keeps its LEADING inline padding while at rest
       (scrolling to any slide past the first consumes it), every slide but
       the first ended up flush against the left edge with the shortfall
       dumped entirely on the right: not centered, not even close. Zeroing
       it here and giving each item the same amount as its own padding
       means every slide is genuinely the full track width, so its content
       centers the same way regardless of scroll position. */
    padding-left:0;padding-right:0;
    overflow-x:auto;
    scroll-snap-type:x mandatory;
    -webkit-overflow-scrolling:touch;
    scrollbar-width:none;
  }
  .hero__metatrack::-webkit-scrollbar{display:none}
  .hero__metaitem{flex:0 0 100%;padding:0 var(--sp-4);scroll-snap-align:start}
  [data-clone]{display:flex}
}

/* ---------------------------------------------------------- pitch */
.pitch__top{
  display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:var(--sp-4);
  margin-bottom:var(--sp-6);
}
.pitch__heading{font-size:clamp(34px,4.6vw,54px);letter-spacing:-.02em;max-width:14ch}
.pitch__from{
  font-family:var(--font-ui);font-weight:400;font-size:12.5px;color:var(--gray-500);
  text-transform:uppercase;letter-spacing:.06em;
  flex:none;
}

/* "Centered on mobile" in the hero covered the hamburger overlay, but each
   section's own heading (this one and work/care/says/friends below) was
   still left-aligned there - a separate thing entirely, missed the first
   time round. Body copy (care__text, friends__what, …) stays left-aligned
   on purpose: centering reads fine for a short eyebrow+heading pair, not
   for multi-sentence paragraphs. */
@media (max-width:760px){
  .pitch__top{flex-direction:column;align-items:center;text-align:center}
  .pitch__heading{max-width:none;margin:0 auto}
}

.pitch__grid{display:grid;grid-template-columns:1fr;gap:var(--sp-3)}
@media (min-width:760px){ .pitch__grid{grid-template-columns:repeat(3,1fr)} }

.pitch__card{
  background:var(--gray-100);
  border-radius:var(--r-xl);
  padding:var(--sp-5);
  display:flex;flex-direction:column;gap:var(--sp-2);
  transition:background-color .6s var(--ease);
  cursor:pointer;
}
/* A step lighter than --gray-100, not all the way to white - the jump to
   pure white read as an instant flash rather than a fade, since it was
   both a bigger colour step and a distinct color entirely off the gray
   scale, not a shade of it. */
.pitch__card:hover{background:#f7f8fa}
.pitch__type{font-family:var(--font-heading);font-weight:700;font-size:21px;letter-spacing:-.008em}
.pitch__what{color:var(--gray-700);font-size:13.5px;line-height:1.65}

/* ---------------------------------------------------------- work */
.work__top{margin-bottom:var(--sp-6);max-width:56ch}
.work__heading{font-size:clamp(34px,4.6vw,54px);letter-spacing:-.02em;margin-bottom:var(--sp-2)}
.work__intro{color:var(--gray-700);font-size:14px}
@media (max-width:760px){ .work__top{text-align:center;margin-left:auto;margin-right:auto} }

/* Apple-style bleed carousel: deliberately outside .wrap in the HTML so its
   box spans the full viewport, not the content column's max-width - that's
   what lets it scroll all the way to the true right edge of the screen
   instead of stopping at 1160px. padding-left uses the same max()-based
   formula .wrap resolves to internally, so the first slide still lands
   exactly on the content edge above it despite the track itself being
   full width. Below 900px it flips to the --sp-4 gutter, matching .wrap. */
.work__track{
  display:flex;
  gap:var(--sp-4);
  list-style:none;margin:0;
  overflow-x:auto;
  scroll-snap-type:x mandatory;
  -webkit-overflow-scrolling:touch;
  scrollbar-width:none;
  cursor:grab;
  padding:0 var(--sp-4) var(--sp-2);
  padding-left:max(var(--sp-4), calc((100vw - var(--container)) / 2 + var(--sp-4)));
  /* Matches padding-left exactly, and it has to: a snap container is free to
     scroll straight past its own leading padding to bring the first snap
     point flush with the edge, which is exactly what Chrome did here - the
     first slide rendered flush-left, not inset, despite the padding above
     being correct. scroll-padding tells the snap algorithm itself about the
     inset, rather than leaving it to guess from ordinary box padding. */
  scroll-padding-left:max(var(--sp-4), calc((100vw - var(--container)) / 2 + var(--sp-4)));
}
@media (min-width:900px){
  .work__track{
    padding-left:max(var(--sp-6), calc((100vw - var(--container)) / 2 + var(--sp-6)));
    scroll-padding-left:max(var(--sp-6), calc((100vw - var(--container)) / 2 + var(--sp-6)));
  }
}
.work__track::-webkit-scrollbar{display:none}
/* js/main.js adds this while a mouse drag is in progress - scroll-snap
   fighting a drag mid-motion is what made early versions of this feel
   sticky, so snapping is suspended until the pointer lets go. */
.work__track.is-dragging{cursor:grabbing;scroll-snap-type:none}

.work__slide{flex:0 0 clamp(280px,55vw,620px);scroll-snap-align:start}
.work__slide img{
  width:100%;aspect-ratio:4/3;object-fit:cover;
  border-radius:var(--r-xl);background:var(--gray-100);
  box-shadow:var(--shadow-sm);
  -webkit-user-drag:none;user-select:none;
}
.work__slide--text{
  aspect-ratio:4/3;border-radius:var(--r-xl);
  background:linear-gradient(155deg,var(--ink),#2c2c31);
  display:flex;align-items:center;justify-content:center;
  padding:var(--sp-5);text-align:center;
  color:rgba(255,255,255,.75);
  font-family:var(--font-heading);font-weight:700;font-size:19px;letter-spacing:-.01em;
}

/* ---------------------------------------------------------- says (testimonials) */
.says__heading{font-size:clamp(34px,4.6vw,54px);letter-spacing:-.02em;margin-bottom:var(--sp-5)}
@media (max-width:760px){
  .says .eyebrow{display:block;text-align:center}
  .says__heading{text-align:center}
}

.says__track{
  display:flex;
  gap:var(--sp-3);
  list-style:none;margin:0;
  overflow-x:auto;
  scroll-snap-type:x mandatory;
  -webkit-overflow-scrolling:touch;
  scrollbar-width:none;
  cursor:grab;
  padding:0 var(--sp-4) var(--sp-2);
  padding-left:max(var(--sp-4), calc((100vw - var(--container)) / 2 + var(--sp-4)));
  scroll-padding-left:max(var(--sp-4), calc((100vw - var(--container)) / 2 + var(--sp-4)));
}
@media (min-width:900px){
  .says__track{
    padding-left:max(var(--sp-6), calc((100vw - var(--container)) / 2 + var(--sp-6)));
    scroll-padding-left:max(var(--sp-6), calc((100vw - var(--container)) / 2 + var(--sp-6)));
  }
}
.says__track::-webkit-scrollbar{display:none}
.says__track.is-dragging{cursor:grabbing;scroll-snap-type:none}

.says__nav{margin-top:var(--sp-2)}

/* Close to .work__slide's mobile width (below 760px) but a touch wider -
   testimonial text reads better with a little more room than the work
   carousel's images need. Above 760px they widen into a "2 cards + a peek
   of the 3rd" desktop layout instead - see the media query below for why
   that uses different numbers. */
.says__card{
  flex:0 0 clamp(300px,58vw,620px);scroll-snap-align:start;
  background:var(--gray-100);
  border-radius:var(--r-xl);
  padding:var(--sp-5);
  display:flex;flex-direction:column;gap:var(--sp-3);
  transition:background-color .6s var(--ease);
  cursor:pointer;
}
/* The desktop card width used to be clamp(260px,42vw,380px). Below the
   point where 42vw drops under 260px, the clamp pins the card to a FIXED
   260px while the viewport keeps shrinking - and somewhere in that flat
   zone (around 540-590px here) the leftover space after 2 cards lands
   almost exactly on zero, so the next card's edge doesn't peek in at all
   and it reads as a static 2-up grid instead of a carousel. This range
   (268px min, 39vw, 358px max) doesn't have that problem: the point
   where the 39vw term would drop below 268px (vw < ~687px) sits below
   this rule's own 760px floor, so the flat/minimum branch of the clamp
   never actually engages here - card width is always the scaling 39vw
   term in the widths this rule covers, so the peek is never a coincidence
   of a stuck fixed width, it's an actual proportion of the viewport. */
@media (min-width:760px){
  .says__card{flex:0 0 clamp(268px,39vw,358px)}
}
.says__card:hover{background:#f7f8fa}
.says__stars{display:flex;gap:4px}
.says__star{width:16px;height:16px;fill:var(--ink)}
.says__quote{font-family:var(--font-heading);font-weight:400;font-size:16.5px;line-height:1.55;letter-spacing:-.01em;color:var(--ink)}
.says__who{display:flex;align-items:center;gap:12px;margin-top:auto}
.says__face{
  width:38px;height:38px;border-radius:var(--r-pill);flex:none;
  background:var(--gray-200);
  display:flex;align-items:center;justify-content:center;
  font-family:var(--font-heading);font-weight:700;font-size:13px;
  color:var(--ink);
}
.says__id{display:flex;flex-direction:column}
.says__name{font-weight:700;font-size:14px;color:var(--ink)}
.says__co{font-size:13px;color:var(--gray-500)}

/* ------------------------------------------- carousel nav (chevrons) */
/* Shared by the work and testimonials bleed-carousels. Hidden on mobile:
   touch already scrolls those tracks natively, and there's no room for
   a control pair under a full-bleed track that narrow. */
.carousel__nav{display:flex;gap:var(--sp-2);justify-content:flex-end;margin-top:var(--sp-3)}
.carousel__btn{
  width:44px;height:44px;flex:none;
  border-radius:var(--r-pill);
  border:1px solid var(--line);
  background:var(--white);
  color:var(--ink);
  display:flex;align-items:center;justify-content:center;
  cursor:pointer;
  -webkit-user-select:none;user-select:none;
  -webkit-tap-highlight-color:transparent;
  transition:background-color .3s var(--ease),opacity .3s var(--ease);
}
.carousel__btn svg{width:18px;height:18px}
.carousel__btn--next svg{transform:scaleX(-1)}
.carousel__btn:hover:not(:disabled){background:var(--gray-100)}
.carousel__btn:disabled{opacity:.3;cursor:default;pointer-events:none}

/* ---------------------------------------------------------- care */
.care__top{max-width:60ch;margin-bottom:var(--sp-6)}
.care__heading{font-size:clamp(34px,4.6vw,54px);letter-spacing:-.02em;margin-bottom:var(--sp-2)}
.care__strap{font-family:var(--font-heading);font-weight:700;color:var(--gray-500);margin-bottom:var(--sp-3);font-size:16px}
.care__text{color:var(--gray-700);font-size:15.5px;line-height:1.75}
@media (max-width:760px){
  .care__heading,.care__strap{text-align:center}
}

/* term pill */
.terms{display:flex;flex-direction:column;gap:var(--sp-5)}
.terms__pill{
  position:relative;display:inline-flex;align-self:flex-start;
  background:var(--gray-100);border-radius:var(--r-pill);padding:5px;gap:2px;
}
.terms__opt{
  position:relative;z-index:1;padding:9px 18px;border-radius:var(--r-pill);
  font-family:var(--font-ui);font-weight:700;font-size:13.5px;color:var(--gray-500);
  transition:color .25s;white-space:nowrap;
}
.terms__opt[aria-checked="true"]{color:var(--white)}
.terms__opt .terms__short{display:none}
.terms__thumb{
  position:absolute;top:5px;left:5px;height:calc(100% - 10px);
  border-radius:var(--r-pill);background:var(--ink);
  transition:transform .35s var(--ease), width .35s var(--ease);
  z-index:0;
}
@media (max-width:520px){
  .terms__opt .terms__long{display:none}
  .terms__opt .terms__short{display:inline}
}

.plans{display:grid;grid-template-columns:1fr;gap:var(--sp-3)}
@media (min-width:760px){ .plans{grid-template-columns:repeat(2,1fr)} }

.plan{
  background:var(--gray-100);border-radius:var(--r-xl);
  padding:var(--sp-5);display:flex;flex-direction:column;gap:var(--sp-3);
}
.plan__name{font-size:21px}
.plan__blurb{color:var(--gray-500);font-size:14.5px;margin-top:-6px}
.plan__set{display:none;flex-direction:column;gap:4px}
.plan__set.is-active{display:flex}
.plan__price{font-size:38px;letter-spacing:-.015em}
.plan__per{font-size:13.5px;color:var(--gray-500);font-weight:600;font-family:var(--font-ui)}
.plan__equiv{font-size:13px;color:var(--gray-500)}
.plan__cta{align-self:flex-start;margin-top:6px}

.plan__list{display:flex;flex-direction:column;gap:10px;margin-top:var(--sp-2);padding-top:var(--sp-3);border-top:1px solid var(--line-soft)}
.plan__list li{display:flex;align-items:flex-start;gap:10px;font-size:14px;color:var(--gray-700)}
.plan__tick{flex:none;width:18px;height:18px;color:var(--ink);margin-top:2px}
.plan__tick svg{width:100%;height:100%;fill:none;stroke:currentColor;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}

/* faq */
.faq{margin-top:var(--sp-4);border-top:1px solid var(--line)}
.faq__item{border-bottom:1px solid var(--line)}
.faq__q{
  width:100%;display:flex;align-items:center;justify-content:space-between;gap:var(--sp-3);
  padding:var(--sp-3) 0;text-align:left;font-family:var(--font-heading);font-weight:600;font-size:16px;
}
.faq__mark{width:18px;height:18px;flex:none;transition:transform .3s var(--ease);color:var(--gray-500)}
.faq__mark path{fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.faq__q[aria-expanded="true"] .faq__mark{transform:rotate(180deg)}
.faq__panel{max-height:0;overflow:hidden;transition:max-height .4s var(--ease)}
.faq__a{padding:0 0 var(--sp-3);color:var(--gray-700);font-size:13px;line-height:1.7;max-width:64ch}

/* ---------------------------------------------------------- about */
.about__grid{display:grid;grid-template-columns:1fr;gap:var(--sp-5)}
@media (min-width:900px){
  .about__grid{grid-template-columns:1.1fr 1fr;gap:var(--sp-8);align-items:center}
}

.about__heading{font-size:clamp(34px,4.6vw,54px);letter-spacing:-.02em;margin-bottom:var(--sp-4);max-width:18ch}
.about__text{font-size:16px;line-height:1.75;color:var(--gray-700);max-width:62ch}
.about__text + .about__text{margin-top:var(--sp-3)}

/* object-position is pulled up toward the top of the frame - a 16:9 crop
   of a portrait photo loses most of its height, and centering it the
   usual way cuts off the top of the head. */
.about__photo{aspect-ratio:9/8;border-radius:var(--r-xl);overflow:hidden;background:var(--gray-100)}
.about__photo img{width:100%;height:100%;object-fit:cover;object-position:50% 0%}

@media (max-width:760px){
  .about .eyebrow{display:block;text-align:center}
  .about__heading{text-align:center;max-width:none}
  .about__text{text-align:center;max-width:none}
}

/* ---------------------------------------------------------- friends */
@media (max-width:760px){
  .friends .eyebrow{display:block;text-align:center}
  #friends-heading{text-align:center}
}
/* Two columns so a second friend has somewhere to go; with only the one,
   grid-template-columns just leaves the second column's space empty rather
   than stretching the single card to fill it, which is the point - a lone
   card at full section width was the "mess" this replaces. */
.friends__grid{display:grid;grid-template-columns:1fr;gap:var(--sp-3)}
@media (min-width:700px){ .friends__grid{grid-template-columns:repeat(2,1fr)} }

.friends__row{
  display:flex;flex-direction:column;gap:var(--sp-3);
  padding:var(--sp-5);background:var(--gray-100);border-radius:var(--r-xl);
  transition:background-color .6s var(--ease);
  cursor:pointer;
}
a.friends__row:hover{background:#f7f8fa}
/* Rosie doesn't link anywhere, so the card that says so shouldn't look
   clickable either - same layout, no pointer cursor, no hover swap. */
.friends__row--static{cursor:default}
/* Shown large rather than cropped into the small round .friends__face -
   Rosie's is evidence she was actually there, so the pavilion behind her
   is the point. Every friend shares this same 4:3 box for a consistent
   grid; object-position is pulled toward the top since a portrait crop
   has so little headroom to begin with that centering the crop the usual
   way cuts into the top of the head. Rosie's own source is already 4:3,
   so nothing is actually cropped for her regardless of this value. */
.friends__photo{width:100%;height:auto;aspect-ratio:4/3;object-fit:cover;object-position:50% 20%;border-radius:var(--r-lg);display:block}

.friends__face{
  width:56px;height:56px;border-radius:var(--r-md);flex:none;
  background:var(--gray-100);display:flex;align-items:center;justify-content:center;
  font-family:var(--font-heading);font-weight:700;color:var(--gray-500);
}
.friends__role{display:block;font-size:12.5px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--gray-500);margin-bottom:4px}
.friends__nameline{display:flex;align-items:center;gap:8px;margin-bottom:8px}
.friends__name{font-family:var(--font-heading);font-weight:700;font-size:19px}
.friends__go{width:16px;height:16px;color:var(--gray-400)}
.friends__go path{fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.friends__what{color:var(--gray-700);font-size:13px;line-height:1.7}

/* ---------------------------------------------------------- contact / footer */
/* The angled top edge, replicated from the live site's .close: the left
   corner sits --cutA down from the top, the right corner sits at the very
   top, and clip-path draws the straight line between them. padding-top
   absorbs the slice the clip removes (roughly .82 of --cutA, the same
   fraction the live site uses) plus this section's usual --sp-8, so the
   heading never sits in the cut-off wedge. */
.contact{
  position:relative;overflow:hidden;
  background:var(--ink);color:var(--white);
  clip-path:polygon(0 var(--cutA), 100% 0, 100% 100%, 0 100%);
  padding-top:calc(var(--cutA) * .82 + var(--sp-8));
}
.contact__grid{display:grid;grid-template-columns:1fr;gap:var(--sp-7)}
@media (min-width:900px){ .contact__grid{grid-template-columns:1fr 1.15fr;gap:var(--sp-8)} }

.contact__eyebrow{color:rgba(255,255,255,.45)}
.contact__heading{color:var(--white);font-size:clamp(36px,5.6vw,62px);letter-spacing:-.02em;margin-bottom:var(--sp-3)}
.contact__biglink{
  display:inline-block;font-family:var(--font-heading);font-weight:700;
  font-size:clamp(18px,2.2vw,22px);color:var(--white);margin-bottom:var(--sp-3);
  border-bottom:1.5px solid rgba(255,255,255,.3);
}
.contact__biglink:hover{border-color:var(--white)}
.contact__blurb{color:rgba(255,255,255,.6);font-size:14px;line-height:1.7;max-width:44ch}

/* Fields: a name and a line, nothing boxed - replicated from the live
   site's contact form rather than the rounded-card treatment used
   elsewhere on this page. Deliberately the odd one out: a form reads as
   a form, not another card. */
.field{display:flex;flex-direction:column;gap:6px;margin-bottom:var(--sp-4)}
.field__label{font-size:12.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:rgba(255,255,255,.45)}
.field__opt{text-transform:none;font-weight:400;letter-spacing:0}
.field__input,.field__select{
  display:block;width:100%;
  background:transparent;
  border:0;border-bottom:1px solid rgba(255,255,255,.26);
  border-radius:0;
  padding:9px 0 11px;color:var(--white);
  font-family:var(--font-ui);font-size:16px;line-height:1.5;
  transition:border-color .25s;
  appearance:none;
}
.field__input::placeholder{color:rgba(255,255,255,.35)}
.field__input:focus,.field__select:focus{outline:none;border-bottom-color:var(--white)}
.field__input.is-invalid,.field__select.is-invalid{border-bottom-color:#ff8478}
textarea.field__input{resize:vertical;min-height:110px}

.cform__row{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-3)}
@media (max-width:520px){ .cform__row{grid-template-columns:1fr} }

/* Select: the native control stays for its value and for touch devices
   (enquiry.js leaves it alone there - iOS/Android's own picker beats a
   reimplementation). On pointer devices JS swaps in a button + listbox
   styled below, since CSS alone can't style a native option list. */
.select{position:relative}
.field__select{appearance:none;width:100%;padding-right:28px;cursor:pointer;text-align:left}
.field__select[data-empty="1"]{color:rgba(255,255,255,.35)}
.select__arrow{
  position:absolute;right:2px;bottom:17px;
  width:9px;height:9px;pointer-events:none;
  border-right:1.5px solid rgba(255,255,255,.5);border-bottom:1.5px solid rgba(255,255,255,.5);
  transform:rotate(45deg);
  transition:transform .3s var(--ease);
}
.select.is-open .select__arrow{transform:rotate(-135deg)}

.select__panel{
  position:absolute;z-index:20;left:0;right:0;top:calc(100% + 8px);
  margin:0;padding:6px 0;list-style:none;
  background:var(--ink-soft);
  border-radius:var(--r-md);
  max-height:264px;overflow-y:auto;
  opacity:0;transform:translateY(-6px);pointer-events:none;
  transition:opacity .2s, transform .2s var(--ease);
  box-shadow:var(--shadow-lg);
}
.select.is-open .select__panel{opacity:1;transform:none;pointer-events:auto}
.select__option{padding:11px 16px;font-size:15px;line-height:1.4;color:var(--white);cursor:pointer;transition:background-color .15s}
.select__option:hover,.select__option.is-active{background:rgba(255,255,255,.08)}

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

.cform__submit{
  width:100%;justify-content:center;
  background:var(--white);color:var(--ink);
  margin-top:var(--sp-2);
}
.cform__submit:disabled{opacity:.6;pointer-events:none}
.cform__status{font-size:13.5px;color:rgba(255,255,255,.5);margin-top:var(--sp-2)}
.cform__status:empty{display:none}

.site-footer__inner{
  display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--sp-3);
  padding-top:var(--sp-5);margin-top:var(--sp-7);border-top:1px solid rgba(255,255,255,.1);
  font-size:13.5px;color:rgba(255,255,255,.5);
}
.site-footer__nav{display:flex;gap:var(--sp-4)}
.site-footer__nav a:hover{color:var(--white)}

/* ---------------------------------------------------------- confirmation dialog */
.conf{
  position:fixed;inset:0;z-index:120;
  display:flex;align-items:center;justify-content:center;
  padding:clamp(16px,4vw,40px);
  visibility:hidden;pointer-events:none;
}
.conf[hidden]{display:none}
.conf.is-open{visibility:visible;pointer-events:auto}
.conf__scrim{position:absolute;inset:0;background:rgba(0,0,0,.55);opacity:0;transition:opacity .38s;backdrop-filter:blur(3px)}
.conf.is-open .conf__scrim{opacity:1}
.conf__panel{
  position:relative;width:100%;max-width:460px;
  background:var(--paper);color:var(--ink);
  border-radius:var(--r-xl);
  padding:clamp(34px,4vw,52px) clamp(26px,3.4vw,46px) clamp(30px,3.6vw,44px);
  opacity:0;transform:scale(.96) translateY(10px);
  transition:opacity .42s, transform .42s var(--ease);
}
.conf.is-open .conf__panel{opacity:1;transform:none}
.conf__eyebrow{margin:0 0 16px;font-family:var(--font-ui);font-weight:700;font-size:12.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--gray-500)}
.conf__title{margin:0 0 14px;font-size:clamp(26px,3.2vw,34px);line-height:1.12;letter-spacing:-.02em}
.conf__text{margin:0;font-size:13px;line-height:1.75;color:var(--gray-700)}
.conf__close{
  margin-top:clamp(24px,2.8vw,34px);
  display:inline-flex;align-items:center;justify-content:center;
  padding:14px 30px;border-radius:var(--r-pill);
  background:var(--ink);color:var(--white);
  cursor:pointer;font-family:var(--font-ui);font-weight:600;font-size:13.5px;
  transition:background-color .25s, color .25s, box-shadow .25s;
}
.conf__close:hover{background:transparent;color:var(--ink);box-shadow:inset 0 0 0 1.5px var(--ink)}

/* Sheet on phones (and short landscape) instead of a centered modal -
   easier to reach with a thumb, and the drag handle plus swipe-to-dismiss
   in enquiry.js only make sense pinned to an edge. */
@media (max-width:720px),(max-height:460px){
  .conf{align-items:flex-end;padding:0}
  .conf__panel{
    max-width:none;border-radius:var(--r-xl) var(--r-xl) 0 0;
    padding:14px clamp(22px,6vw,34px) calc(30px + env(safe-area-inset-bottom));
    transform:translateY(100%);
    transition:transform .46s var(--ease), opacity .2s;
    opacity:1;max-height:88vh;overflow-y:auto;
  }
  .conf.is-open .conf__panel{transform:translateY(0)}
  .conf__panel::before{
    content:"";display:block;width:38px;height:4px;border-radius:2px;
    margin:0 auto clamp(20px,4vw,28px);background:var(--line);
  }
  .conf__close{width:100%}
}

/* ---------------------------------------------------------- reveal-on-scroll */
.r-item{opacity:0;transform:translateY(18px);transition:opacity .6s var(--ease), transform .6s var(--ease)}
.reveal.is-in .r-item{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){
  .r-item{opacity:1;transform:none;transition:none}
}
