/* ============================================================
   FLOVO overrides on the BYQ "Human+Machine" template.
   Loaded last. Nothing here edits the template's own sheets, so the template can be
   re-exported and dropped back in without losing our work.
   ============================================================ */

/* ---------- THE MARK ----------
   Carried over from site-v2 unchanged. Every value was set by measuring rendered pixels,
   and the metric that matters is white AREA, not distance:
     stroke   bracket 18px against a 22px F stem = 0.82 of the stem
     optical  [ -> F 9206px2 against O -> ] 9285px2, ratio 1.009. The O's curve pulls away
              from the bracket top and bottom, so the right bracket sits 5px closer
              (24px vs 29px) to look the same distance away.
   Do not change one value without re-measuring the others. */
.mark{ font-family:"42 Dotsans", "Inter Tight", Helvetica, Arial, sans-serif;
  font-size:16px; line-height:1; display:inline-flex; align-items:center;
  white-space:nowrap; color:currentColor; }
.mark .br{ font-weight:400; font-size:1.11em; line-height:1; transform:translateY(-0.045em); }
.mark .wm{ font-weight:500; text-transform:uppercase; letter-spacing:0.045em;
  margin-left:0.05em; margin-right:calc(0.047em - 0.045em); }

/* ---------- THE HERO STATEMENT ----------
   The template sets its h1 as one flowing serif sentence. Flovo's is three stacked lines
   that have to break exactly where the copy says, because the three lines are three
   claims and a line that wraps mid-claim reads as one. */
.headline-home-hero h1{ text-wrap:balance; }
@media (max-width:600px){
  /* let it wrap naturally on a phone rather than forcing three long lines to fit */
  .headline-home-hero h1 br{ display:none; }
}

/* the video is 1448x1920 portrait, so on a wide screen it crops to the middle band.
   The glass forms are centred, which is why this clip survives the crop. */
.video-home-hero video{ object-fit:cover; object-position:center; }

/* ---------- THE HERO SCRIM ----------
   White type on video measured 1.12:1 at its worst pixel, where the headline crosses the
   blown-out glass highlights. That is not marginal, it is unreadable, and video is worse
   than photography for this because every frame is different: you cannot eyeball one
   frame and call it done.
   Solved rather than chosen. White text needs the background at or under luminance 0.183
   for AA. The worst pixel measured sRGB 242. A black scrim at 0.55 alpha brings that to
   5.18:1; 0.62 gives headroom for frames we did not sample.
   It is a gradient, not a flat wash, because the top of the frame carries no text and is
   the most beautiful part of the clip. The scrim is heaviest exactly where the words are. */
.hero-home-section .video-home-hero::after{
  content:""; position:absolute; inset:0; z-index:1; pointer-events:none;
  background:linear-gradient(to bottom,
    rgba(0,0,0,0.36) 0%,
    rgba(0,0,0,0.30) 32%,
    rgba(0,0,0,0.62) 56%,
    rgba(0,0,0,0.80) 76%,
    rgba(0,0,0,0.86) 100%);
}
/* the content has to sit above the scrim it is protected by */
.hero-home-section .main-container{ position:relative; z-index:2; }

@media (max-width:600px){
  /* a phone crops harder into the bright centre of the clip, so the floor comes up */
  .hero-home-section .video-home-hero::after{
    background:linear-gradient(to bottom,
      rgba(0,0,0,0.44) 0%, rgba(0,0,0,0.40) 30%,
      rgba(0,0,0,0.66) 58%, rgba(0,0,0,0.82) 80%, rgba(0,0,0,0.88) 100%);
  }
}

/* ---------- THE HERO MARK ----------
   The template's nav mark inherits a near-black (#333) because its own logo is an SVG
   that carries its own colour. Ours is live type, so it has to be told. On a dark video
   it needs to be paper, not ink. */
.brand-nav .mark{ color:#FFFFFF; }

/* ---------- THE HERO STATEMENT, SPACED SO IT CANNOT COLLIDE ----------
   Measured: 88px type on a 76px line-height, with a rendered glyph box of 114px. That is
   a 38px overlap per line, and with a face like Instrument Serif, whose descenders are
   long, the 'y' of "Named by AI." was landing in the cap-height of "Chosen". Tight
   leading is the look, but ink touching ink is not tight, it is broken.

   Measured by rendering the type and counting separate bands of ink:
     lowercase, any face  touches at anything under 0.98, because the descender of "by"
                          lands in the cap-height of the line beneath it
     UPPERCASE            has no descenders at all, so the constraint disappears
   This is why site-v2's hero runs 0.94 and never collides: it is set in caps. The tight
   stack Dom approved is not tight in spite of being uppercase, it is tight because of it.
   Same treatment here, same values, so the two sites speak with one voice. */
.headline-home-hero h1.stack{
  font-family:var(--font--heading-serif);
  font-weight:500;
  text-transform:uppercase;
  /* Both ends scale with the longest line. A flat 28px floor fits the homepage's 17
     characters at 320px and overflows /flows/'s 22 by 74px, because below about 420px the
     floor wins the clamp and the vw factor stops mattering. */
  font-size:clamp(var(--hero-min, 28px), var(--hero-vw, 8.6vw), 104px);
  line-height:0.94;
  letter-spacing:-0.028em;
}
/* Each line is its own block and never wraps. Caps are wider than lowercase, so without
   this the statement re-wraps at narrow widths into four or five lines, and THOSE extra
   lines collide. Measured at 320px it wrapped into a single fused band. A wrapped line
   also breaks a claim in half, and there are three claims here, one per line. */
.headline-home-hero h1.stack .l{ display:block; white-space:nowrap; }

/* the corner labels sit on the darkest part of the scrim, so they can afford to be
   quiet, but 48% of white over video is not readable. Solved: 0.72 gives 8.9:1 here. */
.hero-corner{ color:rgba(255,255,255,0.78); }

/* ---------- THE NAV LOCKUP ----------
   Measured: 12px from the burger to the mark, 20px from the mark to the button. The
   template's own logo is an SVG with its own built-in side bearing, so the asymmetry was
   invisible with their mark and obvious with ours, which is live type.
   Equal box gaps give equal optical gaps here because the two brackets are mirror images
   of each other, so their side bearings cancel. 16px is the midpoint of what was there. */
.master-nav .left-nav{ gap:16px; }
.master-nav{ gap:16px; }
.master-nav .cta-small{ margin-left:0; }

/* ---------- THE DISPLAY FACE ----------
   Instrument Serif is BYQ's voice, not Flovo's. The mark, the site-v2 hero and everything
   Dom has approved is Inter Tight, so the template's own heading token is repointed at it
   rather than overriding forty selectors one at a time. One variable, one cascade.
   Inter Tight ships as a single 44KB variable file covering 100-900, so five weights cost
   what one static weight would. */
:root, body{
  --font--heading-serif: "Inter Tight", "42 Dotsans", "Helvetica Neue", Helvetica, Arial, sans-serif;
}

/* the footer mark replaces a script logotype that ran the full width of the page, so it
   is set at display scale rather than nav scale */
.footer-mark{ font-size:clamp(48px,9vw,132px); font-weight:500; letter-spacing:-0.01em; color:#FFF; }

/* Promoting a styled div to a real heading is right for the document outline, but an h2
   arrives carrying the browser's own 20px/10px margin, which pushed "What we do" 10px low
   inside its pill. The element's job is unchanged; only its tag is. */
/* Only the margin. Adding font-size:inherit here stripped the display size off every
   service title and rendered them at body scale on a grey field. The tag changed; the
   type never should have. */
h2.label-small, h3.text-h6, h3.text-h1, h2.text-h4{ margin:0; }

/* ---------- TESTIMONIALS ----------
   Replaces the template's empty Journal slider. Real reviews, quoted verbatim with the
   reviewer named and the source stated. The 5.0 from 19 is displayed as plain text and
   deliberately NOT marked up as AggregateRating: Google's structured data policy does not
   let a business claim reviews collected on Google as its own aggregate. Showing it is
   fine, claiming it in schema is not. */
/* The measure belongs on the TEXT, never on the container. `max-width:24ch` on the
   blockquote computed against ITS font-size (16px) and came out at 215px, which then
   strangled the 49px quote inside it down to nine characters a line. Exactly the fault
   that squeezed every nested grid on site-v2. The container is unconstrained; the quote
   carries its own measure, and its type is sized for the slide it actually sits in. */
.testimonial{ margin:0; max-width:none; }
.testimonial-quote{
  font-family:var(--font--heading-serif); font-weight:500;
  font-size:clamp(17px,1.45vw,21px); line-height:1.28; letter-spacing:-0.012em;
  max-width:none; text-wrap:pretty; color:#FFF;
}
.testimonial-foot{ display:flex; flex-wrap:wrap; align-items:baseline;
  gap:6px 18px; margin-top:clamp(22px,2.6vw,34px); }
.testimonial-cite{ font-style:normal; font-size:15px; color:#FFF; }
.testimonial-src{ font-size:13.5px; color:rgba(255,255,255,0.62); }
.testimonial-rating{ color:rgba(255,255,255,0.62); }
.slider-journal .slide-blog{ padding-right:clamp(20px,2.2vw,32px); }
@media (max-width:768px){ .testimonial, .testimonial-quote{ max-width:100%; } }

/* ---------- THE ABOUT BLOCK ----------
   The template gives this row a 480px bottom margin, sized for the circular image slider
   that used to follow it. That slider is deleted (it was 500px of empty carousel showing
   bare slide numbers once BYQ's monogram badges came out), so the margin goes with it. */
.home-about-halves{ margin-bottom:clamp(64px,8vw,128px) !important; }

/* ---------- THE QUESTIONS ----------
   Replaces the three number tiles. This is the single strongest AI-search block on the
   page: assistants answer with a shortlist and quote whoever answers the question most
   specifically, so a page of plainly-answered questions is the thing they lift from.
   Every answer stays in the DOM whether the fold is open or shut, so a crawler and an
   assistant read all nine either way and only a human has to click.
   Mechanics carried over from site-v2: height animates, never opacity, so text is never
   rendered at partial contrast. */
.home-faq-section{ padding-block:clamp(72px,9vw,140px); }
.faq-head{ display:flex; align-items:baseline; justify-content:space-between;
  gap:clamp(20px,3vw,44px); flex-wrap:wrap; margin-bottom:clamp(30px,4vw,54px); }
.faq-note{ color:rgba(255,255,255,0.56); max-width:44ch; }

.folds{ border-top:1px solid rgba(255,255,255,0.22); }
.fold{ border-bottom:1px solid rgba(255,255,255,0.14); }
.foldh{ margin:0; font-size:inherit; font-weight:inherit; }
.fold-head{ width:100%; display:grid; grid-template-columns:56px minmax(0,1fr) 30px;
  gap:clamp(12px,2vw,28px); align-items:baseline; text-align:left;
  background:none; border:0; padding:clamp(20px,2.4vw,28px) 0; cursor:pointer; color:inherit; }
.fold-n{ font-family:"Geist Mono",ui-monospace,monospace; font-size:12px;
  color:rgba(255,255,255,0.5); font-variant-numeric:tabular-nums; }
.fold-title{ font-family:var(--font--heading-serif); font-weight:500;
  font-size:clamp(17px,1.9vw,25px); letter-spacing:-0.018em; line-height:1.16; color:#FFF; }
.fold-icon{ font-family:"Geist Mono",ui-monospace,monospace; font-size:17px;
  color:rgba(255,255,255,0.5); justify-self:end;
  transition:transform .35s cubic-bezier(.16,1,.3,1), color .2s; }
.fold.open .fold-icon{ transform:rotate(45deg); color:#FFF; }
.fold-body{ display:grid; grid-template-rows:0fr;
  transition:grid-template-rows .45s cubic-bezier(.2,.7,.2,1); }
.fold.open .fold-body{ grid-template-rows:1fr; }
.fold-inner{ overflow:hidden; }
/* the measure sits on the TEXT, never on a container */
.fold-pad{ font-size:clamp(15px,1.5vw,17px); line-height:1.6;
  color:rgba(255,255,255,0.82); max-width:72ch;
  padding:0 0 clamp(22px,2.6vw,30px) calc(56px + clamp(12px,2vw,28px)); }
@media (max-width:640px){
  .fold-head{ grid-template-columns:34px minmax(0,1fr) 26px; gap:12px; }
  .fold-pad{ padding-left:46px; }
}

/* ---------- SLIDER ARROWS ----------
   The template centres its arrows over the slide, which is fine on an image card and
   wrong over a quote: the left arrow was sitting on top of Jack Zuvelek's second line.
   Moved out to the head of the section, beside the heading, where they read as controls
   rather than as something dropped on the text. */
.slider-journal .arrow-slider{ top:auto; bottom:100%; margin-bottom:clamp(18px,2.2vw,28px);
  width:44px; height:44px; border-radius:50%; background:rgba(255,255,255,0.08);
  border:1px solid rgba(255,255,255,0.18); display:flex; align-items:center;
  justify-content:center; transition:background .2s, border-color .2s; }
.slider-journal .arrow-slider:hover{ background:rgba(255,255,255,0.16); border-color:rgba(255,255,255,0.4); }
.slider-journal .arrow-slider.left{ left:auto; right:60px; }
.slider-journal .arrow-slider.right{ right:0; }

/* ---------- THE DISPLAY FACE, AT EVERY WIDTH ----------
   The template does not just set the heading font once. It RE-DECLARES
   --font--heading-serif back to "Instrument Serif" inside @media (max-width: 767px), so
   an unconditional :root override holds on desktop and silently loses on a phone. The
   hero was Inter Tight at 768px and Instrument Serif at 390px.
   Overridden inside the same query rather than with !important, so the template's own
   cascade still works and only this one value changes. */
@media screen and (max-width: 767px){
  body{
    --font--heading-serif: "Inter Tight", "42 Dotsans", "Helvetica Neue", Helvetica, Arial, sans-serif;
  }
}

/* the hero video element used to be positioned by Webflow's background-video component,
   which we just removed along with its double-fetch. It positions itself now. */
.video-home-hero{ position:relative; overflow:hidden; }
.video-home-hero > video{ position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; object-position:center; }


/* ---------- TEMPLATE CHROME BELOW THE 44px FLOOR ----------
   The export's own nav mark, both CTAs and the footer link all render under 44px tall,
   and its button labels drop to 10px on a phone. Measured across 44 runs. */
.brand-nav, .cta-small, .cta-main, .footer-middle-link{
  min-height:44px; display:inline-flex; align-items:center;
}
.cta-main{ padding-block:10px; }
/* This was written to lift the CTA label off 10px on a phone, and .gsap_split_letter
   was included because the label's own letters are split. But SplitText applies that
   class to EVERY element it touches, so the rule forced 12px !important onto every
   split heading and paragraph on the site: the text rendered tiny, and because each
   letter is its own inline-block, spaced out as well. Scoped to the button, where it
   was always meant to live. */
.button-text{ font-size:12px !important; }
.button-text .gsap_split_letter, .button-text .gsap_split_word{ font-size:inherit !important; }

/* SVG icons shipped with no width/height, so they contribute layout shift */
.menu-icon{ width:20px; height:20px; }
/* The SVG ships with no intrinsic ratio, so without a height it collapsed and then
   resized on load: CLS 0.286 on the guides page, reproduced over three runs the moment
   this rule came off. It stays.
   It is NOT in the flow: the template positions it absolute at inset:400px 0 auto. I
   briefly removed this rule believing it was pushing content down on mobile, because my
   spacing tool counted an absolutely-positioned overlay as ink at the section's edge and
   reported that boundary as 58px when it is really 116px like every other one. The tool
   was wrong, not the rule. */
.marquee-dots{ width:100%; height:auto; }

/* ---------- THE INNER-PAGE HERO ----------
   .hero-home-section is only dark on the homepage because a video fills it. Reused without
   one, its own cream ground shows through and the near-white headline sits on it at
   1.11:1, which is invisible rather than merely poor. Exactly the fault that made site-v2's
   enquiry form disappear: a component built for one ground, reused on another.
   Inner heroes carry their own ground, matched to the body so the page reads as one sheet.
   #F8F7F3 on #101012 is 17.1:1. */
/* .section.hero-home-section is two classes; .hero-inner alone is one and loses. */
.section.hero-home-section.hero-inner{ background:#101012; }
/* where a clip plays, the section ground IS the frame, so it matches the homepage's */
.section.hero-home-section.hero-inner.hero-vid{ background:var(--_colors---bg--bg-1); }
.hero-inner .headline-home-hero h1.stack, .hero-inner .hero-corner{ color:#F8F7F3; }
/* no video means no scrim, and no scrim means nothing to lighten */
/* The scrim was switched off when these heroes had no video behind them. They do now,
   so it comes back: kept only where a clip actually plays. */
.hero-inner:not(.hero-vid) .video-home-hero::after{ display:none; }
/* the offer clip measures 8.9:1 behind the headline on its own, so this is insurance for
   the brightest frames rather than a rescue, and it stays light enough to keep the clip */
.hero-vid .video-home-hero::after{
  content:""; position:absolute; inset:0; pointer-events:none;
  background:linear-gradient(180deg, rgba(8,8,10,0.62) 0%, rgba(8,8,10,0.34) 42%, rgba(8,8,10,0.70) 100%);
}
/* the wrapper stays where the template puts it: inside the section's 8px padding, with
   the 8px radius, so the section's ground shows as a frame that follows the window. It
   was absolute and inset:0 here, which filled the padding and erased the frame. */
.hero-vid .video-home-hero video{ width:100%; height:100%; object-fit:cover; display:block;
  position:absolute; inset:0; z-index:0; }
/* The whole stack is declared, because the moment the container moved INSIDE the video
   wrapper it became a sibling of the scrim. The scrim inherited z-index:1 from the
   template, the container was also 1, and equal z-index paints in DOM order: the scrim
   came last, so it washed grey over the headline, the button and the terms. */
.hero-vid .video-home-hero::after{ z-index:1; }
.hero-vid .w-layout-blockcontainer{ position:relative; z-index:2; }
/* the corner labels drop to 10px and then 8px on a phone. 12 is the floor. */
.hero-corner{ font-size:12px !important; line-height:1.5; }
/* the primary CTA measured 42-43px: min-height alone loses to the template's own padding */
.cta-main{ min-height:48px; }

/* the template drops .label-small to 8px on a phone. Under 12px is not a style choice,
   it is a legibility failure, and these labels carry the offer names. */
.label-small{ font-size:12px !important; letter-spacing:0.08em; }

/* ---------- GUIDES ----------
   A guide is a document, not a pitch, so it gets a reading measure and a quieter scale
   than the offer pages. The answer block is the part written to be lifted whole by an
   assistant, so it is set larger than the body it introduces and bounded top and bottom
   to mark it as a unit. */
/* a guide's folded body carries lists and tables, not only paragraphs */
.fold-pad p + p, .fold-pad ul, .fold-pad ol, .fold-pad .manifest{ margin-top:var(--x4,20px); }
.fold-pad ul, .fold-pad ol{ padding-left:1.2em; }
.fold-pad li + li{ margin-top:.5em; }
.fold-pad b, .fold-pad strong{ color:#FFF; font-weight:500; }
.fold-pad .manifest{ display:block; }
.fold-pad .mhead{ display:flex; justify-content:space-between; gap:20px;
  font-size:12px; letter-spacing:0.06em; text-transform:uppercase;
  color:rgba(255,255,255,0.5); padding-bottom:12px; border-bottom:1px solid rgba(255,255,255,0.18); }
.fold-pad .mrow{ display:grid; grid-template-columns:minmax(0,1fr) auto; gap:20px;
  align-items:baseline; padding:14px 0; border-bottom:1px solid rgba(255,255,255,0.10); }
.fold-pad .mrow b{ font-weight:500; }
.fold-pad .mrow .v{ font-family:"Geist Mono",ui-monospace,monospace; font-size:13px;
  color:rgba(255,255,255,0.78); white-space:nowrap; }
@media (max-width:560px){ .fold-pad .mrow{ grid-template-columns:1fr; gap:4px; } }

/* ---------- THE CLOSE ----------
   Ends every page. One column: the form, then call / email / book under it. The Calendly
   box that used to sit beside the form came out on 4 Nov 2026 (empty frame in two
   browsers, Dom: "dw about calendar"); the booking page is a plain link now. */
.close-section{ padding-block:clamp(72px,9vw,140px); }
.close-head{ display:flex; align-items:baseline; justify-content:space-between;
  gap:clamp(20px,3vw,44px); flex-wrap:wrap; margin-bottom:clamp(26px,3.4vw,46px); }
.close-note{ color:rgba(255,255,255,0.56); }
.close{ display:grid; grid-template-columns:minmax(0,1fr); align-items:start; }
.close-lead{ font-size:clamp(15px,1.6vw,18px); line-height:1.5;
  color:rgba(255,255,255,0.78); max-width:46ch; margin-bottom:clamp(22px,2.6vw,32px); }

.enq{ border-top:1px solid rgba(255,255,255,0.22); padding-top:clamp(18px,2.2vw,26px); }
.enqrow{ display:grid; grid-template-columns:52px minmax(0,1fr); gap:clamp(12px,2vw,28px);
  align-items:baseline; border-bottom:1px solid rgba(255,255,255,0.14); }
.enqn{ font-family:"Geist Mono",ui-monospace,monospace; font-size:12px;
  color:rgba(255,255,255,0.5); font-variant-numeric:tabular-nums; padding-top:16px; }
.field{ display:flex; flex-direction:column; padding:10px 0 6px; }
.field label{ font-size:12px; letter-spacing:0.04em; color:rgba(255,255,255,0.56); }
.field input, .field select, .field textarea{
  border:0; outline:none; background:none; border-radius:0; -webkit-appearance:none;
  font-family:var(--font--body, inherit); font-size:16px; min-height:44px;
  color:#F8F7F3; padding-top:4px; width:100%; }
.field textarea{ resize:vertical; min-height:52px; }
.field select{ min-height:44px; color:#F8F7F3; }
.field select option{ color:#111; }
.field input::placeholder, .field textarea::placeholder{ color:rgba(255,255,255,0.34); }
.enqrow:focus-within{ background:rgba(255,255,255,0.05); }
.enqrow:focus-within .enqn{ color:#F8F7F3; }
.enqfoot{ display:flex; align-items:center; justify-content:flex-end;
  gap:clamp(18px,2.6vw,32px); flex-wrap:wrap; padding-top:clamp(20px,2.6vw,30px); }
/* .consent (the Flovo Note tick box) left the enquiry form 4 Nov 2026 */
.hp{ position:absolute; left:-9999px; }
.enq-submit{ min-height:48px; }

.enqmeta{ display:flex; align-items:baseline; gap:clamp(14px,2vw,26px); flex-wrap:wrap;
  border-top:1px solid rgba(255,255,255,0.22); padding-top:clamp(18px,2.2vw,24px);
  margin-top:clamp(30px,4vw,54px); }
.enqmail{ font-size:16px; color:#F8F7F3; min-height:44px; display:inline-flex;
  align-items:center; border-bottom:1px solid rgba(255,255,255,0.26); }
.enqmail:hover{ border-bottom-color:#F8F7F3; }
/* label and link travel as one unit, so a phone never breaks "Or email" from its address */
.enqpair{ display:inline-flex; align-items:baseline; gap:clamp(10px,1.2vw,16px); }
.enqpair .label-small{ white-space:nowrap; }
.enqmeta .label-small:last-child{ margin-left:auto; color:rgba(255,255,255,0.5); }

@media (max-width:640px){
  .enqrow{ grid-template-columns:34px minmax(0,1fr); gap:12px; }
  .enqmeta .label-small:last-child{ margin-left:0; }
}

/* ---------- footer legal row ---------- */
.footer-bottom-links{ display:flex; align-items:center; gap:clamp(14px,2vw,28px); flex-wrap:wrap; }
.foot-link{ color:rgba(255,255,255,0.55); min-height:44px; display:inline-flex; align-items:center; }
.foot-link:hover, .foot-link:focus-visible{ color:#F8F7F3; }

/* ---------- THE STICKY BAR ----------
   Off the page until the hero is behind you, gone again at the close block. It was two
   flat tiles, one of which pointed at the scan. One button now, wearing the same
   .cta-main treatment as the nav's Let's Talk, roll-up and all, so the site has one
   primary action that looks the same everywhere it appears. */
.moneybar{ position:fixed; left:0; right:0; bottom:0; z-index:60;
  padding:10px 12px calc(10px + env(safe-area-inset-bottom,0px));
  background:linear-gradient(180deg, rgba(8,8,10,0) 0%, rgba(8,8,10,0.82) 42%, rgba(8,8,10,0.96) 100%);
  transform:translateY(102%); transition:transform .42s cubic-bezier(.22,.61,.36,1); }
.moneybar.on{ transform:none; }
.moneybar .mb-cta{ width:100%; min-height:52px; justify-content:center; }
@media (min-width:1024px){ .moneybar{ display:none; } }
@media (prefers-reduced-motion:reduce){ .moneybar{ transition:none; } }

/* ---------- THE NOTE ----------
   A card, bottom left, not a screen-covering modal: it interrupts someone mid-page, and a
   full takeover for an email address is a bad trade. aria-modal stays false for the same
   reason, and it never steals focus on a phone.

   Dressed like the rest of the site now rather than a plain box: the template's glass
   treatment, a hairline that draws across the top as it arrives, the serif italic used
   for the turn in the sentence, and the real .cta-main button so the submit behaves
   exactly like every other Let's Talk on the site, roll-up and all. */
.note-pop{
  position:fixed; z-index:70;
  left:clamp(16px,3vw,32px); bottom:clamp(16px,3vw,32px);
  width:min(420px, calc(100vw - 32px));
  padding:clamp(22px,2.5vw,30px);
  color:#F8F7F3;
  background:linear-gradient(168deg, rgba(30,30,34,0.96) 0%, rgba(12,12,14,0.97) 62%);
  border:1px solid rgba(248,247,243,0.16);
  border-radius:var(--radius--radius-1, 8px);
  box-shadow:inset 0 1px 0 rgba(248,247,243,0.14), 0 30px 70px rgba(0,0,0,0.62);
  -webkit-backdrop-filter:blur(24px); backdrop-filter:blur(24px);
  opacity:0; transform:translateY(22px) scale(0.985);
  transform-origin:0 100%;
  pointer-events:none;
  transition:opacity .62s ease, transform .72s cubic-bezier(.16,1,.3,1);
  overflow:hidden;
}
.note-pop.on{ opacity:1; transform:none; pointer-events:auto; }
.note-pop[hidden]{ display:none; }

/* the hairline draws across the top once the card has landed */
.np-rule{ position:absolute; top:0; left:0; height:1px; width:0;
  background:linear-gradient(90deg, rgba(248,247,243,0) 0%, rgba(248,247,243,0.85) 40%, rgba(248,247,243,0) 100%);
  transition:width .9s cubic-bezier(.16,1,.3,1) .18s; }
.note-pop.on .np-rule{ width:100%; }

.nphead{ display:flex; align-items:center; justify-content:space-between; gap:12px; }
.npk{ font-family:"Geist Mono",ui-monospace,monospace; font-size:12px; letter-spacing:0.12em;
  text-transform:uppercase; color:rgba(248,247,243,0.56); }

/* a drawn cross, so it matches the site's hairline language instead of a glyph */
.npx{ appearance:none; background:none; border:0; cursor:pointer;
  width:44px; height:44px; margin:-10px -12px -10px 0; position:relative; }
.npx-i, .npx-i::after{ content:""; position:absolute; left:50%; top:50%; width:13px; height:1px;
  background:rgba(248,247,243,0.62); transition:background .25s ease, transform .35s ease; }
.npx-i{ transform:translate(-50%,-50%) rotate(45deg); }
.npx-i::after{ left:0; top:0; transform:rotate(-90deg); transform-origin:center; }
.npx:hover .npx-i, .npx:hover .npx-i::after{ background:#F8F7F3; }
.npx:hover .npx-i{ transform:translate(-50%,-50%) rotate(135deg); }

.nph{ font-size:clamp(21px,2.3vw,26px); line-height:1.14; letter-spacing:-0.018em;
  margin:16px 0 0; color:#F8F7F3; }
.nph em{ font-family:var(--font--heading-serif,"Instrument Serif",serif); font-style:italic;
  font-weight:400; letter-spacing:-0.01em; }
.npp{ font-size:14px; line-height:1.5; color:rgba(248,247,243,0.70); margin:14px 0 0; }

.npf{ margin-top:20px; display:grid; gap:10px; }
.npfield{ display:flex; flex-direction:column; gap:2px;
  border-bottom:1px solid rgba(248,247,243,0.24); padding-bottom:6px;
  transition:border-color .25s ease; }
.npfield:focus-within{ border-bottom-color:#F8F7F3; }
.npl{ font-family:"Geist Mono",ui-monospace,monospace; font-size:12px; letter-spacing:0.08em;
  text-transform:uppercase; color:rgba(248,247,243,0.5); }
.npin{ border:0; outline:none; background:none; border-radius:0; -webkit-appearance:none;
  font-size:16px; min-height:44px; color:#F8F7F3; width:100%; }
.npin::placeholder{ color:rgba(248,247,243,0.34); }
.npb{ width:100%; min-height:48px; justify-content:center; }
.npfine{ font-size:11.5px; line-height:1.45; color:rgba(248,247,243,0.44); margin-top:14px; }
.npok{ display:none; font-size:15.5px; line-height:1.45; margin:16px 0 0; color:#F8F7F3; }
.note-pop.done .npf, .note-pop.done .npfine, .note-pop.done .npp{ display:none; }
.note-pop.done .npok{ display:block; }

@media (prefers-reduced-motion:reduce){
  .note-pop{ transition:opacity .2s ease; transform:none; }
  .np-rule{ transition:none; width:100%; }
}
@media (max-width:620px){
  /* clear of the sticky bar, which is the more valuable of the two */
  .note-pop{ left:12px; right:12px; width:auto;
    bottom:calc(56px + 12px + env(safe-area-inset-bottom,0px)); }
}

/* ---------- resting states the scroll interactions assume ----------
   The template's a-112 / a-113 interactions animate .home-work-section and
   .home-overlap-section to a dark ground with light text. Neither section declares a
   ground of its own, so until the scroll trigger fires they inherit the cream body while
   their contents are already light: near-white on cream, 1.19:1, on first paint and for
   anyone whose JS never runs. Declaring where the animation LANDS makes the resting state
   correct and turns the animation into a no-op instead of a rescue. */
.section.home-work-section, .section.home-overlap-section{
  background-color:#101012; color:#F8F7F3; }
/* and their contents have to follow. The interaction animates the SECTION's colour, but
   .marquee-text-large and .text-light-64 hard-code light, so at the cream end of the
   animation they stayed near-white on near-white: 1.19:1 and 1.12:1, at rest, at every
   width. currentColor in the color property resolves to the inherited value, which is
   the animated one, so these track the ground instead of fighting it. */
.home-work-section .marquee-text-large,
.home-overlap-section .marquee-text-large{ color:inherit; }
.home-work-section .text-light-64, .home-work-section .text-light-48,
.home-overlap-section .text-light-64, .home-overlap-section .text-light-48{
  color:color-mix(in srgb, currentColor 68%, transparent); }

/* ---------- the enquiry submit ----------
   .cta-main is an <a> everywhere else in the template, so nothing ever reset the UA
   button background. As a <button> it painted buttonface grey (239,239,239) under
   near-white text: 1.07:1. The same class of fault as site-v2's invisible form, and it
   shipped on all twelve pages. The primary action gets the primary treatment instead:
   cream ground, dark text, which is the only pairing on this site that measures 17:1. */
.enq-submit{ background:#F8F7F3; border:0; color:#101010; -webkit-appearance:none; appearance:none; }
.enq-submit .button-bg{ background-color:#F8F7F3; box-shadow:none; }
.enq-submit:hover .button-bg{ background-color:#fff; }
.enq-submit .button-text{ color:#101010; text-shadow:0 1.5em 0 #101010; }
.enq-submit:hover .button-text{ color:#101010; }

/* ---------- inline links in prose ----------
   2.02:1 in the guides and on the privacy page. A link a reader is meant to follow has
   to be legible before it is decorative. */
.prose a, .fold-pad a, .legal a, .elink, .answer a{
  color:#F8F7F3; text-decoration:underline; text-underline-offset:0.18em;
  text-decoration-color:rgba(255,255,255,0.42); }
.prose a:hover, .fold-pad a:hover, .legal a:hover, .elink:hover, .answer a:hover{
  text-decoration-color:#F8F7F3; }

/* the note's two smallest labels sat at 11px and 11.5px. The floor is 12px. */
.npk{ font-size:12px; }
.npfine{ font-size:12px; }

/* ---------- the "How we work" chip ----------
   .label-master is a template component built for a dark ground: an 8%-light film with
   light text. It sits in .home-work-section, which the scroll interaction animates cream,
   and the interaction inverts the chip's text on the way. So it reads dark-on-dark at the
   top of the scroll and light-on-cream at the bottom: 1.19:1 at both ends, never in
   between. Given the ground under it moves, the chip stops taking part: one solid dark
   fill, one text colour, legible against cream and against black.
   !important is beating an inline declaration the interaction writes each frame. The
   optimisation pass removes that binding outright and this drops back to a normal rule. */
.home-work-section .label-master.work-animated{
  background-color:#101012 !important;
  color:#F8F7F3 !important;
  box-shadow:inset 0 0 0 1px rgba(248,247,243,0.20);
  -webkit-backdrop-filter:none; backdrop-filter:none; }




/* ---------- skip link ----------
   Off-screen until focused, then the first thing a keyboard reaches. Without it every
   tab session started with the whole nav, on every page. */
.skip{ position:absolute; left:-9999px; top:0; z-index:200;
  background:#F8F7F3; color:#101010; padding:14px 20px; font-size:15px;
  min-height:44px; display:inline-flex; align-items:center; }
.skip:focus{ left:12px; top:12px; outline:2px solid #101010; outline-offset:2px; }

/* one visible focus treatment for everything, since the template ships none */
:where(a,button,input,select,textarea,[tabindex]):focus-visible{
  outline:2px solid #F8F7F3; outline-offset:2px; border-radius:2px; }

/* ---------- the primary call to action ----------
   `.button-bg` paints every CTA with accent-1, #808288, under near-white 12px text.
   Sampled from the rendered pixels rather than the stylesheet: 3.58:1, where 12px text
   needs 4.5:1. This is the site's main button, in the nav and in the hero, on all twelve
   pages, and the mobile sweep never caught it because the hero is excluded from that
   check (it sits over video, which the ancestor walk cannot read).
   The accent goes down to #6A6C72, which measures 4.90:1 against the same text and is
   two shades deeper on screen. The token itself is left alone because .color-four,
   .cta-arrow and .icon-quote also read it and none of those carry small text. */
.button-bg{ background-color:#6A6C72; }
.cta-main:hover .button-bg, .cta-small:hover .button-bg{ background-color:#7A7C83; }
/* the close block's submit keeps its own cream treatment, set earlier */
.enq-submit .button-bg{ background-color:#F8F7F3; }
.enq-submit:hover .button-bg{ background-color:#fff; }

/* ---------- the inner-page hero ----------
   This used to push the headline to the top of the section, because these heroes had no
   video and 100vh of empty black sat above it: 54% of the first screen at 1280 and 74% at
   390. They have a clip behind them now, so the reason is gone and the override with it.
   They lay out exactly like the homepage again, which is what Dom asked for: headline low,
   CTA under it, terms pinned to the floor. */

/* ---------- THE PROOF BAND ----------
   One number, one claim. Side by side on a shared baseline, between two rules, so it
   reads as a piece of evidence rather than a number stranded in an empty box. The number
   column is sized off the number itself so "#1" and "20-30%" both sit correctly without
   either being padded out to match the other. */
.fig-section{ padding-block:clamp(56px,7vw,104px); }
.figband{
  display:grid;
  grid-template-columns:minmax(0,auto) minmax(0,1fr);
  gap:clamp(28px,5vw,80px);
  align-items:center;
  border-top:1px solid rgba(255,255,255,0.22);
  border-bottom:1px solid rgba(255,255,255,0.22);
  padding-block:clamp(34px,4.6vw,64px);
}
.fignum{
  font-family:"42 Dotsans","Inter Tight",Helvetica,Arial,sans-serif;
  font-weight:800;
  font-size:clamp(80px,13vw,204px);
  line-height:0.80;
  letter-spacing:-0.045em;
  color:#F8F7F3;
  white-space:nowrap;
  font-variant-numeric:tabular-nums;
}
.figbody{ min-width:0; }
.figlab{
  font-family:"Geist Mono",ui-monospace,Menlo,monospace;
  font-size:12px; letter-spacing:0.10em; text-transform:uppercase;
  color:rgba(255,255,255,0.52);
  margin-bottom:clamp(10px,1.4vw,16px);
}
.figclaim{
  font-size:clamp(17px,1.9vw,23px);
  line-height:1.38;
  letter-spacing:-0.011em;
  color:rgba(255,255,255,0.86);
  max-width:60ch;
  margin:0;
}
@media (max-width:767px){
  .figband{ grid-template-columns:1fr; gap:clamp(16px,4vw,24px); align-items:start; }
  .fignum{ line-height:0.86; }
}

/* ---------- THE SERVICES LIST ----------
   The template dims this list on hover by RECOLOURING it: `.services-list:hover` goes to
   white at 32% and the hovered `.service-item` to solid white. Correct on the dark
   section it was designed for, wrong on ours, which is cream. And the ground is not even
   fixed: a scroll interaction moves this section between cream and dark as you pass it,
   so naming a colour fixes one end and breaks the other. My first two attempts did
   exactly that, in both directions.

   So nothing here sets a colour at all. The list and the items inherit the section's own
   colour, which is already animated to suit whichever ground it is on, and the emphasis
   is done with OPACITY, which cannot invert against any ground. Hovering fades the rows
   you are not pointing at and nudges the one you are. */
.home-services-section .services-list,
.home-services-section .services-list:hover,
.home-services-section .service-item,
.home-services-section .service-item:hover,
.home-services-section .service-item .text-h1,
.home-services-section .service-item .text-medium,
.home-services-section .service-item .right-service-item div{
  color:inherit !important;
}
.home-services-section .service-item{
  opacity:1;
  transition:opacity .38s ease, transform .48s cubic-bezier(.22,.61,.36,1);
}
/* the rows you are not pointing at step back */
.home-services-section .services-list:hover .service-item{ opacity:.34; }
/* and the one you are comes forward */
.home-services-section .services-list .service-item:hover{
  opacity:1;
  transform:translateX(clamp(6px,0.8vw,14px));
}
/* a rule that draws in from the left under the row you are on */
.home-services-section .service-item{ position:relative; }
.home-services-section .service-item::after{
  content:""; position:absolute; left:0; bottom:0; height:1px; width:0;
  background:currentColor; opacity:.45;
  transition:width .5s cubic-bezier(.22,.61,.36,1);
}
.home-services-section .services-list .service-item:hover::after{ width:100%; }

/* on touch there is no hover, so nothing may depend on entering one */
@media (hover:none){
  .home-services-section .services-list:hover .service-item{ opacity:1; }
  .home-services-section .services-list .service-item:hover{ transform:none; }
  .home-services-section .service-item::after{ display:none; }
}
@media (prefers-reduced-motion:reduce){
  .home-services-section .service-item{ transition:opacity .2s ease; }
  .home-services-section .services-list .service-item:hover{ transform:none; }
  .home-services-section .service-item::after{ transition:none; }
}



/* ---------- THE BOUNDARY BETWEEN SECTIONS ----------
   The template pays 160px top and 200-232px bottom on every section, and our components
   add their own margins on top of that. Measured ink-to-ink at 1420: 359px between the
   last Google review and the next heading, and 485px between the services list and the
   band under it. Dom called it twice, on the reviews boundary both times.

   One value for every section boundary, so any two adjacent sections sit the same
   distance apart and reordering them cannot change the rhythm. The hero is excluded: it
   is a full-height composition and pays its own way. */
main > section:not(.hero-home-section):not(.read-section){
  padding-top:clamp(58px,6.4vw,104px);
  padding-bottom:clamp(58px,6.4vw,104px);
}

/* two sections kept their own bottom padding through the boundary rule and stayed out of
   rhythm: the overlap band at 251px and the close block. Brought into line. */
main > section.home-overlap-section{ padding-bottom:clamp(58px,6.4vw,104px); }
main > section.home-faq-section{ padding-bottom:clamp(58px,6.4vw,104px); }

/* the How we work blurb is two paragraphs now, so it needs a gap between them */
.home-overlap-section .master-about-content > div > p + p{ margin-top:1.1em; }
.home-overlap-section p{ margin:0; }

/* ---------- the hero corner meta on a phone ----------
   Two columns pushed apart with space-between. The homepage's lines are short enough to
   sit side by side at 390, but the offer pages carry longer terms and every column broke
   into orphans: "A SITE REBUILT TO / RANK" against "LATE WEEKS COME OFF / IT". Below
   560px they stack instead, both left aligned, which reads as one list rather than two
   collided ones. */
@media (max-width:560px){
  .home-hero-bottom-tile{
    flex-direction:column;
    align-items:flex-start;
    gap:clamp(12px,3vw,18px);
  }
  .home-hero-bottom-tile .text-align-right{ text-align:left; }
}

/* ---------- REVEAL ----------
   The theme animates its sections in on scroll, but the FAQ and the reviews were the only
   two on the homepage with no animation hook of any kind, so they simply appeared. The
   theme's own splitter only targets .no-margins, .link-text and .button-text, none of
   which those sections use, so this gives them the same motion language rather than
   fighting webflow.js for its.

   Gated on html.rv-on so that with JS off nothing is ever hidden: the class is added by
   flovo.js before first paint, and if it never runs the content is simply visible. */
/* Transform only, never opacity (DESIGN-RULES 10): text is never painted at partial
   contrast, and an accessibility snapshot taken mid-reveal (Lighthouse caught .fold-n at
   1.05:1 and .fold-title at 1.89:1 on the guide pages) reads the real colours. */
html.rv-on .rv{
  transform:translateY(22px);
  transition:transform .82s cubic-bezier(.16,1,.3,1);
  transition-delay:calc(var(--rv, 0) * 70ms);
}
html.rv-on .rv.in{ transform:none; }
@media (prefers-reduced-motion:reduce){
  html.rv-on .rv{ transform:none; transition:none; transition-delay:0ms; }
}

/* ---------- the statement block's trailing margin ----------
   .wrap-home-about carries margin-bottom:160px from the template, on top of the 91px the
   section already pays. Measured ink to ink at 1420: 342px against a 182px median on the
   homepage under the hero, and again on all three offer pages between the statement and
   the number band. Both are the gaps Dom pointed at. The section owns the boundary; the
   block inside it does not get to add to it. */
main > section .wrap-home-about{ margin-bottom:0; }

/* ---------- SELECTED WORK: the result panel ----------
   What the build actually did, over the photo on hover. Only cards carrying real numbers
   get one: a card with nothing true to say gets no panel rather than an invented one.

   It was three stacked rows of sentence-length text, which wrapped and covered half the
   photo, and on touch there is no hover so it sat there permanently. Three columns of
   number-over-label now: it reads as a scoreboard in one glance and takes a strip rather
   than a slab. */
.project-item.has-res .image-wrap-project{ position:relative; overflow:hidden; }
.workres{
  position:absolute; left:0; right:0; bottom:0;
  padding:clamp(14px,1.6vw,20px) clamp(14px,1.6vw,20px) clamp(13px,1.4vw,18px);
  background:linear-gradient(180deg, rgba(8,8,10,0) 0%, rgba(8,8,10,0.72) 46%, rgba(8,8,10,0.92) 100%);
  transform:translateY(100%); opacity:0;
  transition:opacity .38s ease, transform .5s cubic-bezier(.16,1,.3,1);
  pointer-events:none;
}
.project-item.has-res:hover .workres,
.project-item.has-res:focus-within .workres{ opacity:1; transform:none; }

.wr-list{ list-style:none; margin:0; padding:0;
  display:flex; flex-wrap:wrap; column-gap:clamp(14px,1.8vw,22px); row-gap:8px; }
/* Each stat keeps its own width and the row wraps when the card is too narrow for the
   set. Three equal grid columns put "ChatGPT's pick" and "Every enquiry" in 100px cells
   from 601 to 1023px and the nowrap values painted straight through each other (Dom's
   screenshot, 3 Nov 2026). */
.wr-list li{ display:flex; flex-direction:column; gap:2px; flex:0 0 auto; max-width:100%; }
.wr-n{ font-family:"42 Dotsans","Inter Tight",Helvetica,Arial,sans-serif; font-weight:800;
  font-size:clamp(17px,1.55vw,23px); line-height:1; letter-spacing:-0.022em; color:#F8F7F3;
  font-variant-numeric:tabular-nums; white-space:nowrap; }
.wr-t{ font-family:"Geist Mono",ui-monospace,monospace;
  font-size:12px; line-height:1.25; letter-spacing:0.02em;
  color:rgba(248,247,243,0.72); }

/* no hover on touch, so it stays put rather than never appearing */
@media (hover:none){
  .workres{ opacity:1; transform:none; }
}
@media (prefers-reduced-motion:reduce){
  .workres{ transition:opacity .2s ease; transform:none; }
}

/* no hover on touch, so the panel is simply always there */
@media (hover:none){
  .workres{ opacity:1; transform:none; }
  .wr-list li{ opacity:1; transform:none; }
}
@media (prefers-reduced-motion:reduce){
  .workres, .wr-list li{ transition:opacity .2s ease; transform:none; }
}

/* ---------- SELECTED WORK: back to a grid ----------
   The template lays this out as a zigzag: every item 50% wide with the even ones pushed
   right, in a flex column. Six clients that way is a very long staggered ribbon, and the
   images are a fixed 548px tall rather than square. A grid of squares reads as a body of
   work; a zigzag reads as a scroll you have to get to the end of.

   The section's own blurred ground, the reveal and the result panel all stay as they are;
   only the arrangement changes. */
.projects h2.label-small{ margin:0 0 clamp(26px,3vw,44px); }

.project-list{
  display:grid;
  grid-template-columns:repeat(3, minmax(0,1fr));
  gap:clamp(16px,1.8vw,26px);
  flex-flow:initial;
}
.project-item{ width:auto; }
.project-item:nth-child(2n){ margin-left:0; }

/* square, which is what makes a grid read as a grid */
.image-wrap-project{ height:auto; aspect-ratio:1/1; }
.image-wrap-project .image-cover{ width:100%; height:100%; object-fit:cover; display:block; }

/* the caption row under each card sits in one line at this size */
.project-item .text-wrap-project-card{
  grid-template-columns:auto auto; justify-content:space-between; align-items:baseline;
  gap:clamp(8px,1vw,16px);
}
.project-item .text-h6{ font-size:clamp(15px,1.15vw,19px); }

@media (max-width:1023px){
  .project-list{ grid-template-columns:repeat(2, minmax(0,1fr)); }
}
@media (max-width:600px){
  .project-list{ grid-template-columns:repeat(2, minmax(0,1fr)); gap:12px; }
  .project-item .text-h6{ font-size:14px; }
  .project-item .text-wrap-project-card{ grid-template-columns:1fr; justify-content:start; gap:2px; }
  /* The grid is 2-up here, so a card is about 173px wide at 390 and 140px at 320. Three
     stats at the 12px floor filled 82% of the card and wrapped. One stat, the strongest,
     which is a hook rather than a table. The full set is there from 600px up. */
  .wr-list{ grid-template-columns:1fr; gap:4px; }
  /* number over label, not beside it. Side by side, a long label like "built to the
     marque's standard" had no room left in the flex row at 320 and pushed 8px past the
     viewport. Stacked it wraps inside the card instead. */
  .wr-list li{ flex-direction:column; align-items:flex-start; gap:1px; min-width:0; }
  .wr-list li:nth-child(n+2){ display:none; }
  .wr-t{ min-width:0; overflow-wrap:anywhere; }
  .wr-n{ font-size:14px; }
  .wr-t{ font-size:12px; }
}

/* the card captions had the same fault as the services list: .card-project names
   color--light, and this section is animated cream at the end a reader sees it on, so
   "DesignFit" and "COMMERCIAL FITOUT" were near-white on near-white. They follow the
   section's own animated colour instead, so they are legible at both ends of it. */
.home-work-section .card-project,
.home-work-section .card-project .text-h6{ color:inherit; }
.home-work-section .card-project .text-light-64{
  color:color-mix(in srgb, currentColor 62%, transparent); }

/* ---------- the What we do titles ----------
   88px in a 662px column, so anything past about 14 characters broke, and two-word
   titles like "Structured data" came out one word per line. Sized to the column instead,
   with text-wrap:balance so a title that does wrap splits evenly rather than dropping a
   single orphan onto the second line. */
.home-services-section .service-item .text-h1{
  font-size:clamp(30px, 4.4vw, 62px);
  line-height:1.02;
  letter-spacing:-0.028em;
  text-wrap:balance;
}
@media (max-width:767px){
  .home-services-section .service-item .text-h1{ font-size:clamp(28px, 8vw, 40px); }
}

/* ---------- the service title's column ----------
   The template collapses .left-service-item to `grid-template-columns:100px` at tablet,
   which is fine for a number and nothing else. Our titles land in that 100px track and
   break one word per line: measured 104px wide inside a 704px column at 768. The title
   gets the rest of the row back, the same as the desktop layout. */
@media (min-width:600px) and (max-width:1023px){
  .home-services-section .left-service-item{
    grid-template-columns:64px minmax(0,1fr);
    align-items:baseline;
  }
}
@media (max-width:599px){
  .home-services-section .left-service-item{
    grid-template-columns:minmax(0,1fr);
    place-items:start;
  }
}
.home-services-section .service-item .text-h1{ min-width:0; }

/* ---------- the FAQ note ----------
   A full sentence set in uppercase mono at 12px, wrapping over three lines with the
   mono's tight leading, reads as a system message rather than a line of copy. Mono
   uppercase is for labels of a few words. This is a sentence, so it gets set as one. */
.faq-head .faq-note{
  font-family:var(--font--body, inherit);
  text-transform:none;
  letter-spacing:0;
  font-size:13.5px;
  line-height:1.5;
  max-width:34ch;
  color:rgba(248,247,243,0.62);
}

/* ---------- the button roll-up ----------
   The template ships the rig and never triggers it: .button-text-mask clips with
   overflow:hidden and .button-text carries a copy of itself below via text-shadow. No
   button on the site animated, verified on the nav, the hero and the sticky bar.

   The rig is also mis-set. The mask is as tall as the line-height, which is 16px on
   .cta-small and 24px on .cta-main, but the shadow offset is a flat 1.5em = 18px on both.
   For the trick to work those two must be the same number: the copy has to sit exactly
   one mask-height below so it arrives where the original left. At 24px vs 18px the copy
   was already peeking 6px into the mask at rest and landed 6px high on hover, which is
   why the text looked like it just vanished.

   Both are now driven off one --roll value per button size, and the copy takes
   currentColor so it is right on the grey buttons and on the cream submit alike. */
/* ---- the CTA roll-up, REMOVED 2 Sep 2026 -------------------------------------------
   There was a CSS roll here: a --roll custom property per button size, a text-shadow
   override, and a translateY on .button-text on hover. It made the sticky bar's button
   go completely blank on hover, which Dom reported three times before it was found.

   Why. The template ALREADY rolls these buttons. GSAP SplitText breaks the label into
   .gsap_split_letter elements and animates those by -18px on hover, and the template's
   own text-shadow is 0 1.5em 0, which is exactly 18px at the 12px button size. The
   shadow copy lands precisely where the glyphs were. It worked.

   The CSS above then translated the PARENT .button-text by another -24px. Measured on
   hover: parent -24, letters -18, so the glyphs sat 42px above a 24px mask. Both the
   text and its shadow were outside the mask, and the button painted as a bare slab.

   The original diagnosis was wrong in a specific way worth recording: it read the 6px
   difference between the 24px mask and the 18px shadow as the fault, and forced both to
   24. But the shadow offset has to match the ROLL DISTANCE, not the mask height. Two
   measurements hid it: the parent's geometry looked correct because only the parent was
   measured, and the letters are runtime SplitText elements that appear in no markup.

   Nothing replaces this. The template's animation is the effect Dom asked for. */

/* the gap under the work grid was 160px of margin on a button that no longer exists */
.home-work-section .button-wrap-home-work{ display:none; }
main > section.home-work-section{ padding-bottom:clamp(58px,6.4vw,104px); }

/* ---- Pass 21, REVERTED 1 Sep 2026 ------------------------------------------------
   A transition-duration was added here on background-color and color for the three
   scroll-animated sections, to soften the grid -> what-we-do flip that completes in 20px
   of scroll. It was wrong and Dom caught it: "the animation is bugging on this part of
   the page".

   Why it was wrong. Those colours are not set once, they are driven by a scroll scrub
   that writes a new value every frame. Putting a transition on a continuously-driven
   property means the painted colour is permanently chasing the scroll position, so the
   whole section washes through intermediate greys while you scroll and the text sitting
   on it goes with it. The measurement that "proved" the fix only sampled the size of the
   per-frame step, which the transition does reduce. It never asked what the page looks
   like mid-scroll, which is the only thing that mattered.

   The underlying 20px scroll window is real and still there. Fixing it properly means
   editing the window in the minified ix2 payload, not masking it in CSS. */

/* ---- Pass 22: contextual guide link under the FAQ ---------------------------------
   Never names a colour. The FAQ section sits on a ground the scroll interactions animate
   between cream and near-black, so an ink value that reads correctly at one end is
   invisible at the other. Inherit the section's animated colour, and use alpha for the
   quieter state. This is the fourth rule on this page to be written this way and the
   first three broke when they named a colour. */
.faq-more{
  margin:clamp(30px,3.4vw,50px) 0 0;
  padding-top:clamp(20px,2.2vw,30px);
  border-top:1px solid color-mix(in srgb, currentColor 16%, transparent);
  color:inherit;
}
.faq-more-link{
  color:inherit;
  font-size:clamp(13px,1.15vw,16px);
  line-height:1.45;
  letter-spacing:.01em;
  text-decoration:none;
  display:inline-flex; align-items:baseline; gap:.45em;
  opacity:.68;
  transition:opacity .3s ease, gap .3s cubic-bezier(.22,.61,.36,1);
}
.faq-more-link:hover, .faq-more-link:focus-visible{ opacity:1; gap:.75em; }
.faq-more-link:focus-visible{ outline:2px solid currentColor; outline-offset:4px; border-radius:2px; }
@media (prefers-reduced-motion: reduce){ .faq-more-link{ transition:none; } }

/* ---- breadcrumbs -------------------------------------------------------------------
   Visible trail to match the BreadcrumbList. No colour is named: these sit on grounds the
   scroll interactions animate between cream and near-black, so they inherit whatever the
   section is painting and use alpha for the quieter links. */
.crumbs{ margin:0 0 clamp(20px,2.4vw,34px); color:inherit; }
.crumb-list{ display:flex; flex-wrap:wrap; align-items:baseline; gap:.5em;
  list-style:none; margin:0; padding:0;
  font-family:"Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size:12px; letter-spacing:.08em; text-transform:uppercase; }
.crumb{ display:flex; align-items:baseline; gap:.5em; opacity:.55; }
.crumb:last-child{ opacity:1; }
.crumb + .crumb::before{ content:"/"; opacity:.5; }
.crumb a{ color:inherit; text-decoration:none; transition:opacity .25s ease; }
.crumb a:hover, .crumb a:focus-visible{ opacity:1; text-decoration:underline; text-underline-offset:3px; }
.crumb a:focus-visible{ outline:2px solid currentColor; outline-offset:3px; }
@media (prefers-reduced-motion:reduce){ .crumb a{ transition:none; } }

/* ============================================================
   PASS 27 (2 Sep 2026): the type ladder, the document hero, the link rows, How we work.
   ============================================================ */

/* ---------- THE TYPE LADDER ----------
   Measured before this pass: eight body sizes (13.5, 16, 17, 18, 20, 22, 23 and 27px),
   section heads in a different face from every other heading (42 Dotsans 400 for
   .text-h4 against Inter Tight for the hero, the service titles and the fold titles),
   display weights split between 400 and 500, and the breadcrumbs asking for a face
   spelt "GeistMono" that no @font-face declares, so they rendered in the system mono.
   One ladder, referenced everywhere below. Display is Inter Tight 500. Body is
   42 Dotsans 400. Labels and numerals are Geist Mono. Nothing else. */
:root{
  --f-label: 12px;                          /* mono uppercase labels, the floor */
  --f-small: 13.5px;                        /* a caption, a source line, a footnote */
  --f-body:  16px;                          /* controls, blurbs, footer */
  --f-read:  clamp(16px, 1.25vw, 17px);     /* long-form reading inside a fold */
  --f-lead:  clamp(17px, 1.55vw, 20px);     /* a section's opening line */
  --f-quote: clamp(18px, 1.9vw, 25px);      /* a fold title, a review, an answer */
  --f-h3:    clamp(24px, 2.4vw, 34px);      /* a section head */
  --f-h2:    clamp(30px, 4.4vw, 62px);      /* a service title, a guide row */
  --f-h1:    clamp(34px, 5.4vw, 84px);      /* a document title on the hero */
  --lh-tight: 1.02; --lh-quote: 1.22; --lh-read: 1.6; --lh-lead: 1.5;
  --ink-2: color-mix(in srgb, currentColor 78%, transparent);   /* secondary text */
  --ink-3: color-mix(in srgb, currentColor 56%, transparent);   /* tertiary text, labels */
  --rule-1: color-mix(in srgb, currentColor 22%, transparent);  /* a boundary rule */
  --rule-2: color-mix(in srgb, currentColor 14%, transparent);  /* a row rule */
}
/* section heads: the display face, one weight, one size */
h2.text-h4, .text-h4{ font-family:var(--font--heading-serif); font-weight:500;
  font-size:var(--f-h3); line-height:1.08; letter-spacing:-0.022em; }
/* every Inter Tight heading carries the same weight the hero does */
.text-h1, .text-h3, .fold-title, .testimonial-quote, .doc-h1{ font-weight:500; }
.fold-title{ font-size:var(--f-quote); line-height:var(--lh-quote); }
/* a review is read, not scanned: lead size, so the longest one stays a card and not a column */
.testimonial-quote{ font-size:var(--f-lead); line-height:1.32; }
.fold-pad{ font-size:var(--f-read); line-height:var(--lh-read); }
.close-lead, .figclaim{ font-size:var(--f-lead); line-height:var(--lh-lead); }
.figclaim{ letter-spacing:-0.008em; }
.testimonial-src, .faq-head .faq-note{ font-size:var(--f-small); }
.right-service-item div, .enqmail{ font-size:var(--f-body); }
/* the template's body copy runs 20px on a 24px line, which is display leading on body text */
.home-work-section .hww-right, .home-overlap-section p{ font-size:var(--f-body); line-height:1.55; }
/* the label line-height collapses to 10px on a phone in the template; 12px type needs air */
.label-small{ line-height:1.35 !important; }

/* ---------- THE DOCUMENT HERO ----------
   The guides, the legal pages and the thank-you page on the same inset video frame as
   every other first screen: headline low in the frame, terms in the corners, the frame's
   8px of section ground showing around it. `.hero-doc-short` trims the frame to about
   three quarters of the viewport so the answer is one scroll away, never two. */
.section.hero-home-section.hero-doc-short{ min-height:clamp(560px, 76vh, 900px); }
.hero-doc .home-hero-wrap{ justify-content:flex-end; }
.hero-doc .doc-head{ max-width:none; }
.hero-doc .crumbs{ color:#F8F7F3; margin-bottom:clamp(22px,2.6vw,36px); }
.doc-h1{ font-family:var(--font--heading-serif); font-weight:500;
  font-size:var(--f-h1); line-height:var(--lh-tight); letter-spacing:-0.03em;
  max-width:18ch; color:#F8F7F3; margin:0; text-wrap:balance; }
.doc-lead{ font-size:var(--f-lead); line-height:var(--lh-lead);
  color:rgba(248,247,243,0.82); max-width:54ch; margin:clamp(18px,2.2vw,28px) 0 0; }
.hero-doc .home-hero-bottom-tile{ margin-top:clamp(28px,4vw,56px); }
@media (max-width:767px){ .doc-h1{ max-width:none; } }

/* ---------- THE ANSWER BAND ----------
   "In short", the block written to be lifted whole by an assistant. It wears the proof
   band's two rules and mono label, so the answer reads as evidence, not as an intro. */
/* the band sits one boundary below the frame and one above the folds, like every other
   section: measured 73px and 249px ink to ink before these two lines, against the
   site's 180 to 200 */
.guide-answer-section{ padding-top:clamp(20px,2.4vw,34px) !important; padding-bottom:clamp(20px,2.4vw,36px) !important; }
.answer.figband{ display:block; }
.answer .figlab{ margin-bottom:clamp(16px,2vw,24px); }
.answer p{ font-family:var(--font--heading-serif); font-weight:500;
  font-size:var(--f-quote); line-height:var(--lh-quote); letter-spacing:-0.018em;
  max-width:58ch; color:#F8F7F3; margin:0; }
.answer p + p{ margin-top:0.7em; }
.answer p b, .answer p strong{ font-weight:600; }

/* ---------- LINK ROWS ----------
   A services row that is a link. The whole row is the target and it says so on the
   right, in words, before anyone hovers. Hovering does what the services list does. */
.service-link{ text-decoration:none; color:inherit; }
.svc-go{ display:block; margin-top:clamp(12px,1.4vw,18px); font-family:"Geist Mono",ui-monospace,monospace;
  font-size:var(--f-label); letter-spacing:0.08em; text-transform:uppercase; opacity:.62;
  transition:opacity .3s ease; }
.service-link:hover .svc-go, .service-link:focus-visible .svc-go{ opacity:1; }
.service-link:focus-visible{ outline:2px solid currentColor; outline-offset:6px; border-radius:2px; }
/* a guide title is a sentence, so it wraps; the column gives it the room */
.home-services-section .service-link .text-h1{ font-size:clamp(26px,3.4vw,50px); max-width:18ch; }
@media (max-width:767px){ .home-services-section .service-link .text-h1{ font-size:clamp(24px,6.6vw,34px); max-width:none; } }

/* ---------- HOW WE WORK ----------
   Was a chip on the left and a wall of two paragraphs on the right. Same facts, set so
   they can be read in a glance: a claim in the display face, three numbered questions,
   four terms. The chip stays because the scroll interaction animates it. */
.hww{ align-items:start; }
.hww-left{ flex-direction:column; align-items:flex-start; gap:clamp(22px,2.6vw,34px); }
.hww-claim{ font-family:var(--font--heading-serif); font-weight:500;
  font-size:var(--f-h3); line-height:1.12; letter-spacing:-0.022em; max-width:22ch; margin:0; }
.hww-list{ list-style:none; margin:0; padding:0; border-top:1px solid var(--rule-1); }
.hww-list li{ display:grid; grid-template-columns:40px minmax(0,1fr); gap:clamp(12px,1.6vw,22px);
  align-items:baseline; padding:clamp(14px,1.6vw,20px) 0; border-bottom:1px solid var(--rule-2); }
.hww-n{ font-family:"Geist Mono",ui-monospace,monospace; font-size:var(--f-label); opacity:.56;
  font-variant-numeric:tabular-nums; }
.hww-list b{ font-weight:500; display:block; margin-bottom:2px; }
.hww-terms{ list-style:none; margin:clamp(22px,2.6vw,34px) 0 0; padding:0; display:grid;
  grid-template-columns:repeat(2,minmax(0,1fr)); gap:clamp(10px,1.2vw,16px) clamp(20px,2.4vw,32px);
  color:var(--ink-2); font-size:var(--f-small); line-height:1.5; }
.hww-terms li{ padding-left:14px; position:relative; }
.hww-terms li::before{ content:""; position:absolute; left:0; top:.62em; width:6px; height:1px; background:currentColor; opacity:.7; }
@media (max-width:767px){ .hww-terms{ grid-template-columns:1fr; } }

/* ---------- the FAQ tail is a short list now: the guide link plus the hub ---------- */
.faq-more{ display:flex; flex-direction:column; gap:clamp(10px,1.2vw,14px); align-items:flex-start; }

/* ---------- the note clears the sticky bar ----------
   The bar is 52px of button plus 20px of padding plus the safe area. The note was
   sitting at 56px and overlapping it on every phone. */
@media (max-width:620px){ .note-pop{ bottom:calc(74px + 12px + env(safe-area-inset-bottom,0px)); } }
@media (min-width:621px) and (max-width:1023px){ .note-pop{ bottom:calc(74px + 16px + env(safe-area-inset-bottom,0px)); } }

/* ---------- the reviews slider ----------
   Height follows the visible quotes (flovo.js) rather than the tallest one in the deck. */
.slider-journal .w-slider-mask{ transition:height .55s cubic-bezier(.22,.61,.36,1); }
@media (prefers-reduced-motion:reduce){ .slider-journal .w-slider-mask{ transition:none; } }

/* ---------- titles never break mid-word ----------
   The template sets `word-break:break-all` on .left-service-item under 600px, which is
   how "Shopify" rendered as "Shopif / y" and "named" as "name / d" on the guides index
   at 390. Words break between words. */
.home-services-section .left-service-item{ word-break:normal; overflow-wrap:break-word; }
.home-services-section .service-item .text-h1{ overflow-wrap:break-word; hyphens:manual; }

/* ---------- the document hero scrim ----------
   Solved, not chosen. The document title sits higher in a shorter frame than the offer
   headline does, across the brightest band of the clip, and the lead and the breadcrumb
   are small type that needs 4.5:1. Sampled behind the ink over seven frames of the loop:
   title 2.73:1, lead 4.17:1, crumbs 3.06:1 under the offer pages' scrim. This gradient
   is heavier through the middle where the words are and re-measured to clear AA on the
   worst frame at both widths. */
.hero-doc .video-home-hero::after{
  background:linear-gradient(180deg, rgba(8,8,10,0.66) 0%, rgba(8,8,10,0.58) 30%, rgba(8,8,10,0.74) 55%, rgba(8,8,10,0.84) 78%, rgba(8,8,10,0.88) 100%);
}
/* the hub heroes read 2.99:1 on the first frame at 1440: the floor of the offer scrim comes
   up two points so the headline clears 3:1 on every frame, not on most of them */
.hero-vid:not(.hero-doc) .video-home-hero::after{
  background:linear-gradient(180deg, rgba(8,8,10,0.62) 0%, rgba(8,8,10,0.42) 40%, rgba(8,8,10,0.74) 100%);
}

/* the two links under a FAQ are targets, so they clear 44px like every other control;
   measured 19px tall across 132 runs once they stopped being inline text */
.faq-more-link{ min-height:44px; display:inline-flex; align-items:center; }
.faq-more{ gap:0; }

/* ---------- inside a fold, the reading size holds ----------
   The guide bodies carry raw <p>, <li> and <h3> from the source, and the template sizes
   a bare p at 20px on a 24px line and a bare h3 at 40px. Inside a fold they take the
   fold's reading size, and a sub-heading steps up one rung, not four. */
.fold-pad p, .fold-pad li, .fold-pad a, .fold-pad strong, .fold-pad b{ font-size:inherit; line-height:inherit; }
.fold-pad h3, .fold-pad h4{ font-family:var(--font--heading-serif); font-weight:500;
  font-size:var(--f-quote); line-height:var(--lh-quote); letter-spacing:-0.018em;
  margin:1.5em 0 .55em; max-width:40ch; }
.fold-pad > h3:first-child, .fold-pad > h4:first-child{ margin-top:0; }
/* the honeypot is off screen; it does not need the browser's inset border either */
.hp input{ border:0; }


/* ---------- THE CLOSE, FRAMED ----------
   Dom, 3 Sep: "the forms at the bottom of every page being black on black, its a little
   unclear its trying to get an enquiry... have a border around that section similar to
   the hero video border". The block sits in the same 8px frame the hero video sits in:
   a lifted panel with the site's hairline, the radius the frame uses, and the ground one
   step up from the page so the form reads as an object, not as more page. Field rules
   and placeholders come up a step for the same reason. */
main > section.close-section{ padding:0 var(--_spacing---spacing--8) clamp(58px,6.4vw,104px); }
.close-panel{
  border-radius:var(--radius--radius-3, 8px);
  background:linear-gradient(168deg, #1B1B1F 0%, #141417 62%);
  border:1px solid rgba(248,247,243,0.16);
  box-shadow:inset 0 1px 0 rgba(248,247,243,0.10);
  padding-block:clamp(48px,5.6vw,88px);
}
.close-section .enq, .close-section .enqmeta{ border-top-color:rgba(248,247,243,0.30); }
.close-section .enqrow{ border-bottom-color:rgba(248,247,243,0.22); }
.close-section .field input::placeholder, .close-section .field textarea::placeholder{ color:rgba(255,255,255,0.46); }
.close-section .field label{ color:rgba(255,255,255,0.68); }
.close-section .enqn{ color:rgba(255,255,255,0.6); }
@media (max-width:767px){ .close-panel{ padding-block:clamp(40px,8vw,56px); } }

/* the light end of the homepage scrubs is the site's paper now (webflow.js payload edited
   3 Sep); the resting ground for What we do matches, so a page with JS off agrees */
.section.home-services-section{ background-color:var(--_colors---color--light); }

/* ---- the menu, v4 (26 Oct 2026) ----
   Eight items now, not six. At the template's h1 size the list ran past the bottom of a
   phone, "Google Business Profile" wrapped to three lines, and the h1 line-height cut the
   descenders off g and y. Sized to the viewport height so all eight fit, with room under
   each line for its descenders, and the panel scrolls rather than clipping if a short
   screen still cannot hold it. */
.nav-menu{overflow-y:auto;overscroll-behavior:contain}
.nav-menu .menu-list{grid-row-gap:clamp(6px,1.2vh,14px);padding-top:clamp(84px,12vh,120px);padding-bottom:32px}
.nav-menu .nav-link{font-size:clamp(26px,min(4.6vh,7.4vw),64px);line-height:1.14;padding-bottom:.08em;overflow:visible}

/* ---- the call button, v4 (26 Oct 2026) ----
   Trades call, they do not fill in forms. Dom's mobile sits beside the main button in the
   phone bar, outlined so the form stays the primary action. */
.moneybar{ display:flex; gap:8px; align-items:stretch; }
.moneybar .mb-cta{ flex:1 1 auto; width:auto; }
.mb-call{ flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center;
  min-height:52px; padding:0 22px; border-radius:6px; border:1px solid rgba(248,247,243,0.55);
  color:#F8F7F3; font-family:"Geist Mono",ui-monospace,monospace; font-size:13px;
  letter-spacing:.08em; text-transform:uppercase; text-decoration:none; background:rgba(8,8,10,0.6); }
.mb-call:focus-visible{ outline:2px solid #F8F7F3; outline-offset:2px; }

/* Footer close band under 768: the template stacks the two lines and centres the column,
   which left "Ask for the free report." indented under a left-aligned first line (seen
   at 320 to 767 on every page, 3 Nov 2026). Both lines start at the gutter. */
@media (max-width:767px){
  .footer-mid-tile{ align-items:flex-start; }
}

/* Headings and the close-block statements keep their last line from being a single
   word. Browsers without text-wrap:pretty ignore it. */
h2, h3, .text-h2, .text-h3, .text-h4{ text-wrap:pretty; }
