/*
Theme Name:  OveliThub Next
Theme URI:   https://www.ovelit.com/
Author:      OveliThub
Description: The OveliThub site theme. Content comes from the ovelithub-cms plugin through ovh_field(); SEO meta comes from SiteSEO. The design system lives in assets/system.css.
Version:     1.0.0
Requires PHP: 8.1
Text Domain: ovelithub-next
Tags:        custom-colors, custom-menu, translation-ready
*/

/* The system is loaded as a separate file so it stays legible and cacheable.
   This file exists for the theme header WordPress requires. */

/* Skip link: off-screen until focused. Not part of the shared system. */
.skip{position:absolute;left:-9999px;top:0;z-index:200;padding:12px 18px;
  background:var(--surface);color:var(--ink);border-radius:0 0 10px 0;font-weight:700}
.skip:focus{left:0}
.ovh-hp{position:absolute;left:-9999px}

/* Team member page header: photo beside the name from tablet up. Scoped to
   .person-lead, not .person -- .person is also the team CARD, and a grid rule
   there split every card into two columns. */
.person-lead{display:grid;gap:var(--s6);align-items:center}
@media (min-width:760px){.person-lead{grid-template-columns:minmax(0,240px) minmax(0,1fr)}}
.person-photo{margin:0}
.person-photo img{width:100%;height:auto;border-radius:var(--r-md);display:block}

/* Card titles are links, but should read as headings. */
.card h3{margin:0}
.card h3 a{color:inherit;text-decoration:none}
.card h3 a:hover{color:var(--blue)}

/* Review card head: where the review was left on the left, how it scored on
   the right. .quote-top and .stars already come from the system; only the
   source lockup is new. */
.quote-src{display:inline-flex;align-items:center;gap:8px;min-width:0;
  font-size:12.5px;font-weight:800;letter-spacing:.01em;color:var(--ink-2)}
.quote-src svg{flex:none;display:block;width:18px;height:18px}
.quote-src>span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

/* Attribution: the client's face, then their name over their role. */
.quote figcaption{display:flex;align-items:center;gap:12px;margin-top:auto;
  padding-top:var(--s4);border-top:1px solid var(--line)}
.quote-face{flex:none;width:44px;height:44px;border-radius:50%;overflow:hidden;
  display:grid;place-items:center;background:var(--grad);color:#fff}
.quote-face img{width:100%;height:100%;object-fit:cover;display:block;border-radius:50%}
.quote-face--text{font-weight:900;font-size:15px;letter-spacing:.02em}
.quote-who{display:flex;flex-direction:column;gap:1px;min-width:0}
.quote-who b{font-weight:800;font-size:15px;line-height:1.25}
.quote-who>span{color:var(--muted);font-size:13.5px;line-height:1.35}

/* Problem/solution lists: icon in the gutter, text aligned to it. */
.marks{display:grid;gap:var(--s3);list-style:none;margin:var(--s6) 0 0;padding:0}
.marks li{display:flex;gap:14px;align-items:flex-start}
.marks li svg{flex:none;margin-top:2px}
.split .btn-row{margin-top:var(--s7)}
.split .h2{margin:var(--s5) 0 0}

/* Brand lockup as an image. Height-capped so the nav bar keeps its rhythm. */
.brand--img{display:inline-flex;align-items:center;padding:0}
/* The lockup stacks a wordmark over a tagline, so it needs more height than a
   single-line mark to stay legible: 44px in a 73px nav bar, a little larger
   again in the footer where there is no bar to sit inside. */
.brand--img img{height:50px;width:auto;display:block}
.foot .brand--img img{height:60px}
@media (max-width:900px){.brand--img img{height:45px}.foot .brand--img img{height:52px}}
@media (max-width:600px){.brand--img img{height:40px}.foot .brand--img img{height:48px}}

/* Spacing corrections ------------------------------------------------
   The trust band sat too far from the hero above it and from the band
   below; the logo rail needed less air under the label. */
.trust{padding:var(--s6) 0}
.trust .logo-rail{margin-top:var(--s4)}
@media (max-width:900px){.trust{padding:var(--s5) 0}}

/* Need Help? stacks a label over a value, which made the column run
   taller than its neighbours. Tighter rows bring the baselines back
   into line with the plain link columns. */
.foot-help li{margin-bottom:var(--s4)}
.foot-help li:last-child{margin-bottom:0}
.foot-help .cap{display:block;font-size:11px;letter-spacing:.11em;
  text-transform:uppercase;margin-bottom:2px;line-height:1.3}
.foot-help li br{display:none}
.foot-help li>span:not(.cap),.foot-help li>a{font-size:14.5px;line-height:1.45}

/* Footer subscribe box.
   One pill rather than two controls: the field and the button share a single
   track, so the box reads as one object instead of a bordered input sitting
   next to a button. The track keeps the footer's own translucent surface --
   a white pill lit up brighter than anything else on a dark footer. Nothing
   wraps: the field shrinks (min-width:0) and the button holds its width,
   which is what keeps the pill intact in a narrow column. */
.foot-sub{margin:var(--s5) 0}
.foot-sub-label{font-size:14px;color:var(--muted);margin:0 0 var(--s3)}
.foot-sub-form{display:flex;align-items:center;flex-wrap:nowrap;gap:0;max-width:360px;
  background:rgba(255,255,255,.05);border:1px solid var(--line-strong);
  border-radius:var(--r-pill);padding:5px 5px 5px 6px;
  transition:border-color var(--t-fast) var(--ease-out),box-shadow var(--t-fast) var(--ease-out)}
.foot-sub-form:hover{border-color:var(--blue-lift)}
.foot-sub-form:focus-within{border-color:var(--blue-lift);box-shadow:0 0 0 3px rgba(2,105,208,.28)}
.foot-sub-form input{flex:1 1 auto;min-width:0;padding:10px 10px 10px 14px;border:0;
  background:transparent;color:#fff;font:inherit;font-size:14.5px;border-radius:var(--r-pill)}
.foot-sub-form input::placeholder{color:#7f93ad}
.foot-sub-form input:focus{outline:none}
.foot-sub-form .btn{flex:0 0 auto;border-radius:var(--r-pill);white-space:nowrap}
@media (max-width:400px){.foot-sub-form .btn{padding-inline:var(--s4)}}
.foot-sub-ok,.foot-sub-err{display:flex;align-items:center;gap:8px;font-size:14px;margin:0}
.foot-sub-ok{color:var(--ok)}
.foot-sub-err{color:var(--pink-text)}
.screen-reader-text{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* Team portrait with a real photo. The system's .portrait is a gradient disc
   sized for initials; a 300x250 photo dropped into it overflowed the card, so
   the photo is cropped to the disc instead. Faces sit above centre in a
   portrait crop, hence the raised focal point. */
.portrait--photo{background:none;box-shadow:0 14px 30px rgba(2,105,208,.22)}
.portrait--photo img{width:100%;height:100%;display:block;border-radius:50%;
  object-fit:cover;object-position:center 22%}

/* Blog card thumbnail: edge to edge inside the media box on every screen.
   Without this the image sat at its natural size on the gradient plate. */
.post-media--img{background:var(--navy-2,#0b1b33)}
.post-media--img::before{display:none}
.post-media img{position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;display:block;border-radius:inherit}
.post-media--img span{position:absolute;left:50%;bottom:12px;transform:translateX(-50%)}

/* Hero panel charts. The shapes carry no claim of their own, so they are
   allowed to draw themselves in once and then hold still. */
.ovh-chart .ovh-line{stroke-dasharray:520;stroke-dashoffset:520;
  animation:ovhDraw 1.7s cubic-bezier(.22,.8,.3,1) .2s forwards}
.ovh-chart .ovh-area,.ovh-chart .ovh-prev,.ovh-chart .ovh-guide,
.ovh-chart .ovh-ticks,.ovh-donut .ovh-donut-ticks{opacity:0;animation:ovhIn .8s ease forwards}
.ovh-chart .ovh-ticks{animation-delay:.35s}
.ovh-chart .ovh-prev{animation-delay:.55s}
.ovh-chart .ovh-area{animation-delay:.9s}
.ovh-chart .ovh-guide{animation-delay:1.5s}
.ovh-donut .ovh-donut-ticks{animation-delay:.2s}
.ovh-chart .ovh-dots circle{opacity:0;transform-box:fill-box;transform-origin:center;
  animation:ovhPop .45s cubic-bezier(.2,.9,.3,1.4) forwards}
.ovh-chart .ovh-dots circle:nth-child(1){animation-delay:.75s}
.ovh-chart .ovh-dots circle:nth-child(2){animation-delay:1s}
.ovh-chart .ovh-dots circle:nth-child(3){animation-delay:1.25s}
.ovh-chart .ovh-pulse{transform-box:fill-box;transform-origin:center;
  animation:ovhPulse 2.8s ease-in-out 1.6s infinite}
.ovh-donut .ovh-ring{transform-box:fill-box;transform-origin:center;
  animation:ovhRing 1.1s cubic-bezier(.22,.8,.3,1) .3s both}
@keyframes ovhDraw{to{stroke-dashoffset:0}}
@keyframes ovhIn{to{opacity:1}}
@keyframes ovhPop{from{opacity:0;transform:scale(.3)}to{opacity:1;transform:scale(1)}}
@keyframes ovhPulse{0%,100%{transform:scale(1);opacity:.22}50%{transform:scale(1.55);opacity:.06}}
@keyframes ovhRing{from{opacity:0;transform:rotate(-28deg) scale(.86)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){
  .ovh-chart .ovh-area,.ovh-chart .ovh-prev,.ovh-chart .ovh-guide,.ovh-chart .ovh-ticks,
  .ovh-chart .ovh-dots circle,.ovh-donut .ovh-ring,.ovh-donut .ovh-donut-ticks{
    animation:none;opacity:1;transform:none}
  .ovh-chart .ovh-line{animation:none;stroke-dashoffset:0}
  .ovh-chart .ovh-pulse{animation:none;opacity:.22}
}

/* Bios differ in length; the profile link should still line up across the row. */
.card.person{display:flex;flex-direction:column}
.card.person>.link{margin-top:auto;align-self:center;padding-top:var(--s5)}

/* Line legend for the trend chart: same rhythm as the donut legend. */
.legend--line .ln{width:16px;height:3px;border-radius:2px;display:inline-block}
.legend--line .ln--now{background:linear-gradient(90deg,#4DA3F0,#A374F0 60%,#FF3D7F)}
.legend--line .ln--prev{background:repeating-linear-gradient(90deg,rgba(147,166,191,.75) 0 3px,transparent 3px 6px)}

/* Closing CTA -------------------------------------------------------------
   'What happens next' is a real sequence, so it is numbered and joined by a
   rule rather than repeating the same tick three times. The heading was
   capped at 17ch, which left a dead gap between it and the note card on wide
   screens; a longer measure lets it fill its own column. */
@media (min-width:1100px){.cta h2{max-width:24ch}}
.cta-steps{list-style:none;margin:0;padding:0;display:grid;gap:var(--s4)}
.cta-steps li{position:relative;display:flex;gap:14px;align-items:flex-start;
  font-size:15px;line-height:1.5;color:#D6E4F5}
.cta-steps li:not(:last-child)::before{content:"";position:absolute;left:13px;top:30px;
  bottom:calc(-1 * var(--s4));width:1px;
  background:linear-gradient(180deg,rgba(255,255,255,.24),rgba(255,255,255,.04))}
.cta-step{flex:none;width:27px;height:27px;border-radius:50%;display:grid;place-items:center;
  font-size:12.5px;font-weight:800;color:#fff;background:rgba(255,255,255,.09);
  border:1px solid rgba(255,255,255,.2);font-variant-numeric:tabular-nums}
.cta-steps li:first-child .cta-step{background:var(--grad);border-color:transparent;
  box-shadow:0 6px 16px rgba(248,10,98,.28)}

/* Theme switch: the three-way control competes with the logo and the menu
   button for a narrow bar, so it steps down twice before the sheet opens.
   Kept above a 30px tap target rather than shrunk to an icon. */
@media (max-width:900px){
  .theme-switch{padding:2px}
  .theme-switch button{padding:6px 10px;font-size:11.5px}
}
@media (max-width:600px){
  .theme-switch{gap:1px}
  .theme-switch button{padding:6px 9px;font-size:11px}
}

/* Back to top. Sits clear of the footer corner, appears only once there is
   something to scroll back from, and is unreachable by keyboard while off. */
.to-top{position:fixed;right:clamp(16px,2.4vw,32px);bottom:clamp(16px,2.4vw,32px);z-index:60;
  width:46px;height:46px;border-radius:50%;display:grid;place-items:center;cursor:pointer;padding:0;
  color:var(--ink-2);background:var(--surface);border:1px solid var(--line-strong);box-shadow:var(--e2);
  opacity:0;visibility:hidden;transform:translateY(12px);
  transition:opacity var(--t-base) var(--ease-out),transform var(--t-base) var(--ease-out),
             visibility var(--t-base) var(--ease-out),background var(--t-fast) var(--ease-out),
             color var(--t-fast) var(--ease-out),border-color var(--t-fast) var(--ease-out)}
.to-top.is-on{opacity:1;visibility:visible;transform:none}
.to-top:hover{background:var(--grad);color:#fff;border-color:transparent;transform:translateY(-3px)}
.to-top:focus-visible{outline:2px solid var(--blue);outline-offset:3px}
@media (max-width:600px){.to-top{width:42px;height:42px}}
@media (prefers-reduced-motion:reduce){.to-top{transition:opacity .01s,visibility .01s}
  .to-top,.to-top:hover{transform:none}}

/* A result coming back from the form handler, shown at the top of the body
   the answer belongs to. */
.form-alert{margin:0;padding:12px 15px;border-radius:12px;font-size:14.5px;line-height:1.45;
  color:var(--pink-text);background:rgba(248,10,98,.08);border:1px solid rgba(248,10,98,.28)}
.consent span{flex:1}

/* Hero headline ----------------------------------------------------------
   The display size hit its 4.5rem cap at about 1290px and stayed there, while
   the column it sits in keeps getting narrower: a 1280px laptop wrapped the
   headline onto six lines. Scaling with the viewport instead holds it to four
   lines from laptop to ultrawide, and still lands on exactly 72px at 1920,
   which is the width the approved design was drawn at. */
@media (min-width:941px){
  .hero .display{font-size:clamp(2.6rem,3.75vw,4.5rem);text-wrap:balance}
}

/* Light theme ground -------------------------------------------------------
   The stock light palette sits pure-white cards on an almost-white ground,
   which reads as glare across a large screen. Every light surface steps down
   a little and keeps the same blue bias, with slightly stronger lines and
   shadows so cards still lift off the page. Plain :root on purpose: the dark
   blocks in the system are :root:not([data-theme="light"]) and
   :root[data-theme="dark"], both more specific, so dark mode is untouched. */
:root{
  --ground:#E9EFF7;
  --surface:#F8FBFE;
  --surface-2:#DFE9F5;
  --line:rgba(2,105,208,.16);
  --line-strong:rgba(2,105,208,.30);
  --e1:0 1px 2px rgba(6,21,40,.06), 0 2px 8px rgba(6,21,40,.05);
  --e2:0 6px 18px rgba(6,21,40,.09), 0 2px 6px rgba(6,21,40,.05);
  --e3:0 26px 60px rgba(6,21,40,.16), 0 8px 20px rgba(6,21,40,.07);
  --logo-op:.68;
}

/* The trust band stays dark in both themes, so the hero and the client logos
   read as one block instead of the strip switching to a light plate under a
   navy hero. The logos are masked shapes painted with --ink, and the label
   uses --muted, so the band redefines those two tokens for its own subtree
   rather than restating every rule. */
.trust{background:var(--navy);border-bottom-color:rgba(148,190,240,.16);
  --ink:#E8F0FA;--ink-2:#B9C9DD;--muted:#93A6BF;--logo-op:.76;
  --line-strong:rgba(148,190,240,.30)}
.trust .lg:hover{background:#7CC0FF}

/* .band--surface and .card both painted themselves with --surface, so on an
   alternating band the cards had the same fill as the band behind them and
   only a hairline told them apart. In light mode the band now takes its own
   deeper tone and the cards keep the lighter one. Dark mode keeps the value
   it had, declared through all three theme states so the toggle works both
   ways. */
:root{--band-alt:#DFE9F5}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--band-alt:var(--surface)}}
:root[data-theme="dark"]{--band-alt:var(--surface)}
.band--surface{background:var(--band-alt)}

/* Keep the real menu on laptops ------------------------------------------
   The system folds the nav into the mobile sheet below 1300px, which was sized
   for a text wordmark. With the image logo and the header CTA the bar is wider,
   but it still fits comfortably above 1200px -- a 1280 or 1366 laptop was
   getting a hamburger for no reason. This restores the desktop nav for that
   band by putting back the base declarations the 1300px block overrides.
   .nav-sheet returns to display:contents, which makes it generate no box at
   all, so its fixed positioning goes with it -- visibility still inherits,
   hence the explicit reset. */
@media (min-width:1200px) and (max-width:1300px){
  .nav{backdrop-filter:blur(14px);background:rgba(7,23,43,.92)}
  .nav .wrap{gap:var(--s4);min-height:72px;flex-wrap:nowrap;position:static}
  .nav-toggle{display:none}
  .theme-switch{margin-left:0}
  .nav-sheet{display:contents;visibility:visible}
  .nav-extra,.nav-more,.nav-sheet .nd{display:none}
  .nav-sheet ul{display:flex;align-items:center;gap:14px;margin-left:auto;flex-wrap:nowrap;
    padding:0;background:none;overflow:visible;counter-reset:none}
  .nav-sheet li{border-bottom:0;animation:none}
  /* :not(.nav-cta) matters -- .btn--primary paints its gradient with
     background, so resetting background on every link in the row flattened the
     header button to a plain dark pill. */
  .nav-sheet li:not(.nav-cta) a{display:inline;padding:0;border-radius:0;color:#B7CBE2;
    background:none;column-gap:0;white-space:nowrap}
  .nav-sheet li:not(.nav-cta) a::before,.nav-sheet li:not(.nav-cta) a::after{content:none}
  .nav-sheet .nl{display:inline;font-size:14px;font-weight:600;letter-spacing:normal;line-height:inherit}
  .nav-sheet li:not(.nav-cta) a:hover{background:none;color:#fff}
  .nav-sheet li:not(.nav-cta) a:hover .nl{color:#fff}
  .nav-sheet li:not(.nav-cta) a[aria-current]{background:none}
  .nav-sheet li a[aria-current] .nl{color:#fff}
  .nav-sheet .nav-cta{margin-top:0;margin-left:var(--s2)}
  .nav-sheet .nav-cta a.btn{display:inline-flex;align-items:center;justify-content:center;
    padding:11px 18px;font-size:13.5px;border-radius:var(--r-pill);color:#fff;white-space:nowrap}
  .brand--img img{height:42px}
}

/* Modal on small screens ---------------------------------------------------
   Below 560px the system gives the dialog height:100% and removes the body's
   max-height, but dialog.modal is overflow:hidden and nothing else scrolls --
   so on a phone the form could not be scrolled at all and the submit button
   was unreachable. The dialog becomes a flex column instead: head and foot
   hold their size, the body takes the rest and scrolls. The [open] guard
   matters -- an unconditional display would override the UA rule that keeps a
   closed dialog hidden. */
@media (max-width:560px){
  dialog.modal[open]{display:flex;flex-direction:column}
  .modal-head,.modal-foot{flex:0 0 auto}
  .modal-body{flex:1 1 auto;min-height:0;overflow-y:auto;-webkit-overflow-scrolling:touch;
    padding:var(--s5);overscroll-behavior:contain}
  .modal-ok{flex:1 1 auto;overflow-y:auto}
  .modal-foot{flex-wrap:wrap;gap:var(--s3);padding:var(--s4) var(--s5)
    calc(var(--s4) + env(safe-area-inset-bottom,0px))}
  .modal-foot .btn{width:100%;justify-content:center}
  .f2{grid-template-columns:1fr}
}

/* Mobile menu position ------------------------------------------------------
   The sheet is placed with top:calc(var(--probar) + var(--navh,66px)). Neither
   variable was ever defined -- an undefined var in a calc makes the whole
   declaration invalid, so top fell back to auto and the open menu sat over the
   header instead of below it. These are the defaults; app.js then keeps --navh
   equal to the real bar height. --probar is the reading-progress bar the
   prototype had and this theme does not, hence 0. */
:root{--probar:0px;--navh:66px}

/* Service cards: the tagline sits above the title as a caption, the way the
   team cards put a role above a name. */
/* Sentence case on purpose: these taglines run to a full sentence on some
   services, and uppercase with letter-spacing turns a sentence into a wall. */
.card:not(.card--ask) .cap{display:block;margin-bottom:6px;font-size:13px;font-weight:700;
  line-height:1.35;letter-spacing:0;text-transform:none;color:var(--ink-2)}
.card .cap + h3{margin-top:0}
.card--ask .cap{display:block;margin-bottom:6px}
.card .ic{margin-bottom:var(--s5)}

/* The approved FAQ summary carries a number, but the system sheet never styled
   .acc-num, so it rendered at question weight and read as part of the sentence.
   Same treatment as the other numeric markers on the page (.symptom-i, .ed-num). */
.acc-num{flex:none; min-width:1.2em; font-size:11px; font-weight:800; letter-spacing:.1em;
  color:var(--blue); margin-top:7px; font-variant-numeric:tabular-nums}

/* Case-study card screenshots.
   .case-media is a gradient panel with a 34px grid drawn by ::before and a
   centred browser mock. A record with a featured image shows the image instead:
   the grid comes off (it reads as a defect over a photograph) and becomes a
   bottom scrim so the industry pill stays legible over a light shot. */
.case-media .case-shot{position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; z-index:1; display:block}
.case-media:has(.case-shot)::before{z-index:2; background-image:none;
  background:linear-gradient(180deg,rgba(6,21,40,0) 48%,rgba(6,21,40,.62))}
.case-media b{z-index:3}

/* The record's opening band takes the same screenshot as its card.
   Same reasoning as .case-media: the 34px grid reads as a defect over a
   photograph, so where a shot is present it becomes an even scrim instead --
   even rather than bottom-weighted here, because this label pill sits in the
   middle of the band rather than in a corner. */
.figure-band .case-shot{position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; z-index:1; display:block}
.figure-band:has(.case-shot)::before{z-index:2; background-image:none;
  background:linear-gradient(180deg,rgba(6,21,40,.18),rgba(6,21,40,.5))}
.figure-band span{z-index:3}

/* Team member contact row.
   .socials is styled for the dark footer -- light text on a translucent white
   ground. On a light card that reads as an empty box, so the card variant takes
   its colours from the theme tokens instead and sits a size smaller. */
.socials--card{justify-content:center; gap:8px; margin-top:var(--s4)}
.socials--card a{width:34px; height:34px; border-radius:10px; color:var(--muted);
  background:var(--surface-2); border:1px solid var(--line)}
.socials--card a:hover{background:var(--grad); color:#fff; border-color:transparent}

/* --------------------------------------------------------------------------
   Two-card grids. The system sheet defines .g-3 and .g-4 but never .g-2, so
   every block that happened to render exactly two cards -- Mission & Vision
   among them -- fell back to one column and stacked.
   -------------------------------------------------------------------------- */
/* min(320px,100%) rather than a bare 320px: auto-fit still gives two columns
   wherever they fit, but a 320px track can no longer be wider than the column it
   sits in, which is what pushed a 320px-wide phone sideways. */
.g-2{grid-template-columns:repeat(auto-fit,minmax(min(320px,100%),1fr))}

/* Mission & Vision: the same card, given the weight a two-card band can carry. */
.card--duo{padding:var(--s7);
  background:radial-gradient(120% 110% at 100% 0%,rgba(122,63,208,.10),transparent 58%),
             radial-gradient(90% 90% at 0% 100%,rgba(2,105,208,.08),transparent 60%),
             var(--surface)}
.card--duo::before{content:"";position:absolute;inset:0 0 auto;height:3px;background:var(--grad)}
.card--duo .ic{width:58px;height:58px;border-radius:18px}
.card--duo .ic svg{width:27px;height:27px}
.card--duo h3{font-size:clamp(1.2rem,1.7vw,1.42rem);letter-spacing:-.02em}
.card--duo p{font-size:16px;color:var(--ink-2);margin-top:var(--s4);line-height:1.65}

/* Member hero portrait with a real photo. .spot-face is a gradient disc sized
   for initials; the photo rendered at its natural size and spilled square over
   the ring, so it is cropped to the disc the way the team card already is. */
.spot-face--photo{padding:3px;overflow:hidden;background:var(--grad);
  box-shadow:0 22px 48px rgba(2,105,208,.38)}
.spot-face--photo img{width:100%;height:100%;display:block;border-radius:50%;
  object-fit:cover;object-position:center 22%}
.spot-face--photo::after{content:"";position:absolute;inset:3px;border-radius:50%;pointer-events:none;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.22)}

/* ...and the ring around it is the card's hairline, not a heavy rotating band,
   so the two portraits read as the same component at two sizes. */
.spot-ring{inset:-9%;border-radius:50%;background:none;animation:none;
  -webkit-mask:none;mask:none;border:1px dashed rgba(255,255,255,.30)}
.spot-glow{inset:-34%}

/* Nothing is pinned beside the portrait any more, so it stands on its own and is
   sized up to carry the column. */
.spotlight{--spot-d:clamp(160px,19vw,256px)}
.spot-face{width:var(--spot-d);font-size:clamp(44px,5.2vw,72px)}
/* Stacked, the portrait leads and the column centres under it. The portrait's
   caption goes, because the role eyebrow and the h1 directly beneath it say the
   same two things; so does the breadcrumb, which on a phone costs a whole row
   for a trail the back button already covers. The BreadcrumbList in the page's
   JSON-LD is untouched, so the search result keeps its trail either way. */
@media (max-width:600px){
  .phero-split{gap:var(--s5)}
  .phero-split .spotlight{order:-1;padding-top:0}
  .phero-split .spotlight figcaption{display:none}
  .phero-split>div{text-align:center}
  .phero-split h1,.phero-split .lead{margin-inline:auto}
  .phero-split .btn-row,.phero-split .socials{justify-content:center}
}

/* CEO brief on the about page. */
.split-media--ceo{grid-template-columns:minmax(220px,340px) minmax(0,1fr);align-items:center}
.split-media--ceo p+p{margin-top:var(--s4)}
.ceo-frame{position:relative;width:100%;max-width:340px;justify-self:start}
@media (max-width:900px){.ceo-frame{justify-self:center}}
.ceo-orb{position:absolute;z-index:0;left:-19%;top:-17%;width:70%;aspect-ratio:1;border-radius:50%;
  background:radial-gradient(circle,rgba(2,105,208,.34),rgba(248,10,98,.16) 55%,transparent 72%);
  filter:blur(4px);pointer-events:none}
.ceo-grid{position:absolute;z-index:0;right:-20px;bottom:-20px;width:64%;height:58%;
  border-radius:var(--r-md);opacity:.75;pointer-events:none;
  background-image:linear-gradient(var(--line-strong) 1px,transparent 1px),
                   linear-gradient(90deg,var(--line-strong) 1px,transparent 1px);
  background-size:20px 20px;
  -webkit-mask:linear-gradient(135deg,transparent 34%,#000);
  mask:linear-gradient(135deg,transparent 34%,#000)}
/* The same 3px gradient rim the hero portrait carries, so the two faces on the
   site read as one treatment. */
.ceo-plate{position:relative;z-index:1;padding:3px;border-radius:calc(var(--r-lg) + 3px);
  background:var(--grad);box-shadow:0 18px 40px rgba(2,105,208,.20)}
@media (max-width:900px){.split-media--ceo{grid-template-columns:1fr}}
/* The source portraits are 300x250, so the plate is kept square and capped
   rather than stretched to a tall 4:5 crop that would upscale them to mush. */
.ceo-shot{position:relative;margin:0;aspect-ratio:1/1;width:100%;
  border-radius:var(--r-lg);overflow:hidden;
  background:linear-gradient(150deg,#0B2647,#0269D0 55%,#7A3FD0);isolation:isolate}
.ceo-shot img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  object-position:center 18%;display:block}
.ceo-initials{position:absolute;inset:0;display:grid;place-items:center;color:#fff;
  font-size:clamp(46px,6vw,72px);font-weight:900;letter-spacing:-.04em}
.ceo-shot figcaption{position:absolute;left:0;right:0;bottom:0;z-index:2;color:#fff;
  padding:var(--s8) var(--s5) var(--s5);
  background:linear-gradient(180deg,rgba(6,21,40,0),rgba(6,21,40,.55) 45%,rgba(6,21,40,.9))}
.ceo-shot figcaption b{display:block;font-size:1.08rem;font-weight:800;letter-spacing:-.02em}
.ceo-shot figcaption span{display:block;font-size:13px;color:#9CC8F5;margin-top:5px}
.ceo-quote{position:relative;margin-top:var(--s6);padding-left:var(--s5);color:var(--ink);
  font-size:clamp(1.02rem,1.4vw,1.14rem);font-weight:600;line-height:1.6}
.ceo-quote::before{content:"";position:absolute;left:0;top:5px;bottom:5px;width:3px;
  border-radius:3px;background:var(--grad)}

/* Contact sidebar tiles. The prototype styles each one inline; the same values,
   as classes, so the editor never has to hand-write a style attribute. */
.ctile{display:flex;gap:13px;align-items:flex-start}
.ctile .ic{width:38px;height:38px;border-radius:11px;margin:0;flex:none}
.ctile .ic svg{width:17px;height:17px}
.ctile b{display:block;font-size:11px;font-weight:800;letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);margin-bottom:4px}
.ctile a{font-weight:600;text-decoration:none;font-size:15px;color:var(--ink)}
.ctile a:hover{color:var(--blue)}
.ctile .ctile-value{font-weight:600;font-size:15px;color:var(--ink)}
.ctile .small{margin-top:4px}


/* ---------- standard packages (campaign page) ---------------------------
   The offer card already carries the badge, checklist, meta list and full-width
   button a pricing tier needs, so a package reuses it rather than inventing a
   second card. What it adds is the price line, and a three-column grid: with
   twelve packages in an auto-fit grid the tiers of one service would wrap into
   the next service's row, which reads as one twelve-tier ladder instead of four
   ladders of three. */
.offers--pack{grid-template-columns:repeat(auto-fit,minmax(min(100%,280px),1fr))}
@media (min-width:1000px){.offers--pack{grid-template-columns:repeat(3,minmax(0,1fr))}}
.offer--pack .offer-top{margin-bottom:calc(var(--s3) * -1)}
.pack-price{display:flex; align-items:baseline; gap:9px; flex-wrap:wrap;
  margin-top:var(--s2) !important; padding-bottom:var(--s3)}
.pack-price b{font-size:clamp(25px,2.8vw,31px); font-weight:800; line-height:1;
  letter-spacing:-.02em; color:var(--ink)}
.pack-price span{font-size:13px; font-weight:700; letter-spacing:.02em; color:var(--muted)}
.offer--pack p{margin-top:0}
.pack-note{margin-top:var(--s6); font-size:14px; line-height:1.65; color:var(--muted);
  text-align:center; max-width:62ch; margin-inline:auto}
.pack-empty{margin-top:var(--s6); text-align:center; color:var(--muted)}


/* ---------- packages filter bar ----------------------------------------
   .toolbar puts the count left and pushes the filter right with margin-left
   auto, which works for the blog's three short category names and breaks for
   five service names -- the last button ran off the edge and collided with the
   count. Under a centred heading the filter reads better as a centred pill
   group with the count as its caption. */
.packbar{display:flex; flex-direction:column; align-items:center; gap:var(--s4);
  margin:calc(var(--s7) * .8) 0 0}
.packbar .seg2{display:flex; flex-wrap:wrap; justify-content:center; gap:4px;
  padding:5px; border:1px solid var(--line); border-radius:var(--r-pill);
  background:var(--surface-2); max-width:100%}
.packbar .seg2 button{font:inherit; font-size:13.5px; font-weight:700; cursor:pointer;
  background:transparent; border:1px solid transparent; color:var(--ink-2);
  border-radius:var(--r-pill); padding:9px 18px; white-space:nowrap;
  transition:all var(--t-fast) var(--ease-out)}
.packbar .seg2 button:hover{color:var(--ink); background:var(--surface)}
.packbar .seg2 button[aria-pressed="true"]{background:var(--grad); color:#fff;
  border-color:transparent; box-shadow:0 4px 14px rgba(2,105,208,.30)}
.packbar-count{font-size:13.5px; color:var(--muted); letter-spacing:.01em}
.packbar-count b{color:var(--ink); font-weight:800}
@media (max-width:560px){.packbar .seg2 button{padding:8px 13px; font-size:12.5px}}

/* ---------- article extras ---------------------------------------------- */
/* The record hero already carries the title, the lead and the meta line, so a
   16/8 figure under it pushed the first paragraph below the fold. */
.figure-band--post{aspect-ratio:16/6}
@media (max-width:700px){.figure-band--post{aspect-ratio:16/8}}

.amet .avatar--photo{overflow:hidden; padding:0; display:block; flex:none}
.amet .avatar--photo img{width:100%; height:100%; border-radius:50%; object-fit:cover;
  object-position:center 22%; display:block}
.amet b a{color:inherit; text-decoration:none}
.amet b a:hover{color:var(--blue)}
.share.is-copied{border-color:var(--blue); color:var(--blue)}
.authorcard .portrait--shot{overflow:hidden; border-radius:50%}
.authorcard .portrait--shot::after{inset:-5px}
.authorcard .portrait--shot img{width:100%; height:100%; border-radius:50%;
  object-fit:cover; object-position:center 22%; display:block}

/* wp_link_pages() nests its links, so the row has to be laid out on the
   wrapper as well as on the nav. */
.pager .nav-links{display:flex; align-items:center; justify-content:center;
  gap:8px; flex-wrap:wrap}
.pager .screen-reader-text{position:absolute; width:1px; height:1px; overflow:hidden;
  clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0; padding:0;
  background:none; min-width:0}

/* A blog featured image is a designed banner -- it carries its own headline,
   logo and artwork -- not a photograph that survives a crop. Cropping it to
   the band's ratio cut the logo off the top, and the centred category pill
   landed in the middle of the artwork. On a post the band therefore takes the
   image's own proportions and the pill moves out to a corner. Case study
   records keep the cropped treatment: those are photographs, and there the
   even band is the point. */
.figure-band--shot{aspect-ratio:auto; display:flex; justify-content:center;
  background:var(--surface-2)}
.figure-band--shot::before{display:none}
/* width:auto rather than 100%: an undersized upload then sits centred and
   sharp on the tinted band instead of being stretched into a blur, which
   also makes it obvious which posts still need a proper banner. */
.figure-band--shot .case-shot{position:static; inset:auto; width:auto; height:auto;
  max-width:100%; object-fit:contain; display:block}
.figure-band--shot span{position:absolute; left:var(--s4); bottom:var(--s4); z-index:3}

/* ---------- Contact Form 7 inside the design -----------------------------
   CF7 wraps every control in a span, which would become a grid item in
   .field (display:grid) and leave the input at its intrinsic width. Making
   the wrapper a block hands the row back to the input. Everything else here
   is CF7's own furniture -- validation tips, the response line, the spinner
   -- given the design's type and colour. */
.wpcf7-form-control-wrap{display:block}
.field .wpcf7-form-control{width:100%}
.wpcf7-not-valid-tip{margin-top:5px;font-size:12px;font-weight:600;color:var(--pink-text)}
.field input.wpcf7-not-valid,.field select.wpcf7-not-valid,.field textarea.wpcf7-not-valid{border-color:var(--pink-text)}
.wpcf7-response-output{margin:var(--s5) 0 0;padding:12px 15px;border:1px solid var(--line);
  border-radius:var(--r-md);font-size:14px;line-height:1.55;color:var(--ink-2);background:var(--surface)}
.wpcf7 form.invalid .wpcf7-response-output,.wpcf7 form.unaccepted .wpcf7-response-output{border-color:var(--pink-text);color:var(--pink-text)}
.wpcf7 form.sent .wpcf7-response-output{border-color:var(--ok);color:var(--ok)}
.wpcf7-spinner{margin-left:10px;vertical-align:middle}
.wpcf7 .ajax-loader{vertical-align:middle}
/* The modal keeps its submit in the footer bar, pointing at the form by id,
   so CF7's own button would be a second one. */
dialog.modal .ovh-cf7-submit{display:none}
dialog.modal .wpcf7-response-output{margin-bottom:var(--s5)}
/* The subscribe pill is a flex row, so CF7's wrapper has to flex too. */
.foot-sub-form .wpcf7-form-control-wrap{flex:1 1 auto;min-width:0}
.foot-sub-form .wpcf7-response-output{flex:1 0 100%;margin:var(--s3) 0 0;background:transparent;border:0;padding:0}
.foot-sub-form .wpcf7-spinner{flex:none}

/* Header mega menu -----------------------------------------------------------
   Any top-level menu item that has children in Appearance > Menus grows a
   panel. Nothing below names Services, so adding, renaming, reordering or
   removing items in the admin changes the menu and no template or rule has to
   follow. The desktop bar starts at 1200px (see the band further up), so the
   hover panel and the mobile accordion split at exactly that width. */
.nav-sub-toggle{display:none}
.nav-caret{flex:none}

@media (min-width:1200px){
  /* the panel is measured against the header row, never against one link, so a
     wide panel cannot be pushed off the right edge by a late menu item */
  .nav .wrap{position:relative}
  .nav-sheet li.has-sub{align-self:stretch;display:flex;align-items:center}
  .nav-sheet li.has-sub>a{display:inline-flex;align-items:center;gap:5px}
  .nav-sheet li.has-sub>a .nav-caret{opacity:.7;transition:transform var(--t-fast) var(--ease-out)}
  .nav-sheet li.has-sub:hover>a .nav-caret,
  .nav-sheet li.has-sub:focus-within>a .nav-caret{opacity:1;transform:rotate(180deg)}

  .nav .mega{position:absolute;left:50%;top:100%;width:min(880px,calc(100vw - 48px));z-index:70;
    padding-top:10px;opacity:0;visibility:hidden;pointer-events:none;
    transform:translateX(-50%) translateY(6px);
    transition:opacity var(--t-base) var(--ease-out),transform var(--t-base) var(--ease-out),
               visibility 0s linear var(--t-base)}
  .nav-sheet li.has-sub:hover>.mega,
  .nav-sheet li.has-sub:focus-within>.mega,
  .nav-sheet li.has-sub.open>.mega{opacity:1;visibility:visible;pointer-events:auto;
    transform:translateX(-50%) translateY(0);
    transition:opacity var(--t-base) var(--ease-out),transform var(--t-base) var(--ease-out),visibility 0s}

  .nav .mega-in{display:grid;grid-template-columns:minmax(0,.8fr) minmax(0,2fr);gap:var(--s6);
    padding:var(--s6);border-radius:20px;
    background:radial-gradient(120% 120% at 0% 0%,rgba(2,105,208,.28),transparent 58%),#0A1B31;
    border:1px solid rgba(255,255,255,.13);
    box-shadow:0 34px 70px -28px rgba(0,0,0,.8)}
  .nav .mega-aside{display:flex;flex-direction:column;align-items:flex-start;
    padding-right:var(--s5);border-right:1px solid rgba(255,255,255,.09)}
  .nav .mega-eyebrow{font-size:10.5px;font-weight:800;letter-spacing:.16em;text-transform:uppercase;
    color:#7FA8D8;margin:0}
  .nav .mega-note{margin:10px 0 0;font-size:13px;line-height:1.5;color:#9DB4CE}
  .nav .mega-all{display:inline-flex;align-items:center;gap:7px;margin-top:auto;padding-top:var(--s4);
    font-size:13.5px;font-weight:700;color:#fff;text-decoration:none}
  .nav .mega-all svg{transition:transform var(--t-fast) var(--ease-out)}
  .nav .mega-all:hover svg{transform:translateX(4px)}

  .nav .mega-list{display:grid;grid-template-columns:1fr 1fr;gap:4px;margin:0;padding:0;
    counter-reset:none;list-style:none}
  .nav .mega-list li{border-bottom:0;animation:none}
  .nav .mega-list li a{display:block;padding:12px 14px;border-radius:13px;color:#EAF2FC;
    text-decoration:none;background:transparent;
    transition:background var(--t-fast) var(--ease-out)}
  .nav .mega-list li a::before,.nav .mega-list li a::after{content:none;counter-increment:none}
  .nav .mega-list li a:hover,.nav .mega-list li a:focus-visible{background:rgba(255,255,255,.07)}
  .nav .mega-list li a[aria-current]{background:linear-gradient(90deg,rgba(2,105,208,.32),rgba(248,10,98,.16) 75%,transparent)}
  .nav .mega-list .mg-t{display:block;font-size:14.5px;font-weight:700;letter-spacing:-.01em;color:#fff}
  .nav .mega-list .mg-d{display:block;margin-top:3px;font-size:12.5px;line-height:1.45;color:#93AAC6}
}
@media (min-width:1200px) and (prefers-reduced-motion:reduce){
  .nav .mega{transition:none}
}

/* --- the sheet on phones and tablets --- */
@media (max-width:1199px){
  /* the chevron used to sit on every row, which read as though each one opened
     something; only a row that actually holds pages carries one now */
  .nav-sheet li a::after{content:none}
  .nav-sheet li.has-sub{position:relative}
  .nav-sheet li.has-sub>a{padding-right:56px}
  .nav-sheet li.has-sub>a .nav-caret{display:none}

  .nav-sub-toggle{display:grid;place-items:center;position:absolute;top:10px;right:2px;
    width:40px;height:40px;padding:0;cursor:pointer;color:#9DB4CE;
    border:1px solid rgba(255,255,255,.14);border-radius:12px;background:rgba(255,255,255,.06);
    transition:color var(--t-fast) var(--ease-out),border-color var(--t-fast) var(--ease-out)}
  .nav-sub-toggle .nav-caret{width:14px;height:14px;transition:transform var(--t-fast) var(--ease-out)}
  .nav-sub-toggle[aria-expanded="true"]{color:#fff;border-color:rgba(255,110,156,.55)}
  .nav-sub-toggle[aria-expanded="true"] .nav-caret{transform:rotate(180deg)}

  .nav .mega{display:none}
  .nav-sheet li.has-sub.open>.mega{display:block}
  .nav .mega-in{display:block;padding:0 0 var(--s4) 34px}
  .nav .mega-aside{display:none}
  .nav .mega-list{display:grid;gap:2px;margin:0;padding:0;counter-reset:none;list-style:none}
  .nav .mega-list li{border-bottom:0}
  .nav .mega-list li a{display:block;padding:11px 13px;border-radius:12px;color:#EAF2FC;
    background:rgba(255,255,255,.04);text-decoration:none}
  .nav .mega-list li a::before,.nav .mega-list li a::after{content:none;counter-increment:none}
  .nav .mega-list li a[aria-current]{background:linear-gradient(90deg,rgba(2,105,208,.30),rgba(248,10,98,.14) 75%,transparent)}
  .nav .mega-list .mg-t{display:block;font-size:15px;font-weight:700;color:#fff}
  .nav .mega-list .mg-d{display:block;margin-top:2px;font-size:12px;line-height:1.4;color:#8AA4C4}
}

/* The hero panel's two illustrative charts are decoration; on a phone they cost
   a screenful above the fold and shrink to the point of being unreadable, so the
   card keeps its figures and drops the drawings. */
@media (max-width:640px){
  .hero .panel-charts{display:none}
}

/* The menu row wraps in a flex line of its own, so a link only ever grows as
   tall as its text. Stretching the list to the bar's height removes the strip of
   dead space a pointer crosses on its way down to the panel -- crossing it left
   both the item and the panel, and the panel shut before it could be reached. */
@media (min-width:1200px){
  .nav-sheet ul{align-self:stretch}
}

/* The staggered entrance is for the sheet opening, not for a panel expanded
   later: replaying it left the service rows blank for a beat after the tap. */
@media (max-width:1199px){
  .nav.open .nav-sheet .mega-list li{animation:none}
}

/* Contact Form 7 renders every text input with size="40", which is an intrinsic
   width of about 376px. Inside .field the width:100% above already overrides it,
   but the footer subscribe pill has no .field wrapper, so on a 360px phone that
   one input stuck 41px past the viewport -- and because the footer is on every
   page, every page could be dragged sideways. min-width:0 matters as much as the
   width: a flex item refuses to shrink below its intrinsic size without it. */
.wpcf7 input[type="text"],.wpcf7 input[type="email"],.wpcf7 input[type="url"],
.wpcf7 input[type="tel"],.wpcf7 input[type="number"],.wpcf7 input[type="date"],
.wpcf7 select,.wpcf7 textarea{max-width:100%;min-width:0}
.foot-sub-form{position:relative}
/* Only the field, never the button: width:100% on every control stretched the
   Subscribe button across the whole pill. */
.foot-sub-form .wpcf7-form-control-wrap{flex:1 1 0%;min-width:0}
.foot-sub-form .wpcf7-form-control-wrap .wpcf7-form-control{width:100%;min-width:0}
/* The spinner is always in the DOM and only toggles visibility, so in a flex row
   it reserved 34px next to the button and pushed the pill past the screen. Lift
   it out of the flow and it still shows while a subscription is in flight. */
.foot-sub-form .wpcf7-spinner{position:absolute;top:50%;right:10px;margin:0;
  transform:translateY(-50%);pointer-events:none}

/* The grid motif behind the CEO portrait is hung 20px outside its frame. On a
   phone the frame already fills the column, so those 20px were the last of the
   page's sideways drift; tucked in, the about page stops overflowing. */
@media (max-width:600px){
  .ceo-grid{right:0;bottom:-14px}
}

/* Free calculators ----------------------------------------------------------
   The inputs sit beside the figures so a change and its consequence are on
   screen together; below 900px they stack and the figures follow the inputs,
   which is the order someone fills them in. Amber is the one colour the token
   set does not carry, so it is defined here for all three theme states. */
:root{--tool-warn:#8A5A00}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--tool-warn:#E0A63C}}
:root[data-theme="dark"]{--tool-warn:#E0A63C}

.calc{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);
  gap:var(--s7);align-items:start;margin-top:var(--s7)}
@media (max-width:900px){.calc{grid-template-columns:1fr;gap:var(--s6)}}

.calc-in .f2{gap:var(--s5)}
.calc .field label .u{color:var(--muted);font-weight:600}
.calc .field input{font-variant-numeric:tabular-nums}
/* A number input's spinner is 20px of nothing on a field this narrow. */
.calc .field input::-webkit-outer-spin-button,
.calc .field input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.calc .field input[type="number"]{-moz-appearance:textfield;appearance:textfield}

.calc-actions{display:flex;flex-wrap:wrap;gap:var(--s3);margin-top:var(--s6)}
.calc-note{display:flex;align-items:flex-start;gap:9px;margin-top:var(--s5);
  font-size:13px;line-height:1.55;color:var(--muted)}
.calc-note svg{flex:none;margin-top:2px;color:var(--blue)}

/* Deliberately not sticky. A sticky panel inside this grid stopped whole tiles
   repainting in one Chromium capture, and a panel that follows the reader is not
   worth a rendering risk on the one page whose job is to be trusted. */
.calc-out{
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:var(--s5);box-shadow:var(--e1)}

.calc-verdict{display:grid;gap:4px;margin:0 0 var(--s4);padding:13px 15px;
  border-radius:var(--r-md);border:1px solid var(--line-strong);background:var(--surface)}
.calc-verdict b{font-size:15px;font-weight:800;letter-spacing:-.01em;color:var(--ink)}
.calc-verdict em{font-style:normal;font-size:13px;line-height:1.5;color:var(--ink-2)}
.calc-verdict[data-state="ok"]{border-color:color-mix(in srgb,var(--ok) 45%,transparent)}
.calc-verdict[data-state="ok"] b{color:var(--ok)}
.calc-verdict[data-state="warn"]{border-color:color-mix(in srgb,var(--tool-warn) 45%,transparent)}
.calc-verdict[data-state="warn"] b{color:var(--tool-warn)}
.calc-verdict[data-state="bad"]{border-color:color-mix(in srgb,var(--pink-text) 40%,transparent)}
.calc-verdict[data-state="bad"] b{color:var(--pink-text)}

.calc-res{display:grid;gap:0}
.res{display:grid;gap:2px;padding:12px 0;border-bottom:1px solid var(--line)}
.res:last-child{border-bottom:0;padding-bottom:0}
.res span{font-size:12.5px;font-weight:700;color:var(--ink-2)}
.res b{font-size:1.375rem;font-weight:900;letter-spacing:-.03em;color:var(--ink);
  font-variant-numeric:tabular-nums;line-height:1.15}
.res i{font-style:normal;font-size:11.5px;color:var(--muted)}
.res--lead{padding-top:0}
.res--lead+.res--lead{padding-top:12px}
.res--lead b{font-size:2.125rem;background:var(--grad);-webkit-background-clip:text;
  background-clip:text;color:transparent}

/* The hub cards reuse .card, so only the strapline under the grid is new. */
.tools-note{max-width:70ch;margin:var(--s6) auto 0;text-align:center;
  font-size:13.5px;line-height:1.6;color:var(--muted)}

/* Printing is how a visitor keeps a copy: there is no PDF on the server to
   download, and nothing is stored, so the page itself is the artefact. */
@media print{
  .nav,.foot,.to-top,.cta,.calc-actions,.calc-note,dialog.modal{display:none !important}
  .phero{padding:0 0 var(--s5);background:none;color:#000}
  .phero .orbs,.phero .grain,.phero .aurora,.phero .mesh,.phero figure{display:none !important}
  .phero h1{font-size:22pt}
  .phero .lead,.phero .deck{font-size:11pt}
  .band{padding:var(--s5) 0}
  .calc{grid-template-columns:1fr 1fr;gap:24px}
  .calc-out{position:static;box-shadow:none;border:1px solid #999;background:none}
  .res b{color:#000 !important;background:none;-webkit-text-fill-color:#000}
  .rv{opacity:1 !important;transform:none !important}
  a[href]::after{content:""}
}

/* On the narrowest phones still in use (around 320px, less once a scrollbar is
   counted) the logo, the theme switch and the menu button together were wider
   than the bar, and the whole page could be dragged sideways because of it. */
@media (max-width:360px){
  .nav .wrap{gap:var(--s3)}
  .brand--img img{height:34px}
  .theme-switch{padding:2px}
  .theme-switch button{padding:6px 9px;font-size:11.5px}
  .nav-toggle{width:38px;height:38px}
}

/* The starting numbers are placeholders, and they have to look it. A figure at
   full contrast reads as something the visitor already typed, which is exactly
   what was reported: the example numbers looked like real answers. Firefox gives
   a placeholder its own opacity, so the value is set rather than inherited. */
.calc .field input::placeholder{color:var(--muted);opacity:.62;font-weight:500}
.calc .field input::-webkit-input-placeholder{color:var(--muted);opacity:.62}

/* While nothing has been typed the figures on the right are worked from those
   placeholders, so they are dimmed to the same degree and labelled. One keystroke
   in any field and both go back to full strength. */
.calc.is-example .calc-res{opacity:.72}
.calc-example{display:flex;align-items:flex-start;gap:8px;margin:0 0 var(--s4);
  padding:9px 12px;border-radius:var(--r-sm);border:1px dashed var(--line-strong);
  font-size:12.5px;line-height:1.45;color:var(--muted)}
@media print{.calc.is-example .calc-res{opacity:1}}

/* ---------------------------------------------------------------------------
   The free SEO check.

   The form is three elements and the report is drawn by assets/seo-audit.js, so
   everything below is either the field the visitor types into or the shape the
   report lands in. Colour follows the calculators: --ok for a pass, --tool-warn
   for something worth a look, --pink-text for something that needs fixing.
   --------------------------------------------------------------------------- */

/* A heading band sitting directly on the cards that belong to it. */
.band--tight{padding-bottom:0}

.audit{margin-top:var(--s7);max-width:920px}
.audit-row{display:grid;gap:9px}
.audit-row>label{font-weight:650;font-size:14px}
.audit-go{display:flex;gap:var(--s3);align-items:stretch;flex-wrap:wrap}
.audit-go input{flex:1 1 320px;min-width:0;padding:14px 16px;font:inherit;font-size:16px;
  color:var(--ink);background:var(--surface);border:1px solid var(--line-strong);
  border-radius:var(--r-sm);box-shadow:var(--e1)}
.audit-go input::placeholder{color:var(--muted);opacity:.62}
.audit-go .btn{flex:0 0 auto}
.audit-row small{color:var(--muted);font-size:12.5px;line-height:1.5}

.audit-note{display:flex;align-items:flex-start;gap:10px;margin-top:var(--s5);
  padding:11px 14px;border-radius:var(--r-sm);border:1px solid var(--line);
  background:var(--surface-2);color:var(--muted);font-size:13px;line-height:1.55}
.audit-note svg{flex:0 0 auto;margin-top:3px;color:var(--blue)}

.audit-msg{margin-top:var(--s4);padding:11px 14px;border-radius:var(--r-sm);
  border:1px solid var(--line-strong);background:var(--surface);font-size:14px}
.audit-msg[data-kind="bad"]{color:var(--pink-text);
  border-color:color-mix(in srgb,var(--pink-text) 45%,transparent)}

/* --- the report ----------------------------------------------------------- */

.rep{margin-top:var(--s7)}
.rep-top{display:flex;gap:var(--s6);align-items:center;flex-wrap:wrap;
  padding:var(--s6);border-radius:var(--r-lg);border:1px solid var(--line);
  background:var(--surface);box-shadow:var(--e2)}
.rep-score{position:relative;flex:0 0 auto;width:132px;height:132px}
.rep-score svg{display:block;width:100%;height:100%}
.rep-track{stroke:var(--line-strong)}
.rep-arc{stroke:var(--ok)}
.rep-score[data-state="warn"] .rep-arc{stroke:var(--tool-warn)}
.rep-score[data-state="bad"] .rep-arc{stroke:var(--pink-text)}
.rep-score-mid{position:absolute;inset:0;display:grid;place-content:center;
  text-align:center;line-height:1.1}
.rep-score-mid b{font-size:38px;font-weight:750;letter-spacing:-.03em;
  font-variant-numeric:tabular-nums}
.rep-score-mid span{display:block;margin-top:3px;font-size:10px;color:var(--muted);
  letter-spacing:.05em;text-transform:uppercase}

/* The report's own heading. It is written by the script, so it carries no theme
   class -- the size is set here rather than left to the browser default. */
.rep-meta>h3{font-size:clamp(1.3rem,2.2vw,1.65rem);font-weight:800;
  letter-spacing:-.02em;line-height:1.2}
.rep-meta>h3:focus{outline:none}

.rep-meta{flex:1 1 340px;min-width:0}
.rep-url{margin-top:6px;font-size:14px;overflow-wrap:anywhere}
.rep-hops{margin-top:4px;font-size:12.5px;color:var(--muted);overflow-wrap:anywhere}
.rep-tally{display:flex;flex-wrap:wrap;gap:8px;margin-top:var(--s4)}
.rep-chip{display:inline-flex;align-items:baseline;gap:5px;padding:5px 11px;
  border-radius:var(--r-pill);border:1px solid var(--line-strong);
  font-size:12.5px;color:var(--muted)}
.rep-chip b{font-size:14px;color:var(--ink);font-variant-numeric:tabular-nums}
.rep-chip[data-state="good"]{border-color:color-mix(in srgb,var(--ok) 45%,transparent)}
.rep-chip[data-state="good"] b{color:var(--ok)}
.rep-chip[data-state="warn"]{border-color:color-mix(in srgb,var(--tool-warn) 45%,transparent)}
.rep-chip[data-state="warn"] b{color:var(--tool-warn)}
.rep-chip[data-state="bad"]{border-color:color-mix(in srgb,var(--pink-text) 45%,transparent)}
.rep-chip[data-state="bad"] b{color:var(--pink-text)}
.rep-facts{display:flex;flex-wrap:wrap;gap:var(--s5);margin-top:var(--s5);
  padding-top:var(--s4);border-top:1px solid var(--line)}
.rep-facts li{display:grid;gap:2px}
.rep-facts span{font-size:10.5px;letter-spacing:.07em;text-transform:uppercase;color:var(--muted)}
.rep-facts b{font-size:16px;font-variant-numeric:tabular-nums}

.rep-acts{display:flex;justify-content:space-between;gap:var(--s4);flex-wrap:wrap;
  margin-top:var(--s5)}
.rep-filter,.rep-acts-r{display:flex;gap:8px;flex-wrap:wrap}
.rep-filter .btn[aria-pressed="true"]{background:var(--blue);border-color:var(--blue);color:#fff}
.rep-filter .btn[disabled]{opacity:.45;pointer-events:none}

.rep-group{margin-top:var(--s6)}
.rep-group h3{display:flex;align-items:baseline;justify-content:space-between;
  gap:var(--s4);flex-wrap:wrap;padding-bottom:10px;border-bottom:1px solid var(--line-strong)}
.rep-count{font-size:12px;font-weight:650;letter-spacing:.02em;color:var(--ok)}
.rep-count[data-state="warn"]{color:var(--tool-warn)}

.rep-list{margin-top:var(--s3)}
.rep-row{display:flex;gap:14px;padding:14px 2px;border-bottom:1px solid var(--line)}
.rep-dot{flex:0 0 auto;width:11px;height:11px;margin-top:8px;border-radius:50%;background:var(--ok)}
.rep-row[data-state="warn"] .rep-dot{background:var(--tool-warn)}
.rep-row[data-state="bad"] .rep-dot{background:var(--pink-text)}
.rep-body{min-width:0}
.rep-name{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;font-weight:650}
.rep-tag{padding:2px 8px;border-radius:var(--r-pill);font-size:10.5px;font-weight:700;
  letter-spacing:.05em;text-transform:uppercase;color:var(--ok);
  background:color-mix(in srgb,var(--ok) 13%,transparent)}
.rep-row[data-state="warn"] .rep-tag{color:var(--tool-warn);
  background:color-mix(in srgb,var(--tool-warn) 15%,transparent)}
.rep-row[data-state="bad"] .rep-tag{color:var(--pink-text);
  background:color-mix(in srgb,var(--pink-text) 13%,transparent)}
.rep-found{margin-top:3px;font-size:14.5px;color:var(--ink-2);overflow-wrap:anywhere}
.rep-why{margin-top:5px;font-size:13px;line-height:1.55;color:var(--muted)}
.rep-foot{margin-top:var(--s6);font-size:13px;color:var(--muted)}

/* "Show me only what needs work" hides the passing rows, and any group left
   with nothing in it goes with them. */
.rep[data-show="open"] .rep-row[data-state="good"]{display:none}
.rep[data-show="open"] .rep-group[data-open="0"]{display:none}

@media (max-width:700px){
  .rep-top{padding:var(--s5);gap:var(--s5)}
  .rep-score{width:104px;height:104px}
  .rep-score-mid b{font-size:30px}
  /* "OUT OF 100" is wider than the smaller dial's opening, so it steps down
     with it rather than running under the ring. */
  .rep-score-mid span{font-size:9px;letter-spacing:.02em}
  .audit-go .btn{width:100%;justify-content:center}
  .rep-facts{gap:var(--s4)}
}

@media print{
  .audit,.rep-acts{display:none !important}
  .rep-top{box-shadow:none;border:1px solid #999}
  .rep[data-show="open"] .rep-row[data-state="good"]{display:flex}
  .rep[data-show="open"] .rep-group[data-open="0"]{display:block}
  .rep-row{break-inside:avoid}
}

/* The mega panel on a laptop-width screen -------------------------------------
   Between 1200px and 1300px the header switches to the compact bar, and that
   block sets display:inline, padding:0 and white-space:nowrap on every link
   inside a menu item so the bar itself can never wrap onto a second line. The
   panel's links live inside those same items. The panel's own rules win back
   display and padding -- they come later at equal specificity -- but nothing in
   them mentions white-space, so the nowrap stayed: each item's description
   became one unbreakable line that ran out of the panel and, on a 1280px
   screen, off the right of the window. The panel's links are blocks and have to
   wrap, so they say so here, with a class more than the compact-bar rule. */
@media (min-width:1200px){
  .nav .nav-sheet .mega-list{grid-template-columns:repeat(2,minmax(0,1fr))}
  .nav .nav-sheet .mega-list li a{display:block;padding:12px 14px;border-radius:13px;
    white-space:normal;overflow-wrap:break-word;color:#EAF2FC;background:transparent}
  .nav .nav-sheet .mega-list li a:hover,
  .nav .nav-sheet .mega-list li a:focus-visible{background:rgba(255,255,255,.07);color:#fff}
  .nav .nav-sheet .mega-list li a[aria-current]{
    background:linear-gradient(90deg,rgba(2,105,208,.32),rgba(248,10,98,.16) 75%,transparent)}
  .nav .nav-sheet .mega-list .mg-t{display:block;white-space:normal;
    font-size:14.5px;font-weight:700;letter-spacing:-.01em;color:#fff}
  .nav .nav-sheet .mega-list .mg-d{display:block;margin-top:3px;white-space:normal;
    font-size:12.5px;line-height:1.45;color:#93AAC6}
  .nav .nav-sheet .mega-all{display:inline-flex;align-items:center;gap:7px;
    margin-top:auto;padding:var(--s4) 0 0;white-space:normal;color:#fff}
  /* Belt and braces: whatever a future header rule does to the links, the panel
     itself cannot paint outside its own box. */
  .nav .mega-in{max-width:100%;overflow:hidden}
}

/* The results, in two columns ------------------------------------------------
   Six to eight figures in one tall column made the panel a scroll of its own and
   pushed the last two below the fold on a laptop. Two columns put the whole
   answer on screen at once, and because the two headline figures are always
   first in ovelithub_tool_defs() they take the top row between them. The rule
   between rows is drawn on the top edge, not the bottom, so it works whatever
   the number of figures -- odd counts included. */
.calc-res{grid-template-columns:repeat(2,minmax(0,1fr));column-gap:var(--s5)}
.res{padding:14px 0;border-bottom:0;border-top:1px solid var(--line)}
.calc-res>.res:nth-child(-n+2){border-top:0;padding-top:0}
.res:last-child{padding-bottom:0}
.res--lead{padding-top:0}
.res--lead+.res--lead{padding-top:0}

@media (max-width:520px){
  .calc-res{grid-template-columns:1fr}
  .calc-res>.res:nth-child(2){border-top:1px solid var(--line);padding-top:14px}
}

/* How it works, as numbered cards --------------------------------------------
   The numbering carries meaning here: these are the steps of one calculation in
   the order it is worked, so a reader can follow a single figure from the first
   card to the last. A step whose heading is a formula is set in a monospaced
   face, because a formula and a sentence are not the same kind of thing and
   should not look like it. */
.tool-steps{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(300px,100%),1fr));
  gap:var(--s4);margin:var(--s7) 0 0;padding:0;list-style:none}
.tool-step{display:grid;gap:9px;align-content:start;padding:var(--s5);
  border:1px solid var(--line);border-radius:var(--r-md);
  background:var(--surface);box-shadow:var(--e1)}
.tool-step-n{display:grid;place-items:center;width:29px;height:29px;border-radius:9px;
  background:var(--grad);color:#fff;font-size:12.5px;font-weight:800;
  font-variant-numeric:tabular-nums}
.tool-step-t{font-size:15px;font-weight:750;line-height:1.35;letter-spacing:-.012em;color:var(--ink)}
.tool-step-t--calc{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
  font-size:13.5px;font-weight:600;letter-spacing:0;color:var(--blue);
  overflow-wrap:anywhere}
.tool-step p{font-size:14px;line-height:1.62;color:var(--ink-2)}

/* The assumptions ------------------------------------------------------------
   Amber down the left edge, the same colour the calculators use for "worth a
   look", because that is exactly what these are: not errors, but the things the
   arithmetic cannot see. Copy written as label-then-explanation becomes cards;
   copy written as plain paragraphs keeps its shape inside one panel. */
.tool-caveats{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(290px,100%),1fr));
  gap:var(--s4);margin-top:var(--s7)}
.tool-caveat{padding:var(--s5);border:1px solid var(--line);
  border-left:3px solid color-mix(in srgb,var(--tool-warn) 55%,transparent);
  border-radius:var(--r-md);background:var(--surface)}
.tool-caveat h3{margin-bottom:7px;font-size:15px;font-weight:750;letter-spacing:-.012em;color:var(--ink)}
.tool-caveat p{font-size:14px;line-height:1.62;color:var(--ink-2)}
.tool-caveats--prose{display:block;max-width:860px;margin-inline:auto;padding:var(--s6);
  border:1px solid var(--line);
  border-left:3px solid color-mix(in srgb,var(--tool-warn) 55%,transparent);
  border-radius:var(--r-md);background:var(--surface)}
.tool-caveats--prose p{font-size:15px;line-height:1.7;color:var(--ink-2)}
.tool-caveats--prose p+p{margin-top:var(--s4)}

/* The check sits under a centred heading, so it is centred too -- left-aligned
   under a centred head read as a mistake, and was reported as one. */
.audit{margin-inline:auto}
.rep{max-width:1020px;margin-inline:auto}
/* An explanation is prose and wants a prose measure, even inside a wide panel. */
.rep-why{max-width:84ch}

@media print{
  .tool-step,.tool-caveat,.tool-caveats--prose{break-inside:avoid;box-shadow:none}
}

/* The hero panel's two chart captions used to be h5, which made the home page
   jump straight from h1 to h5 -- an outline break for a screen reader and a
   flag on every audit. They label a decorative figure, not a section. */
.chartbox .chartbox-t{margin:0;font-size:13px;font-weight:700}

/* --------------------------------------------------------------------------
   Service card motifs
   A monoline drawing bleeds out of the card's top-right corner, clipped by the
   card's own radius and overflow. It sits behind the copy, so the card is
   exactly the height it was before any figure existed. Each service gets one
   hue from the palette the brand gradient already spans.
   -------------------------------------------------------------------------- */
.svcm{position:absolute;top:-26px;right:-30px;z-index:0;
  width:186px;height:186px;pointer-events:none;opacity:.16;
  transition:opacity .35s ease,transform .35s ease}
.svcm svg{display:block;width:100%;height:100%}
.af_service > *:not(.svcm){position:relative;z-index:1}
.card--feature .svcm{width:252px;height:252px;top:50%;right:7%;
  transform:translateY(-50%);opacity:.28}
.card--feature:hover .svcm,.card--feature:focus-within .svcm{
  transform:translateY(-50%) translateX(-6px);opacity:.44}
.card:hover .svcm,.card:focus-within .svcm{opacity:.34;transform:translate3d(-5px,5px,0)}

.svcm--web{color:var(--blue)}
.svcm--growth{color:var(--violet)}
.svcm--ops{color:var(--ok)}
.svcm--brand{color:var(--pink-text);opacity:.21}

/* .card--feature stops being two columns at 1080px, so below that its motif
   goes back to the corner like every other card's. */
@media (max-width:1080px){
  .card--feature .svcm{width:200px;height:200px;top:-28px;right:-32px;
    transform:none;opacity:.16}
  .card--feature:hover .svcm,.card--feature:focus-within .svcm{
    transform:translate3d(-5px,5px,0);opacity:.34}
}
@media (max-width:600px){
  .svcm{width:142px;height:142px;top:-18px;right:-22px}
  .card--feature .svcm{width:150px;height:150px;top:-18px;right:-22px}
}
@media (prefers-reduced-motion:reduce){
  .svcm{transition:none}
  .card:hover .svcm,.card:focus-within .svcm,
  .card--feature:hover .svcm,.card--feature:focus-within .svcm{transform:none}
}
@media (min-width:1081px) and (prefers-reduced-motion:reduce){
  .card--feature .svcm,.card--feature:hover .svcm,
  .card--feature:focus-within .svcm{transform:translateY(-50%)}
}
@media print{.svcm{display:none}}
