/* GENERATED FILE. Do not edit by hand.
   Source: the site-shell regions of index.html. Regenerate with:
     node scripts/build-site-shell.mjs
   Every page except the homepage loads this. The homepage carries the same rules inline,
   which is why the two can never disagree; npm test fails if this copy falls behind. */

/* Chakra Petch for headings and IBM Plex Sans for reading, chosen by Jacob on 3 October 2026 from the font mock-up (both OFL, files and licences in assets/fonts). */
@font-face{font-family:'Chakra Petch';font-style:normal;font-weight:500;font-display:swap;src:url('/assets/fonts/chakra-petch-500-latin.woff2') format('woff2');}
@font-face{font-family:'Chakra Petch';font-style:normal;font-weight:600;font-display:swap;src:url('/assets/fonts/chakra-petch-600-latin.woff2') format('woff2');}
@font-face{font-family:'Chakra Petch';font-style:normal;font-weight:700;font-display:swap;src:url('/assets/fonts/chakra-petch-700-latin.woff2') format('woff2');}
@font-face{font-family:'IBM Plex Sans';font-style:normal;font-weight:400 700;font-display:swap;src:url('/assets/fonts/ibm-plex-sans-400-700-latin.woff2') format('woff2');}
@font-face{font-family:'IBM Plex Sans';font-style:italic;font-weight:400;font-display:swap;src:url('/assets/fonts/ibm-plex-sans-italic-400-latin.woff2') format('woff2');}
:root{
  --bg:            oklch(0.972 0.016 195);
  /* Hue-free white avoids colour-mix rotation and keeps older-browser support. */
  --surface:       #fff;
  --surface-sunk:  oklch(0.955 0.008 85);
  --ink:           oklch(0.19 0.022 220);
  /* Secondary copy. Darkened from 0.44 so small labels (timeline, footer
     descriptions, form hints) clear WCAG AA with room to spare on cream. */
  --ink-soft:      oklch(0.38 0.020 220);
  /* One step stronger than --ink-soft, for the small subtitles that carry the plain-English
     explanations. They were the quietest text on the page and the most load-bearing. */
  --ink-mid:       oklch(0.32 0.022 220);
  --line:          oklch(0.885 0.008 85);
  --brand:         oklch(0.30 0.052 245);
  --brand-deep:    oklch(0.215 0.045 245);
  --on-brand:      oklch(0.975 0.008 200);
  /* Text on the navy bands. Lifted from 0.84 so hints, founder copy and
     form notes stay clearly above the AA floor on --brand and --brand-deep. */
  --on-brand-soft: oklch(0.91 0.018 210);
  /* Text sitting ON the accent colour. --accent is a dark teal here and a bright turquoise in
     the dark theme, so one fixed value cannot read on both: it has to flip with the theme.
     Added 20 September 2026 after the "In short" pill measured 1.70:1 in dark on all seven
     case studies, which is what idea 68 of that day's audit asked somebody to go and check. */
  --on-accent:     oklch(0.975 0.008 200);
  --brand-line:    oklch(0.50 0.050 225);
  --signal:        oklch(0.76 0.145 190);
  --signal-ink:    oklch(0.20 0.045 195);
  --signal-text:   oklch(0.34 0.070 220);
  --glow:          oklch(0.76 0.145 190 / 0.08);
  /* The teal panel and its edge, plus the confirm green. Used by the Systems Studio rather
     than by this page, and defined here because this is where the site's colours live: until
     15 September 2026 the Studio carried its own copies of all three and they drifted. */
  --wash:          oklch(0.955 0.030 195);
  --wash-line:     oklch(0.855 0.060 195);
  --success:       oklch(0.45 0.090 160);

  --display:'Chakra Petch', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --body:'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;

  /* Glass layer (assets/glass.css), added 1 October 2026. */
  --glass:       rgb(255 255 255 / .62);
  --glass-edge:  rgb(255 255 255 / .85);
  --glass-shadow:0 18px 50px -22px oklch(0.45 0.07 200 / .35);
  --blob-a:      oklch(0.86 0.10 190 / .55);
  --blob-b:      oklch(0.82 0.09 250 / .38);
  --blob-c:      oklch(0.85 0.10 165 / .38);

  --e-out: cubic-bezier(0.16, 1, 0.3, 1);
  --e-soft: cubic-bezier(0.22, 1, 0.36, 1);

  --shadow-lift: 0 1px 2px oklch(0.2 0.02 220 / 0.04),
                 0 8px 24px -12px oklch(0.2 0.02 220 / 0.12),
                 0 24px 60px -30px oklch(0.2 0.02 220 / 0.18);
  /* Sticky pill: 14px nav padding + 68px bar. The scroll offset is taller
     than the bar so a focused control and its outline clear the shadow. */
  --site-header-height: 82px;
  --site-header-offset: 112px;
}
/* Where the `none` hue parses, say it explicitly. Same colour, clearer intent. */
@supports (color: oklch(1 0 none)){
  :root{ --surface: oklch(1 0 none); }
}

@media all{
  :root:not([data-theme="light"]){
    --bg:            oklch(0.14 0.02 215);
    --surface:       oklch(0.205 0.020 220);
    --surface-sunk:  oklch(0.128 0.016 220);
    --ink:           oklch(0.965 0.006 200);
    --ink-soft:      oklch(0.84 0.012 210);
    --ink-mid:       oklch(0.90 0.010 210);
    --line:          oklch(0.305 0.020 215);
    --brand:         oklch(0.245 0.050 245);
    --brand-deep:    oklch(0.18 0.040 245);
    --on-brand:      oklch(0.975 0.008 200);
    --on-brand-soft: oklch(0.91 0.016 200);
    --on-accent:     oklch(0.17 0.030 215);
    --brand-line:    oklch(0.45 0.045 220);
    --signal:        oklch(0.79 0.135 188);
    --signal-ink:    oklch(0.19 0.043 194);
    --signal-text:   oklch(0.82 0.135 188); /* a step lighter than --signal so small teal words keep 4.5:1 over the brightest glow behind a glass card (8 Oct 2026) */
    --glow:          oklch(0.79 0.135 188 / 0.10);
    --wash:          oklch(0.265 0.042 195);
    --wash-line:     oklch(0.38 0.05 195);
    --success:       oklch(0.82 0.090 165);
    --glass:       rgb(255 255 255 / .065);
    --glass-edge:  rgb(255 255 255 / .17);
    --glass-shadow:0 22px 50px -24px rgb(0 0 0 / .65);
    --blob-a:      oklch(0.50 0.10 190 / .55);
    --blob-b:      oklch(0.40 0.11 250 / .5);
    --blob-c:      oklch(0.45 0.10 165 / .45);
    --shadow-lift: 0 1px 2px oklch(0 0 0 / 0.2),
                   0 10px 30px -12px oklch(0 0 0 / 0.5),
                   0 30px 70px -30px oklch(0 0 0 / 0.6);
  }
}
:root[data-theme="dark"]{
  --bg:            oklch(0.14 0.02 215);
  --surface:       oklch(0.205 0.020 220);
  --surface-sunk:  oklch(0.128 0.016 220);
  --ink:           oklch(0.965 0.006 200);
  --ink-soft:      oklch(0.84 0.012 210);
  --ink-mid:       oklch(0.90 0.010 210);
  --line:          oklch(0.305 0.020 215);
  --brand:         oklch(0.245 0.050 245);
  --brand-deep:    oklch(0.18 0.040 245);
  --on-brand:      oklch(0.975 0.008 200);
  --on-brand-soft: oklch(0.91 0.016 200);
  --on-accent:     oklch(0.17 0.030 215);
  --brand-line:    oklch(0.45 0.045 220);
  --signal:        oklch(0.79 0.135 188);
  --signal-ink:    oklch(0.19 0.043 194);
  --signal-text:   oklch(0.82 0.135 188); /* a step lighter than --signal so small teal words keep 4.5:1 over the brightest glow behind a glass card (8 Oct 2026) */
  --glow:          oklch(0.79 0.135 188 / 0.10);
  --wash:          oklch(0.265 0.042 195);
  --wash-line:     oklch(0.38 0.05 195);
  --success:       oklch(0.82 0.090 165);
  --glass:       rgb(255 255 255 / .065);
  --glass-edge:  rgb(255 255 255 / .17);
  --glass-shadow:0 22px 50px -24px rgb(0 0 0 / .65);
  --blob-a:      oklch(0.50 0.10 190 / .55);
  --blob-b:      oklch(0.40 0.11 250 / .5);
  --blob-c:      oklch(0.45 0.10 165 / .45);
  --shadow-lift: 0 1px 2px oklch(0 0 0 / 0.2),
                 0 10px 30px -12px oklch(0 0 0 / 0.5),
                 0 30px 70px -30px oklch(0 0 0 / 0.6);
}

*,*::before,*::after{box-sizing:border-box}

/* ---- focus, designed rather than left to the browser default ---- */
:where(a,button,[tabindex]):focus-visible{
  outline:2.5px solid var(--signal);
  outline-offset:3px;
  border-radius:7px;
}
.band :where(a,button):focus-visible,
.cta :where(a,button):focus-visible{
  outline-color:var(--on-brand);
}

/* ---- skip link ---- */
.skip{
  position:absolute;left:-9999px;top:0;
  background:var(--signal);color:var(--signal-ink);
  font-weight:600;font-size:0.95rem;
  padding:13px 22px;border-radius:9px;
  text-decoration:none;z-index:500;
}
.skip:focus{left:18px;top:18px;}

/* screen-reader-only, still focusable and still announced */
.sr{
  position:absolute;width:1px;height:1px;
  padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;
}

html{scroll-behavior:smooth;--page-scroll-pad:var(--site-header-offset);scroll-padding-top:var(--page-scroll-pad);}
/* The sticky header sits inside that padding, so WebKit and Chromium scrolled the page up
   by up to the padding every time keyboard focus landed on a header control from mid-page.
   A negative scroll margin of the same size tells them the control is already in view.
   Pages that change the padding change --page-scroll-pad, never scroll-padding-top. */
#nav :is(a,button,input,select,summary){scroll-margin-top:calc(-1 * var(--page-scroll-pad));}
@media (prefers-reduced-motion: reduce){ html{scroll-behavior:auto;} }

body{
  margin:0;
  background:var(--bg);
  color:var(--ink);
  font-family:var(--body);
  font-size:17px;
  line-height:1.6;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}

/* film grain, sits above everything, never intercepts a click */
.grain{
  position:fixed;inset:0;z-index:900;pointer-events:none;
  opacity:0.035;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23n)'/%3E%3C/svg%3E");
  mix-blend-mode:overlay;
}
@media all{ :root:not([data-theme="light"]) .grain{opacity:0.05;} }

.wrap{width:100%;max-width:1160px;margin:0 auto;padding:0 28px;}
.narrow{max-width:770px;}

/* A grid or flex item defaults to a min-content floor, so one wide child (a
   form input's intrinsic width, a long email address) pushes the whole track
   past the viewport. Every track child opts out of that floor. */
.hero-grid > *, .step > *,
.services > *{ min-width:0; }
.field input, .field textarea{ width:100%; min-width:0; }
.cta-mail a, .form-note{ overflow-wrap:anywhere; }

h1,h2,h3,h4{margin:0;font-family:var(--display);font-weight:400;letter-spacing:-0.02em;text-wrap:balance;}
p{margin:0;text-wrap:pretty;}
a{color:inherit;}

/* ============ scroll progress ============ */
.progress{
  position:fixed;top:0;left:0;height:2px;width:100%;
  transform:scaleX(0);transform-origin:0 50%;
  background:var(--signal);
  z-index:400;
  will-change:transform;
}

/* ============ nav ============ */
/* Keep sticky header rules scoped to #nav; the page contains other nav elements. */
/* The bar sits in a header element so every page has a banner landmark (23 Oct 2026). The header is
   exactly the bar's height, so it takes over the sticking; #nav stays sticky but has nowhere to move. */
.site-header{position:sticky;top:0;z-index:300;}
#nav{
  position:sticky;top:0;z-index:300;
  padding:14px 20px 0;
}
/* The bar is a detached pill rather than a full-bleed strip: background, border and
   shadow live on .nav-inner so page colour shows around it at every width, not just
   above .wrap's own max-width. .stuck (still toggled by site-nav.js on scroll)
   only has to deepen what is already there. */
.nav-inner{
  display:flex;align-items:center;justify-content:space-between;gap:24px;
  height:68px;
  padding:0 22px;
  border-radius:20px;
  border:1px solid var(--line);
  background:color-mix(in oklch, var(--surface) 82%, transparent);
  box-shadow:0 14px 34px -18px oklch(0.19 0.022 220 / .35);
  transition:height 0.4s var(--e-soft), box-shadow 0.4s var(--e-soft), border-color 0.4s var(--e-soft);
}
@supports (backdrop-filter: blur(8px)){
  .nav-inner{backdrop-filter:blur(14px) saturate(140%);}
}
#nav.stuck .nav-inner{
  border-color:color-mix(in oklch, var(--ink) 14%, var(--line));
  box-shadow:0 18px 40px -16px oklch(0.19 0.022 220 / .45);
}
.nav-actions{position:relative;display:flex;align-items:center;gap:12px;}
.nav-socials{display:flex;align-items:center;gap:6px;padding-right:10px;margin-right:2px;border-right:1px solid var(--line);}
.nav-socials a{
  display:grid;place-items:center;width:40px;height:40px;border-radius:50%;
  color:var(--ink-soft);transition:color .25s var(--e-out),background .25s var(--e-out);
}
.nav-socials a:hover{color:var(--ink);background:color-mix(in oklch,var(--ink) 8%,transparent);}
.nav-socials svg{width:16px;height:16px;}
@media (max-width:640px){.nav-socials{display:none;}}
/* overflow:hidden with no padding or gap: the two buttons below tile edge to edge, so every
   pixel inside the pill belongs to one button or the other. Jacob had to click precisely on
   the light-mode circle for the toggle to respond; the 3px padding and 2px gap around and
   between the old circular buttons were dead space that swallowed a click near either edge. */
.theme-switch{
  display:inline-flex;align-items:center;
  border:1px solid var(--line);border-radius:999px;background:var(--surface);
  overflow:hidden;
}
.theme-switch button{
  width:32px;height:30px;padding:0;display:grid;place-items:center;border:0;
  background:transparent;color:var(--ink-soft);cursor:pointer;
  transition:color .25s var(--e-out),background .25s var(--e-out),box-shadow .25s var(--e-out);
}
.theme-switch button:hover{color:var(--ink);background:var(--surface-sunk);}
.theme-switch button[aria-pressed="true"]{
  color:var(--signal-ink);background:var(--signal);box-shadow:inset 0 0 0 1px oklch(0.2 0.04 195 / .2);
}
.theme-switch svg{width:15px;height:15px;stroke-width:1.9;}
/* The pill clips anything outside it, so the focus ring is drawn just inside the button, in the
   button's own text colour so it shows on the lit (chosen) button as well as the plain one. */
.theme-switch button:focus-visible{outline:2px solid currentColor;outline-offset:-4px;border-radius:999px;}
.sheet-theme{display:flex;align-items:center;justify-content:space-between;gap:18px;margin-top:18px;padding-top:18px;border-top:1px solid var(--line);}
.sheet-theme > span{font-size:.78rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-soft);}
/* Two quiet group labels split the menu's eight links into what to explore
   and how to start, same caption style as "Appearance" below. */
.sheet-group-label{font-size:.78rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-soft);}
.sheet-group-label-start{margin-top:12px;}
.sheet-theme .theme-switch button{width:44px;height:44px;}
/* Keep the sticky frame a fixed height. Shrinking it near the scroll threshold
   changes page geometry and can make the header repeatedly cross that same
   threshold, which presents as a visible shake at the top of the page. */
.logo{
  text-decoration:none;display:flex;align-items:center;flex:none;
}
/* A full 44px tap area for the home link on phones and touch tablets; the bar is already that tall. */
@media (max-width:640px), (pointer:coarse) and (max-width:1024px){ .logo{min-height:44px;} }
.logo-lockup{
  display:block;width:205px;height:auto;flex:none;
}
/* the wordmark is dark artwork on light grounds and white artwork on dark ones,
   so one of the pair is always hidden */
.logo-on-dark{display:none;}
@media all{
  :root:not([data-theme="light"]) .logo-on-light{display:none;}
  :root:not([data-theme="light"]) .logo-on-dark{display:block;}
}
:root[data-theme="dark"] .logo-on-light{display:none;}
:root[data-theme="dark"] .logo-on-dark{display:block;}
:root[data-theme="light"] .logo-on-light{display:block;}
:root[data-theme="light"] .logo-on-dark{display:none;}
.nav-links{display:flex;gap:6px;align-items:center;}
.nav-links a{
  text-decoration:none;font-size:0.92rem;font-weight:500;color:var(--ink-soft);
  padding:8px 14px;border-radius:7px;position:relative;
  transition:color 0.3s var(--e-out), background 0.3s var(--e-out);
}
.nav-links a:hover{color:var(--ink);background:color-mix(in oklch,var(--ink) 6%,transparent);}

/* AI systems groups the Systems Studio catalogue as a hover/focus flyout, desktop only. */
.nav-drop{position:relative;}
.nav-drop-trigger{display:inline-flex;align-items:center;gap:5px;}
.nav-caret{transition:transform 0.3s var(--e-out);}
.nav-drop:hover .nav-caret,.nav-drop:focus-within .nav-caret{transform:rotate(180deg);}
.nav-drop-panel{
  position:absolute;top:100%;left:0;margin-top:4px;min-width:190px;
  display:flex;flex-direction:column;gap:1px;padding:6px;
  background:var(--surface);border:1px solid var(--line);border-radius:10px;
  box-shadow:var(--shadow-lift);
  opacity:0;visibility:hidden;transform:translateY(-4px);
  transition:opacity 0.25s var(--e-out),transform 0.25s var(--e-out),visibility 0.25s;
}
.nav-drop:hover .nav-drop-panel,.nav-drop:focus-within .nav-drop-panel{opacity:1;visibility:visible;transform:none;}
/* Escape closes the menu even while the pointer or focus is still on it; the script clears the
   class once both have left, so the next hover or Tab opens it again. */
.nav-drop.is-dismissed .nav-drop-panel{opacity:0;visibility:hidden;transform:translateY(-4px);}
.nav-drop.is-dismissed .nav-caret{transform:none;}
.nav-drop-panel a{padding:9px 11px;border-radius:6px;}
/* The last dropdown (About) opens towards the left, so its panel stays inside the bar instead of hanging past the right edge. */
.nav-drop-end .nav-drop-panel{left:auto;right:0;min-width:0;}

/* The header's Call button was removed on 26 September 2026, at Jacob's request: the number is
   on the contact section already. On narrow screens the Book button now takes its slot, so the
   header still carries one way to reach him. */

.nav-links a,#nav .btn-sm span{white-space:nowrap;}
@media (min-width:1200px){
  .nav-inner{gap:16px;}
  .nav-links{gap:2px;}
  .nav-links a{padding:8px 6px;font-size:0.78rem;}
}
/* The link for the section currently on screen carries the same pill the
   portfolio page uses for its own active link. Colour and background only, so
   the link never changes width and the bar never shifts. */
.nav-links a.active{color:var(--ink);background:color-mix(in oklch,var(--ink) 6%,transparent);}
/* The free website check link at the foot of the What I do menu, in the accent colour so it reads as a tool and not another page. It sits in the menu because a seventh top-level link overflowed the bar between 1280 and 1440 pixels wide. */
.nav-links a.nav-check{color:var(--signal-text);font-weight:600;}
.nav-links a.nav-check:hover{color:var(--ink);}

/* ---- mobile menu ---- */
.burger{
  display:none;width:44px;height:44px;flex:none;
  background:none;border:1px solid var(--line);border-radius:9px;
  cursor:pointer;padding:0;
  /* A column, not a grid: grid rows stretched the three lines across the whole button. */
  flex-direction:column;align-items:center;justify-content:center;gap:4px;
}
.burger span{display:block;width:20px;height:2px;background:var(--ink);border-radius:2px;
  transition:transform 0.45s var(--e-out), opacity 0.3s var(--e-out);}
.menu-open .burger span:nth-child(1){transform:translateY(6px) rotate(45deg);}
.menu-open .burger span:nth-child(2){opacity:0;}
.menu-open .burger span:nth-child(3){transform:translateY(-6px) rotate(-45deg);}

.sheet{
  position:fixed;top:var(--site-header-height);right:0;bottom:0;left:0;z-index:250;
  background:var(--bg);
  display:flex;flex-direction:column;justify-content:center;justify-content:safe center;gap:6px;
  padding:12px 32px 28px;
  overflow-y:auto;overscroll-behavior:contain;
  scroll-padding-top:12px;
  opacity:0;visibility:hidden;
  transform:translateY(-8px);
  transition:opacity 0.4s var(--e-out), transform 0.5s var(--e-out), visibility 0.4s;
}
.menu-open .sheet{opacity:1;visibility:visible;transform:none;}
.sheet :where(a,button):focus-visible{scroll-margin-top:12px;scroll-margin-bottom:12px;}
.sheet a{
  font-family:var(--display);font-size:2.3rem;line-height:1.35;
  text-decoration:none;color:var(--ink);letter-spacing:-0.02em;
}
.sheet a:last-child{color:var(--signal-text);}
/* Each smaller link under a heading takes a 44px tall press (it was 32px; final audit pass). */
.sheet a.sheet-sub{display:flex;align-items:center;min-height:44px;font-size:1.3rem;line-height:1.2;color:var(--ink-soft);margin:-4px 0 -4px 4px;}
/* The current section, mirrored from the desktop nav's own active pill:
   background tint plus a small accent dot, scaled for the sheet's larger type. */
.sheet a.active{
  display:inline-flex;align-items:center;gap:12px;
  background:color-mix(in oklch,var(--ink) 6%,transparent);
  border-radius:10px;padding:2px 16px;
}
.sheet a.active::before{
  content:"";flex:none;width:9px;height:9px;border-radius:50%;background:var(--signal);
}
@media (max-width:1279px){
  .nav-links{display:none}
  #nav .btn-sm{display:none}
  /* two classes, so it beats the rule above without needing !important */
  #nav .btn-sm.nav-contact{display:inline-flex;min-height:44px;padding:10px 15px;}
  #nav .theme-switch{display:none;}
  .burger{display:flex}
}
@media (min-width:1280px){
  .sheet{display:none}
}
/* On a phone-height screen (a 390x844 phone has 762px under the header) the open menu, light and
   dark switch included, fits without scrolling: the big links close up to 44px rows (and 2rem type, the same type-to-row ratio as before), which is
   still the tap size, and the spacing around the switch tightens (final audit pass; the switch
   sat at 837 to 879px, below the bottom of the screen). Taller screens keep the airier layout. */
@media (max-height:940px){
  .sheet{gap:0;padding-top:8px;padding-bottom:14px;}
  .sheet a{display:flex;align-items:center;min-height:44px;font-size:2rem;line-height:1.15;}
  .sheet a.sheet-sub{margin-block:0;}
  .sheet-group-label-start{margin-top:8px;}
  .sheet-theme{margin-top:8px;padding-top:8px;}
}

/* ============ buttons ============ */
.btn{
  position:relative;overflow:hidden;
  display:inline-flex;align-items:center;justify-content:center;gap:9px;
  font-family:var(--body);font-size:0.95rem;font-weight:600;
  padding:14px 26px;border-radius:9px;
  text-decoration:none;border:1px solid transparent;cursor:pointer;
  will-change:transform;
  transition:transform 0.5s var(--e-out), box-shadow 0.4s var(--e-out),
             background 0.3s var(--e-out), color 0.3s var(--e-out), border-color 0.3s var(--e-out);
}
.btn span{position:relative;z-index:2;display:inline-flex;align-items:center;gap:9px;}
.btn::before{
  content:"";position:absolute;inset:0;z-index:1;
  background:radial-gradient(circle at var(--mx,50%) var(--my,50%),
             color-mix(in oklch,#fff 34%,transparent), transparent 62%);
  opacity:0;transition:opacity 0.35s var(--e-out);
}
.btn:hover::before{opacity:1;}
.btn-primary{background:var(--signal);color:var(--signal-ink);
  box-shadow:0 6px 20px -8px var(--signal);}
.btn-primary:hover{box-shadow:0 14px 34px -10px var(--signal);}
.btn-ghost{border-color:var(--line);color:var(--ink);}
.btn-ghost:hover{border-color:var(--ink);}
.btn-outline-brand{background:transparent;border-color:var(--brand-line);color:var(--on-brand);}
.btn-outline-brand:hover{background:color-mix(in oklch,var(--on-brand) 12%,transparent);}
.btn-sm{padding:10px 19px;font-size:0.89rem;}
.btn .arw{transition:transform 0.45s var(--e-out);}
.btn:hover .arw{transform:translateX(4px);}

@media (max-width:640px){
  body{font-size:16px;}
  .wrap{padding:0 20px;}
  .logo-lockup{width:174px;}
}
/* The full "Request a free chat" does not fit beside the logo and menu on a phone, so the
   header shows the short form there; the link keeps the full wording as its accessible name.
   Below 480px even the short form is tight (from 421 to 479px it pushed the menu button into or past the right edge), so the visible label becomes "Book" and the bar's
   side padding shrinks. The button stays on screen: hiding it left the only booking action
   inside the menu. */
#nav .nav-cta-short,#nav .nav-cta-mini{display:none;}
@media (max-width:700px){ #nav .nav-cta-full{display:none;} #nav .nav-cta-short{display:inline;} }
@media (max-width:480px){
  #nav{padding-left:8px;padding-right:8px;}
  #nav .nav-inner{gap:8px;padding:0 8px;}
  .logo-lockup{width:132px;}
  #nav .btn-sm.nav-contact{display:inline-flex;min-height:44px;padding:8px 11px;}
  #nav .nav-cta-full,#nav .nav-cta-short{display:none;}
  #nav .nav-cta-mini{display:inline;}
}
/* At 320px the logo, call icon, Enquire button and menu button came to 28px more than the bar holds,
   so the page scrolled sideways (WCAG 1.4.10, 23 Oct 2026). The gaps close up and the logo steps down. */
@media (max-width:360px){
  .logo-lockup{width:110px;}
  #nav .nav-inner{gap:6px;}
  #nav .nav-actions{gap:4px;}
  #nav .btn-sm.nav-contact{padding:8px 9px;}
}
