/* ============================================================
   Novatrix Digital
   1. tokens          7. selected work
   2. base            8. who it's for + process
   3. buttons         9. pricing
   4. header         10. testimonials
   5. hero           11. contact + form
   6. sections       12. faq, cta, footer, responsive
   ============================================================ */

/* ── 1. tokens ───────────────────────────────────────────── */
:root{
  color-scheme:light;          /* the design is light-only — don't let the OS invert controls */
  accent-color:#e2952b;

  /* surface + text
     The two greys are set at real contrast ratios on --paper, not by eye:
     --slate ≈ 6.6:1 and --slate-lt ≈ 4.8:1, so even the smallest caption
     clears WCAG AA. Going lighter than this is what made the old small
     labels wash out on bright screens. */
  --ink:#0e1a2b;
  --ink-2:#1c2b40;
  --ink-hi:#1a2c45;            /* one step up from ink — pressed/hover fills */
  --slate:#4c5c70;
  /* Was #65717f, which measured 4.33:1 on --paper-2 — under the 4.5:1 WCAG AA
     floor for normal text, so the "Delivered" labels in the work section were
     failing. It passed on --paper (4.77:1); the darker panel is what tipped
     it. Darkened one step to clear the floor on every surface it lands on:
     4.73:1 on --paper-2, 5.20:1 on --paper, 5.43:1 on white. */
  --slate-lt:#5f6b79;
  --paper:#fbfaf7;
  --paper-2:#f2efe9;
  --foot:#0a1522;
  --line:#e3ded4;
  --line-2:#cfc8bb;
  --white:#fff;

  /* accents */
  --amber:#e2952b;
  --amber-hi:#f2ab48;
  --amber-lt:#f7dfb4;
  --amber-dp:#a3691a;          /* dark enough to read as a mark on --amber-lt */
  --blue:#1b4b7f;

  /* states */
  --green:#1f8a4c;
  --green-hi:#1a7541;
  --danger:#c0432f;
  --danger-bg:#fdf5f3;
  --success:#1c6b46;

  --shadow-sm:0 1px 2px rgba(14,26,43,.06), 0 2px 8px rgba(14,26,43,.04);
  --shadow-md:0 2px 4px rgba(14,26,43,.05), 0 12px 28px -12px rgba(14,26,43,.16);
  --shadow-lg:0 4px 8px rgba(14,26,43,.06), 0 28px 56px -20px rgba(14,26,43,.22);

  /* type ────────────────────────────────────────────────────
     Two families, each doing one job:
       Fraunces — an editorial serif, on the display headings, the
                  wordmark, the prices and the quotes. This is where the
                  "premium" reads from; it is never used below ~1rem.
       Inter    — everything you actually read or click: body copy, nav,
                  buttons, labels, form fields, card titles.
     Both are loaded from Google Fonts in each page's <head>. The names
     after them are the fallbacks that show while the files fetch, or if
     the visitor is offline — so nothing ever renders unstyled.

     To try a different display face, change --display in one place:
     "Playfair Display" (sharper, more formal) and "Cormorant Garamond"
     (lighter, more classical) both drop in without touching anything
     else — just swap the family name in the <head> link too. */
  --display:"Fraunces",Georgia,"Iowan Old Style","Times New Roman",serif;
  --serif:"Fraunces",Georgia,"Iowan Old Style","Times New Roman",serif;
  --sans:"Inter","Segoe UI Variable Text","Segoe UI",-apple-system,BlinkMacSystemFont,
         "Helvetica Neue",Arial,sans-serif;
  --mono:"Cascadia Mono",ui-monospace,"SFMono-Regular",Consolas,monospace;

  /* The page's content column. It follows the screen instead of sitting at a
     fixed 1180px, which on a wider monitor left a band of empty paper down
     each side while the layout stayed the same size.

     92vw is the lower of the two on any screen narrower than about 1565px, so
     the column simply grows with the window. Past that the 1440px cap takes
     over, and it is a readability limit rather than a stylistic one: a line of
     text much beyond that is measurably harder to read, because the eye loses
     its place travelling back to the start of the next line. So a very wide
     monitor still shows margins — deliberately. */
  --wrap:min(92vw,1440px);
  --gut:clamp(20px,5vw,64px);
  --r-sm:10px;
  --r:14px;
  --r-lg:22px;
  --r-pill:100px;
  --ease:cubic-bezier(.22,.61,.36,1);

  --focus:var(--amber);
  --focus-w:2.5px;
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth;scroll-padding-top:96px}
/* Edge and Chrome on Windows let the whole page rubber-band away from the cursor when
   you press and drag past the top or the side — the page slides, then springs back.
   This turns that off: the page scrolls, but it never leaves its own edges. */
html{overscroll-behavior:none}
@media (prefers-reduced-motion:reduce){html{scroll-behavior:auto}}

body{
  margin:0;
  font-family:var(--sans);
  /* never below 16px, on any screen — that is the floor where body copy
     stops being a squint on a phone (and the size iOS stops zooming at) */
  font-size:clamp(16px,.36vw + 14.9px,17px);
  line-height:1.65;
  color:var(--ink-2);
  background:var(--paper);
  font-optical-sizing:auto;
  font-synthesis:none;        /* use the real italic/bold cuts, never a faked slant */
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  overflow-x:hidden;
  overflow-x:clip;            /* clip, not hidden: hidden still leaves the page pannable
                                 sideways by drag even with no scrollbar. hidden stays
                                 above it as the fallback for older browsers. */
  overscroll-behavior:none;
  overflow-wrap:break-word;   /* a long email or URL can't blow out a 320px screen */
}
/* set while the mobile menu is open, so the page behind it can't scroll */
body.nav-open{overflow:hidden}

/* ── 2. base ─────────────────────────────────────────────── */
h1,h2,h3,h4{
  margin:0;color:var(--ink);
  font-weight:600;line-height:1.16;letter-spacing:-.018em;
  text-wrap:balance;
}
/* Only the two display sizes take the serif. h3 and h4 are card titles and
   small labels — at those sizes a serif goes muddy, so they stay in the sans
   and keep their edge. That split is the whole type system. */
h1,h2{font-family:var(--display);font-optical-sizing:auto}
h1{font-size:clamp(2.1rem,4.5vw,3.5rem);line-height:1.07;letter-spacing:-.022em}
h2{font-size:clamp(1.62rem,2.85vw,2.4rem);line-height:1.13}
h3{font-size:1.16rem;font-weight:620;line-height:1.34;letter-spacing:-.014em}
/* The small uppercase label. It used to be carried by the <h4> tag, but every
   place the site used one sat directly under an <h2>, which skips a level and
   reads as a gap in the outline to a screen reader. The tags are <h3> now and
   the look moved onto this class, so the heading level and the type size are
   no longer forced to agree. h4 keeps the rule because a synced article can
   still produce one from a deep Medium heading. */
h4,.h-label{font-size:.8rem;font-weight:640;letter-spacing:.1em;text-transform:uppercase}
p{margin:0}
/* the italic phrase inside a headline: same family, real italic, one notch
   lighter — it reads as a change of voice rather than a change of font */
h1 em,h2 em{
  font-family:var(--display);font-style:italic;font-weight:450;letter-spacing:-.004em;
}
a{color:inherit;text-decoration:none}
ul,ol{margin:0;padding:0;list-style:none}
img,svg{max-width:100%}
/* Stop the browser picking an image up and floating a copy under the cursor when
   someone click-drags. Real content stays selectable — this only kills image drag. */
img,svg{-webkit-user-drag:none}
button{
  font:inherit;color:inherit;border:0;background:none;cursor:pointer;
  text-align:inherit;-webkit-tap-highlight-color:transparent;
}
button,a{touch-action:manipulation}   /* no 300ms delay waiting for a double-tap */
input,select,textarea{font:inherit;color:inherit}

/* One focus ring for the whole site. It must NOT set border-radius —
   the outline already follows each element's own shape, and forcing a
   radius here squares off the pill buttons and round icon buttons. */
:focus-visible{outline:var(--focus-w) solid var(--focus);outline-offset:3px}
/* amber ring on an amber button is invisible — go white on those */
.plan-hero .btn:focus-visible,.btn-light:focus-visible{outline-color:var(--white)}

/* Anyone who asks for less motion gets less motion, everywhere. */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms!important;
    animation-iteration-count:1!important;
    transition-duration:.01ms!important;
    scroll-behavior:auto!important;
  }
}

.wrap{width:100%;max-width:var(--wrap);margin-inline:auto;padding-inline:var(--gut)}
.sec{padding-block:clamp(58px,7.5vw,108px)}

.skip{
  position:absolute;left:14px;top:-60px;z-index:200;
  padding:10px 18px;border-radius:0 0 10px 10px;
  background:var(--ink);color:var(--white);font-size:.9rem;
  transition:top .2s var(--ease);
}
.skip:focus{top:0}

/* eyebrow / labels */
.eyebrow{
  display:flex;align-items:center;gap:10px;
  margin-bottom:16px;
  font-size:.76rem;font-weight:640;letter-spacing:.12em;text-transform:uppercase;
  color:var(--slate);
}
.eyebrow .dash{width:26px;height:2px;background:var(--amber);flex:none}
.eyebrow.light{color:rgba(255,255,255,.74)}
/* for a long, list-style eyebrow: tighter tracking and a real line-height so
   it reads as a strapline when it wraps, instead of a stack of shouting */
.eyebrow.long{letter-spacing:.085em;line-height:1.75;align-items:start}
.eyebrow.long .dash{margin-top:.72em}

.sec-head{max-width:660px;margin-bottom:clamp(34px,4vw,58px)}
/* Grid, not flex: as a flex row these two would wrap the moment the heading
   and the note together exceeded the 1052px content width, dropping the note
   onto its own line with the flex gap left hanging above it. */
.sec-head.split{
  display:grid;grid-template-columns:minmax(0,1.3fr) minmax(0,1fr);
  gap:clamp(24px,4vw,72px);align-items:end;max-width:none;
}
.sec-head.split .sec-note{margin-top:0}
.sec-head.split>div{max-width:640px}
.sec-note{margin-top:18px;color:var(--slate);max-width:44ch;font-size:1.03em;line-height:1.62}
.sec-note.wide{max-width:56ch}
.sec-note a{color:var(--blue);text-decoration:underline;text-decoration-thickness:1.5px;text-underline-offset:3px}

/* ── 3. buttons ──────────────────────────────────────────────
   Every variant is just three custom properties: resting fill,
   hover fill, text. Nothing below re-declares background or
   colour directly, so states stay in sync automatically.       */
.btn{
  --bg:var(--ink);
  --bg-hi:var(--ink-hi);
  --fg:var(--white);
  --ring:transparent;
  display:inline-flex;align-items:center;gap:9px;
  padding:14px 24px;border-radius:var(--r-pill);
  background:var(--bg);color:var(--fg);
  font-size:.95rem;font-weight:600;letter-spacing:-.008em;white-space:nowrap;
  box-shadow:var(--shadow-sm);
  transition:transform .22s var(--ease),box-shadow .22s var(--ease),
             background-color .18s,color .18s;
}
.btn svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round;transition:transform .22s var(--ease)}
.btn:hover{transform:translateY(-2px);box-shadow:var(--shadow-md);background:var(--bg-hi)}
.btn:hover svg{transform:translateX(3px)}
.btn:active{transform:translateY(0) scale(.985)}

.btn-sm{padding:10px 19px;font-size:.88rem}
.btn-ghost{
  --bg:transparent;--fg:var(--ink);--bg-hi:var(--paper-2);--ring:var(--line-2);
  box-shadow:inset 0 0 0 1.5px var(--ring);
}
.btn-ghost:hover{--ring:var(--ink);box-shadow:inset 0 0 0 1.5px var(--ring)}
.btn-light{--bg:var(--white);--fg:var(--ink);--bg-hi:var(--amber-lt)}
.btn-wa{--bg:var(--green);--fg:var(--white);--bg-hi:var(--green-hi);margin-top:26px}
/* the two calls to action under the contact details */
.contact-actions{display:flex;flex-wrap:wrap;gap:12px;align-items:center}
.contact-actions .btn-wa{margin-top:26px}
.contact-actions .btn-ghost{margin-top:26px}
.btn-wa:hover svg{transform:none}       /* leading icon — nothing to point at */
.btn-full{width:100%;justify-content:center}

/* the submit button really does go disabled while a message is in flight */
.btn:disabled,.btn[aria-disabled="true"]{
  background:var(--bg);opacity:.5;cursor:not-allowed;
  transform:none;box-shadow:var(--shadow-sm);
}
.btn:disabled svg{transform:none}

/* on touch there is no pointer to leave, so a lift on tap just sticks */
@media (hover:none){
  .btn:hover,.why-card:hover,.svc:hover,.case:hover,.work-list li:hover,
  .social a:hover,.to-top:hover,.arrows button:hover{transform:none}
}

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

/* no JavaScript: everything that JS would normally open stays open */
.no-js .acc-panel{height:auto}
.no-js .chart .line{stroke-dashoffset:0}
.no-js .chart .area,.no-js .chart .pts circle{opacity:1}
.no-js .slides{display:block}
.no-js .slide+.slide{margin-top:38px;padding-top:34px;border-top:1px solid var(--line)}
.no-js .slider-ui,.no-js .burger{display:none}

/* ── 3. header ───────────────────────────────────────────── */
.site-head{
  position:sticky;top:0;z-index:100;
  background:rgba(251,250,247,.82);
  backdrop-filter:saturate(140%) blur(12px);
  border-bottom:1px solid transparent;
  transition:border-color .3s,box-shadow .3s,background .3s;
}
.site-head.stuck{border-bottom-color:var(--line);box-shadow:0 6px 24px -18px rgba(14,26,43,.5)}
.head-inner{display:flex;align-items:center;justify-content:space-between;gap:24px;
  min-height:70px}

.brand{display:inline-flex;align-items:center;gap:11px;flex:none}
/* The NX artwork is drawn to glow on a dark field: on the cream header its silver
   half all but vanishes. Keeping it on the same dark tile the old lettered mark used
   solves that and lets one lockup serve both the header and the footer — which is
   the point, since two different marks on one page read as two different companies.
   The wordmark beside it stays live text in the serif: it stays sharp at any zoom,
   is selectable and searchable, and costs nothing to load. */
.brand-mark{
  display:grid;place-items:center;width:37px;height:37px;flex:none;
  border-radius:9px;background:var(--ink);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.08);
}
/* the artwork carries its own margin, so it is inset from the tile edge */
.brand-mark img{display:block;width:29px;height:auto}
/* block, not a flex column: as a flex column the name and the strapline became two
   separate flex items and sat side by side. Inline text plus a block <small> stacks
   them, which is the shape the lockup needs. */
.brand-text{
  display:block;line-height:1.05;
  font-family:var(--display);
  font-size:1.26rem;font-weight:600;letter-spacing:-.012em;color:var(--ink);
  /* "Novatrix Digital" is two words and must never break across them */
  white-space:nowrap;
}
/* The strapline stays at every width. It used to be hidden below 480px, which left
   the phone showing a different logo from the desktop — a bare name against the
   full lockup. It has to hold one line at "Shopify & Digital Marketing", which is ten
   characters longer than the old wording, so the size and the tracking both ease off
   on the way down: at the 320px floor the whole lockup measures 224px and still clears
   the burger. */
.brand-text small{
  display:block;font-family:var(--sans);
  font-size:clamp(.55rem,1.9vw,.6rem);
  font-weight:600;letter-spacing:.12em;text-transform:uppercase;
  color:var(--slate-lt);margin-top:5px;
  /* one line, always: broken over two it stops reading as part of the mark */
  white-space:nowrap;
}

.nav{display:flex;align-items:center;gap:clamp(12px,2.4vw,34px)}
.nav>ul{display:flex;align-items:center;gap:clamp(10px,1.9vw,28px)}
/* scoped to the list, so the CTA button keeps its own button styling
   instead of needing an !important to win the colour back */
.nav>ul a{
  position:relative;padding:6px 2px;
  font-size:.94rem;font-weight:540;letter-spacing:-.006em;color:var(--ink-2);
  transition:color .18s;
}
.nav>ul a::after{
  content:"";position:absolute;left:0;right:100%;bottom:0;height:1.5px;
  background:var(--amber);transition:right .28s var(--ease);
}
.nav>ul a:hover{color:var(--ink)}
.nav>ul a:hover::after,.nav>ul a.is-here::after{right:0}

.burger{display:none;width:42px;height:42px;border-radius:10px;padding:11px 9px}
.burger span{display:block;height:2px;background:var(--ink);border-radius:2px;
  transition:transform .28s var(--ease),opacity .2s}
.burger span+span{margin-top:5px}
.burger[aria-expanded="true"] span:nth-child(1){transform:translateY(7px) rotate(45deg)}
.burger[aria-expanded="true"] span:nth-child(2){opacity:0}
.burger[aria-expanded="true"] span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}

/* ── 4. hero ─────────────────────────────────────────────── */
.hero{position:relative;padding-top:clamp(44px,6vw,80px);overflow:hidden}
.hero::before{
  content:"";position:absolute;inset:0;z-index:-1;
  background:
    radial-gradient(680px 420px at 88% -6%,rgba(226,149,43,.13),transparent 68%),
    radial-gradient(560px 380px at 4% 0%,rgba(27,75,127,.09),transparent 70%);
}
.hero-grid{
  display:grid;grid-template-columns:minmax(0,1.14fr) minmax(0,.86fr);
  gap:clamp(32px,5vw,72px);align-items:center;
  padding-bottom:clamp(48px,6vw,84px);
}
.hero h1{margin-bottom:22px}
.lede{
  font-size:1.13em;line-height:1.55;letter-spacing:-.006em;
  color:var(--slate);max-width:50ch;
}
/* the detail paragraph under a lede — steps down in size so the opening
   sentence stays the thing the eye lands on */
.hero-sub{margin-top:16px;font-size:.98em;line-height:1.62;color:var(--slate);max-width:58ch}
.hero-actions{display:flex;gap:12px;flex-wrap:wrap;margin-top:32px}

.hero-proof{
  display:flex;flex-wrap:wrap;gap:clamp(20px,3.4vw,46px);
  margin-top:clamp(38px,4.5vw,54px);padding-top:26px;
  border-top:1px solid var(--line);
}
.hero-proof li{display:flex;flex-direction:column;gap:2px}
.hero-proof strong{
  font-family:var(--display);
  font-size:1.6rem;font-weight:600;letter-spacing:-.018em;color:var(--ink);
}
.hero-proof span{font-size:.86rem;color:var(--slate);max-width:15ch;line-height:1.42}

/* hero panel */
.hero-panel{position:relative}
.panel-card{
  background:var(--white);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:var(--shadow-lg);overflow:hidden;
}
.panel-top{
  display:flex;align-items:center;justify-content:space-between;
  padding:13px 18px;border-bottom:1px solid var(--line);background:var(--paper-2);
}
.dot-row{display:flex;gap:6px}
.dot-row i{width:9px;height:9px;border-radius:50%;background:var(--line-2)}
.dot-row i:first-child{background:var(--amber)}
.panel-label{font-family:var(--mono);font-size:.7rem;letter-spacing:.1em;
  text-transform:uppercase;color:var(--slate-lt)}
/* the extra bottom padding is clearance for the badge that overlaps the
   card's bottom edge — without it the badge sits on top of the foot text */
.panel-body{padding:22px 22px 42px}
.panel-foot{
  margin-top:22px;padding-top:16px;border-top:1px dashed var(--line);
}
.panel-foot strong{display:block;font-size:.88rem;font-weight:600;color:var(--ink)}
.panel-foot span{font-size:.8rem;color:var(--slate)}

/* Sits inside the card's own width on purpose. Hanging it off the left edge
   put it under the page gutter, where body{overflow-x:hidden} clipped it. */
.panel-badge{
  position:absolute;right:20px;bottom:-21px;left:auto;
  display:flex;align-items:center;gap:9px;
  padding:11px 17px;border-radius:var(--r-pill);
  background:var(--ink);color:#fff;box-shadow:var(--shadow-md);
  font-size:.74rem;line-height:1.34;font-weight:580;letter-spacing:.005em;
}
.panel-badge svg{width:22px;height:22px;flex:none;fill:var(--amber);stroke:none}

/* client strip */
.strip{
  border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  background:var(--paper-2);
  padding-block:20px;
}
.strip-label{
  text-align:center;margin-bottom:12px;
  font-size:.72rem;letter-spacing:.14em;text-transform:uppercase;color:var(--slate-lt);
}
/* The strip scrolls on its own, so dragging across it only ever produced a smeared
   selection that followed the cursor. Nothing in here is worth copying. */
.marquee{
  overflow:hidden;mask-image:linear-gradient(90deg,transparent,#000 9%,#000 91%,transparent);
  user-select:none;-webkit-user-select:none;
}
.marquee-track{
  display:flex;align-items:center;gap:26px;width:max-content;
  animation:slide 42s linear infinite;
}
.marquee:hover .marquee-track{animation-play-state:paused}
/* one tool = optional logo + its name. If the logo file isn't there yet,
   js/main.js drops the <img> and the name stands on its own. */
.tool{display:flex;align-items:center;gap:10px;white-space:nowrap}
.tool-logo{
  display:none;   /* main.js reveals it only once the file loads */
  width:24px;height:24px;flex:none;object-fit:contain;
}
.marquee-track span{
  font-size:1.02rem;font-weight:580;letter-spacing:-.015em;
  color:var(--slate);white-space:nowrap;
}
/* the gap does the separating now that each item is a logo + label pair */
.marquee-track{gap:34px}
@keyframes slide{to{transform:translateX(-50%)}}
@media (prefers-reduced-motion:reduce){
  .marquee-track{animation:none;flex-wrap:wrap;justify-content:center;width:auto}
}

/* ── 5. why us ───────────────────────────────────────────── */
.why-grid{
  display:grid;grid-template-columns:repeat(3,1fr);
  gap:clamp(16px,2vw,26px);
}
.why-card{
  padding:30px 26px 28px;border-radius:var(--r);
  background:var(--white);border:1px solid var(--line);
  transition:transform .28s var(--ease),box-shadow .28s var(--ease),border-color .2s;
}
.why-card:hover{transform:translateY(-4px);box-shadow:var(--shadow-md);border-color:var(--line-2)}
.why-num{
  display:block;margin-bottom:20px;
  font-family:var(--mono);font-size:.82rem;letter-spacing:.08em;color:var(--amber);
}
.why-card h3{margin-bottom:10px}
.why-card p{color:var(--slate);font-size:.97em;line-height:1.62}

/* the answer half of a problem card: ruled off from the problem above it and
   set a shade darker, so the eye can tell "here's what's wrong" from
   "here's what we do about it" without a label spelling it out */
.why-fix{
  margin-top:18px;padding-top:16px;
  border-top:1px dashed var(--line-2);
  color:var(--ink-2)!important;
}
.why-fix strong{font-weight:620;color:var(--ink)}

/* four-across variant, used by the SEO/AEO/GEO/LSEO section */
.why-grid.grid-4{grid-template-columns:repeat(4,1fr)}
/* two-across, for the same four channels once each one carries a list */
.why-grid.grid-2{grid-template-columns:repeat(2,1fr)}
.grid-4 .why-num,.grid-2 .why-num{
  font-family:var(--sans);font-size:.72rem;font-weight:660;letter-spacing:.12em;
  margin-bottom:16px;
}
/* five cards over two columns leaves the last one alone — let it run wide
   rather than sit in a half-empty row */
.why-grid.grid-2 .why-card:nth-child(5):last-child{grid-column:1/-1}
/* the badge earns a pill when it sits above a whole block of detail */
.grid-2 .why-num{
  display:inline-block;padding:5px 11px;border-radius:var(--r-pill);
  background:var(--amber-lt);color:var(--amber-dp);text-transform:uppercase;
}

/* the centred statement that closes a section — used under the four search
   channels and under the selected work */
.sec-close{
  margin-top:clamp(30px,4vw,48px);
  padding-top:clamp(28px,3.4vw,40px);
  border-top:1px solid var(--line);
  text-align:center;
}
/* The closing note's heading. On most pages a section h2 sits above it, so it
   is an h3 there. On /reviews nothing does, and an h3 straight after the h1
   skips a level — so that one is an h2. Both are styled identically here,
   including the sans and the h3 weight that an h2 would otherwise override
   with the display serif: the tag is about the outline, this is about size. */
.sec-close h2,.sec-close h3{
  font-size:clamp(1.22rem,2.1vw,1.6rem);margin-bottom:12px;
  font-family:var(--sans);font-weight:620;line-height:1.34;letter-spacing:-.014em;
}
.sec-close p{max-width:66ch;margin-inline:auto;color:var(--slate)}
.sec-close p + p{margin-top:14px;font-size:.92rem;color:var(--slate-lt)}
.sec-close a{
  color:var(--blue);text-decoration:underline;
  text-decoration-thickness:1.5px;text-underline-offset:3px;
}

.ava{
  display:grid;place-items:center;width:42px;height:42px;border-radius:50%;flex:none;
  background:rgba(255,255,255,.1);box-shadow:inset 0 0 0 1px rgba(255,255,255,.18);
  font-size:.8rem;font-weight:640;letter-spacing:.04em;color:#fff;
}

/* ── services ────────────────────────────────────────────── */
.services{background:var(--paper-2);border-block:1px solid var(--line)}
.svc-grid{
  display:grid;grid-template-columns:repeat(6,1fr);
  gap:clamp(16px,1.8vw,24px);
}
.svc{
  grid-column:span 2;
  padding:26px 24px;border-radius:var(--r);
  background:var(--white);border:1px solid var(--line);
  transition:transform .28s var(--ease),box-shadow .28s var(--ease);
}
.svc:hover{transform:translateY(-4px);box-shadow:var(--shadow-md)}
.svc-lg{grid-column:span 3;display:grid;grid-template-columns:150px 1fr;
  gap:26px;align-items:start;padding:26px}
.svc h3{margin-bottom:9px}
.svc p{color:var(--slate);font-size:.96em;line-height:1.6}
/* the "supporting" tag on the two secondary service cards */
.svc h3 small{
  display:inline-block;margin-left:8px;vertical-align:middle;
  padding:3px 8px;border-radius:100px;
  background:var(--paper-2);border:1px solid var(--line);
  font-size:.62rem;font-weight:620;letter-spacing:.1em;text-transform:uppercase;
  color:var(--slate-lt);
}

/* ── service rows ────────────────────────────────────────────
   The three core services run full width so a ten-item "Includes"
   list can sit in two columns; the two supporting ones sit half
   width beside each other, where a single column still fits.     */
.svc-full{grid-column:1/-1}
.svc-half{grid-column:span 3}
.svc-tag{
  margin-bottom:12px;
  font-size:1.03rem;font-weight:600;letter-spacing:-.012em;
  color:var(--ink-2)!important;
}
.incl-label{
  margin-top:20px;padding-top:16px;
  border-top:1px dashed var(--line);
  font-size:.7rem;font-weight:640;letter-spacing:.12em;text-transform:uppercase;
  color:var(--slate-lt)!important;
}
/* when the label is present it owns the rule, so the list drops its own */
.incl-label + .deliver{margin-top:12px;padding-top:0;border-top:0}
.deliver.cols{display:block;columns:2;column-gap:clamp(22px,3.4vw,48px)}
.deliver.cols li{break-inside:avoid;margin-bottom:9px}

/* link out from a summary service card to that service's own page */
.svc-more{margin-top:22px}

/* real product logos sitting on the tinted art panel, each on its own white
   chip so a coloured mark never fights the gradient behind it */
.art-logos{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;align-items:center}
.art-logos li{
  display:grid;place-items:center;width:40px;height:40px;border-radius:11px;
  background:var(--white);box-shadow:var(--shadow-sm);
}
.art-logos img{width:21px;height:21px;object-fit:contain}
.svc-ico img{width:22px;height:22px;object-fit:contain}

.svc-art{border-radius:11px;aspect-ratio:4/3;overflow:hidden;background:var(--paper-2)}
.svc-art img{display:block;width:100%;height:100%;object-fit:cover;transition:transform .45s var(--ease)}
.svc:hover .svc-art img{transform:scale(1.035)}
.art-a{background:linear-gradient(140deg,#eef3f9,#dde8f3)}
.art-b{background:linear-gradient(140deg,#fdf2e2,#f8e4c6)}
.art-c{background:linear-gradient(140deg,#eaf3ee,#d7e8de)}
.svc-art svg{width:100%;height:100%;fill:none;stroke:var(--blue);stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round;opacity:.72}
.art-b svg{stroke:#b9752a}
.art-c svg{stroke:#2f6d52}

.svc-ico{
  display:grid;place-items:center;width:44px;height:44px;border-radius:11px;
  margin-bottom:18px;background:var(--paper-2);
}
.svc-ico svg{width:22px;height:22px;fill:none;stroke:var(--ink);stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round}
.svc:hover .svc-ico{background:var(--amber-lt)}

.deliver{display:grid;gap:8px;margin-top:18px;padding-top:16px;
  border-top:1px dashed var(--line)}
.deliver li{
  position:relative;padding-left:19px;
  font-size:.87rem;line-height:1.5;color:var(--slate);
}
.deliver li::before{
  content:"";position:absolute;left:1px;top:.5em;
  width:9px;height:5px;border-left:1.8px solid var(--amber);
  border-bottom:1.8px solid var(--amber);transform:rotate(-45deg);
}

/* ── metrics band ────────────────────────────────────────── */
.band{background:var(--ink);color:#fff;position:relative;overflow:hidden}
.band::before{
  content:"";position:absolute;inset:0;
  background:radial-gradient(700px 300px at 78% 120%,rgba(226,149,43,.16),transparent 70%);
}
.band .wrap{position:relative}
.band h2{color:#fff;max-width:22ch}
.band-head{margin-bottom:clamp(34px,4vw,54px)}
.stats{display:grid;grid-template-columns:repeat(4,1fr);gap:clamp(20px,2.6vw,34px)}
.stats li{padding-top:22px;border-top:1px solid rgba(255,255,255,.16)}
/* Figures stay in the sans on purpose: these count up in JS, and the sans
   is the family with dependable tabular figures — every digit the same
   width, so nothing jitters while the number climbs. */
.stat{
  display:block;margin-bottom:9px;
  font-size:clamp(2.35rem,4.4vw,3.4rem);font-weight:640;line-height:1;
  letter-spacing:-.03em;color:var(--amber);
  font-variant-numeric:tabular-nums;
}
.stat-title{
  display:block;margin-bottom:7px;
  font-size:1.02rem;font-weight:620;letter-spacing:-.015em;color:#fff;
}
/* 32ch rather than 24: the column width is the real constraint now that each
   figure carries a sentence under it, not three words */
.stats p{font-size:.9rem;color:rgba(255,255,255,.72);max-width:32ch;line-height:1.5}
.band-head .sec-note{color:rgba(255,255,255,.72);max-width:62ch}

/* ── toolkit ─────────────────────────────────────────────── */
.stack{background:var(--paper-2);border-block:1px solid var(--line)}
.stack-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:clamp(18px,2.4vw,30px)}
/* six columns read better as two rows of three than as one cramped row */
.stack-6{grid-template-columns:repeat(3,1fr);gap:clamp(20px,2.6vw,34px) clamp(24px,3.4vw,48px)}
/* four across — grouped rule lists and integration groups */
.stack-4{grid-template-columns:repeat(4,1fr);gap:clamp(20px,2.6vw,34px) clamp(22px,3vw,40px)}
.stack-col h3{
  padding-bottom:11px;margin-bottom:14px;border-bottom:1px solid var(--line-2);
  color:var(--slate);
}
.stack-col li{
  display:flex;align-items:center;gap:11px;
  padding:8px 0;font-size:.94rem;font-weight:540;color:var(--ink-2);
  border-bottom:1px dotted var(--line);
}
.stack-col li:last-child{border-bottom:0}

/* Fixed 18px slot for every row — a product logo or a line icon, either way the
   names stay on one vertical line, including while a logo file is still loading. */
.tool-ico{flex:none;display:grid;place-items:center;width:18px;height:18px}
.tool-ico img{
  width:100%;height:100%;object-fit:contain;
  filter:none;opacity:1;          /* full colour here — the marquee is the greyscale one */
}
.tool-ico svg{
  width:100%;height:100%;fill:none;stroke:var(--slate-lt);
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;
}

/* ── growth chart ────────────────────────────────────────── */
/* Both remaining users of this grid (capabilities on the home page, "who runs
   it" on about) put a .facts table in the right column. Centred, the heading
   floated halfway down while the table started at the top, so the two columns
   never shared a top edge — they line up from the first line now. */
.chart-grid{
  display:grid;grid-template-columns:minmax(0,.92fr) minmax(0,1.08fr);
  gap:clamp(32px,5vw,68px);align-items:start;
}
.delta{margin-top:30px;display:grid;gap:0}
.delta li{
  display:flex;flex-wrap:wrap;gap:6px 20px;align-items:baseline;
  justify-content:space-between;
  padding:16px 0;border-bottom:1px solid var(--line);
}
.delta li:first-child{border-top:1px solid var(--line)}
.delta-label{font-size:.96rem;font-weight:550;color:var(--ink)}
.delta-val{display:inline-flex;align-items:center;gap:11px}
.delta-val em{font-style:normal;color:var(--slate-lt);font-size:.95rem}
.delta-val strong{
  font-size:1.3rem;font-weight:640;letter-spacing:-.022em;color:var(--ink);
  font-variant-numeric:tabular-nums;
}
.delta-val svg{width:17px;height:17px;fill:none;stroke:var(--amber);stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}
.chart-note{margin-top:20px;font-size:.89rem;line-height:1.6;color:var(--slate-lt);max-width:46ch}

.chart{
  margin:0;padding:clamp(20px,2.6vw,30px);
  background:var(--white);border:1px solid var(--line);border-radius:var(--r-lg);
  box-shadow:var(--shadow-md);
}
.chart figcaption{
  margin-bottom:16px;padding-bottom:14px;border-bottom:1px solid var(--line);
  font-size:.76rem;letter-spacing:.12em;text-transform:uppercase;color:var(--slate-lt);
}
.chart svg{width:100%;height:auto;display:block;overflow:visible}
.chart .grid line{stroke:var(--line);stroke-width:1;stroke-dasharray:3 5}
.chart .ylab text,.chart .xlab text{
  font-family:var(--mono);font-size:10px;fill:var(--slate-lt);
}
.chart .ylab text{text-anchor:end}
.chart .xlab text{text-anchor:middle}
.chart .line{
  fill:none;stroke:var(--amber);stroke-width:2.6;
  stroke-linecap:round;stroke-linejoin:round;
  stroke-dasharray:500;stroke-dashoffset:500;   /* ≈ the polyline's own length */
}
.chart.drawn .line{transition:stroke-dashoffset 1.6s var(--ease);stroke-dashoffset:0}
.chart .area{opacity:0;transition:opacity .9s .5s ease}
.chart.drawn .area{opacity:1}
.chart .pts circle{fill:var(--white);stroke:var(--amber);stroke-width:2.4;
  opacity:0;transition:opacity .35s ease}
.chart.drawn .pts circle{opacity:1}
.chart.drawn .pts circle:nth-child(2){transition-delay:.35s}
.chart.drawn .pts circle:nth-child(3){transition-delay:.6s}
.chart.drawn .pts circle:nth-child(4){transition-delay:.85s}
.chart.drawn .pts circle:nth-child(5){transition-delay:1.1s}
.chart.drawn .pts circle:nth-child(6){transition-delay:1.35s}
.chart .pt-last{fill:var(--amber);stroke:var(--white);stroke-width:3}
@media (prefers-reduced-motion:reduce){
  .chart .line{stroke-dashoffset:0}
  .chart .area,.chart .pts circle{opacity:1}
}

/* ── hero audit checklist ────────────────────────────────── */
/* "We audit the" — the lead-in that turns the ticks below into a sentence */
.audit-lead{
  margin-bottom:14px;
  font-size:.72rem;font-weight:640;letter-spacing:.12em;text-transform:uppercase;
  color:var(--slate-lt);
}
.audit-list{display:grid;gap:13px}
.audit-list li{
  display:flex;align-items:flex-start;gap:11px;
  font-size:.93rem;line-height:1.5;color:var(--ink-2);
}
.tick{
  position:relative;width:19px;height:19px;flex:none;margin-top:1px;
  border-radius:50%;background:var(--amber-lt);
}
.tick::after{
  content:"";position:absolute;left:6px;top:5.5px;
  width:7px;height:4px;
  border-left:2px solid var(--amber-dp);border-bottom:2px solid var(--amber-dp);
  transform:rotate(-45deg);
}

/* ── why clients choose us ───────────────────────────────────
   Six one-line reasons, two across. Sits on plain paper between the
   tinted services band and the search section, so it reads as a pause
   rather than another card grid. */
.choose-list{
  display:grid;grid-template-columns:1fr 1fr;
  gap:0 clamp(28px,4vw,64px);
}
.choose-list li{
  display:flex;align-items:flex-start;gap:13px;
  padding:17px 0;border-bottom:1px solid var(--line);
  font-size:1.01rem;line-height:1.5;color:var(--ink-2);
}
/* the last row in each column keeps its rule off */
.choose-list li:nth-last-child(-n+2){border-bottom:0;padding-bottom:0}
/* One column on a phone. Two columns of a 320px screen give each reason about
   140px, which breaks these one-line points into four ragged lines each. */
@media (max-width:620px){
  .choose-list{grid-template-columns:1fr}
  /* stacked, only the true last item drops its rule — the second-to-last is
     now mid-list rather than the foot of a column */
  .choose-list li:nth-last-child(2){border-bottom:1px solid var(--line);padding-bottom:17px}
}

/* ── client work cards (clients.html) ────────────────────────
   Three across, each a 16:9 preview above the detail. The preview is a
   styled browser frame by default; drop a real screenshot into img/ and
   swap in the <img> that sits commented inside each card.               */
.work-grid{
  display:grid;grid-template-columns:repeat(3,1fr);
  gap:clamp(18px,2.4vw,28px);align-items:stretch;
}
.work-card{
  display:flex;flex-direction:column;
  border-radius:19px;overflow:hidden;
  background:var(--white);border:1px solid var(--line);
  transition:transform .3s var(--ease),box-shadow .3s var(--ease),border-color .2s;
}
.work-card:hover{
  transform:translateY(-5px);box-shadow:var(--shadow-lg);border-color:var(--line-2);
}
.work-shot{
  position:relative;aspect-ratio:16/9;
  background:linear-gradient(145deg,#eef3f9,#dde8f3);
  border-bottom:1px solid var(--line);overflow:hidden;
}
.work-shot img{display:block;width:100%;height:100%;object-fit:cover;object-position:top center}
/* the live screenshot sits on top of the browser-frame placeholder, so if the
   screenshot service is slow or down, onerror removes it and the frame shows
   through instead of a broken-image icon */
.shot-live{position:absolute;inset:0;z-index:1}
/* browser chrome for the placeholder */
.shot-bar{
  position:absolute;inset:0 0 auto;height:26px;
  display:flex;align-items:center;gap:5px;padding:0 10px;
  background:rgba(255,255,255,.72);border-bottom:1px solid rgba(14,26,43,.07);
}
.shot-bar i{width:7px;height:7px;border-radius:50%;background:var(--line-2);flex:none}
.shot-bar em{
  margin-left:8px;font-style:normal;font-family:var(--mono);
  font-size:.66rem;letter-spacing:.02em;color:var(--slate-lt);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
.shot-face{
  position:absolute;inset:26px 0 0;display:grid;place-items:center;padding:14px;
  text-align:center;font-family:var(--display,var(--serif));
  font-size:clamp(1rem,1.5vw,1.24rem);font-weight:600;letter-spacing:-.02em;
  color:var(--blue);opacity:.62;
}
.work-body{display:flex;flex-direction:column;flex:1;padding:22px 22px 24px}
.work-name{font-size:1.12rem;font-weight:640;letter-spacing:-.02em;color:var(--ink)}
.work-industry{
  margin-top:4px;font-size:.78rem;font-weight:600;letter-spacing:.06em;
  text-transform:uppercase;color:var(--amber);
}
.badges{display:flex;flex-wrap:wrap;gap:7px;margin-top:16px}
.badges li{
  padding:5px 11px;border-radius:var(--r-pill);
  background:var(--paper-2);border:1px solid var(--line);
  font-size:.74rem;font-weight:560;color:var(--slate);
}
.work-visit{
  display:inline-flex;align-items:center;gap:7px;
  margin-top:auto;padding-top:20px;
  font-size:.92rem;font-weight:590;color:var(--blue);
}
.work-visit svg{
  width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round;transition:transform .22s var(--ease);
}
.work-card:hover .work-visit svg{transform:translate(2px,-2px)}
.work-visit.is-soon{color:var(--slate-lt);font-weight:520}

/* ── featured quote (home page) ───────────────────────────────
   One testimonial, straight after the results numbers: the figures make
   the claim, a named client confirms it. Links through to the full page. */
.quote-sec{padding-block:clamp(46px,5.6vw,80px)}
.quote{margin:0;text-align:center;max-width:60ch;margin-inline:auto}
.quote .stars{justify-content:center;margin-bottom:20px}
.quote blockquote{margin:0}
.quote blockquote p{
  font-family:var(--display,var(--serif));
  font-size:clamp(1.18rem,2.1vw,1.6rem);line-height:1.45;letter-spacing:-.014em;
  color:var(--ink);
}
/* The quote marks are written into the HTML here rather than added with
   ::before/::after, so the closing one can be glued to the final word with
   .nb — otherwise it wraps onto a line of its own. */
.quote .nb{white-space:nowrap}
.quote figcaption{
  display:flex;align-items:center;justify-content:center;gap:13px;
  margin-top:26px;text-align:left;
}
.quote figcaption .ava{background:var(--ink);box-shadow:none}
.quote figcaption strong{display:block;color:var(--ink);font-weight:620;font-size:.97rem}
.quote figcaption small{color:var(--slate);font-size:.86rem}
.quote-more{
  display:inline-block;margin-top:26px;
  font-size:.92rem;font-weight:560;color:var(--blue);
  text-decoration:underline;text-decoration-thickness:1.5px;text-underline-offset:3px;
}
.quote-more:hover{color:var(--ink)}

/* ── selected work / case studies ────────────────────────── */
.cases{background:var(--paper-2);border-block:1px solid var(--line)}
.case-grid{
  display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(16px,2vw,24px);
  align-items:start;
}
.case{
  display:flex;flex-direction:column;
  padding:28px 26px;border-radius:var(--r-lg);
  background:var(--white);border:1px solid var(--line);
  transition:transform .28s var(--ease),box-shadow .28s var(--ease);
}
.case:hover{transform:translateY(-4px);box-shadow:var(--shadow-md)}
.case-head{padding-bottom:18px;margin-bottom:18px;border-bottom:1px solid var(--line)}
.case-sector{
  display:inline-block;margin-bottom:9px;
  font-size:.7rem;font-weight:640;letter-spacing:.12em;text-transform:uppercase;
  color:var(--amber);
}
.case-head h3{font-size:1.22rem}
.case-body{margin:0;flex:1}
.case-body dt{
  margin-bottom:5px;
  font-size:.7rem;font-weight:650;letter-spacing:.12em;text-transform:uppercase;
  color:var(--slate-lt);
}
.case-body dd{margin:0 0 18px;font-size:.95rem;color:var(--slate);line-height:1.6}
.case-body dd:last-of-type{margin-bottom:0}
.case-res{
  margin:18px 0 14px;
  font-size:.7rem;font-weight:650;letter-spacing:.12em;text-transform:uppercase;
  color:var(--slate-lt);
}
.case-stats{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin-top:auto}
.case-stats li{
  padding:12px 14px;border-radius:11px;
  background:var(--paper-2);border:1px solid var(--line);
}
.case-stats strong{
  display:block;font-size:1.14rem;font-weight:660;letter-spacing:-.025em;color:var(--ink);
}
.case-stats span{
  display:block;margin-top:3px;font-size:.75rem;color:var(--slate-lt);line-height:1.4;
}

/* home-page project summary — the full write-ups live on clients.html */
.work-list{
  display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(16px,2vw,24px);
  align-items:stretch;
}
/* Direct children only. The cards now contain their own <ul> lists, and a bare
   ".work-list li" would turn every bullet inside them into a white card too. */
.work-list > li{
  display:flex;flex-direction:column;gap:8px;
  padding:28px 26px;border-radius:var(--r);
  background:var(--white);border:1px solid var(--line);
  transition:transform .28s var(--ease),box-shadow .28s var(--ease);
}
.work-list > li:hover{transform:translateY(-4px);box-shadow:var(--shadow-md)}
.work-type{
  font-size:.7rem;font-weight:640;letter-spacing:.12em;text-transform:uppercase;
  color:var(--amber);
}
.work-title{font-size:1.2rem;font-weight:620;letter-spacing:-.018em;color:var(--ink)}
.work-win{font-size:.94rem;color:var(--slate);line-height:1.56}
/* the card is a flex column with its own 8px gap, so the "Delivered" label
   needs far less margin here than it does inside a service card */
.work-list .incl-label{margin-top:6px}
/* measured outcomes, pulled out of the delivered list so the proof stands up.
   margin-top:auto pins them to the bottom, keeping them level across a row. */
.work-result{
  margin-top:auto;padding-top:15px;
  border-top:1px solid var(--line);
  display:grid;gap:7px;
}
.work-result li{
  position:relative;padding-left:22px;
  font-size:.88rem;font-weight:600;line-height:1.45;color:var(--ink);
}
.work-result li::before{
  content:"";position:absolute;left:2px;top:.4em;
  width:8px;height:8px;
  border-top:2px solid var(--amber);border-right:2px solid var(--amber);
  transform:rotate(-45deg);
}

/* ── who it's for ────────────────────────────────────────── */
.aud-grid{
  display:grid;grid-template-columns:minmax(0,.88fr) minmax(0,1.12fr);
  gap:clamp(30px,5vw,68px);align-items:center;
}
.aud-copy .btn{margin-top:28px}
.aud-list{display:grid;gap:0}
.aud-list li{
  display:grid;grid-template-columns:minmax(0,.72fr) minmax(0,1fr);
  gap:6px 24px;align-items:baseline;
  padding:17px 0;border-bottom:1px solid var(--line);
}
.aud-list li:first-child{border-top:1px solid var(--line)}
.aud-list strong{font-size:1.02rem;font-weight:600;color:var(--ink);letter-spacing:-.012em}
.aud-list span{font-size:.92rem;color:var(--slate);line-height:1.55}

/* ── 6. process ──────────────────────────────────────────── */
.steps{
  display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(20px,3vw,40px);
  counter-reset:s;
}
.steps-4{grid-template-columns:repeat(4,1fr);gap:clamp(18px,2.4vw,30px)}
/* two-across, for when each phase carries a description and a checklist —
   four columns of that would be a narrow ribbon of wrapped words */
.steps-2{grid-template-columns:repeat(2,1fr);gap:clamp(30px,3.6vw,56px)}
/* Every rule below is scoped with `>` so it only ever hits a phase, never the
   bullets inside one. Written as plain `.steps li` it also matched every
   `.step-list li`, which gave each bullet the 34px number gutter and the
   dashed rule meant for the phase heading. */
.steps>li:nth-child(4) .step-n{background:var(--slate);color:#fff}
/* the one-line promise under each phase heading */
.step-tag{
  margin-bottom:10px;
  font-size:1.01rem;font-weight:600;letter-spacing:-.012em;
  color:var(--ink-2)!important;
}
.incl-label + .step-list{margin-top:12px}
.step-list{margin-top:16px;display:grid;gap:6px}
.step-list li{
  padding:0 0 0 16px;position:relative;
  font-size:.86rem;line-height:1.5;color:var(--slate-lt);
}
.step-list li::before{
  content:"";position:absolute;left:0;top:.62em;width:7px;height:1.5px;
  background:var(--line-2);
}
.steps>li{position:relative;padding-top:34px}
.steps>li::before{
  content:"";position:absolute;top:16px;left:44px;right:0;height:1px;
  background:linear-gradient(90deg,var(--line-2) 0 60%,transparent 60%);
  background-size:8px 1px;background-repeat:repeat-x;
}
.step-n{
  position:absolute;top:0;left:0;
  display:grid;place-items:center;width:34px;height:34px;border-radius:50%;
  background:var(--ink);color:#fff;font-size:.88rem;font-weight:620;
}
.steps>li:nth-child(2) .step-n{background:var(--blue)}
.steps>li:nth-child(3) .step-n{background:var(--amber);color:var(--ink)}
.steps h3{margin:22px 0 10px}
.steps>li>p{color:var(--slate);font-size:.97em;line-height:1.6}

/* Side by side, the two phases in a row carry descriptions of different
   lengths, so "We review" and "Your roadmap includes" started at different
   heights and the checklists below them never lined up. Each phase borrows
   the parent's rows, which makes every band — heading, promise, description,
   label, checklist — share one baseline across the row.
   Everything here is optional polish: without subgrid the phases simply stack
   as before, which is why it sits behind @supports. */
@supports (grid-template-rows:subgrid){
  @media (min-width:921px){
    .steps-2>li{
      display:grid;grid-row:span 5;grid-template-rows:subgrid;row-gap:0;
    }
    /* the checklist stays at the top of its band instead of stretching */
    .steps-2>li>.step-list{align-self:start}
  }
}

/* ── 7. pricing ──────────────────────────────────────────── */
.pricing{background:var(--paper-2);border-block:1px solid var(--line)}
.plans{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(16px,2vw,24px);
  align-items:start}
.plan{
  display:flex;flex-direction:column;gap:22px;
  padding:32px 28px;border-radius:var(--r-lg);
  background:var(--white);border:1px solid var(--line);
}
.plan header p{margin-top:7px;font-size:.93rem;line-height:1.55;color:var(--slate);min-height:2.6em}
.price{
  padding-bottom:22px;border-bottom:1px solid var(--line);
  font-family:var(--display);
  font-size:2.2rem;font-weight:600;letter-spacing:-.022em;color:var(--ink);
  line-height:1.08;
}
.price em{
  display:block;margin-top:9px;font-family:var(--sans);
  font-size:.4em;font-style:normal;font-weight:520;letter-spacing:0;color:var(--slate-lt);
}
/* a currency figure, if a plan ever shows one: the sans carries ₹ on every
   machine, and tabular figures keep the columns from shifting */
.price .cur{font-family:var(--sans);font-weight:560;letter-spacing:-.02em}
.price-word{font-size:1.55rem;letter-spacing:-.018em}
.price-word em{font-size:.55em;margin-top:7px}
.feat{flex:1;display:grid;gap:11px}
.feat li{position:relative;padding-left:26px;font-size:.94rem;line-height:1.5;color:var(--ink-2)}
.feat li::before{
  content:"";position:absolute;left:2px;top:.52em;
  width:11px;height:6px;border-left:2px solid var(--amber);border-bottom:2px solid var(--amber);
  transform:rotate(-45deg);
}
.plan-hero{
  position:relative;background:var(--ink);border-color:var(--ink);
  box-shadow:var(--shadow-lg);
  transform:translateY(-10px);
}
.plan-hero h3,.plan-hero .price{color:#fff}
.plan-hero header p,.plan-hero .feat li{color:rgba(255,255,255,.85)}
.plan-hero .price{border-bottom-color:rgba(255,255,255,.16)}
.plan-hero .price em{color:rgba(255,255,255,.64)}
.plan-hero .btn{background:var(--amber);color:var(--ink)}
.plan-hero .btn{--bg-hi:var(--amber-hi)}
.ribbon{
  position:absolute;top:-11px;left:28px;
  padding:4px 13px;border-radius:var(--r-pill);background:var(--amber);color:var(--ink);
  font-size:.7rem;font-weight:650;letter-spacing:.08em;text-transform:uppercase;
}
/* the number above each plan name, and the "who it suits" line that closes it */
.plan-n{
  font-size:.7rem;font-weight:640;letter-spacing:.12em;text-transform:uppercase;
  color:var(--amber);
}
.plan-best{
  margin-top:auto;padding-top:16px;border-top:1px dashed var(--line);
  font-size:.88rem;line-height:1.5;color:var(--slate);
}
.plan-best strong{color:var(--ink);font-weight:620}
.plan-hero .plan-n{color:var(--amber-hi)}
.plan-hero .plan-best{border-top-color:rgba(255,255,255,.16);color:rgba(255,255,255,.72)}
.plan-hero .plan-best strong{color:#fff}
.plan .incl-label{margin-top:0;padding-top:0;border-top:0}
/* the transparent-pricing points under the three plans — one column, so the
   five statements read as a single list rather than a scattered grid */
.pricing-points{
  grid-template-columns:1fr;
  max-width:620px;margin:0 auto clamp(22px,2.8vw,32px);text-align:left;
}
.pricing-points li:nth-last-child(-n+2){border-bottom:1px solid var(--line);padding-bottom:17px}
.pricing-points li:last-child{border-bottom:0;padding-bottom:0}

.plans-foot{
  margin-top:26px;text-align:center;font-size:.88rem;color:var(--slate-lt);
}
.plans-foot a{
  color:var(--blue);font-weight:540;
  text-decoration:underline;text-decoration-thickness:1.5px;text-underline-offset:3px;
}
.plans-foot a:hover{color:var(--ink)}

/* ── 9. contact ──────────────────────────────────────────── */
.contact-grid{display:grid;grid-template-columns:minmax(0,.88fr) minmax(0,1.12fr);
  gap:clamp(34px,5vw,74px)}
.contact-details{margin-top:34px;display:grid;gap:2px}
.contact-details li{
  display:flex;align-items:flex-start;gap:14px;
  padding:15px 0;border-top:1px solid var(--line);
}
.contact-details li:last-child{border-bottom:1px solid var(--line)}
.contact-details svg{
  width:20px;height:20px;flex:none;margin-top:3px;
  fill:none;stroke:var(--amber);stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;
}
.contact-details span{display:flex;flex-direction:column;
  font-size:.76rem;letter-spacing:.1em;text-transform:uppercase;color:var(--slate-lt)}
.contact-details a,.contact-details em{
  margin-top:4px;font-size:1.02rem;font-style:normal;letter-spacing:normal;
  text-transform:none;color:var(--ink);font-weight:540;
}
.contact-details a:hover{color:var(--blue);text-decoration:underline;text-underline-offset:3px}

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

/* Every track here is minmax(0,…) and every control is width:100%. Without
   that, the <select> reports the width of its longest option ("Shopify
   website design or migration") as its minimum, which pushed the whole
   field row wider than the card and left the inputs hanging over the edge. */
.form{
  display:grid;grid-template-columns:minmax(0,1fr);gap:18px;align-content:start;
  padding:clamp(24px,3.4vw,38px);border-radius:var(--r-lg);
  background:var(--white);border:1px solid var(--line);box-shadow:var(--shadow-md);
}
.form-lead{
  padding-bottom:18px;margin-bottom:2px;border-bottom:1px solid var(--line);
  font-size:1.16rem;font-weight:620;letter-spacing:-.018em;color:var(--ink);
}
.form-small{
  margin-top:-6px;text-align:center;font-size:.84rem;color:var(--slate-lt);
}
.row{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:16px}
.field{display:flex;flex-direction:column;gap:7px;min-width:0}
.field label{font-size:.89rem;font-weight:580;color:var(--ink)}
.field label small{font-weight:400;color:var(--slate-lt)}
.field input,.field select,.field textarea{
  width:100%;min-width:0;max-width:100%;
  padding:12px 14px;border-radius:10px;
  border:1.5px solid var(--line-2);background:var(--paper);
  /* Explicit, and it has to stay explicit. The reset at the top of this file
     gives inputs `color:inherit`, and the home-page form sits inside .close,
     which is dark with near-white text — inheriting that put white type on
     the near-white field background, so what you typed was invisible while
     the placeholder (which sets its own colour) still showed. */
  color:var(--ink);
  transition:border-color .18s,background .18s,box-shadow .18s;
  font-size:.97rem;
}
/* the placeholder is a hint, not content — but it still has to be readable */
.field ::placeholder{color:var(--slate-lt);opacity:1}
.field textarea{resize:vertical;min-height:118px;line-height:1.55}
.field input:hover,.field textarea:hover,.field select:hover{border-color:var(--line-2)}
.field input:focus,.field select:focus,.field textarea:focus{
  outline:none;border-color:var(--blue);background:var(--white);
  box-shadow:0 0 0 3.5px rgba(27,75,127,.12);
}
.field.bad input,.field.bad textarea{border-color:var(--danger);background:var(--danger-bg)}
.err{font-size:.8rem;color:var(--danger);min-height:0}
.field.bad .err{min-height:1.2em}

.form-note{min-height:1.3em;font-size:.88rem;color:var(--blue);font-weight:520}
.form-note.ok{color:var(--success)}

/* ── 10. faq ─────────────────────────────────────────────── */
.faq{background:var(--paper-2);border-top:1px solid var(--line)}
.faq-grid{display:grid;grid-template-columns:minmax(0,.8fr) minmax(0,1.2fr);
  gap:clamp(30px,5vw,70px)}
.faq-side{align-self:start;position:sticky;top:106px}
.acc-item{border-bottom:1px solid var(--line-2)}
.acc-item:first-child{border-top:1px solid var(--line-2)}
.acc-btn{
  display:flex;align-items:center;justify-content:space-between;gap:20px;
  width:100%;padding:21px 2px;text-align:left;
  font-size:1.05rem;font-weight:580;color:var(--ink);letter-spacing:-.014em;
  line-height:1.4;transition:color .18s;
}
.acc-btn:hover{color:var(--blue)}
.acc-btn i{
  position:relative;width:20px;height:20px;flex:none;
  transition:transform .3s var(--ease);
}
.acc-btn i::before,.acc-btn i::after{
  content:"";position:absolute;inset:50% 0 auto;height:1.8px;background:var(--ink);
  border-radius:2px;transition:transform .3s var(--ease),opacity .2s;
}
.acc-btn i::after{transform:rotate(90deg)}
.acc-btn[aria-expanded="true"] i::after{transform:rotate(0);opacity:0}
.acc-panel{
  overflow:hidden;height:0;
  transition:height .34s var(--ease);
}
.acc-panel p{
  padding:0 46px 24px 2px;color:var(--slate);font-size:.98em;line-height:1.68;
}
/* an answer can run to more than one paragraph — only the last one carries
   the full bottom padding, or the panel gains a gap it doesn't need */
.acc-panel p:not(:last-child){padding-bottom:15px}

/* ── risk-reversal strip ─────────────────────────────────── */
.promise{padding-block:clamp(38px,4.5vw,60px);border-top:1px solid var(--line)}
.promises{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(20px,3vw,44px)}
.promises-4{grid-template-columns:repeat(4,1fr);gap:clamp(20px,2.6vw,34px)}
.promises li{padding-top:20px;border-top:2px solid var(--amber)}
.promises strong{
  display:block;margin-bottom:7px;
  font-size:1.04rem;font-weight:620;letter-spacing:-.015em;color:var(--ink);
}
.promises span{font-size:.93rem;color:var(--slate);line-height:1.58}

/* ── closing audit form ──────────────────────────────────── */
/* White text on navy at low alpha is the classic "looks fine on my monitor,
   invisible on a phone in daylight" mistake. Everything on the dark blocks
   below sits at .74 or above. */
.close{background:var(--ink);color:rgba(255,255,255,.8);position:relative;overflow:hidden}
.close::before{
  content:"";position:absolute;inset:0;
  background:radial-gradient(620px 300px at 8% 110%,rgba(226,149,43,.18),transparent 70%);
}
.close-grid{
  position:relative;
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,.92fr);
  gap:clamp(34px,5vw,72px);align-items:center;
}
.close h2{color:#fff;max-width:16ch}
.close-lede{margin-top:18px;font-size:1.06em;line-height:1.6;max-width:46ch}
.close-list{display:grid;gap:12px;margin-top:26px}
.close-list li{position:relative;padding-left:27px;font-size:.95rem;line-height:1.55}
.close-list li::before{
  content:"";position:absolute;left:2px;top:.5em;
  width:11px;height:6px;border-left:2px solid var(--amber);border-bottom:2px solid var(--amber);
  transform:rotate(-45deg);
}
.close-alt{margin-top:26px;font-size:.91rem;color:rgba(255,255,255,.66)}
.close-alt a{
  color:#fff;text-decoration:underline;
  text-decoration-thickness:1.5px;text-underline-offset:3px;
}
.close-alt a:hover{color:var(--amber)}
/* "What's included" label on the dark panel */
.close .incl-label{
  margin-top:26px;padding-top:0;border-top:0;
  color:rgba(255,255,255,.5)!important;
}
.close .incl-label + .close-list{margin-top:12px}

/* the strip under the grid: why bother with an audit, and how else to reach us */
.close-foot{
  position:relative;
  margin-top:clamp(38px,4.6vw,62px);
  padding-top:clamp(30px,3.6vw,44px);
}
/* A border-top here would be drawn across the .wrap gutters too, so the rule
   overshot the form above it by a gutter on each side. Drawing it as a
   pseudo-element inset by the gutter keeps it flush with the content. */
.close-foot::before{
  content:"";position:absolute;top:0;left:var(--gut);right:var(--gut);
  height:1px;background:rgba(255,255,255,.14);
}
.close-why{
  display:grid;grid-template-columns:repeat(4,1fr);
  gap:clamp(18px,2.6vw,34px);
}
.close-why li{padding-top:16px;border-top:2px solid var(--amber)}
.close-why strong{
  display:block;margin-bottom:5px;
  font-size:.98rem;font-weight:620;letter-spacing:-.012em;color:#fff;
}
.close-why span{font-size:.88rem;line-height:1.5;color:rgba(255,255,255,.66)}
/* Four across held all the way down to 320px, where each promise was about two
   words wide. It follows the form above it: two across on a tablet, stacked on
   a phone. */
@media (max-width:920px){
  .close-why{grid-template-columns:1fr 1fr}
}
@media (max-width:480px){
  .close-why{grid-template-columns:1fr}
}
.close-final{
  margin-top:clamp(28px,3.4vw,40px);text-align:center;
  font-size:1.02rem;color:rgba(255,255,255,.82);max-width:62ch;margin-inline:auto;
}

/* ── cta strip ───────────────────────────────────────────── */
.cta-strip{background:var(--ink);color:#fff;position:relative;overflow:hidden}
.cta-strip::before{
  content:"";position:absolute;inset:0;
  background:radial-gradient(560px 260px at 12% 110%,rgba(226,149,43,.2),transparent 70%);
}
.cta-inner{
  position:relative;
  display:flex;align-items:center;justify-content:space-between;gap:28px;flex-wrap:wrap;
  padding-block:clamp(40px,5vw,62px);
}
.cta-inner h2{color:#fff;font-size:clamp(1.45rem,2.5vw,2.1rem)}
.cta-inner p{margin-top:9px;color:rgba(255,255,255,.78);font-size:.99rem;line-height:1.55}

/* ── footer ──────────────────────────────────────────────── */
.site-foot{background:var(--foot);color:rgba(255,255,255,.76);font-size:.94rem}
/* Four uneven columns, and every one of them is sized by its longest line rather than
   split evenly:
   — brand: the paragraph is the only body copy in the footer and needs a real measure.
     At an even quarter it wrapped every four words and ran ten lines deep.
   — services: "Shopify Store Design & SEO" is the longest link and has to stay on one.
   — company: short labels, so the smallest share — but "Terms & conditions" still sets
     a floor of about 140px on it.
   — contact: the email address is the longest unbreakable string in the footer, and at
     equal widths it split across two lines as "…@novatrixdigital." / "in". */
.foot-grid{
  display:grid;
  grid-template-columns:minmax(0,1.78fr) minmax(0,1.19fr) minmax(0,.84fr) minmax(0,1.19fr);
  column-gap:clamp(22px,2.8vw,40px);
  row-gap:44px;
  padding-block:clamp(44px,5.5vw,68px) clamp(34px,4vw,48px);
}
/* The footer lockup is the header's, unchanged apart from the text colour — the tile
   is already dark, so unlike the old lettered mark it needs no inverting here. */
.foot-brand .brand{margin-bottom:20px}
.foot-brand .brand-text{color:#fff}
.foot-brand .brand-text small{color:rgba(255,255,255,.62)}
/* 46 characters is the low end of a comfortable measure — narrower than this and the
   paragraph reads as a column of fragments rather than sentences */
.foot-brand p{max-width:46ch;line-height:1.62}
.foot-by{margin-top:12px;font-size:.87rem;color:rgba(255,255,255,.58)}
.foot-col h3{color:#fff;margin-bottom:18px;opacity:.75;font-size:.76rem}
/* The heading is a button on phones (see the + control below). On desktop it is
   disabled, so it has to look and sit exactly like the plain heading it replaced. */
.foot-toggle{
  display:flex;align-items:center;justify-content:space-between;gap:16px;width:100%;
  /* the browser's own button styling drops both of these, and without them the
     headings come back as "Services" instead of "SERVICES" */
  text-transform:inherit;letter-spacing:inherit;
}
.foot-toggle:disabled{cursor:default;opacity:1}
.foot-toggle i{display:none}
.foot-col li{padding:6px 0;line-height:1.5}
.foot-col a{transition:color .18s,padding-left .18s}
.foot-col a:hover{color:var(--amber)}
/* the footer carries the page links, so it also shows which page you're on */
.foot-col a[aria-current="page"]{color:#fff;font-weight:600}
/* break-word only fires when there is no other way to fit the line, so the address
   still stays whole at every width the layout is actually sized for — this is the
   safety net for a browser whose font metrics run wider than the ones measured here */
.foot-contact a{color:#fff;overflow-wrap:break-word}
/* line icons rather than emoji: emoji render differently on every platform —
   flat grey on one, glossy colour on the next — and can't inherit the colour
   of the text beside them */
.foot-contact li{display:flex;align-items:flex-start;gap:11px}
.foot-contact li svg{
  width:16px;height:16px;flex:none;margin-top:.28em;
  fill:none;stroke:var(--amber);stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round;
}

.social{display:flex;gap:9px;margin-top:24px}
.social a{
  display:grid;place-items:center;width:38px;height:38px;border-radius:50%;
  color:rgba(255,255,255,.8);
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.16);
  transition:background .2s,box-shadow .2s,transform .2s var(--ease),color .2s;
}
.social a:hover{background:var(--amber);color:var(--ink);box-shadow:none;transform:translateY(-2px)}
/* These are each brand's own mark, filled rather than stroked, so they take the link's
   colour — white on the dark footer, ink once the amber hover fills in behind them. */
.social svg{width:17px;height:17px;fill:currentColor;stroke:none}

.foot-base{
  display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;
  padding-block:22px;border-top:1px solid rgba(255,255,255,.1);
  font-size:.86rem;color:rgba(255,255,255,.62);
}
.foot-legal{display:flex;gap:22px;flex-wrap:wrap}
.foot-legal a:hover{color:#fff}
/* Between these two widths the content runs to the edge of the screen and the fixed
   back-to-top button lands on the last legal link. Wider than this, the page margins
   are already bigger than the button; narrower, the row stacks and clears it. */
@media (min-width:761px) and (max-width:1320px){
  .foot-base{padding-right:calc(var(--gut) + 56px)}
}

/* back to top */
/* Back in the corner. It sat higher for a while to clear a chat launcher fixed
   to this same spot; with that gone there is nothing to clear, and the raised
   position only left it floating above empty space. If another floating widget
   is ever added down here, this offset and the pair in the narrow-screen block
   below are what to lift. */
.to-top{
  position:fixed;right:20px;bottom:20px;z-index:90;
  display:grid;place-items:center;width:46px;height:46px;border-radius:50%;
  background:var(--ink);color:#fff;box-shadow:var(--shadow-md);
  opacity:0;pointer-events:none;transform:translateY(12px);
  transition:opacity .3s,transform .3s var(--ease),background .2s;
}
.to-top.show{opacity:1;pointer-events:auto;transform:none}
.to-top:hover{background:var(--amber);color:var(--ink)}
.to-top svg{width:19px;height:19px;fill:none;stroke:currentColor;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}

/* ── inner pages ─────────────────────────────────────────────
   about / clients / reviews / contact / legal all share this head */
.page-head{
  position:relative;
  padding-block:clamp(34px,4.5vw,62px) clamp(30px,4vw,52px);
  border-bottom:1px solid var(--line);
  background:
    radial-gradient(620px 300px at 88% -30%,rgba(226,149,43,.12),transparent 70%),
    radial-gradient(520px 280px at 2% 0%,rgba(27,75,127,.08),transparent 72%);
}
.page-head h1{font-size:clamp(1.95rem,4vw,3rem);margin-bottom:18px}
.page-head .lede{max-width:58ch;font-size:1.1em;color:var(--slate)}

.crumbs{margin-bottom:22px;font-size:.84rem;color:var(--slate-lt)}
.crumbs ol{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
.crumbs li{display:flex;align-items:center;gap:8px}
.crumbs li+li::before{content:"/";color:var(--line-2)}
.crumbs a:hover{color:var(--ink);text-decoration:underline;text-underline-offset:3px}
.crumbs [aria-current]{color:var(--slate)}

/* review cards — the slider version of these lives on the home page */
.review-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:clamp(16px,2vw,24px)}
/* three over two columns leaves the last one alone — let it run wide */
.review-grid .review:nth-child(3):last-child{grid-column:1/-1}
.review{
  display:flex;flex-direction:column;
  padding:clamp(26px,3vw,34px);border-radius:var(--r-lg);
  background:var(--white);border:1px solid var(--line);box-shadow:var(--shadow-sm);
}
.stars{display:flex;gap:2px}
.stars svg{width:19px;height:19px;fill:var(--amber);stroke:none}
.review .stars{margin-bottom:18px}
.review blockquote{margin:0;flex:1}
/* the quote itself is the one long passage set in the serif — bigger and
   airier than body copy, because that is what makes it read as a voice */
.review blockquote p{
  font-family:var(--serif);font-size:1.12rem;font-weight:400;
  line-height:1.62;letter-spacing:-.003em;color:var(--ink);
}
/* The opening quote hangs into the margin and sits tight against the first
   word — the way a printed page sets it. Without the negative margins the
   serif's own sidebearing leaves a gap that reads as a typo. */
.review blockquote p::before{content:"“";margin-inline:-.34em -.06em}
.review blockquote p::after{content:"”";margin-inline-start:-.04em}
.review footer{
  display:flex;align-items:center;gap:14px;
  margin-top:24px;padding-top:20px;border-top:1px solid var(--line);
}
.review footer .ava{background:var(--ink);box-shadow:none}
.review footer strong{display:block;color:var(--ink);font-weight:620;font-size:.97rem}
.review footer small{color:var(--slate);font-size:.86rem}
.review-note{margin-top:26px;font-size:.88rem;line-height:1.6;color:var(--slate-lt);max-width:62ch}

/* company facts table, used on about + contact */
.facts{display:grid;margin-top:8px}
.facts>div{
  display:grid;grid-template-columns:minmax(0,.42fr) minmax(0,1fr);gap:5px 24px;
  padding:15px 0;border-bottom:1px solid var(--line);
}
.facts>div:first-child{border-top:1px solid var(--line)}
.facts dt{font-size:.76rem;letter-spacing:.1em;text-transform:uppercase;color:var(--slate-lt)}
.facts dd{margin:0;color:var(--ink-2)}
.facts dd a:hover{color:var(--blue);text-decoration:underline;text-underline-offset:3px}

.page-cta{margin-top:clamp(32px,4vw,48px)}

.legal-body{max-width:70ch}
.legal-body h2{font-size:1.42rem;margin:40px 0 14px}
/* long legal text is the one place on the site people read top to bottom —
   give it the extra leading */
.legal-body p,.legal-body li{color:var(--slate);margin-bottom:12px;line-height:1.72}
.legal-body ul{list-style:disc;padding-left:22px;margin-bottom:16px}
.legal-body li{margin-bottom:7px}
.legal-meta{margin-top:14px;font-family:var(--mono);font-size:.8rem;color:var(--slate-lt)}

/* ── map ─────────────────────────────────────────────────────
   The Google Maps embed needs no API key — it is the plain
   ?output=embed URL, so it keeps working with nothing to renew. */
.map{
  border-radius:var(--r);overflow:hidden;
  border:1px solid var(--line);background:var(--paper-2);
  box-shadow:var(--shadow-sm);
}
.map iframe{
  display:block;width:100%;height:clamp(260px,34vw,400px);border:0;
}
.map-foot{
  display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
  gap:14px 24px;padding:18px 22px;
  background:var(--white);border-top:1px solid var(--line);
}
.map-foot address{
  font-style:normal;font-size:.93rem;color:var(--slate);line-height:1.5;
}
.map-foot address strong{
  display:block;margin-bottom:3px;
  font-size:1.02rem;font-weight:620;letter-spacing:-.015em;color:var(--ink);
}
.map-foot .btn{flex:none}

/* ── responsive ──────────────────────────────────────────── */
@media (max-width:1080px){
  .why-grid.grid-4{grid-template-columns:1fr 1fr}
  .stats{grid-template-columns:repeat(2,1fr)}
  .stack-grid,.stack-6,.stack-4{grid-template-columns:repeat(3,1fr)}
  .promises-4{grid-template-columns:1fr 1fr}
  /* Brand block across the top, then the three link columns in one row.
     Two columns left the third one stranded on a row of its own. The three keep the
     desktop proportions rather than becoming even thirds — even thirds handed
     "Company" a 300px column to hold the word "Clients". */
  .foot-grid{
    grid-template-columns:minmax(0,1.25fr) minmax(0,.75fr) minmax(0,1.1fr);
    column-gap:36px;row-gap:0;
  }
  /* Full width, the paragraph can take a longer measure — and the rule underneath
     reads the two bands as brand / links instead of one loose stack. */
  .foot-brand{
    grid-column:1/-1;
    padding-bottom:34px;margin-bottom:38px;
    border-bottom:1px solid rgba(255,255,255,.09);
  }
  .foot-brand p{max-width:68ch}
  .steps-4{grid-template-columns:1fr 1fr;gap:32px}
}


@media (max-width:920px){
  .close-grid{grid-template-columns:1fr}
  .close h2{max-width:none}
  .chart-grid,.aud-grid{grid-template-columns:1fr}
  .case-grid{grid-template-columns:1fr}
  .work-grid{grid-template-columns:1fr 1fr}
  .case-stats{grid-template-columns:repeat(4,1fr)}
  .work-list{grid-template-columns:1fr 1fr}
  .hero-grid{grid-template-columns:1fr}
  .hero-panel{max-width:460px}
  /* the badge is already square-on and inside the card width — nothing to undo */
  .contact-grid,.faq-grid{grid-template-columns:1fr}
  .faq-side{position:static}
  .why-grid{grid-template-columns:1fr 1fr}
  /* three cards over two columns leaves an orphan, so the third goes wide.
     Scoped away from the 2- and 4-up grids, which have no orphan to fix. */
  .why-grid:not(.grid-2):not(.grid-4) .why-card:nth-child(3){grid-column:1/-1}
  .svc-grid{grid-template-columns:1fr 1fr}
  .svc,.svc-lg{grid-column:span 1}
  .svc-lg{grid-column:1/-1}
  .plans{grid-template-columns:1fr}
  /* Stacked, the cards keep their numbered order — 01, 02, 03. This used to
     carry order:-1 to float the highlighted plan to the top, which read as a
     numbering mistake: the list opened on 02 and put 01 underneath it. The
     "Most chosen" flag marks it well enough where it belongs. */
  .plan-hero{transform:none}
  /* one column: the number moves out to the left of each phase instead of
     sitting above it, so the headings stay on the same baseline as the text */
  .steps{grid-template-columns:1fr;gap:38px}
  .steps>li::before{display:none}
  .steps>li{padding:0 0 0 52px}
  .steps h3{margin-top:0}
  .step-n{top:2px}
  /* the chart SVG scales down with the card, so its labels have to
     scale UP in user units to stay readable — stepped up again at
     620px and 440px further down */
  .chart .ylab text,.chart .xlab text{font-size:12px}
}

/* The nav collapses earlier than the rest of the layout: six items plus a CTA
   need roughly 860px of header before they start crowding the logo.
   js/main.js closes the open menu using this same width — keep them in step. */
/* 980px, not 860px: with seven nav items plus the CTA the horizontal bar
   starts clipping the Free audit button below roughly 1000px, so the burger
   has to take over earlier than it used to. If you remove a nav item, this
   can go back down — check the narrowest desktop width before you do. */
/* 1120px, not 980px: Blog made it eight items, and eight plus the CTA need
   about 970px of content between the wordmark and the right gutter. Below
   roughly 1120px there is less than that, and the Free audit button was
   crossing the gutter and running off the side of the header.

   Drop a nav item and this can come back down — 980px carried seven, 860px
   carried six. Whatever it becomes, the matchMedia call in js/main.js has to
   say the same number: it is what closes an open menu when a phone turns
   sideways into a width where the bar is showing anyway. */
@media (max-width:1180px){
  /* anchored to the bottom of the header rather than a hardcoded 70px,
     so it can never drift out of alignment with it */
  .burger{display:block}
  .nav{
    position:absolute;top:100%;left:0;right:0;
    flex-direction:column;align-items:stretch;gap:0;
    padding:8px var(--gut) 24px;
    background:var(--paper);border-bottom:1px solid var(--line);
    box-shadow:var(--shadow-lg);
    max-height:0;overflow:hidden;visibility:hidden;
    overscroll-behavior:contain;
    transition:max-height .38s var(--ease),visibility .38s,padding .38s var(--ease);
  }
  .nav.open{max-height:min(72vh,520px);visibility:visible;overflow:auto}
  /* dvh excludes the mobile browser chrome, so the menu can't run off-screen */
  @supports (height:100dvh){.nav.open{max-height:min(72dvh,520px)}}

  /* without JS the burger can't open anything, so the menu just stays open */
  .no-js .head-inner{flex-wrap:wrap}
  .no-js .nav{
    position:static;max-height:none;visibility:visible;overflow:visible;
    width:100%;padding:0 0 14px;background:none;border:0;box-shadow:none;
  }
  .nav>ul{flex-direction:column;align-items:stretch;gap:0}
  .nav>ul li{border-bottom:1px solid var(--line)}
  .nav>ul a{display:block;padding:15px 2px;font-size:1.05rem}
  .nav>ul a::after{display:none}
  .nav-cta{margin-top:20px;justify-content:center}
}

@media (max-width:760px){
  /* iOS zooms the whole page when you focus a field under 16px.
     This is the entire reason for the rule — don't lower it. */
  .field input,.field select,.field textarea{font-size:16px}

  /* roomier tap targets in the footer lists */
  .foot-col li{padding:9px 0}

  .why-grid,.why-grid.grid-2{grid-template-columns:1fr}
  .why-card:nth-child(3){grid-column:auto}
  .promises,.promises-4{grid-template-columns:1fr;gap:24px}
  .work-list{grid-template-columns:1fr}
  .work-grid{grid-template-columns:1fr}
  .svc-grid{grid-template-columns:1fr}
  .svc-lg{grid-template-columns:120px 1fr;gap:20px}
  /* two columns of "Includes" would be two words wide at this size */
  .deliver.cols{columns:1}
  .stack-grid,.stack-6,.stack-4{grid-template-columns:1fr 1fr}
  .steps-4{grid-template-columns:1fr}
  .delta li{flex-direction:column;align-items:flex-start;gap:8px}
  .case-stats{grid-template-columns:1fr 1fr}
  .aud-list li{grid-template-columns:1fr;gap:4px}
  .row{grid-template-columns:1fr}
  .sec-head.split{grid-template-columns:1fr;align-items:start}
  .sec-head.split .sec-note{margin-top:6px}
  /* Fully stacked, this footer ran past three screens on a phone before you reached
     the copyright. Each list now collapses behind a + control, same as the FAQ, so
     the whole footer is about one screen and you open only the part you want. */
  .foot-grid{grid-template-columns:1fr;column-gap:0;row-gap:0;padding-block:40px 26px}
  .foot-brand{padding-bottom:26px;margin-bottom:0;border-bottom:1px solid rgba(255,255,255,.09)}
  .foot-brand p{max-width:none}
  .foot-col{border-bottom:1px solid rgba(255,255,255,.09)}

  /* the whole heading row is the tap target — 48px tall, the floor for a thumb */
  .foot-col h3{margin-bottom:0;opacity:.88}
  .foot-toggle{padding:17px 2px}
  .foot-toggle i{
    display:block;position:relative;width:15px;height:15px;flex:none;
  }
  .foot-toggle i::before,.foot-toggle i::after{
    content:"";position:absolute;inset:50% 0 auto;height:1.6px;
    background:currentColor;border-radius:2px;
    transition:transform .3s var(--ease),opacity .2s;
  }
  .foot-toggle i::after{transform:rotate(90deg)}          /* the + upright */
  .foot-toggle[aria-expanded="true"] i::after{transform:rotate(0);opacity:0}

  .foot-col ul{overflow:hidden;transition:height .32s var(--ease)}
  .foot-col li:first-child{padding-top:0}
  .foot-col li:last-child{padding-bottom:18px}

  /* The email address and phone number are what someone opens a footer on a phone to
     find, so this column never collapses — js/main.js gives it no + control. Its
     heading is plain text rather than a button, so it has to carry the toggle's own
     padding to sit on the same rhythm as the two above it. Its rule comes off as well:
     the .foot-base border directly below it would otherwise double up. */
  .foot-contact{border-bottom:0}
  .foot-contact h3{padding:17px 2px 13px}

  /* the back-to-top button is pinned to the bottom-right of the screen and lands on
     top of the last legal link once you reach the end of the page */
  .foot-base{flex-direction:column;align-items:flex-start;gap:10px;padding-bottom:64px}

  /* the 46px right gutter on answers costs ~14% of a 320px screen */
  .acc-panel p{padding-right:6px}
  .review-grid{grid-template-columns:1fr}
  .facts>div{grid-template-columns:1fr;gap:2px}

  /* a live blur behind a sticky header is the most expensive thing on the
     page to scroll past on a budget Android. The header is near-opaque
     anyway, so make it fully opaque and drop the filter. */
  .site-head{backdrop-filter:none;background:var(--paper)}
}

@media (max-width:620px){
  .why-grid.grid-4{grid-template-columns:1fr}
  .chart .ylab text,.chart .xlab text{font-size:17px}
}

@media (max-width:480px){
  .stats{grid-template-columns:1fr;gap:16px}
  .hero-proof{gap:18px 26px}
  .stack-grid,.stack-6,.stack-4{grid-template-columns:1fr}
  .case-stats{grid-template-columns:1fr 1fr}
  .svc-lg{grid-template-columns:1fr}
  .svc-art{max-width:150px}
  .panel-badge{font-size:.68rem;padding:10px 13px}
  /* env() is 0 unless the page opts into the notch area, so this is
     simply 14px everywhere else — no fallback needed */
  .to-top{
    right:14px;bottom:calc(14px + env(safe-area-inset-bottom));
    width:42px;height:42px;
  }
}

@media (max-width:440px){
  .chart .ylab text,.chart .xlab text{font-size:22px}
}

/* Below this width two buttons side by side start looking accidental.
   Full-width stacked reads as a deliberate choice and is easier to hit. */
@media (max-width:420px){
  .hero-actions{flex-direction:column;align-items:stretch;gap:10px}
  .hero-actions .btn{width:100%;justify-content:center}
  .btn-wa{width:100%;justify-content:center}
}

/* print */
@media print{
  .site-head,.to-top,.cta-strip,.hero-panel,.marquee{display:none}
  body{background:#fff}
  .sec{padding-block:24px;page-break-inside:avoid}
}

/* ── success in numbers (home) ───────────────────────────────
   Added whole and last, its own breakpoints included, so the section
   reads — and could be lifted out — in one piece. Every value below is
   a token that already existed; nothing above this line is touched.

   The band is tinted like the services, work and pricing sections so it
   separates the two plain-paper sections it sits between. */
.figures{background:var(--paper-2);border-block:1px solid var(--line)}
/* centred head — the four cards below are centred, and a left-aligned
   heading over them left the section looking hung off one corner */
.figures .sec-head{margin-inline:auto;text-align:center}
.figures .eyebrow{justify-content:center}
.figures .sec-note{margin-inline:auto}

.fig-grid{
  display:grid;grid-template-columns:repeat(4,1fr);
  gap:clamp(16px,2vw,26px);
  margin:0;padding:0;list-style:none;
}
.fig{margin:0}

/* The hover lift is on the card and the scroll reveal on the <li>, so the
   two never write to the same transform — a card hovered mid-reveal would
   otherwise drop back to wherever the reveal had got to. */
.fig-card{
  height:100%;
  padding:clamp(28px,3.2vw,38px) 20px clamp(24px,2.6vw,30px);
  border-radius:var(--r);
  background:var(--white);border:1px solid var(--line);
  text-align:center;
  transition:transform .3s var(--ease),box-shadow .3s var(--ease),border-color .2s;
}
.fig-card:hover{transform:translateY(-6px);box-shadow:var(--shadow-md);border-color:var(--line-2)}
/* on touch there is no pointer to leave, so a lift on tap just sticks */
@media (hover:none){.fig-card:hover{transform:none}}

/* Figures in the sans for the same reason as the metrics band: it is the
   family with dependable tabular figures, so no digit changes width while
   the number climbs and the card never twitches. */
.fig-num{
  display:block;
  font-size:clamp(3rem,4.6vw,4rem);font-weight:640;line-height:1;
  letter-spacing:-.03em;color:var(--ink);
  font-variant-numeric:tabular-nums;
}
.fig-sym{color:var(--amber);margin-left:.03em}
/* the same 26px amber dash the eyebrows use, holding the label off the figure */
.fig-rule{display:block;width:26px;height:2px;margin:18px auto;background:var(--amber)}
.fig-label{
  display:block;
  font-size:.78rem;font-weight:640;letter-spacing:.12em;text-transform:uppercase;
  color:var(--slate);line-height:1.5;
}

/* Fade-up, staggered a card apart from main.js. The hidden state is added
   by JS as well, so with scripting off the cards are simply there — the
   same bargain .rv makes higher up this file. */
.fig.rv-fig{opacity:0;transform:translateY(22px);transition:opacity .7s var(--ease),transform .7s var(--ease)}
.fig.rv-fig.in{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){.fig.rv-fig{opacity:1;transform:none;transition:none}}

/* 4 across, 2 on a tablet, 1 on a phone — the same two widths the metrics
   band steps at, so the two sets of figures break in the same places */
@media (max-width:1080px){.fig-grid{grid-template-columns:repeat(2,1fr)}}
@media (max-width:480px){.fig-grid{grid-template-columns:1fr}}

/* ── 404 (404.html) ──────────────────────────────────────────
   Used by one page and written last, its own breakpoints included, so it
   reads in one piece. Every colour, radius and shadow below is a token
   that already existed — nothing above this line is touched.

   The page borrows the homepage's shape on purpose. A visitor who has hit
   a dead link is already unsure whether the site is broken, and landing on
   the same header, the same two-column hero and the same white browser card
   answers that before they read a word. What changes is only what the card
   shows: the address that failed, instead of the audit checklist.

   The section is .err-hero and NOT .err. `.err` was already taken, near the
   top of this file, by the validation message under a form field — and this
   block sits lower, so a bare `.err` here won it on the cascade and put up
   to 74px of padding under the name, email and message fields of every form
   on the site. Nothing about the rule looked wrong; it was only wrong about
   which elements it would reach. Anything added below needs a name checked
   against the whole file, not just against this block. */
.err-hero{position:relative;padding-top:clamp(40px,5.5vw,74px);overflow:hidden}
/* the hero's own wash, tuned one notch warmer to the right so the eye lands
   on the card rather than drifting off the top corner */
.err-hero::before{
  content:"";position:absolute;inset:0;z-index:-1;
  background:
    radial-gradient(700px 430px at 86% -8%,rgba(226,149,43,.15),transparent 68%),
    radial-gradient(560px 380px at 2% 2%,rgba(27,75,127,.09),transparent 70%);
}
.err-grid{
  display:grid;grid-template-columns:minmax(0,1.08fr) minmax(0,.92fr);
  gap:clamp(34px,5vw,72px);align-items:center;
  padding-bottom:clamp(52px,6.5vw,88px);
}
.err-copy h1{margin-bottom:20px}

/* Three lines answering "did I break something?" before it is asked. Same
   ticks as the hero checklist, held off the buttons by a rule. */
.err-why{
  display:grid;gap:12px;
  margin-top:clamp(32px,4vw,44px);padding-top:24px;
  border-top:1px solid var(--line);
}
.err-why li{
  display:flex;align-items:flex-start;gap:11px;
  font-size:.93rem;line-height:1.5;color:var(--slate);
}

/* the browser card */
.err-panel{position:relative}

/* The address bar, between the window chrome and the body. Mono, because it
   is a URL and everything else on the site that quotes machine text is mono.
   js/main.js writes the real path into .err-path with textContent — the
   value comes from the URL, so it is never allowed near innerHTML. */
.err-bar{
  display:flex;align-items:center;gap:9px;
  padding:13px 18px;border-bottom:1px dashed var(--line);
  font-family:var(--mono);font-size:.76rem;line-height:1.5;
}
.err-bar svg{
  width:15px;height:15px;flex:none;
  fill:none;stroke:var(--danger);stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round;
}
/* The domain never breaks. It is the half of the address that is fine, and
   splitting it as "novatrixdigital" / ".in" reads as part of the fault. */
.err-host{color:var(--slate-lt);white-space:nowrap}
/* the wavy red underline is the whole point of the bar: it marks which half
   of the address is the problem, and it is the one thing on the page that
   says "this bit" rather than "something" */
.err-path{
  color:var(--ink);font-weight:600;
  text-decoration:underline wavy var(--danger);
  text-decoration-thickness:1.5px;text-underline-offset:4px;
  /* A hand-typed path can be any length and there is nowhere inside a card
     for it to go, so it breaks anywhere rather than push the card wider.
     min-width:0 is what lets a flex item shrink below its text at all —
     without it the path stays full width and the bar overflows. */
  min-width:0;overflow-wrap:anywhere;
}

.err-body{text-align:center;padding-top:clamp(26px,3.4vw,38px)}
/* The one piece of display type on the page, in the same serif as the
   headings. aria-hidden in the markup — a screen reader gets "Error 404" in
   the eyebrow and "we can't find that page" in the h1, and reading the
   numerals a third time adds nothing. */
.err-num{
  font-family:var(--display);
  font-size:clamp(4.6rem,10vw,7rem);font-weight:600;line-height:.88;
  letter-spacing:-.045em;color:var(--ink);
  font-variant-numeric:tabular-nums;
}
.err-num span{color:var(--amber)}
.err-num-label{
  margin-top:16px;
  font-size:.74rem;font-weight:640;letter-spacing:.12em;text-transform:uppercase;
  color:var(--slate-lt);
}
/* the reassurance line, left-aligned again because it is a sentence */
.err-body .panel-foot{margin-top:clamp(26px,3.2vw,34px);text-align:left}

/* The answer, when the page has one — unhidden by js/404.js only when the
   address that failed resolves to something. It sits between the buttons and
   the tick list, which is where the eye already is after reading them.

   It is a card rather than a line of text because it is the most useful
   thing on the page when it appears, and it has to out-rank the two buttons
   directly above it without being a third button competing with them. The
   amber wash is the same one the ticks and the eyebrow dash use. */
.err-guess{
  margin-top:24px;padding:18px 20px 19px;
  border-radius:var(--r);
  background:linear-gradient(180deg,#fdf6e9,#fbf1de);
  border:1px solid var(--amber-lt);
  box-shadow:var(--shadow-sm);
  max-width:44ch;
}
.err-guess[hidden]{display:none}     /* [hidden] loses to display:block otherwise */
.err-guess-lead{
  font-size:.78rem;font-weight:640;letter-spacing:.1em;text-transform:uppercase;
  color:var(--amber-dp);
  /* the lead can be a full sentence ("this is an agency site, not a shop"),
     so it needs a real line-height rather than a label's */
  line-height:1.5;
}
/* the destination itself: display serif, the size of an h3, so it reads as
   the answer rather than as another link in a paragraph */
.err-guess-link{
  display:inline-flex;align-items:center;gap:9px;
  margin-top:9px;
  font-family:var(--display);font-size:1.32rem;font-weight:600;
  letter-spacing:-.018em;line-height:1.25;color:var(--ink);
}
.err-guess-link svg{
  width:19px;height:19px;flex:none;
  fill:none;stroke:var(--amber-dp);stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round;
  transition:transform .22s var(--ease);
}
.err-guess-link:hover{color:var(--amber-dp)}
.err-guess-link:hover svg{transform:translateX(3px)}
/* the runners-up, when two of them are genuinely close */
.err-guess-alt{margin-top:11px;font-size:.88rem;color:var(--slate)}
.err-guess-alt[hidden]{display:none}
.err-guess-alt a{
  font-weight:600;color:var(--blue);
  text-decoration:underline;text-decoration-thickness:1.5px;text-underline-offset:3px;
}
.err-guess-alt a:hover{color:var(--ink)}

/* "Back to the page you came from" — only ever shown when the referrer was
   this site, so it is genuinely a way back rather than a guess. Quiet on
   purpose: it is the third-best action here, behind the suggestion and the
   two buttons, and it should look it. */
.err-back-line{margin-top:18px;min-height:0}
.err-back{
  display:inline-flex;align-items:center;gap:8px;
  font-size:.9rem;font-weight:580;color:var(--slate);
}
.err-back[hidden]{display:none}
.err-back svg{
  width:16px;height:16px;flex:none;
  fill:none;stroke:currentColor;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round;
  transition:transform .22s var(--ease);
}
.err-back:hover{color:var(--ink)}
.err-back:hover svg{transform:translateX(-3px)}

/* ── search ──────────────────────────────────────────────────
   A search box that cannot search is worse than none, so with JavaScript
   off the whole band goes — the index further down covers the same ground
   without it. */
.find-band{background:var(--paper-2);border-block:1px solid var(--line)}
.no-js .find-band{display:none}

.find{max-width:680px}
.find-label{
  display:block;margin-bottom:10px;
  font-size:.89rem;font-weight:580;color:var(--ink);
}
/* the "/" shortcut, drawn as the key it is. A keyboard hint is no use to
   anyone on a phone, so it goes at the width the keyboard does. */
.find-label kbd{
  display:inline-block;margin-inline:2px;padding:1px 7px 2px;
  border-radius:6px;background:var(--white);
  border:1px solid var(--line-2);border-bottom-width:2px;
  font-family:var(--mono);font-size:.78em;font-weight:600;line-height:1.5;
  color:var(--slate);
}
.find-hint-key{color:var(--slate-lt);font-weight:400}
@media (max-width:760px){
  .find-label kbd,.find-hint-key{display:none}
}
/* One rounded field with the icon inside it and the button flush to the end,
   so it reads as a single control rather than three parts in a row. */
.find-box{
  display:flex;align-items:center;gap:10px;
  padding:7px 8px 7px 16px;border-radius:var(--r-pill);
  background:var(--white);border:1px solid var(--line-2);
  transition:border-color .2s,box-shadow .2s;
}
.find-box:focus-within{
  border-color:var(--amber);
  box-shadow:0 0 0 3px rgba(226,149,43,.16);
}
.find-ico{
  width:19px;height:19px;flex:none;
  fill:none;stroke:var(--slate-lt);stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round;
}
.find-input{
  flex:1;min-width:0;
  border:0;background:none;padding:9px 0;
  font-size:1rem;color:var(--ink);
}
.find-input:focus{outline:none}      /* the ring is on the box, not the field */
.find-input::placeholder{color:var(--slate-lt);opacity:1}
/* Chrome and Edge draw their own clear button on type="search" and it lands
   on top of the Search button at narrow widths */
.find-input::-webkit-search-cancel-button{display:none}
.find-go{flex:none}

.find-hint{margin-top:12px;font-size:.88rem;color:var(--slate-lt);min-height:1.4em}

.find-list{display:grid;gap:8px;margin-top:16px}
.find-hit{
  display:grid;gap:3px;
  padding:14px 16px;border-radius:var(--r-sm);
  background:var(--white);border:1px solid var(--line);
  transition:border-color .18s,box-shadow .18s,transform .18s var(--ease);
}
.find-hit:hover{border-color:var(--line-2);box-shadow:var(--shadow-sm);transform:translateX(3px)}
.find-hit strong{font-size:1rem;font-weight:620;letter-spacing:-.012em;color:var(--ink)}
.find-hit span{font-size:.9rem;line-height:1.5;color:var(--slate)}
.find-hit em{
  font-family:var(--mono);font-style:normal;font-size:.76rem;
  color:var(--slate-lt);overflow-wrap:anywhere;
}

/* ── the way back ────────────────────────────────────────────
   Six cards, three across. A 404 only earns its place if it ends the visit
   on a real page, so each one is a whole link — icon, name, what is on it,
   and where it goes — rather than a bare list of slugs. */
.lost-grid{
  display:grid;grid-template-columns:repeat(3,1fr);
  gap:clamp(16px,2vw,24px);
}
.lost{
  display:flex;flex-direction:column;height:100%;
  padding:26px 24px;border-radius:var(--r);
  background:var(--white);border:1px solid var(--line);
  transition:transform .28s var(--ease),box-shadow .28s var(--ease),border-color .2s;
}
.lost:hover{transform:translateY(-4px);box-shadow:var(--shadow-md);border-color:var(--line-2)}
/* on touch there is no pointer to leave, so a lift on tap just sticks */
@media (hover:none){.lost:hover{transform:none}}
.lost-ico{
  display:grid;place-items:center;width:44px;height:44px;border-radius:11px;
  margin-bottom:18px;background:var(--paper-2);
  transition:background .2s;
}
.lost-ico svg{
  width:22px;height:22px;
  fill:none;stroke:var(--ink);stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round;
}
.lost:hover .lost-ico{background:var(--amber-lt)}
.lost strong{
  font-size:1.16rem;font-weight:620;line-height:1.34;letter-spacing:-.014em;
  color:var(--ink);
}
.lost-say{margin-top:9px;font-size:.94rem;line-height:1.58;color:var(--slate)}
/* pinned to the bottom by margin-top:auto so the six arrows sit on one line
   however unevenly the descriptions wrap */
.lost-go{
  display:inline-flex;align-items:center;gap:8px;
  margin-top:auto;padding-top:18px;
  font-size:.86rem;font-weight:620;letter-spacing:.005em;color:var(--blue);
}
.lost-go svg{
  width:16px;height:16px;flex:none;
  fill:none;stroke:currentColor;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round;
  transition:transform .22s var(--ease);
}
.lost:hover .lost-go svg{transform:translateX(3px)}

/* ── the complete index ──────────────────────────────────────
   Four columns of plain links, each with the line of context that tells you
   whether it is the one you want. This is the part that works with nothing
   switched on — no JavaScript, no search, no guessing. */
.index-grid{
  display:grid;grid-template-columns:repeat(4,1fr);
  gap:clamp(24px,3vw,44px);
}
.index-col h3{
  padding-bottom:12px;margin-bottom:6px;
  border-bottom:2px solid var(--amber);
  font-size:.78rem;font-weight:640;letter-spacing:.12em;text-transform:uppercase;
  color:var(--slate);
}
.index-col li{padding:11px 0;border-bottom:1px solid var(--line)}
.index-col li:last-child{border-bottom:0}
.index-col a{
  display:block;
  font-size:.98rem;font-weight:580;letter-spacing:-.008em;color:var(--ink);
  transition:color .18s;
}
.index-col a:hover{color:var(--blue);text-decoration:underline;text-underline-offset:3px}
.index-col span{display:block;margin-top:2px;font-size:.84rem;line-height:1.45;color:var(--slate-lt)}

.index-report{
  margin-top:clamp(34px,4vw,52px);padding-top:24px;
  border-top:1px solid var(--line);
  font-size:.95rem;color:var(--slate);
}
.index-report a{
  font-weight:620;color:var(--blue);
  text-decoration:underline;text-decoration-thickness:1.5px;text-underline-offset:3px;
}
.index-report a:hover{color:var(--ink)}

/* Steps where the homepage hero and the service cards already step, so the
   404 breaks at the same widths as the pages it points at. */
@media (max-width:980px){
  /* Stacked, the card moves up to sit directly under the headline — the
     address that failed is the answer to "what happened?", and leaving it
     at the bottom of the column made a visitor read the whole page before
     seeing it. Everything else keeps its written order underneath.

     display:contents is what makes that possible: it dissolves .err-copy
     as a box so its children become grid items in their own right, which
     is the only way to interleave the card between two of them. The div
     carries no styling or semantics of its own, so nothing is lost with
     it. A browser too old to support it simply keeps the source order —
     the card last — which is the layout that shipped before this. */
  .err-grid{grid-template-columns:1fr;row-gap:0}
  .err-copy{display:contents}
  .err-copy > .eyebrow{order:1}
  .err-copy > h1{order:2}
  .err-panel{order:3}
  .err-copy > .lede{order:4}
  .err-copy > .hero-actions{order:5}
  .err-copy > .err-guess{order:6}
  .err-copy > .err-back-line{order:7}
  .err-copy > .err-why{order:8}

  /* With row-gap off, the spacing between these is each element's own
     margin. Only the card needs one: 44px at the bottom because the
     "one click away" badge hangs 21px below its edge and would otherwise
     land on the paragraph underneath. */
  .err-copy > h1{margin-bottom:24px}
  .err-panel{margin:0 0 44px;max-width:460px}
  .err-copy > .lede{max-width:none}

  .lost-grid{grid-template-columns:1fr 1fr}
  .index-grid{grid-template-columns:1fr 1fr}
}
@media (max-width:760px){
  .lost-grid{grid-template-columns:1fr}
  /* Two columns of links at phone width put four words on a line. One
     column, with each heading acting as its own divider, reads faster. */
  .index-grid{grid-template-columns:1fr;gap:30px}
}
/* At phone width the Search button costs more room than the field it takes it
   from, so the pill becomes a rounded box and the button drops onto its own
   full-width line under the input. Hiding it instead would leave the Enter
   key as the only way to search, with nothing on screen saying so. */
@media (max-width:480px){
  .find-box{flex-wrap:wrap;border-radius:var(--r);padding:10px 14px}
  .find-go{flex:1 0 100%;justify-content:center;margin-top:4px}
}
/* The two buttons stack below 420px on their own: this page uses .hero-actions
   for them, and that rule is already in the responsive block further up. */

/* ── blog (blog.html + blog/*.html) ──────────────────────────
   Added whole and last, its own breakpoints included, so the blog reads —
   and could be lifted out — in one piece. Every colour, radius, shadow and
   easing below is a token that already existed; nothing above this line is
   touched.

   Two things are styled here. The listing at /blog, which borrows the work
   cards' shape so an article and a case study feel like the same kind of
   object. And the article page itself at /blog/<slug>, which is the only
   page on the site that is mostly prose — so it gets a narrower measure than
   .wrap gives, and .prose puts back the list bullets, link underlines and
   paragraph spacing that the site-wide reset strips off everywhere else. */

/* the "N articles, written by…" line under the blog lede */
.blog-count{margin-top:20px;font-size:.9rem;color:var(--slate-lt)}
.blog-count strong{
  color:var(--ink);font-weight:640;
  font-variant-numeric:tabular-nums;   /* the count changes; the line must not shift */
}

/* date · reading time · author. The separator is an <i> rather than a "·"
   character so it keeps its size and colour whatever the font is doing. */
.post-meta{
  display:flex;flex-wrap:wrap;align-items:center;gap:9px;
  font-size:.82rem;color:var(--slate-lt);letter-spacing:.01em;
}
.post-meta i{width:3px;height:3px;border-radius:50%;background:var(--line-2);flex:none}

.post-tags{display:flex;flex-wrap:wrap;gap:7px;margin-top:16px}
.post-tags span{
  padding:5px 11px;border-radius:var(--r-pill);
  background:var(--paper-2);border:1px solid var(--line);
  font-size:.75rem;font-weight:560;color:var(--slate);letter-spacing:.01em;
}

/* ── the listing ──────────────────────────────────────────── */
.blog-sec{padding-top:clamp(40px,5vw,64px)}

/* Shared by both card sizes: the picture. A cover that failed to download
   leaves the tinted panel and the initial behind it, so the card still has a
   deliberate shape rather than a hole. */
.post-card-shot,.post-feature-shot{
  position:relative;display:block;overflow:hidden;background:var(--paper-2);
}
.post-card-shot img,.post-feature-shot img{
  object-fit:cover;transition:transform .5s var(--ease);
}
/* The small card sets its own 16/9 box, so its image can simply fill it. */
.post-card-shot img{display:block;width:100%;height:100%}
.post-card-mark{
  position:absolute;inset:0;display:grid;place-items:center;
  font-family:var(--display);font-size:3.4rem;font-weight:600;
  color:var(--ink);opacity:.16;
}

/* featured — the newest article, across the full width */
.post-feature{margin-bottom:clamp(34px,4vw,54px)}
/* The copy takes the wider half. It used to be the picture's, from when the
   picture was cropped to fill it — now that the cover sits whole on a blurred
   fill, extra width there only makes more blur, while the headline needs every
   character it can get: in the narrower column these titles balanced into four
   lines with a two-word runt in the middle. */
.post-feature-link{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.2fr);
  background:var(--white);border:1px solid var(--line);border-radius:var(--r-lg);
  overflow:hidden;box-shadow:var(--shadow-sm);
  transition:transform .3s var(--ease),box-shadow .3s var(--ease),border-color .2s;
}
.post-feature-link:hover{transform:translateY(-4px);box-shadow:var(--shadow-md);border-color:var(--line-2)}
.post-feature-link:hover .post-feature-shot img{transform:scale(1.035)}
/* The cover is shown WHOLE, and the space around it is filled with a blown-up,
   blurred copy of the same picture.

   Why not simply crop it to fit, as the small cards do: these covers are made
   in Canva with the headline set into the artwork, so a crop does not lose
   scenery, it loses words. Stretched to the height of the copy beside it,
   "SHOPIFY SEO OPTIMISATION / RANK HIGHER. DRIVE TRAFFIC." reached the page as
   "MISATION / GHER. / AFFIC." — three cut-off fragments.

   And why not just centre it and leave the gap: a 16:9 picture in a column as
   tall as four lines of headline leaves a white band above and below, and the
   cover reads as something pasted into the card rather than part of it. The
   blurred fill is the same image, so the bands are always the right colour for
   the picture in front of them, whatever a future cover looks like.

   The blur is on a ::before rather than on the image, because a filter on the
   image would soften the words we just went to this trouble to keep. */
.post-feature-shot{align-self:stretch;min-height:15rem}
.post-feature-shot::before{
  content:"";position:absolute;inset:0;
  background:var(--cover) center/cover no-repeat;
  /* scaled up so the blur has image to bleed from and never shows an edge */
  transform:scale(1.2);filter:blur(26px) saturate(1.15) brightness(.92);
}
.post-feature-shot img{
  position:absolute;inset:0;margin:auto;
  width:100%;height:100%;object-fit:contain;
}
/* No cover downloaded: no backdrop to blur, so the tinted panel and the
   article's initial stand on their own. */
.post-feature-shot:not([style*="--cover"])::before{display:none}
.post-feature-body{
  display:flex;flex-direction:column;align-items:flex-start;justify-content:center;
  padding:clamp(26px,3.2vw,42px);
}
.post-flag{
  display:inline-block;margin-bottom:16px;padding:5px 12px;border-radius:var(--r-pill);
  background:var(--amber-lt);color:var(--amber-dp);
  font-size:.72rem;font-weight:660;letter-spacing:.1em;text-transform:uppercase;
}
.post-feature-title{
  display:block;margin-top:14px;
  font-family:var(--display);font-size:clamp(1.45rem,2.4vw,2.05rem);
  font-weight:600;line-height:1.16;letter-spacing:-.02em;color:var(--ink);
  text-wrap:balance;
}
/* Three lines, then an ellipsis. The excerpt is a taste of the article, not a
   summary of it, and letting it run pushes the copy column well past the
   height of the cover beside it. */
.post-feature-excerpt{
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;
  overflow:hidden;margin-top:14px;color:var(--slate);line-height:1.62;
}

/* the rest, three across */
.blog-grid{
  display:grid;grid-template-columns:repeat(3,1fr);
  gap:clamp(18px,2.2vw,28px);
}
.blog-more-head{margin-bottom:clamp(24px,3vw,38px)}

.post-card{display:flex}
.post-card-link{
  display:flex;flex-direction:column;width:100%;
  background:var(--white);border:1px solid var(--line);border-radius:var(--r);
  overflow:hidden;box-shadow:var(--shadow-sm);
  transition:transform .3s var(--ease),box-shadow .3s var(--ease),border-color .2s;
}
.post-card-link:hover{transform:translateY(-5px);box-shadow:var(--shadow-md);border-color:var(--line-2)}
.post-card-link:hover .post-card-shot img{transform:scale(1.04)}
.post-card-shot{aspect-ratio:16/9}
.post-card-body{display:flex;flex-direction:column;flex:1;padding:22px 22px 24px}
.post-card-title{
  display:block;margin-top:12px;
  font-size:1.09rem;font-weight:620;line-height:1.34;letter-spacing:-.016em;color:var(--ink);
}
.post-card-excerpt{
  display:block;margin-top:10px;flex:1;
  font-size:.94rem;line-height:1.6;color:var(--slate);
}
/* the same read-more affordance the work cards use, so the two card families
   answer a click the same way */
.post-card-more{
  display:inline-flex;align-items:center;gap:7px;margin-top:18px;
  font-size:.88rem;font-weight:600;color:var(--blue);
}
.post-card-more svg{
  width:16px;height:16px;flex:none;fill:none;stroke:currentColor;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round;transition:transform .25s var(--ease);
}
.post-card-link:hover .post-card-more svg,
.post-feature-link:hover .post-card-more svg{transform:translateX(3px)}
/* on touch there is no pointer to leave, so a lift on tap just sticks */
@media (hover:none){
  .post-card-link:hover,.post-feature-link:hover{transform:none}
}

/* ── the article page ─────────────────────────────────────── */
.article-head{border-bottom:0;padding-bottom:clamp(22px,2.6vw,32px)}
/* 26ch, not the 58ch the other page heads use: a headline set at this size
   wants a short measure, and these titles are long. Too narrow, though, and
   balancing them costs a line — at 20ch this one came out as four, with
   "Services: Rank" stranded on its own. */
.article-head h1{max-width:26ch;font-size:clamp(1.85rem,3.6vw,2.85rem)}
.article-meta{margin-top:20px}

/* The cover sits between the head and the text, held to the same measure as
   the prose so the page has one edge rather than two. 68ch is a different
   number of pixels at a different font size, which is why the wrapper takes
   the prose's size before the image measures itself against it. */
.article-cover{margin-block:clamp(8px,1.6vw,20px) clamp(28px,3.4vw,44px);font-size:1.06rem}
.article-cover img{
  display:block;width:100%;max-width:68ch;height:auto;
  border-radius:var(--r-lg);border:1px solid var(--line);
  background:var(--paper-2);
}

/* 68 characters is the measure the body copy is set for. Wider than this and
   the eye loses the start of the next line on a long article. */
.article-wrap{padding-bottom:clamp(46px,5.5vw,78px)}
.prose{max-width:68ch;font-size:1.06rem;line-height:1.75;color:var(--ink-2)}
.prose>*+*{margin-top:1.15em}
.prose h2{
  margin-top:1.9em;font-size:clamp(1.35rem,2.2vw,1.72rem);
  scroll-margin-top:110px;   /* clears the sticky header when linked to */
}
.prose h3{margin-top:1.7em;font-size:1.12rem}
.prose h2+*,.prose h3+*{margin-top:.7em}
.prose strong{font-weight:640;color:var(--ink)}
.prose em{font-style:italic}
/* The site-wide reset takes list markers off every ul and ol, which is right
   for nav and card lists and wrong for an article. Put them back, here only. */
.prose ul,.prose ol{padding-left:1.35em}
.prose ul{list-style:disc}
.prose ol{list-style:decimal}
.prose li{margin-top:.5em;padding-left:.2em}
.prose li::marker{color:var(--amber)}
.prose ol li::marker{color:var(--slate-lt);font-weight:600}
.prose a{
  color:var(--blue);text-decoration:underline;
  text-decoration-thickness:1.5px;text-underline-offset:3px;
  text-decoration-color:rgba(27,75,127,.35);transition:text-decoration-color .2s;
}
.prose a:hover{text-decoration-color:currentColor}
.prose blockquote{
  margin-inline:0;padding:4px 0 4px 22px;border-left:3px solid var(--amber);
  font-family:var(--display);font-size:1.14em;font-style:italic;
  line-height:1.5;color:var(--ink);
}
.prose blockquote p+p{margin-top:.6em}
.prose figure{margin-block:2em}
.prose img{
  display:block;width:100%;height:auto;border-radius:var(--r);
  border:1px solid var(--line);background:var(--paper-2);
}
.prose figcaption{
  margin-top:10px;font-size:.86rem;line-height:1.5;color:var(--slate-lt);text-align:center;
}
.prose pre{
  overflow-x:auto;padding:18px 20px;border-radius:var(--r);
  background:var(--ink);color:#f4f1ea;
  font-family:var(--mono);font-size:.88rem;line-height:1.6;
}
.prose code{
  font-family:var(--mono);font-size:.9em;
  padding:.12em .4em;border-radius:6px;background:var(--paper-2);color:var(--ink);
}
.prose pre code{padding:0;background:none;color:inherit}
.prose hr{height:1px;margin-block:2.2em;border:0;background:var(--line)}

/* Where the article ends and the site takes over again. */
.article-foot{
  display:flex;align-items:center;justify-content:space-between;gap:20px;flex-wrap:wrap;
  max-width:68ch;margin-top:clamp(36px,4vw,54px);padding-top:26px;
  border-top:1px solid var(--line);
}
.article-source{font-size:.9rem;color:var(--slate-lt)}
.article-source a{
  color:var(--blue);text-decoration:underline;
  text-decoration-thickness:1.5px;text-underline-offset:3px;
}
.post-more{background:var(--paper-2);border-block:1px solid var(--line)}
.post-more .sec-close{margin-top:clamp(28px,3.2vw,42px)}

/* 3 across, 2 on a tablet, 1 on a phone — the same steps the work grid takes,
   so a page carrying both breaks in one place rather than two. The featured
   card unstacks at the width its two columns stop being readable. */
@media (max-width:980px){
  .blog-grid{grid-template-columns:repeat(2,1fr)}
}
/* Stacked, there is no column beside the picture setting its height, so it
   takes the cover's own 16:9 — which fits it exactly, and leaves the blurred
   fill nothing to do. */
@media (max-width:860px){
  .post-feature-link{grid-template-columns:1fr}
  .post-feature-shot{aspect-ratio:16/9;min-height:0}
}
@media (max-width:620px){
  .blog-grid{grid-template-columns:1fr}
  .article-head h1{max-width:none}
  .prose,.article-cover{font-size:1.02rem}
  /* full width, and the two ends of the row are no longer opposite each other */
  .article-foot{flex-direction:column;align-items:flex-start}
  .article-foot .btn{width:100%;justify-content:center}
}

@media print{
  .post-more,.article-foot .btn,.blog-count{display:none}
  .prose{max-width:none}
  .prose a{color:inherit;text-decoration:none}
}
