@font-face{font-family:'Inter';font-style:normal;font-weight:400 700;font-display:swap;src:url(/assets/fonts/inter.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Grotesk';font-style:normal;font-weight:500 700;font-display:swap;src:url(/assets/fonts/grotesk.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
:root{color-scheme:light dark;
--ink-rgb:238 243 251;
--bg-rgb:11 15 31;
--accent-rgb:95 211 243;
--blue-rgb:76 141 255;
--coral-rgb:255 138 91;
--shadow-rgb:0 0 0;
--deep:#0b0f1f;
--deep-2:#0d1426;
--panel:#121a2e;
--panel2:#18213a;
--ink:#eef3fb;
--muted:#9ba9c4;
--cyan:#5fd3f3;
--blue:#4c8dff;
--coral:#ff8a5b;
--fill-a:#5fd3f3;
--fill-b:#4c8dff;
--on-fill:#04101d;
--knob:#eef3fb;
--line:rgb(var(--ink-rgb)/.12);
--soft:rgb(var(--ink-rgb)/.06);
--line-strong:rgb(var(--ink-rgb)/.34);
--accent-edge:rgb(var(--accent-rgb)/.35);
--accent-edge-hover:rgb(var(--accent-rgb)/.45);
--accent-edge-strong:rgb(var(--accent-rgb)/.6);
--accent-hint:rgb(var(--accent-rgb)/.06);
--accent-wash:rgb(var(--accent-rgb)/.10);
--accent-wash-2:rgb(var(--accent-rgb)/.14);
--accent-wash-3:rgb(var(--accent-rgb)/.20);
--blue-wash:rgb(var(--blue-rgb)/.05);
--coral-wash:rgb(var(--coral-rgb)/.10);
--shadow:0 24px 70px rgb(var(--shadow-rgb)/.45);
--shadow-md:0 18px 55px rgb(var(--shadow-rgb)/.22);
--shadow-lg:0 30px 70px rgb(var(--shadow-rgb)/.34);
--shadow-pop:0 22px 50px rgb(var(--shadow-rgb)/.32);
--shadow-menu:0 24px 50px rgb(var(--shadow-rgb)/.5);
--shadow-bar:0 -18px 50px rgb(var(--shadow-rgb)/.4);
--shadow-cta:0 12px 34px rgb(var(--blue-rgb)/.32);
--shadow-cta-hover:0 18px 44px rgb(var(--blue-rgb)/.45);
--header-bg:rgb(11 15 31/.78);
--cbar-bg:rgb(13 19 38/.97);
--badge-bg:rgb(18 26 46/.94);
--scrim:rgb(4 8 20/.72);
--grid-line:rgb(255 255 255/.028);
--glow-a:rgb(var(--blue-rgb)/.20);
--glow-b:rgb(var(--accent-rgb)/.14);
--chrome-bg:#0c1424;
--chrome-bar:#16223a;
--chrome-edge:#26344f;
--chrome-line:#243149;
--chrome-url:#8fa3c4;
--bezel-a:#1b2740;
--bezel-b:#101728;
--bezel-c:#131c30;
--bezel-d:#0f1626;
--img-mat:#0c1424;
--radius:14px;
--wrap:1240px}
/* The light map lives under two triggers: the system preference when the
   visitor has not chosen, and an explicit choice. Written twice on purpose —
   it keeps the site correct with JavaScript switched off. */
@media (prefers-color-scheme:light){:root:not([data-theme=dark]){--ink-rgb:16 24 40;
--bg-rgb:246 248 252;
--accent-rgb:14 116 144;
--blue-rgb:29 78 216;
--coral-rgb:194 65 12;
--shadow-rgb:16 24 40;
--deep:#f6f8fc;
--deep-2:#eef2f9;
--panel:#ffffff;
--panel2:#eaeff7;
--ink:#101828;
--muted:#54637d;
--cyan:#0e7490;
--blue:#1d4ed8;
--coral:#c2410c;
--knob:#ffffff;
--line:rgb(var(--ink-rgb)/.14);
--soft:rgb(var(--ink-rgb)/.05);
--line-strong:rgb(var(--ink-rgb)/.5);
--accent-edge:rgb(var(--accent-rgb)/.26);
--accent-edge-hover:rgb(var(--accent-rgb)/.42);
--accent-edge-strong:rgb(var(--accent-rgb)/.55);
--accent-hint:rgb(var(--accent-rgb)/.05);
--accent-wash:rgb(var(--accent-rgb)/.07);
--accent-wash-2:rgb(var(--accent-rgb)/.10);
--accent-wash-3:rgb(var(--accent-rgb)/.14);
--blue-wash:rgb(var(--blue-rgb)/.05);
--coral-wash:rgb(var(--coral-rgb)/.07);
--shadow:0 18px 44px rgb(var(--shadow-rgb)/.10);
--shadow-md:0 10px 28px rgb(var(--shadow-rgb)/.07);
--shadow-lg:0 22px 48px rgb(var(--shadow-rgb)/.12);
--shadow-pop:0 16px 36px rgb(var(--shadow-rgb)/.10);
--shadow-menu:0 20px 44px rgb(var(--shadow-rgb)/.14);
--shadow-bar:0 -12px 34px rgb(var(--shadow-rgb)/.10);
--shadow-cta:0 10px 26px rgb(var(--blue-rgb)/.22);
--shadow-cta-hover:0 16px 36px rgb(var(--blue-rgb)/.30);
--header-bg:rgb(246 248 252/.86);
--cbar-bg:rgb(255 255 255/.97);
--badge-bg:rgb(255 255 255/.92);
--scrim:rgb(16 24 40/.45);
--grid-line:rgb(16 24 40/.05);
--glow-a:rgb(var(--blue-rgb)/.07);
--glow-b:rgb(var(--accent-rgb)/.06);
--chrome-bg:#ffffff;
--chrome-bar:#eaeff7;
--chrome-edge:#d3dbe8;
--chrome-line:#dbe2ee;
--chrome-url:#54637d;
--bezel-a:#dfe6f1;
--bezel-b:#c8d2e2;
--bezel-c:#d3dcea;
--bezel-d:#c2cddf;
--img-mat:#eaeff7}}
:root[data-theme=light]{--ink-rgb:16 24 40;
--bg-rgb:246 248 252;
--accent-rgb:14 116 144;
--blue-rgb:29 78 216;
--coral-rgb:194 65 12;
--shadow-rgb:16 24 40;
--deep:#f6f8fc;
--deep-2:#eef2f9;
--panel:#ffffff;
--panel2:#eaeff7;
--ink:#101828;
--muted:#54637d;
--cyan:#0e7490;
--blue:#1d4ed8;
--coral:#c2410c;
--knob:#ffffff;
--line:rgb(var(--ink-rgb)/.14);
--soft:rgb(var(--ink-rgb)/.05);
--line-strong:rgb(var(--ink-rgb)/.5);
--accent-edge:rgb(var(--accent-rgb)/.26);
--accent-edge-hover:rgb(var(--accent-rgb)/.42);
--accent-edge-strong:rgb(var(--accent-rgb)/.55);
--accent-hint:rgb(var(--accent-rgb)/.05);
--accent-wash:rgb(var(--accent-rgb)/.07);
--accent-wash-2:rgb(var(--accent-rgb)/.10);
--accent-wash-3:rgb(var(--accent-rgb)/.14);
--blue-wash:rgb(var(--blue-rgb)/.05);
--coral-wash:rgb(var(--coral-rgb)/.07);
--shadow:0 18px 44px rgb(var(--shadow-rgb)/.10);
--shadow-md:0 10px 28px rgb(var(--shadow-rgb)/.07);
--shadow-lg:0 22px 48px rgb(var(--shadow-rgb)/.12);
--shadow-pop:0 16px 36px rgb(var(--shadow-rgb)/.10);
--shadow-menu:0 20px 44px rgb(var(--shadow-rgb)/.14);
--shadow-bar:0 -12px 34px rgb(var(--shadow-rgb)/.10);
--shadow-cta:0 10px 26px rgb(var(--blue-rgb)/.22);
--shadow-cta-hover:0 16px 36px rgb(var(--blue-rgb)/.30);
--header-bg:rgb(246 248 252/.86);
--cbar-bg:rgb(255 255 255/.97);
--badge-bg:rgb(255 255 255/.92);
--scrim:rgb(16 24 40/.45);
--grid-line:rgb(16 24 40/.05);
--glow-a:rgb(var(--blue-rgb)/.07);
--glow-b:rgb(var(--accent-rgb)/.06);
--chrome-bg:#ffffff;
--chrome-bar:#eaeff7;
--chrome-edge:#d3dbe8;
--chrome-line:#dbe2ee;
--chrome-url:#54637d;
--bezel-a:#dfe6f1;
--bezel-b:#c8d2e2;
--bezel-c:#d3dcea;
--bezel-d:#c2cddf;
--img-mat:#eaeff7}
:root[data-theme=light]{color-scheme:light}
:root[data-theme=dark]{color-scheme:dark}
/* Repainting every surface at once produces a visible wave; two frames of
   suppressed transitions hide it. */
.theme-swap,.theme-swap *,
.theme-swap *::before,.theme-swap *::after{transition:none!important}
::selection{background:var(--accent-wash-3);color:var(--ink)}
*{box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{margin:0;min-height:100vh;font-family:Inter,system-ui,-apple-system,"Segoe UI",sans-serif;font-size:16px;line-height:1.65;color:var(--ink);background:var(--deep);overflow-x:hidden}
body::before{content:"";position:fixed;inset:0;z-index:-2;pointer-events:none;background:radial-gradient(900px 520px at 78% -8%,var(--glow-a),transparent 60%),radial-gradient(760px 480px at 4% 6%,var(--glow-b),transparent 62%),linear-gradient(180deg,var(--deep) 0%,var(--deep) 60%,var(--deep-2) 100%)}
body::after{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;background-image:linear-gradient(var(--grid-line) 1px,transparent 1px),linear-gradient(90deg,var(--grid-line) 1px,transparent 1px);background-size:64px 64px;mask-image:linear-gradient(to bottom,rgba(0,0,0,.75),transparent 70%)}
img,svg{max-width:100%;height:auto;display:block}
a{color:inherit;text-decoration:none}
h1,h2,h3{font-family:Grotesk,Inter,sans-serif;font-weight:700;line-height:1.12;letter-spacing:-.02em;margin:0}
p{margin:0}
ul,ol{margin:0;padding:0;list-style:none}
.wrap{width:min(100%,var(--wrap));margin-inline:auto;padding-inline:clamp(18px,4vw,34px)}
.sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.eyebrow{font-size:.78rem;font-weight:600;letter-spacing:.16em;text-transform:uppercase;color:var(--cyan);margin-bottom:14px}
.lead{color:var(--muted);font-size:1.05rem;max-width:62ch}
section{scroll-margin-top:88px}
/* Padding does not collapse, so two sections standing next to each other used
   to put their two paddings end to end: at a 1280px viewport that was 102px
   below one and 102px above the next, and the reader met a two-hundred pixel
   hole that looked like the end of the page. The second rule takes the top
   padding off any section that directly follows another, so the space between
   two sections is one gap and not two, while the first section on a page and
   any section following something else keep theirs. */
.sec{padding-block:clamp(52px,6vw,84px)}
.sec + .sec{padding-top:0}
.sec-head{max-width:760px;margin-bottom:clamp(30px,4vw,46px)}
.sec-head h2{font-size:clamp(1.7rem,3.4vw,2.5rem);margin-bottom:14px}
.btn{display:inline-flex;align-items:center;gap:10px;padding:14px 26px;border-radius:999px;font-weight:600;font-size:.95rem;border:1px solid transparent;cursor:pointer;transition:transform .18s ease,box-shadow .18s ease,background .18s ease,border-color .18s ease,scale .3s var(--spring);font-family:inherit}
.btn-primary{background:linear-gradient(120deg,var(--fill-a),var(--fill-b));color:var(--on-fill);box-shadow:var(--shadow-cta)}
.btn-primary:hover{transform:translateY(-2px);box-shadow:var(--shadow-cta-hover)}
.btn-ghost{border-color:var(--line);color:var(--ink);background:rgb(var(--ink-rgb)/.03)}
.btn-ghost:hover{border-color:var(--cyan);color:var(--cyan);transform:translateY(-2px)}
.site-header{position:sticky;top:calc(-1 * var(--tb,41px));z-index:60;backdrop-filter:blur(14px);background:var(--header-bg);border-bottom:1px solid var(--line)}
.topbar{height:40px;overflow:hidden;background:rgb(var(--ink-rgb)/.04);border-bottom:1px solid var(--line)}
.topbar-inner{display:flex;align-items:center;justify-content:flex-end;gap:26px;height:40px;font-size:.84rem}
.topbar .lbl{display:inline}
.topbar a{display:inline-flex;align-items:center;gap:8px;color:var(--muted);white-space:nowrap;transition:color .18s ease}
.topbar a:hover{color:var(--cyan)}
.topbar svg{width:15px;height:15px;flex:0 0 auto;fill:var(--cyan)}
.cbar{position:fixed;left:0;right:0;bottom:0;z-index:90;padding:clamp(14px,2.5vw,22px);background:var(--cbar-bg);border-top:1px solid var(--line);backdrop-filter:blur(12px);box-shadow:var(--shadow-bar)}
.cbar[hidden]{display:none}
.cbar-inner{width:min(100%,var(--wrap));margin-inline:auto;display:flex;flex-wrap:wrap;align-items:center;gap:16px}
.cbar p{color:var(--muted);font-size:.9rem;flex:1 1 320px;min-width:260px}
.cbar p b{color:var(--ink);display:block;font-family:Grotesk,sans-serif;font-size:1rem;margin-bottom:4px}
.cbar-actions{display:flex;flex-wrap:wrap;gap:10px}
.cbar .btn{padding:11px 20px;font-size:.9rem}
.cmodal{border:0;padding:0;background:transparent;max-width:min(560px,92vw);width:100%}
.cmodal::backdrop{background:var(--scrim);backdrop-filter:blur(4px)}
.cmodal-box{background:var(--panel);border:1px solid var(--line);border-radius:18px;padding:clamp(20px,3vw,30px);color:var(--ink);box-shadow:var(--shadow)}
.cmodal-box h2{font-size:1.35rem;margin-bottom:8px}
.cmodal-box>p{color:var(--muted);font-size:.92rem;margin-bottom:18px}
.copt{display:flex;gap:14px;align-items:flex-start;padding:14px 0;border-top:1px solid var(--line)}
.copt div{flex:1}
.copt b{display:block;font-size:.98rem;margin-bottom:3px}
.copt span{color:var(--muted);font-size:.85rem}
.copt .switch{flex:0 0 auto;margin-top:4px}
.copt input:disabled+i{opacity:.55;cursor:not-allowed}
.cmodal-actions{display:flex;flex-wrap:wrap;gap:10px;justify-content:flex-end;margin-top:20px}
.hdr{display:flex;align-items:center;gap:26px;min-height:70px}
.brand{display:flex;align-items:center;flex:0 0 auto}
.lg{display:flex;align-items:center;gap:12px;perspective:460px}
.lg-mark{width:46px;height:46px;flex:0 0 auto;overflow:visible}
.lg-mark rect{transform-origin:60px 60px}
.lg-mark .p1{stroke-dasharray:100;animation:lgDraw .9s cubic-bezier(.5,.05,.2,1) both,lgSpin 26s linear 1s infinite}
.lg-mark .p2{stroke-dasharray:100;animation:lgDraw .9s cubic-bezier(.5,.05,.2,1) .2s both,lgSpinR 19s linear 1.1s infinite}
.lg-mark .p3{animation:lgPop .5s cubic-bezier(.2,1.5,.4,1) .7s both}
.lg-word{font-family:Grotesk,Inter,sans-serif;font-weight:700;font-size:25px;line-height:1;letter-spacing:-.02em;color:var(--ink)}
.lg-word i{font-style:normal;display:inline-block;transform-origin:50% 100%;animation:lgLtr .55s cubic-bezier(.22,1.12,.36,1) calc(.35s + var(--i)*.06s) both}
.lg-word small{display:block;font-size:9.5px;letter-spacing:.26em;color:var(--cyan);font-weight:600;margin-top:4px;animation:lgSub .8s cubic-bezier(.25,1.02,.35,1) .8s both}
@keyframes lgDraw{from{stroke-dashoffset:100}to{stroke-dashoffset:0}}
@keyframes lgSpin{to{transform:rotate(360deg)}}
@keyframes lgSpinR{to{transform:rotate(-360deg)}}
@keyframes lgPop{0%{opacity:0;transform:scale(.4)}100%{opacity:1;transform:scale(1)}}
@keyframes lgLtr{0%{opacity:0;transform:translateY(16px) rotateX(-70deg)}100%{opacity:1;transform:none}}
@keyframes lgSub{0%{opacity:0;letter-spacing:.7em}100%{opacity:1;letter-spacing:.26em}}
.nav{display:flex;align-items:center;gap:26px;margin-left:auto}
.nav a{font-size:.93rem;color:var(--muted);transition:color .18s ease}
.nav a:hover{color:var(--ink)}
.hdr .btn{padding:11px 20px}
.burger{display:none;margin-left:auto;width:44px;height:44px;border-radius:12px;border:1px solid var(--line);background:rgb(var(--ink-rgb)/.04);cursor:pointer;position:relative}
.burger span,.burger::before,.burger::after{content:"";position:absolute;left:12px;right:12px;height:2px;background:var(--ink);border-radius:2px;transition:transform .22s ease,opacity .22s ease}
.burger span{top:21px}
.burger::before{top:14px}
.burger::after{top:28px}
.burger[aria-expanded=true] span{opacity:0}
.burger[aria-expanded=true]::before{transform:translateY(7px) rotate(45deg)}
.burger[aria-expanded=true]::after{transform:translateY(-7px) rotate(-45deg)}
.hero{padding-block:clamp(48px,7vw,92px) clamp(30px,4vw,50px)}
.hero-grid{display:grid;grid-template-columns:1.08fr .92fr;gap:clamp(28px,5vw,64px);align-items:center}
.hero h1{font-size:clamp(2.05rem,5.1vw,3.6rem);margin-bottom:20px}
.hero h1 em{font-style:normal;background:linear-gradient(100deg,var(--cyan),var(--blue));-webkit-background-clip:text;background-clip:text;color:transparent}
.hero .lead{font-size:1.09rem;margin-bottom:30px}
.hero-cta{display:flex;flex-wrap:wrap;gap:14px}
.hero-media{position:relative}
.hero-media img{width:100%;height:auto;border-radius:20px;border:1px solid var(--line);box-shadow:var(--shadow)}
.hero-badge{position:absolute;left:-14px;bottom:22px;display:flex;align-items:center;gap:12px;padding:12px 18px;border-radius:14px;background:var(--badge-bg);border:1px solid var(--line);box-shadow:var(--shadow);backdrop-filter:blur(8px)}
.hero-badge b{font-family:Grotesk,sans-serif;font-size:1.3rem;color:var(--cyan)}
.hero-badge small{color:var(--muted);font-size:.78rem;line-height:1.3;display:block}
.strip{border-block:1px solid var(--line);background:rgb(var(--ink-rgb)/.02);overflow:hidden;padding:16px 0}
.strip-inner{display:flex;gap:44px;width:max-content;animation:slide 38s linear infinite}
.strip span{color:var(--muted);font-size:.86rem;font-weight:500;letter-spacing:.08em;text-transform:uppercase;white-space:nowrap}
@keyframes slide{to{transform:translateX(-50%)}}
.cards3{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
@media(min-width:1001px){.cards3>:last-child:nth-child(3n+1):not(.svc-card){grid-column:1/-1}}
@media(max-width:1000px){.cards3{grid-template-columns:repeat(2,1fr)}.cards3>:last-child:nth-child(2n+1):not(.svc-card){grid-column:1/-1}}
@media(max-width:620px){.cards3{grid-template-columns:1fr}}
.card{padding:26px;border-radius:var(--radius);border:1px solid var(--line);background:linear-gradient(160deg,rgb(var(--ink-rgb)/.05),rgb(var(--ink-rgb)/.015));transition:transform .22s ease,border-color .22s ease,box-shadow .22s ease,scale .3s var(--spring)}
.card:hover{border-color:var(--accent-edge-hover)}
.card .num{font-family:Grotesk,sans-serif;font-size:.85rem;color:var(--cyan);letter-spacing:.14em}
.card h3{font-size:1.18rem;margin:10px 0 10px}
.card p{color:var(--muted);font-size:.95rem}
.showcase{display:grid;grid-template-columns:1fr 1fr;gap:clamp(28px,5vw,64px);align-items:center;padding:clamp(44px,6vw,72px) 0;border-top:1px solid var(--line)}
/* That padding and rule exist to separate showcases from each other when
   several are stacked. A lone showcase inside a section has nothing to be
   separated from, and its 72px was landing on top of the section's own 77px:
   a hundred and fifty pixels of nothing under the buttons before the next
   paragraph began. */
.sec > .showcase:first-child{padding-top:0;border-top:0}
.sec > .showcase:last-child{padding-bottom:0}
.showcase:nth-child(even) .showcase-text{order:2}
.showcase h2{font-size:clamp(1.5rem,3vw,2.15rem);margin-bottom:16px}
.showcase img{border-radius:var(--radius);border:1px solid var(--line);box-shadow:var(--shadow);width:100%}
.ticks{margin-top:22px;display:grid;gap:11px}
.ticks li{position:relative;padding-left:30px;color:var(--muted);font-size:.96rem}
.ticks li::before{content:"";position:absolute;left:0;top:8px;width:16px;height:9px;border-left:2px solid var(--cyan);border-bottom:2px solid var(--cyan);transform:rotate(-45deg)}
.plans{display:grid;grid-template-columns:repeat(4,1fr);gap:18px;align-items:start}
.plan{position:relative;padding:28px 24px;border-radius:var(--radius);border:1px solid var(--line);background:linear-gradient(170deg,rgb(var(--ink-rgb)/.05),rgb(var(--ink-rgb)/.012));display:flex;flex-direction:column;gap:14px;min-height:100%;transition:scale .3s var(--spring)}
.plan.pop{border-color:var(--accent-edge-strong);background:linear-gradient(170deg,var(--accent-wash),var(--blue-wash))}
.tagpop{position:absolute;top:-11px;left:24px;background:linear-gradient(120deg,var(--fill-a),var(--fill-b));color:var(--on-fill);font-size:.7rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;padding:5px 12px;border-radius:999px}
.plan h3{font-size:1.22rem}
.plan .kind{color:var(--muted);font-size:.85rem}
.plan .price{font-family:Grotesk,sans-serif;font-size:1.62rem;color:var(--cyan)}
.plan ul{display:grid;gap:9px;margin-top:2px}
.plan li{position:relative;padding-left:22px;color:var(--muted);font-size:.9rem}
.plan li::before{content:"";position:absolute;left:0;top:9px;width:8px;height:8px;border-radius:50%;background:var(--blue)}
.plan .btn{margin-top:auto;justify-content:center}
.calc{display:grid;grid-template-columns:1.25fr .75fr;gap:clamp(22px,4vw,44px);align-items:start;padding:clamp(24px,3.5vw,38px);border-radius:20px;border:1px solid var(--line);background:linear-gradient(160deg,rgb(var(--ink-rgb)/.05),rgb(var(--ink-rgb)/.015))}
.field{margin-bottom:22px}
.field>span{display:block;font-size:.88rem;color:var(--muted);margin-bottom:10px}
.seg{display:flex;flex-wrap:wrap;gap:10px}
.seg label{position:relative;cursor:pointer}
.seg input{position:absolute;left:0;top:0;opacity:0;width:1px;height:1px;pointer-events:none}
.seg em{display:block;font-style:normal;padding:11px 18px;border-radius:999px;border:1px solid var(--line);font-size:.9rem;color:var(--muted);transition:all .18s ease}
.seg input:checked+em{border-color:var(--cyan);color:var(--on-fill);background:linear-gradient(120deg,var(--fill-a),var(--fill-b));font-weight:600}
.seg input:focus-visible+em{outline:2px solid var(--cyan);outline-offset:2px}
.stepper{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--line);border-radius:999px;padding:5px}
.stepper button{width:36px;height:36px;border-radius:50%;border:0;background:rgb(var(--ink-rgb)/.06);color:var(--ink);font-size:1.2rem;cursor:pointer;line-height:1;transition:background .18s ease,scale .3s var(--spring)}
.stepper button:hover{background:var(--accent-edge)}
.stepper output{min-width:44px;text-align:center;font-family:Grotesk,sans-serif;font-size:1.05rem}
.switch{position:relative;display:flex;align-items:center;gap:12px;cursor:pointer;color:var(--muted);font-size:.94rem}
.switch input{position:absolute;left:0;top:0;width:1px;height:1px;opacity:0;pointer-events:none}
.switch i{width:46px;height:26px;border-radius:999px;background:rgb(var(--ink-rgb)/.1);border:1px solid var(--line);position:relative;transition:background .2s ease;flex:0 0 auto}
.switch i::after{content:"";position:absolute;top:3px;left:3px;width:18px;height:18px;border-radius:50%;background:var(--ink);transition:transform .2s ease}
.switch input:checked+i{background:linear-gradient(120deg,var(--fill-a),var(--fill-b))}
.switch input:checked+i::after{transform:translateX(20px)}
.total{position:sticky;top:96px;padding:26px;border-radius:16px;border:1px solid var(--accent-edge-hover);background:linear-gradient(165deg,var(--accent-wash-2),var(--blue-wash));text-align:center}
.total span{display:block;color:var(--muted);font-size:.85rem}
.total output{display:block;font-family:Grotesk,sans-serif;font-size:clamp(2.1rem,5vw,2.8rem);margin:8px 0 4px;color:var(--ink)}
.total small{color:var(--muted);font-size:.78rem;display:block;margin-bottom:18px}
.total .btn{width:100%;justify-content:center}
.cases{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,320px),560px));justify-content:center;gap:clamp(20px,3vw,40px)}
.case{display:flex;flex-direction:column;border-radius:var(--radius);border:1px solid var(--line);background:linear-gradient(145deg,rgb(var(--ink-rgb)/.10),rgb(var(--ink-rgb)/.03)),var(--panel);box-shadow:var(--shadow-md);overflow:hidden;transition:transform .25s ease,border-color .25s ease,box-shadow .25s ease,scale .3s var(--spring)}
.case:hover{transform:translateY(-6px);border-color:var(--accent-edge-hover);box-shadow:var(--shadow-lg)}
.stage{position:relative;padding:clamp(14px,2vw,22px) clamp(14px,2vw,22px) 0;background:radial-gradient(120% 90% at 50% 0%,var(--accent-wash-2),transparent 60%),var(--chrome-bg);border-bottom:1px solid var(--line)}
.mock{border-radius:10px 10px 0 0;overflow:hidden;border:1px solid var(--chrome-edge);background:var(--chrome-bg);box-shadow:var(--shadow-menu)}
.mockbar{display:flex;align-items:center;gap:7px;padding:9px 12px;background:var(--chrome-bar);border-bottom:1px solid var(--chrome-line)}
.dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto}
.mockurl{margin-left:10px;padding:3px 14px;border-radius:999px;background:var(--chrome-bg);color:var(--chrome-url);font-size:11px;letter-spacing:.3px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mock img{display:block;width:100%;height:clamp(160px,18vw,220px);object-fit:cover;object-position:top center}
.case-meta{display:flex;flex-direction:column;align-items:flex-start;padding:clamp(18px,2vw,26px);flex:1}
.tag{align-self:flex-start;margin-bottom:14px;padding:5px 12px;border:1px solid var(--accent-edge-hover);border-radius:999px;background:var(--accent-wash);color:var(--cyan);font-size:.76rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase}
.case-meta h3{margin:0 0 8px;font-size:clamp(1.5rem,1.2vw + 1.1rem,2.1rem);line-height:1.05}
.case-meta p{margin:0 0 16px;color:var(--muted);font-size:.98rem;flex:1}
.visit{display:inline-flex;align-items:center;gap:8px;color:var(--cyan);font-weight:700}
.visit>span{display:inline-block;transition:transform .2s ease}

.steps{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,190px),1fr));gap:18px;counter-reset:s}
.step{position:relative;padding:24px 20px;border-radius:var(--radius);border:1px solid var(--line);background:rgb(var(--ink-rgb)/.03);transition:scale .3s var(--spring)}
.step b{display:flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:50%;background:linear-gradient(120deg,var(--fill-a),var(--fill-b));color:var(--on-fill);font-family:Grotesk,sans-serif;margin-bottom:14px}
.step h3{font-size:1.04rem;margin-bottom:8px}
.step p{color:var(--muted);font-size:.9rem}
.about{display:grid;grid-template-columns:.95fr 1.05fr;gap:clamp(28px,5vw,60px);align-items:center}
.about img{border-radius:20px;border:1px solid var(--line);box-shadow:var(--shadow)}
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,150px),1fr));gap:16px;margin-top:26px}
.stat{padding:18px;border-radius:12px;border:1px solid var(--line);background:rgb(var(--ink-rgb)/.03);transition:scale .3s var(--spring)}
.stat b{display:block;font-family:Grotesk,sans-serif;font-size:1.5rem;color:var(--cyan)}
.stat span{color:var(--muted);font-size:.82rem}
.cta{text-align:center;padding:clamp(44px,6vw,74px) clamp(20px,4vw,50px);border-radius:22px;border:1px solid var(--accent-edge);background:linear-gradient(150deg,var(--accent-wash-2),var(--blue-wash))}
.cta h2{font-size:clamp(1.6rem,3.4vw,2.4rem);margin-bottom:14px}
.cta p{color:var(--muted);margin:0 auto 26px;max-width:56ch}
.faq{display:grid;gap:12px;max-width:900px}
details{border:1px solid var(--line);border-radius:12px;background:rgb(var(--ink-rgb)/.03);overflow:hidden}
summary{cursor:pointer;padding:18px 52px 18px 20px;font-weight:600;font-size:1rem;position:relative;list-style:none;transition:scale .3s var(--spring)}
summary::-webkit-details-marker{display:none}
summary::after{content:"";position:absolute;right:22px;top:24px;width:9px;height:9px;border-right:2px solid var(--cyan);border-bottom:2px solid var(--cyan);transform:rotate(45deg);transition:transform .2s ease}
details[open] summary::after{transform:rotate(-135deg)}
details p{padding:0 20px 20px;color:var(--muted);font-size:.95rem}
.proof{display:flex;gap:clamp(20px,4vw,44px);align-items:center;flex-wrap:wrap;
  border:1px solid var(--line);border-radius:var(--radius);padding:clamp(24px,3.5vw,40px);
  background:linear-gradient(140deg,var(--accent-wash-2),rgb(var(--ink-rgb)/.02))}
.proof-fig{flex:none;min-width:150px}
.proof-fig b{display:block;font-family:Grotesk,sans-serif;font-size:clamp(2.4rem,6vw,3.6rem);
  line-height:1;letter-spacing:-.03em;color:var(--cyan)}
.proof-fig span{display:block;margin-top:6px;font-size:.82rem;color:var(--muted);
  text-transform:uppercase;letter-spacing:.1em}
.proof-text{flex:1;min-width:min(100%,280px)}
.proof-text p{margin-bottom:10px}
.related h2{font-size:1.15rem;margin-bottom:14px}
.pill-row{display:flex;flex-wrap:wrap;gap:10px}
.pill-link{display:inline-block;padding:9px 17px;border-radius:999px;font-size:.9rem;
  border:1px solid var(--line);background:rgb(var(--ink-rgb)/.04);color:var(--ink);text-decoration:none}
.pill-link:hover{border-color:var(--cyan);color:var(--cyan)}
.footer{border-top:1px solid var(--line);background:rgb(var(--ink-rgb)/.02);padding:clamp(48px,6vw,76px) 0 28px;margin-top:clamp(48px,6vw,80px)}
.foot-grid{display:grid;grid-template-columns:1fr 1.1fr;gap:clamp(30px,5vw,64px)}
.foot-grid h2{font-size:clamp(1.5rem,3vw,2.1rem);margin-bottom:14px}
.contacts{display:grid;gap:12px;margin-top:24px}
.contacts a{display:flex;align-items:center;gap:12px;color:var(--muted);font-size:.96rem;transition:color .18s ease}
.contacts a:hover{color:var(--cyan)}
.contacts svg{width:19px;height:19px;flex:0 0 auto;fill:var(--cyan)}
form{display:grid;gap:14px}
.row2{display:grid;grid-template-columns:1fr 1fr;gap:14px}
label.f{display:grid;gap:7px;font-size:.86rem;color:var(--muted)}
input,textarea,select{font-family:inherit;font-size:.95rem;color:var(--ink);background:rgb(var(--ink-rgb)/.04);border:1px solid var(--line);border-radius:12px;padding:13px 15px;width:100%;transition:border-color .18s ease}
input:focus,textarea:focus,select:focus{outline:none;border-color:var(--cyan)}
textarea{min-height:120px;resize:vertical}
.foot-bottom{display:flex;flex-wrap:wrap;gap:16px;align-items:center;justify-content:space-between;margin-top:44px;padding-top:22px;border-top:1px solid var(--line);color:var(--muted);font-size:.85rem}
.foot-bottom nav{display:flex;flex-wrap:wrap;gap:20px}
.skip{position:absolute;left:0;top:0;z-index:100;padding:12px 20px;background:var(--cyan);color:var(--on-fill);border-radius:0 0 10px 0;font-weight:600;transform:translateY(-140%);transition:transform .18s ease}
.skip:focus{transform:none}
.crumbs{width:min(100%,var(--wrap));margin:0 auto;padding:18px clamp(18px,4vw,34px) 0;color:var(--muted);font-size:.85rem}
.crumbs a{color:var(--muted)}
.crumbs a:hover{color:var(--cyan)}
.crumbs span{color:var(--ink)}
.cards2{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}
.card .visit{margin-top:14px}
.more{margin-top:34px;text-align:center}
.note{color:var(--muted);font-size:.82rem;margin-top:4px}
.loc{display:flex;align-items:center;gap:12px;color:var(--muted);font-size:.96rem}
.loc svg{width:19px;height:19px;flex:0 0 auto;fill:var(--cyan)}
.case-hero{padding-bottom:0}
.case-hero h1{font-size:clamp(2rem,4.6vw,3.1rem);margin:6px 0 16px}
.case-links{display:flex;flex-wrap:wrap;gap:14px;margin-top:26px}
.live{padding:clamp(30px,5vw,58px) clamp(18px,4vw,34px) 0;display:grid;grid-template-columns:1fr 260px;gap:clamp(20px,3vw,40px);align-items:end}
.device{display:flex;flex-direction:column;align-items:center}
.device-screen{width:100%;padding:14px;border-radius:18px;background:linear-gradient(160deg,var(--bezel-a),var(--bezel-b));border:1px solid var(--line);box-shadow:var(--shadow)}
.browser{border-radius:10px;overflow:hidden;border:1px solid var(--chrome-edge);background:var(--chrome-bg)}
.browser-bar{display:flex;align-items:center;gap:7px;padding:9px 12px;background:var(--chrome-bar);border-bottom:1px solid var(--chrome-line)}
.browser-view{position:relative;width:100%;aspect-ratio:1440/900;background:var(--chrome-bg) center top/cover no-repeat;overflow:hidden}
.browser-frame{position:absolute;top:0;left:0;width:1440px;height:900px;border:0;background:#fff;transform-origin:0 0;transform:scale(var(--bs,.42))}
.phone-frame{position:absolute;top:0;left:0;width:390px;height:844px;border:0;background:#fff;transform-origin:0 0;transform:scale(var(--ps,.6))}
.browser-shot,.phone-shot{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:top center}
.device-neck{width:96px;height:22px;background:linear-gradient(180deg,var(--bezel-a),var(--bezel-c));border:1px solid var(--line);border-top:0}
.device-foot{width:210px;height:12px;border-radius:0 0 12px 12px;background:linear-gradient(180deg,var(--bezel-a),var(--bezel-d));border:1px solid var(--line);border-top:0}
.device-phone{width:100%;max-width:260px;padding:12px;border-radius:30px;background:linear-gradient(160deg,var(--bezel-a),var(--bezel-b));border:1px solid var(--line);box-shadow:var(--shadow)}
.phone-view{position:relative;width:100%;aspect-ratio:390/844;border-radius:20px;overflow:hidden;background:var(--chrome-bg) center top/cover no-repeat}
.live-note{grid-column:1/-1;margin-top:18px;text-align:center;color:var(--muted);font-size:.86rem}
.case-body{display:grid;grid-template-columns:1.6fr .9fr;gap:clamp(26px,4vw,54px);align-items:start}
.case-body h2{font-size:clamp(1.5rem,3vw,2.1rem);margin-bottom:14px}
.case-body h3{font-size:1.1rem;margin:28px 0 12px}
.case-side{display:grid;gap:14px;position:sticky;top:96px}
.case-side .stat b{font-size:.8rem;letter-spacing:.12em;text-transform:uppercase;color:var(--cyan)}
.case-side .stat span{display:block;margin-top:6px;color:var(--ink);font-size:.95rem}
.prose{max-width:68ch}
.prose h1{font-size:clamp(1.9rem,4vw,2.7rem);margin-bottom:22px}
.prose h2{font-size:1.25rem;margin:30px 0 10px}
.prose p{color:var(--muted);margin-bottom:12px}
.prose b{color:var(--ink)}
.prose a{color:var(--cyan);text-decoration:underline}
.prose code{background:rgb(var(--ink-rgb)/.07);border:1px solid var(--line);border-radius:5px;padding:1px 6px;font-size:.86em}
/* The entrance moves, it no longer fades. A fade means the text spends part
   of its life at partial opacity, and partial opacity is partial contrast:
   axe measured this very paragraph at 82% and scored it 3.85:1 against a
   threshold of 4.5, even though the colour itself passes at 5.71:1. Text that
   is mid-fade is genuinely harder to read, so the rule was right and the
   animation was wrong. */
@keyframes reveal{from{transform:translateY(16px)}to{transform:none}}
@media (prefers-reduced-motion:no-preference){
/* The reveal finishes as the block clears the bottom edge, not a quarter of
   the way up the screen. At the old range a paragraph sat faded and pushed
   down while it was already being read, which looked like a hole in the page
   rather than an entrance. */
@supports (animation-timeline:view()){.rv{animation:reveal linear both;animation-timeline:view();animation-range:entry 0% entry 92%}}
}
@media (prefers-reduced-motion:reduce){.strip-inner{animation:none}.lg-mark rect,.lg-word i,.lg-word small{animation:none!important;opacity:1}.lg-mark rect{stroke-dashoffset:0}*{transition-duration:.01ms!important}}
@media (max-width:1080px){
.plans{grid-template-columns:repeat(2,1fr)}
.cases{grid-template-columns:repeat(2,1fr)}
}
@media (max-width:1080px){
.live{grid-template-columns:1fr}
.device-phone{margin:0 auto}
.case-body{grid-template-columns:1fr}
.case-side{position:static}
}
@media (max-width:900px){
.cards2{grid-template-columns:1fr}
.burger{display:block}
.nav{position:absolute;top:100%;left:0;right:0;flex-direction:column;align-items:stretch;gap:0;background:var(--deep);box-shadow:var(--shadow-menu);border-bottom:1px solid var(--line);padding:8px 20px 20px;transform:translateY(-140%);transition:transform .26s ease;margin:0;z-index:-1;max-height:calc(100vh - 100%);overflow-y:auto}
.nav[data-open=true]{transform:none}
.nav a{padding:14px 0;border-bottom:1px solid var(--line);font-size:1rem}
.hdr>.btn{display:none}
.hero-grid,.showcase,.calc,.about,.foot-grid{grid-template-columns:1fr}
.showcase:nth-child(even) .showcase-text{order:0}
.hero-media{order:-1}
.total{position:static}
.hero-badge{left:12px}
}
@media (max-width:760px){
.topbar{height:auto}
.topbar-inner{justify-content:center;gap:18px;font-size:.8rem}
.topbar .lbl{display:none}
.topbar a{gap:6px}
.cbar-actions .btn{flex:1 1 auto;justify-content:center}
}
@media (max-width:640px){
.plans,.steps,.cases,.row2,.stats{grid-template-columns:1fr}
.lg-mark{width:40px;height:40px}
.lg-word{font-size:21px}
.lg-word small{font-size:8.5px;letter-spacing:.22em}
}

/* Icons. Stroked, 24-grid, currentColor, so a card that changes colour on
   hover takes its icon with it. */
.ic{width:24px;height:24px;flex:0 0 auto;color:var(--cyan)}
.card-ic{position:relative;display:flex;align-items:center;gap:14px;margin-bottom:12px}
.card-ic h3{margin:0}
.card-ic .ic{width:42px;height:42px;padding:9px;border-radius:13px;border:1px solid var(--accent-edge);background:radial-gradient(120% 120% at 30% 0%,var(--accent-wash-3),var(--blue-wash));box-shadow:inset 0 1px 0 var(--soft),0 6px 18px rgb(var(--accent-rgb)/.15);transition:transform .25s cubic-bezier(.2,.7,.3,1),border-color .25s ease,box-shadow .25s ease}
.card:hover .card-ic .ic{transform:translateY(-3px) scale(1.05);border-color:var(--accent-edge-strong);box-shadow:inset 0 1px 0 var(--soft),0 10px 26px rgb(var(--accent-rgb)/.3)}
a.card{display:block;text-decoration:none;color:inherit}

/* Screenshots of the work itself, three across. */
/* auto-fit rather than a fixed three, because a service with two pictures
   was leaving a third of the row empty. Empty tracks collapse, so two tiles
   take half each and three take a third, with no rule per count. */
.shots{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,260px),1fr));gap:clamp(14px,2vw,22px)}
.shots figure{margin:0;overflow:hidden;border-radius:var(--radius);border:1px solid var(--line);background:var(--panel);box-shadow:var(--shadow-md);transition:transform .25s ease,border-color .25s ease}
.shots figure:hover{transform:translateY(-4px);border-color:var(--accent-edge-hover)}
.shots img{width:100%;aspect-ratio:16/10;object-fit:cover;object-position:top center;display:block}
.shots figcaption{padding:12px 16px;border-top:1px solid var(--line);color:var(--muted);font-size:.88rem}
@media(max-width:900px){.shots{grid-template-columns:1fr 1fr}}
@media(max-width:620px){.shots{grid-template-columns:1fr}}

/* Before and after: the only way to picture a service with nothing to
   photograph. Uses --coral, which the palette had and nothing used. */
.cmp{display:grid;grid-template-columns:1fr 1fr;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}
.cmp>div{padding:clamp(18px,2.4vw,28px)}
.cmp .was{background:linear-gradient(160deg,var(--coral-wash),rgb(var(--ink-rgb)/.012));border-right:1px solid var(--line)}
.cmp .now{background:linear-gradient(160deg,var(--accent-wash-2),var(--blue-wash))}
.cmp h3{font-size:.78rem;letter-spacing:.16em;text-transform:uppercase;margin-bottom:16px}
.cmp .was h3{color:var(--coral)}
.cmp .now h3{color:var(--cyan)}
.cmp li{position:relative;padding-left:28px;margin-bottom:11px;color:var(--muted);font-size:.95rem;list-style:none}
.cmp .was li::before{content:"";position:absolute;left:3px;top:10px;width:13px;height:2px;background:var(--coral);border-radius:2px}
.cmp .now li::before{content:"";position:absolute;left:3px;top:8px;width:14px;height:8px;border-left:2px solid var(--cyan);border-bottom:2px solid var(--cyan);transform:rotate(-45deg)}
@media(max-width:700px){.cmp{grid-template-columns:1fr}.cmp .was{border-right:0;border-bottom:1px solid var(--line)}}

/* Body copy can now be a list, a set of steps or a table, not only paragraphs. */
.prose .bul{margin:6px 0 16px;padding:0;list-style:none}
.prose .bul li{position:relative;padding-left:26px;margin-bottom:9px;color:var(--muted)}
.prose .bul li::before{content:"";position:absolute;left:2px;top:8px;width:13px;height:7px;border-left:2px solid var(--cyan);border-bottom:2px solid var(--cyan);transform:rotate(-45deg)}
.prose .num-list{margin:6px 0 16px;padding-left:0;list-style:none;counter-reset:n}
.prose .num-list li{position:relative;padding-left:38px;margin-bottom:11px;color:var(--muted);counter-increment:n}
.prose .num-list li::before{content:counter(n);position:absolute;left:0;top:-1px;width:25px;height:25px;border-radius:8px;display:grid;place-items:center;font-size:.8rem;color:var(--cyan);border:1px solid var(--accent-edge);background:var(--accent-wash)}
.prose h3{font-size:1rem;margin:22px 0 8px;color:var(--ink)}
.tw{overflow-x:auto;margin:10px 0 18px;border:1px solid var(--line);border-radius:12px}
.tw table{width:100%;border-collapse:collapse;font-size:.94rem}
.tw th{text-align:left;padding:12px 16px;color:var(--ink);font-weight:600;border-bottom:1px solid var(--line);background:rgb(var(--ink-rgb)/.03);white-space:nowrap}
.tw td{padding:12px 16px;color:var(--muted);border-bottom:1px solid var(--line)}
.tw tr:last-child td{border-bottom:0}

/* The estimator result now itemises itself. */
.calc-lines{list-style:none;margin:0 0 14px;padding:0;text-align:left}
.calc-lines li{display:flex;justify-content:space-between;gap:12px;font-size:.85rem;color:var(--muted);padding:5px 0;border-bottom:1px solid rgb(var(--ink-rgb)/.07)}
.calc-lines li b{color:var(--ink);font-weight:600;white-space:nowrap}
.calc-block .total output{font-size:clamp(1.5rem,3.4vw,2.1rem);line-height:1.15}

/* Service illustration in a browser frame, and above the text on a phone —
   until now the first picture on mobile sat below a full screen of words. */
.showcase-media{position:relative}
.showcase .frame{border-radius:12px;overflow:hidden;border:1px solid var(--chrome-edge);background:var(--chrome-bg);box-shadow:var(--shadow)}
.showcase .frame img{border:0;border-radius:0;box-shadow:none;width:100%;display:block}
.dot-r{background:#ff5f57}.dot-y{background:#febc2e}.dot-g{background:#28c840}
@media(max-width:900px){.showcase-media{order:-1}}

/* Services submenu. Eleven pages behind one link is two clicks from anywhere,
   so the menu opens them directly. Hover plus focus-within, so a keyboard
   reaches it too, and no script — the content security policy allows none. */
.has-sub{position:relative;display:inline-flex;align-items:center}
.caret{width:0;height:0;margin-left:6px;border-left:4px solid transparent;border-right:4px solid transparent;border-top:5px solid currentColor;opacity:.6;transition:transform .2s ease}
.has-sub:hover .caret,.has-sub:focus-within .caret{transform:rotate(180deg)}
.sub{position:absolute;top:100%;left:-14px;display:grid;grid-template-columns:1fr 1fr;gap:2px;min-width:430px;padding:10px;border-radius:14px;border:1px solid var(--line);background:var(--deep);box-shadow:var(--shadow-menu);opacity:0;visibility:hidden;transform:translateY(6px);transition:opacity .18s ease,transform .18s ease,visibility .18s;z-index:60}
.has-sub:hover .sub,.has-sub:focus-within .sub{opacity:1;visibility:visible;transform:none}
.sub a{padding:9px 12px;border-radius:9px;font-size:.9rem;white-space:nowrap}
.sub a:hover{background:var(--accent-wash);color:var(--cyan)}

/* Above 900px the flat list gives way to a panel: three groups, and every
   service carries its own picture, its one line and its entry price, so the
   choice is made in the menu instead of on a landing page after it.
   The panel hangs off the header rather than off the Services link, which is
   what lets it run the full width; .has-sub is therefore not a containing
   block, and the two rules below are a pair.
   display rather than visibility is deliberate. A picture inside a
   display:none subtree is never fetched, so ten thumbnails cost nothing on
   any page until the menu is opened.
   The trigger is stretched to the full height of the header row so that the
   panel begins exactly where the trigger ends. Left at the height of its own
   text it sat twenty-two pixels above the panel, and a pointer travelling
   down through that band was over neither of them: the menu closed under the
   hand every time. The remaining case, a pointer moving diagonally towards a
   far column, leaves a trigger only seventy pixels wide no matter how tall it
   is, and is covered by the close delay in main.js. */
.sub{display:none}
.has-sub{position:static;align-self:stretch}
.nav{align-self:stretch}
.mega{position:absolute;top:100%;left:0;right:0;z-index:5;display:none;width:min(var(--wrap),calc(100vw - 32px));margin-inline:auto;grid-template-columns:repeat(3,1fr);gap:6px 26px;padding:22px 20px 16px;border:1px solid var(--line);border-radius:20px;background:linear-gradient(168deg,rgb(var(--ink-rgb)/.05),transparent 46%),var(--deep);box-shadow:var(--shadow-menu)}
.has-sub:hover .mega,.has-sub:focus-within .mega,.has-sub.open .mega{display:grid}
@supports (transition-behavior:allow-discrete){
.mega{opacity:0;translate:0 -8px;transition:opacity .18s ease,translate .18s ease,display .18s allow-discrete}
.has-sub:hover .mega,.has-sub:focus-within .mega,.has-sub.open .mega{opacity:1;translate:0 0}
@starting-style{.has-sub:hover .mega,.has-sub:focus-within .mega,.has-sub.open .mega{opacity:0;translate:0 -8px}}
}
/* The group heading carries the same numeral as the services index, so the
   three groups are recognisably the same three in both places. */
.mhead{display:grid;grid-template-columns:auto 1fr;align-items:baseline;column-gap:9px;row-gap:2px;margin:0 0 6px;padding:0 10px 9px;border-bottom:1px solid var(--line);font-family:Grotesk,sans-serif;font-size:.95rem;color:var(--ink)}
.mhead .mnum{font-style:normal;font-size:.95rem;color:rgb(var(--accent-rgb)/.55)}
.mhead span{grid-column:2;font-family:inherit;font-size:.76rem;font-weight:500;color:var(--muted)}
/* The name and the price share the first line and the description gets the
   whole width beneath them. Kept on one line beside the price, every one of
   the ten descriptions was cut off by its ellipsis, which is the one piece of
   the row that has to be readable. */
/* The thumbnail, the gap and the price chip are sized against the widest
   service name rather than by eye: at 76px and a 13px gap, "Maintenance and
   hosting" and "Migration and rescue" both broke onto a second line, and a
   wrapped name is worse than a wrapped description because the name is what
   the eye is scanning for. Measured across all ten rows, this set leaves none
   of the names wrapped and takes the panel from 501px to 477px. */
.nav .mrow{position:relative;display:grid;grid-template-columns:68px 1fr auto;column-gap:11px;row-gap:3px;padding:10px;border-radius:13px;font-size:.9rem;transition:background .18s ease}
.mrow::before{content:"";position:absolute;left:0;top:10px;bottom:10px;width:2px;border-radius:2px;background:linear-gradient(180deg,var(--fill-a),var(--fill-b));opacity:0;transform:scaleY(.3);transition:opacity .2s ease,transform .25s cubic-bezier(.2,.7,.3,1)}
.mrow img{grid-area:1/1/3/2;align-self:start;width:68px;height:39px;object-fit:cover;border-radius:9px;border:1px solid var(--line);box-shadow:0 3px 10px rgb(var(--shadow-rgb)/.16);transition:transform .25s ease,border-color .2s ease}
.mrow b{grid-area:1/2/2/3;font-size:.9rem;font-weight:600;color:var(--ink)}
.mrow small{grid-area:2/2/3/4;font-size:.78rem;line-height:1.4;color:var(--muted)}
.mfrom{grid-area:1/3/2/4;font-family:Grotesk,sans-serif;font-size:.72rem;color:var(--cyan);white-space:nowrap;padding:2px 8px;border-radius:999px;border:1px solid var(--accent-edge);background:var(--accent-wash)}
.mrow:hover{background:var(--accent-hint)}
.mrow:hover::before{opacity:1;transform:none}
.mrow:hover img{transform:scale(1.05);border-color:var(--accent-edge-hover)}
.mrow:hover .mfrom{border-color:var(--accent-edge-strong)}
.mfoot{grid-column:1/-1;display:flex;flex-wrap:wrap;align-items:center;gap:14px;margin-top:12px;padding:14px 10px 2px;border-top:1px solid var(--line)}
.mfoot small{flex:1;min-width:180px;font-size:.8rem;color:var(--muted)}
.nav .mfoot a{padding:9px 16px;border-radius:999px;border:1px solid var(--line);font-size:.84rem;color:var(--ink);transition:border-color .2s ease,background .2s ease,color .2s ease}
.nav .mfoot a:hover{border-color:var(--accent-edge-hover);background:var(--accent-hint);color:var(--cyan)}
.nav .mfoot .mprice{border-color:transparent;background:linear-gradient(120deg,var(--fill-a),var(--fill-b));color:var(--on-fill)}
.nav .mfoot .mprice:hover{color:var(--on-fill);filter:brightness(1.06)}
/* Between the drawer breakpoint and here, three columns are narrow enough
   that the service names themselves wrap. Two columns and a taller panel
   read better than three columns of two-line headings. The three groups are
   placed by hand rather than left to flow: the four-row group takes a column
   of its own and the two short ones stack beside it, which left to itself
   would instead pile Build and Run into one column and leave the other half
   of the panel empty. */
@media(max-width:1120px){
.mega{grid-template-columns:repeat(2,1fr)}
.mcol:first-child{grid-area:1/1/3/2}
.mcol:nth-child(2){grid-area:1/2/2/3}
.mcol:nth-child(3){grid-area:2/2/3/3}
}
@media(max-width:900px){
/* A panel of pictures does not fit a phone, and the drawer is already the
   vertical list it would have to become. */
.mega,.has-sub:hover .mega,.has-sub:focus-within .mega,.has-sub.open .mega{display:none}
.sub{display:block}
.has-sub{display:block;position:relative}
.has-sub>a{display:block}
.caret{display:none}
.sub{position:static;display:block;min-width:0;opacity:1;visibility:visible;transform:none;background:none;border:0;box-shadow:none;padding:0 0 8px 14px}
.sub a{padding:10px 0;border-bottom:1px solid var(--line);font-size:.92rem;display:block;color:var(--muted)}
}

/* Service cards: a picture, and the whole card is the click target.
   The link stretches over the card with a pseudo-element instead of wrapping
   it. Wrapping would make the accessible name the entire card and would stop
   the text being selectable; this keeps the heading as the link name and the
   copy selectable, while the pointer target covers the full card. */
.svc-card{padding:0;overflow:hidden;display:flex;flex-direction:column;position:relative;isolation:isolate}
.card-media{position:relative;overflow:hidden;background:var(--panel)}
.card-media img{display:block;width:100%;aspect-ratio:16/9;object-fit:cover;transition:transform .5s cubic-bezier(.2,.6,.2,1)}

.card-media::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,rgba(11,18,33,0) 45%,rgba(11,18,33,.85) 100%)}

.svc-card .card-body{padding:20px 22px 22px;display:flex;flex-direction:column;gap:10px;flex:1}
.svc-card .card-ic{margin-bottom:0}
.svc-card h3{margin:0;font-size:1.1rem}
.svc-card p{flex:1;margin:0}
.svc-card .visit{margin-top:2px}

.stretch{color:inherit;text-decoration:none}
.stretch::after{content:"";position:absolute;inset:0;z-index:1}
.svc-card p,.svc-card .visit,
.case-meta p,.case-meta .visit,.case-meta .tag{position:relative;z-index:2}
.svc-card .card-body a:not(.stretch),.svc-card .card-body button{position:relative;z-index:3}
/* Focus lands on the link but the ring has to read as the card. */
.svc-card:has(.stretch:focus-visible),.case:has(.stretch:focus-visible),
.svc-row:has(.stretch:focus-visible),.svc-item:has(.stretch:focus-visible){border-color:var(--cyan);outline:2px solid var(--cyan);outline-offset:3px}
@supports selector(:has(*)){.stretch:focus-visible{outline:none}}

/* Focus that is actually visible, everywhere, without putting a ring on a
   mouse click. */
a:focus-visible,button:focus-visible,summary:focus-visible,input:focus-visible,
select:focus-visible,textarea:focus-visible{outline:2px solid var(--cyan);outline-offset:3px}

/* Comfortable pointer targets on a phone, where the menu and footer links
   were 14px of text with nothing around them. */
@media(max-width:900px){
.nav a,.sub a,.foot-bottom nav a,.contacts a{min-height:44px;display:flex;align-items:center}
.stepper button{width:44px;height:44px}
}

/* Anything that moves, stops moving for anyone who asked it to. */
@media(prefers-reduced-motion:reduce){
.card-media img,.svc-card .card-media img,.sub,.caret{transition:none!important}
.svc-card:hover .card-media img{transform:none}
}

/* Hover only where there is a pointer, and only on things that lead somewhere.
   Without the query these states stick on a touch screen after a tap. */
@media (any-hover:hover){
a.card:hover,.svc-card:hover,.case:hover{transform:translateY(-4px);border-color:var(--accent-edge-hover);box-shadow:var(--shadow-pop)}
.svc-card:hover .card-media img{transform:scale(1.05)}
.svc-card:hover .visit>span,.case:hover .visit>span{transform:translateX(4px)}
.svc-card:active,.case:active{transform:translateY(-2px) scale(.995)}
summary:hover{background:var(--accent-hint)}
}
.case{position:relative;isolation:isolate}

/* The switch hides its checkbox in a 1x1 box, so the global focus ring was
   drawn around a single pixel. Put it on the part you can see. */
.switch input:focus-visible+i{outline:2px solid var(--cyan);outline-offset:3px}
input:focus,textarea:focus,select:focus{border-color:var(--cyan)}

/* Closed means closed: the panel used to slide out of view but keep its links
   in the tab order, so fifteen invisible links sat after the burger. */
@media (max-width:900px){
.nav{visibility:hidden;transition:transform .26s ease,visibility 0s linear .26s}
.nav[data-open=true]{visibility:visible;transition:transform .26s ease}
.hdr{gap:12px}
.hdr>.btn{display:inline-flex;margin-left:auto;padding:10px 16px;font-size:.86rem}
}

/* On a phone the top bar drops its labels, which left the mail and WhatsApp
   links as 15px icons — the two most-tapped targets on the site. */
@media(max-width:760px){
.topbar-inner{justify-content:center;gap:6px}
.topbar a{min-width:44px;min-height:44px;justify-content:center;gap:0;border-radius:10px}
.topbar a[href^="tel:"]{min-width:auto;padding-inline:12px;gap:8px}
.topbar svg{width:20px;height:20px}
/* The cookie bar was taking a third of the first screen. */
.cbar{padding:12px 14px calc(12px + env(safe-area-inset-bottom))}
.cbar p{font-size:.82rem;line-height:1.45}
.cbar-inner{gap:10px}
.cbar-actions{width:100%;gap:8px}
.cbar-actions .btn{flex:1 1 0;min-width:0;padding:12px 6px;font-size:.8rem;min-height:44px}
}

/* An answer at a time, and a line under the open one. */
details[open]>summary{border-bottom:1px solid var(--line)}
details[open] p{padding-top:16px}

/* Say when a link leaves the site. */
.case-links a[target=_blank]::after,.case-side a[target=_blank]::after,
.prose a[target=_blank]::after{content:"↗";margin-left:.35em;font-size:.9em;opacity:.75}

/* A scrolling box nobody can reach with a keyboard is not scrollable. */
.tw:focus-visible{outline:2px solid var(--cyan);outline-offset:2px}

@media(prefers-reduced-motion:reduce){
html{scroll-behavior:auto}
}

/* The card that closes a ragged row. No picture, so it reads as an action
   rather than an eleventh service. */
.card-cta{border-color:var(--accent-edge);background:linear-gradient(160deg,var(--accent-wash),var(--blue-wash))}
.card-cta .card-body{justify-content:center}

/* The logo mark is drawn with presentation attributes in build.py. On a light
   page its outer square would all but vanish, so CSS takes it over. */
.lg-mark .p1{stroke:var(--ink)}
.lg-mark .p2{stroke:var(--cyan)}
.lg-mark .p3{fill:var(--blue)}
/* Bright gradient chips keep their own dark ink in both themes. */
.skip{background:var(--fill-a);color:var(--on-fill)}
/* A field has to be visibly a field on white, and the border needs 3:1. */
input,textarea,select{background:var(--panel);border-color:var(--line-strong)}
input::placeholder,textarea::placeholder{color:var(--muted);opacity:1}
input:-webkit-autofill,textarea:-webkit-autofill,select:-webkit-autofill{-webkit-text-fill-color:var(--ink);-webkit-box-shadow:0 0 0 100px var(--panel) inset;caret-color:var(--ink)}
.switch i{background:rgb(var(--ink-rgb)/.1);border-color:var(--line-strong)}
.switch i::after{background:var(--knob);box-shadow:0 1px 3px rgb(var(--shadow-rgb)/.25)}
/* Photographs are dark in both themes; on a light page they need an edge
   and a mat, or they read as holes in the layout. */
.card-media,.shots figure{background:var(--img-mat)}
/* Every picture is wrapped in <picture> so an AVIF can be offered ahead of
   the WebP. The wrapper has no box of its own, which is what keeps the img
   the grid or flex item it already was: without this, .mrow, .svc-item and
   .about would all place the wrapper instead and their grid-area rules would
   land on nothing. */
picture{display:contents}

/* Theme switch. Two positions, sun and moon, with a knob that slides between
   them. Which side the knob starts on is a question about the visitor, so it
   is answered the same way every other colour on the site is: the media query
   states the system case and the data-theme rules override it. That way the
   knob is already correct in the first painted frame and no script has to
   move it afterwards.
   --step is the distance between the two seats, so the phone-sized variant
   only has to change that one length.
   The state rules set translate outright rather than feeding a 0-or-1 factor
   into a calc. Measured in the browser: with the factor changed to 1 the knob
   still computed translate:0px, while an identical calc on a probe element in
   the same switch gave 46px. A value that changes only because a variable
   underneath it changed does not always start the transition, and the
   property then keeps its old used value. Swapping the whole declaration is
   an ordinary cascade change and animates every time. */
.theme-sw{--step:33px;position:relative;display:inline-flex;align-items:center;gap:2px;margin:0;padding:3px;border:1px solid var(--line);border-radius:999px;background:var(--soft);cursor:pointer;font:inherit;color:inherit;transition:border-color .22s ease,scale .3s var(--spring)}
.theme-sw .ti{position:relative;z-index:2;width:31px;height:31px;padding:7px;border-radius:999px;color:var(--muted);transition:color .22s ease}
.theme-sw .knob{position:absolute;top:3px;left:3px;width:31px;height:31px;border-radius:999px;background:var(--accent-wash-2);translate:0;transition:translate .34s var(--spring)}
.theme-sw:focus-visible{outline:2px solid var(--cyan);outline-offset:2px}
.theme-sw .sun{color:var(--cyan)}
@media (prefers-color-scheme:dark){
:root:not([data-theme=light]) .theme-sw .knob{translate:var(--step)}
:root:not([data-theme=light]) .theme-sw .sun{color:var(--muted)}
:root:not([data-theme=light]) .theme-sw .moon{color:var(--cyan)}
}
:root[data-theme=dark] .theme-sw .knob{translate:var(--step)}
:root[data-theme=dark] .theme-sw .sun{color:var(--muted)}
:root[data-theme=dark] .theme-sw .moon{color:var(--cyan)}
:root[data-theme=light] .theme-sw .knob{translate:0}
:root[data-theme=light] .theme-sw .sun{color:var(--cyan)}
:root[data-theme=light] .theme-sw .moon{color:var(--muted)}
/* Hover has to stay out of the icon colours: which of the two is lit is said
   by three different state rules, and a hover rule of equal weight coming
   after them would light both and erase the answer. */
@media (any-hover:hover){.theme-sw:hover{border-color:var(--accent-edge-hover)}.theme-sw:hover .knob{background:var(--accent-wash-3)}}
@media (max-width:900px){
/* The drawer stretches its children, and a stretched two-position switch is
   a three-hundred-pixel pill with both icons huddled at one end. It sizes to
   its own content instead. */
.theme-sw{--step:46px;align-self:flex-start;margin:14px 0 4px;padding:4px}
.theme-sw .ti{width:44px;height:44px;padding:13px;border-radius:14px}
.theme-sw .knob{top:4px;left:4px;width:44px;height:44px;border-radius:14px}
}

/* A headline that breaks with one word on the last line reads as a mistake;
   balance evens the lines out. The browser drops it past a handful of lines,
   so it costs nothing on long text. */
h1,h2,h3,.hero-badge b,.stat b,.proof-fig b,.cmodal-box h2{text-wrap:balance}
.lead,.card p,.case-meta p,.step p,.prose p,.plan li,.ticks li,details p,.cmp li{text-wrap:pretty}

/* Figures are the argument on this site; lining numerals stop them shifting
   width as they change. */
.stat b,.proof-fig b,.hero-badge b{font-variant-numeric:tabular-nums}

/* Skip layout and paint for what is far below the fold. Deliberately not on
   .sec: Safari before 26 hides skipped content from find-in-page, and people
   do search these pages for prices. Nobody searches the footer or the strip. */
@supports (content-visibility:auto){
.footer,.shots,.live{content-visibility:auto;contain-intrinsic-size:auto 420px}
.strip{content-visibility:auto;contain-intrinsic-size:auto 74px}
}

/* A print stylesheet has no scroll timeline, so a reveal that starts at
   opacity 0 would print blank. */
@media print{.rv{animation:none!important;opacity:1;transform:none}}

/* The brief is the only conversion on the site. The box grows with the answer,
   and a field only turns red once someone has actually typed in it. */
#brief textarea{field-sizing:content;min-height:120px;max-height:45svh}
label.f:has(:user-invalid){color:var(--coral)}
.f :user-invalid{border-color:var(--coral)}

/* Cross-document transitions: 23 separate documents, and the white flash
   between them is the cheapest thing on the site to remove. The header is
   pulled into its own group so it does not cross-fade with itself. */
@media (prefers-reduced-motion:no-preference){
@view-transition{navigation:auto}
::view-transition-old(root),::view-transition-new(root){animation-duration:.22s}
.site-header{view-transition-name:hdr}
::view-transition-group(hdr){animation-duration:0s}
}

/* Home: three services keep a photograph, the rest become an index.
   The strongest hierarchy signal available is whether a thing has a picture. */
.svc-hero{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
.card-media .from{position:absolute;left:14px;bottom:12px;z-index:2;font-family:Grotesk,sans-serif;font-size:.86rem;color:#fff;background:rgb(4 8 20/.5);border:1px solid rgb(255 255 255/.28);border-radius:999px;padding:4px 12px;backdrop-filter:blur(10px) saturate(1.4);box-shadow:inset 0 1px 0 rgb(255 255 255/.22),0 6px 16px rgb(0 0 0/.32);transition:background .3s ease,color .3s ease,border-color .3s ease}
.svc-rest{display:grid;grid-template-columns:repeat(2,1fr);gap:14px;margin-top:20px;list-style:none;padding:0}
.svc-row{position:relative;isolation:isolate;display:flex;align-items:center;gap:14px;min-height:64px;padding:13px 18px;border:1px solid var(--line);border-radius:12px;background:rgb(var(--ink-rgb)/.03);transition:border-color .2s ease,background .2s ease,transform .2s ease,scale .3s var(--spring)}
.svc-row .ic{width:22px;height:22px}
.svc-row .rt{flex:1;min-width:0}
.svc-row h3{margin:0;font-size:1rem}
.svc-row small{display:block;color:var(--muted);font-size:.83rem;line-height:1.4}
.svc-row.is-cta{border-color:var(--accent-edge);background:linear-gradient(120deg,var(--accent-wash),var(--blue-wash))}

/* The index: three groups, ten rows, one deciding fact on every row. */
.svc-group{display:grid;grid-template-columns:280px 1fr;gap:clamp(24px,4vw,48px);padding-block:clamp(28px,4vw,44px);border-top:1px solid var(--line)}
.svc-group:first-of-type{border-top:0;padding-top:0}
.svc-ghead{position:sticky;top:96px;align-self:start}
.svc-ghead .gnum{display:block;font-family:Grotesk,sans-serif;font-size:2.6rem;line-height:1;color:rgb(var(--accent-rgb)/.3);margin-bottom:8px}
.svc-ghead h2{font-size:1.55rem}
.svc-ghead h2 span{display:block;margin-top:6px;font-size:.86rem;font-weight:600;color:var(--muted)}
.svc-ghead p{margin-top:12px;color:var(--muted);font-size:.92rem}
.svc-list{list-style:none;margin:0;padding:0}
.svc-item{position:relative;isolation:isolate;display:grid;grid-template-columns:44px 1fr 132px 104px 22px;column-gap:16px;row-gap:4px;align-items:center;padding:16px 14px;border-top:1px solid transparent;border-image:linear-gradient(90deg,transparent,var(--line) 7%,var(--line) 93%,transparent) 1;transition:background .2s ease,scale .3s var(--spring)}
.svc-item:first-child{border-top:0}
.svc-item .ic{grid-area:1/1/3/2;width:44px;height:44px;padding:11px;border-radius:12px;border:1px solid var(--line);background:rgb(var(--ink-rgb)/.04);color:var(--muted);transition:color .22s ease,border-color .22s ease,background .22s ease,transform .22s ease}
.svc-item h3{grid-area:1/2/2/3;margin:0;font-size:1.1rem;transition:color .2s ease}
.svc-item h3 .tag{margin:0 0 0 10px;padding:2px 9px;font-size:.64rem;vertical-align:3px}
.svc-item p{grid-area:2/2/3/3;margin:0;color:var(--muted);font-size:.9rem}
.svc-item .from{grid-area:1/3/3/4;text-align:right}
.svc-item .thumb{grid-area:1/4/3/5;width:104px;height:59px;object-fit:cover;border-radius:8px;border:1px solid var(--line);opacity:.7;transition:opacity .2s ease,transform .2s ease}
.svc-item .ar{grid-area:1/5/3/6}
.svc-row .from,.svc-item .from{font-family:Grotesk,sans-serif;font-size:.9rem;color:var(--cyan);white-space:nowrap}
.svc-row .ar,.svc-item .ar{color:var(--cyan);opacity:.45;transition:transform .2s ease,opacity .2s ease}
.svc-row small,.svc-row .from,.svc-item p,.svc-item .from{position:relative;z-index:2}
/* Keyboard gets the same feedback as a pointer. */
.svc-row:focus-within,.svc-item:focus-within{background:var(--accent-hint)}
.svc-item:focus-within .thumb{opacity:1}
@media (any-hover:hover){
.svc-row:hover{border-color:var(--accent-edge-hover);background:var(--accent-hint);transform:translateY(-2px)}
.svc-item:hover{background:var(--accent-hint)}
.svc-item:hover h3{color:var(--cyan)}
.svc-item:hover .thumb{opacity:1;transform:scale(1.05)}
.svc-row:hover .ar,.svc-item:hover .ar{opacity:1;transform:translateX(4px)}
}
@media (max-width:1150px){.svc-group{grid-template-columns:240px 1fr}}
@media (max-width:1000px){
.svc-hero{grid-template-columns:repeat(2,1fr)}
.svc-hero>:nth-child(3){grid-column:1/-1;flex-direction:row}
.svc-hero>:nth-child(3) .card-media{flex:0 0 42%}
.svc-hero>:nth-child(3) .card-media img{height:100%;aspect-ratio:auto}
.svc-item{grid-template-columns:44px 1fr 132px 22px}
.svc-item .thumb{display:none}
.svc-item .ar{grid-area:1/4/3/5}
}
@media (max-width:900px){
.svc-group{grid-template-columns:1fr;gap:16px;padding-block:30px}
.svc-ghead{position:static}
.svc-ghead .gnum{font-size:1.9rem}
}
@media (max-width:620px){
.svc-hero{grid-template-columns:1fr}
.svc-hero>:nth-child(3){flex-direction:column}
.svc-hero>:nth-child(3) .card-media{flex:none}
.svc-hero>:nth-child(3) .card-media img{height:auto;aspect-ratio:16/9}
.svc-rest{grid-template-columns:1fr;gap:10px}
.svc-row{display:grid;grid-template-columns:22px 1fr auto;column-gap:12px;row-gap:3px;padding:13px 14px}
.svc-row .ic{grid-area:1/1/3/2}
.svc-row .rt{display:contents}
.svc-row h3{grid-area:1/2/2/3}
.svc-row .from{grid-area:1/3/2/4}
.svc-row small{grid-area:2/2/3/4}
.svc-item{grid-template-columns:34px 1fr auto;column-gap:12px;padding:14px 2px;min-height:56px}
.svc-item .ic{width:20px;height:20px;padding:0;border:0;border-radius:0;background:none}
.svc-item .from{grid-area:1/3/2/4;text-align:left}
.svc-item p{grid-area:2/2/3/4}
.svc-row .ar,.svc-item .ar{display:none}
}

/* ── Cards ────────────────────────────────────────────────────────────────
   Built from the theme tokens, so both themes get all of it for free. The
   one deliberate exception is the price chip: it sits on a photograph rather
   than on the page, so its colours belong to the picture. */

/* "What is included" is four rectangles of equal weight, which reads as a
   wall rather than as a list. A number gives them order, a layered ground
   gives them depth, and the glow under the icon gives the eye a start. */
.cards2{counter-reset:inc}
.inc{position:relative;counter-increment:inc;overflow:hidden;
background:radial-gradient(115% 85% at 0 0,var(--accent-wash),transparent 55%),linear-gradient(160deg,rgb(var(--ink-rgb)/.055),rgb(var(--ink-rgb)/.012));
box-shadow:inset 0 1px 0 var(--soft),var(--shadow-md)}
/* The number comes from a counter: no data, no markup, no maintenance. */
.inc::before{content:counter(inc,decimal-leading-zero);position:absolute;top:14px;right:20px;font-family:Grotesk,sans-serif;font-size:2.5rem;line-height:1;color:rgb(var(--accent-rgb)/.17);transition:color .3s}
.inc .card-ic{padding-right:52px}
.inc .card-ic::before{content:"";position:absolute;left:-16px;top:-18px;width:124px;height:124px;background:radial-gradient(circle,var(--accent-wash-3),transparent 68%);opacity:0;transition:opacity .3s;pointer-events:none}

/* A gradient cannot be a border-color, so the ring is a masked layer that
   fades in. Without mask support the layer would flood the card, which is
   what the feature query is guarding against. */
@supports(mask-composite:exclude){
.inc::after,.svc-card::after{content:"";position:absolute;inset:0;border-radius:inherit;padding:1px;pointer-events:none;z-index:4;opacity:0;transition:opacity .3s;
background:linear-gradient(135deg,var(--accent-edge-strong),transparent 38%,transparent 64%,rgb(var(--blue-rgb)/.45));
mask:conic-gradient(#000) content-box exclude,conic-gradient(#000)}
}

/* A shine crossing the photograph. The pictures sit on --img-mat and are
   dark in both themes, so a white sheen is right in both. */
.svc-card{box-shadow:var(--shadow-md)}
.svc-card .card-media::before{content:"";position:absolute;inset:-40% -70%;z-index:1;pointer-events:none;
background:linear-gradient(72deg,transparent 44%,#ffffff26 50%,transparent 56%);
transform:translateX(-75%);transition:transform .8s cubic-bezier(.2,.6,.2,1)}

/* A rail on the rows, growing from the middle out. Cheaper than flooding the
   background, and it points at the thing that is about to open. */
.svc-row::before,.svc-item::before{content:"";position:absolute;left:0;top:9px;bottom:9px;width:2px;border-radius:2px;
background:linear-gradient(180deg,var(--fill-a),var(--fill-b));
transform:scaleY(0);opacity:0;transition:transform .3s cubic-bezier(.2,.7,.3,1),opacity .3s}

/* The outline draws itself as the card arrives, on the same view() timeline
   the .rv reveal uses. It was hung on hover first, which is exactly the
   mistake this whole pass is meant to correct: on a phone the icons then
   never appear at all. The hidden start state lives inside the keyframes and
   nowhere else, so a browser without scroll-driven animations — Firefox
   still keeps them behind a flag — simply shows the finished icon.
   1.001 rather than 1 because a round cap leaves a dot behind at exactly
   one. The second shape starts a little later, so an icon assembles rather
   than appearing at once. */
@media (prefers-reduced-motion:no-preference){
@supports (animation-timeline:view()){
.inc .ic>*{stroke-dasharray:1;animation:jbd linear both;animation-timeline:view();animation-range:entry 12% cover 24%}
.inc .ic>:nth-child(2){animation-range:entry 18% cover 30%}
@keyframes jbd{from{stroke-dashoffset:1.001}to{stroke-dashoffset:0}}
}}
/* The shine has no resting state worth keeping. */
@media (prefers-reduced-motion:reduce){.svc-card .card-media::before{display:none}}

@media (any-hover:hover){
.inc:hover::after,.svc-card:hover::after{opacity:1}
.inc:hover .card-ic::before{opacity:1}
.inc:hover::before{color:rgb(var(--accent-rgb)/.34)}
.inc:hover{transform:translateY(-3px);border-color:var(--accent-edge);box-shadow:inset 0 1px 0 var(--soft),var(--shadow-lg)}
.svc-card:hover .card-media::before{transform:translateX(75%)}
.svc-card:hover .from{background:linear-gradient(120deg,var(--fill-a),var(--fill-b));color:var(--on-fill);border-color:transparent}
/* Lights up in 160ms, fades out over 300. Attention should arrive faster
   than it leaves. */
.inc:hover::after,.inc:hover::before,.inc:hover .card-ic::before{transition-duration:.16s}
.svc-row:hover::before,.svc-item:hover::before,
.svc-row:focus-within::before,.svc-item:focus-within::before{transform:none;opacity:1}
.svc-item:hover .ic{color:var(--cyan);border-color:var(--accent-edge);background:var(--accent-wash);transform:translateY(-1px)}
}

/* ── Touch ────────────────────────────────────────────────────────────────
   The site had nothing at all under a finger: no press state, no ripple, and
   iOS's own tap highlight was the only feedback anywhere. Everything below is
   about the phone first and happens to also read well with a mouse. */

/* One spring for the whole site. The fallback is a plain ease-out; where
   linear() is available the same token becomes a real damped oscillator
   (zeta .55) that overshoots about twelve per cent, so a press lands with a
   click rather than a bounce. */
:root{--spring:cubic-bezier(.2,.8,.2,1)}
@supports (transition-timing-function:linear(0,1)){
:root{--spring:linear(0,.026 1.9%,.122 4.4%,.7 13.8%,.891 17.5%,1.039 21.9%,1.111 26.2%,1.126 29.4%,1.12 32.5%,1.02 46.2%,.987 55%,.985 61.9%,1.001 83.1%,1)}
}

/* iOS draws a grey box on tap. Replacing it is fine; removing it and putting
   nothing back would leave the interface feeling dead, which is why these two
   declarations travel together with the :active rules below. */
.btn,.stepper button,.seg em,.theme-sw,.card,.case,.plan,.stat,.step,summary,.svc-row,.svc-item{-webkit-tap-highlight-color:transparent;touch-action:manipulation}
/* Controls take a firm press, surfaces a slight one: a button should feel
   like it moved, a card only like it acknowledged. */
.btn:active,.stepper button:active,.seg label:active em,.theme-sw:active{scale:.94}
.card:active,.case:active,.plan:active,.stat:active,.step:active,summary:active,.svc-row:active,.svc-item:active{scale:.985}
/* Under reduced motion the site's global rule cuts every duration to nothing,
   so the press has to be a change of tone rather than of size — it cannot
   simply be dropped, because the native highlight is gone. */
@media (prefers-reduced-motion:reduce){
.btn:active,.stepper button:active,.seg label:active em,.theme-sw:active,
.card:active,.case:active,.plan:active,.stat:active,.step:active,summary:active,.svc-row:active,.svc-item:active{scale:1;filter:brightness(.92)}
}

/* The ring left behind by a tap. One element for the whole page, positioned
   in viewport coordinates, so the handler never measures anything. Square
   with a soft radius rather than round: a port or a target in a network tool,
   not a Material ripple. */
/* Three rings leaving the point that was touched, one behind the other, the
   way a ring spreads on water. They are round because the gesture is round: a
   finger lands and the answer travels out from under it.
   A ring rather than a filled flash, and a thin one: the moment a shape fills
   in and goes bright it stops being a wave and becomes a strike. There was a
   flash here for one release and it was the wrong idea.
   The elements are pooled and moved with translate, so a tap costs three
   composited animations and no layout at all. */
.wave{position:fixed;left:0;top:0;z-index:70;width:26px;height:26px;margin:-13px 0 0 -13px;border-radius:50%;border:1.5px solid rgb(var(--accent-rgb)/.75);box-shadow:0 0 10px rgb(var(--accent-rgb)/.3);opacity:0;pointer-events:none;will-change:transform,opacity}
/* The pointer carries a soft light with it, which the grid over the top picks
   up as it passes. A fixed square with a ready-made gradient, moved by
   transform: feeding the coordinates into a full-screen gradient instead
   would repaint the whole viewport on every frame rather than compositing a
   layer that already exists. It sits with the ambient glow so the grid drawn
   above stays crisp. */
.glow{position:fixed;left:0;top:0;width:380px;height:380px;margin:-190px 0 0 -190px;z-index:-2;pointer-events:none;border-radius:50%;background:radial-gradient(circle,rgb(var(--accent-rgb)/.18),rgb(var(--blue-rgb)/.08) 40%,transparent 70%);opacity:0;transition:opacity .5s ease;will-change:transform}
.glow.on{opacity:1}

/* The stack marquee is the only thing on the page that moves on its own, and
   until now it was the only thing that could not be touched. Hold it to stop
   it; a chip under the finger lights up. */
.strip{touch-action:pan-y}
.strip:active .strip-inner{animation-play-state:paused}
.strip span:active{color:var(--cyan)}
@media (any-hover:hover){.strip:hover .strip-inner{animation-play-state:paused}}

/* How far through the page you are, drawn on the bottom edge of the header.
   Driven by the scroll position itself, so there is no scroll listener. */
@media (prefers-reduced-motion:no-preference){
@supports (animation-timeline:scroll()){
.site-header::after{content:"";position:absolute;left:0;right:0;bottom:-1px;height:2px;background:linear-gradient(90deg,var(--fill-a),var(--fill-b));transform-origin:0 50%;scale:0 1;animation:jbp linear both;animation-timeline:scroll(root block)}
@keyframes jbp{to{scale:1 1}}
}}


/* News tiles and article pages. The tile borrows the case frame, so the only
   thing that needs saying is the picture: edge to edge instead of inset and
   rounded, because there is no browser mock here for it to sit inside. */
.post img{border:0;border-bottom:1px solid var(--line);border-radius:0;box-shadow:none;aspect-ratio:16/9;object-fit:cover}
.post-body img{margin:8px 0 28px}
.post-body .lead{color:var(--muted)}
