/* ---------------------------------------------------------------
   GVS website stylesheet - THE single source of truth for colour,
   type and spacing. Every page links this file. Change a value here
   and it changes everywhere. Never hardcode a colour in a page.
   Mirror any :root change into /PROGRESS.md in the same edit.
   --------------------------------------------------------------- */
:root{
  /* theme: #191970 actions + rotating word + dark panels + footer, #004687 accents/chips,
     #002244 headings, #002D72 check names, #FFFFFF ground. body copy stays neutral grey */
  --paper:#FFFFFF;--surface:#fff;--wash:#E8F0F7;--wash-2:#F2F6FA;
  --ink:#002244;--ink-2:#1A1A1A;--body:#333333;--soft:#575757;--mute:#8A8A8A;
  --line:#E2E9F1;--line-2:#D3DEEA;--line-b:#D6E2EF;
  --acc:#004687;--acc-2:#003468;--acc-soft:#DDE9F4;--acc-3:#4C8DC4;--ok:#004687;
  /* --elec is "what colour is an ACTION", --foot is "what colour is a DARK
     PANEL". They held the same navy from 1 Aug 2026 and they split again on
     11 Aug 2026, when the owner moved actions onto the logo blue: the
     rotating hero word, the primary button and the four hero chip tiles.
     Keeping them as two names is what made that a scoped edit rather than a
     sweep - see the note above --foot. Never merge them back into one. */
  --elec:#0000CD;
  /* the logo blue. Every derived value below follows it, do not pin any of
     them to a hex: .rot's border is 15% --elec on white, .btn-primary's
     hover is --elec x0.80, and both their shadows are --elec at low alpha.
     Re-derive all four together if this moves. Measured on the rendered
     page: white on #0000CD is 11.16:1, and the white line work in the check
     icons on a chip tile is the same 11.16:1 with the cyan accent at
     5.73:1. Was 14.85 / 14.85 / 7.63 on the navy. All still pass. */
  --foot:#191970;
  /* --elec vs --foot is 1.33:1, so an --elec button on a --foot panel is
     invisible. .ctaband is the one place they meet and it inverts to a white
     plate instead. That inversion was already there and is now load-bearing
     rather than merely defensive. The navy icon plates - the mega-menu and
     drawer tiles, the flow step circles, the blog card icons - read --foot
     and not --elec for the same reason: they are plates, not actions. */
  /* Four site colours that were sampled off the PREVIOUS logo (the triangle
     lockup, replaced 11 Aug 2026). They are site values now, not logo values -
     the mark that shipped in its place runs its own blue and green and is not
     themed from here. Scoped to the Why GVS cards, and --sea also carries the
     homepage accent. Do not spread them without deciding the site-wide scheme. */
  --navy:#000080;--sea:#036695;--sky:#01a2d3;--yolk:#FFEA00;
  --sky-1:#F4FBFE;--sky-2:#E7F4FB;--sky-3:#D6EBF7;--sky-4:#BEDDF0;--sky-line:#C6E3F2;
  /* The two page containers. Every band on the site sits in one of them and
     nothing is allowed to opt out - see the note on .wrap below. */
  --maxw:1180px;--maxw-wide:1320px;
  --font:'Inter',ui-sans-serif,system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;
}
*{margin:0;padding:0;box-sizing:border-box}
/* Both declarations are needed and the order matters. `overflow-x:hidden` on
   the root makes the OTHER axis compute to `auto` - that is the spec, not a
   quirk - so <html> stopped being the viewport and became a scroll box.
   Touch scrolling then has to be routed through that box instead of the
   browser's native viewport gesture, and on a phone it reads as the page
   simply not responding to a thumb while a mouse wheel still works.
   `overflow-x:clip` clips exactly the same but establishes no scroll
   container, so overflow-y stays `visible` and the viewport scrolls itself.
   `hidden` stays first as the fallback for Safari before 16, which drops
   `clip` unrecognised. Nothing on the site overflows sideways anyway - the
   marquee is clipped by .mrq - so the fallback costs nothing.
   Do not collapse these two into one. */
html{scroll-behavior:smooth;overflow-x:hidden;overflow-x:clip}
body{background:radial-gradient(48rem 32rem at 82% -16%,rgba(180,208,242,.14),transparent 60%),var(--paper);background-attachment:fixed;color:var(--body);font-family:var(--font);font-optical-sizing:auto;font-size:16.5px;line-height:1.6;-webkit-font-smoothing:antialiased}
/* A fixed background attachment has to be repainted on every scroll frame,
   which drops the page off the compositor and onto the main thread. On a
   touch device that is the difference between a scroll that tracks a thumb
   and one that stutters or stalls. any-pointer:coarse rather than a width
   query on purpose: a touchscreen laptop at desktop width has the same
   problem. The gradient is fixed on mouse-only machines exactly as before. */
@media(any-pointer:coarse){body{background-attachment:scroll}}
h1,h2,h3,h4{font-family:var(--font);color:var(--ink);letter-spacing:-.025em;line-height:1.12;font-weight:700}
button,input,select,textarea{font-family:var(--font)}
a{color:inherit;text-decoration:none}img{max-width:100%;display:block}
/* THE container. Two widths, --maxw and --maxw-wide, and nothing else.
   A band that opts out of both does not look wrong at 1440px, it looks wrong
   at 2000px+, which is what a zoomed-out browser or a large monitor is. The
   header used to carry `max-width:100%` and the logo therefore sat 420px
   outside the hero copy on a wide screen while the nav floated in the middle:
   three islands instead of one bar. If a band has to run edge to edge, the
   BACKGROUND goes full width and the CONTENT stays in a .wrap. Never widen
   the content itself with 100%, 100vw, or an unclamped calc() on vw. */
.wrap{max-width:var(--maxw);margin:0 auto;padding:0 32px}
.btn{display:inline-flex;align-items:center;gap:9px;font-weight:600;font-size:15px;border-radius:11px;padding:13px 22px;transition:transform .5s cubic-bezier(.32,.72,0,1),box-shadow .3s,background .2s;cursor:pointer;border:1px solid transparent;white-space:nowrap}
.btn:active{transform:scale(.98)}
.btn-primary{background:var(--elec);color:#fff;box-shadow:0 2px 4px rgba(0,0,205,.22),0 12px 30px -10px rgba(0,0,205,.45)}.btn-primary:hover{background:#0000A4;transform:translateY(-1px)}
.btn-ghost{background:var(--surface);color:var(--ink);border-color:var(--line-2)}.btn-ghost:hover{border-color:var(--acc);color:var(--acc)}
.sec-h{max-width:46rem}.sec-h h2{font-size:clamp(28px,3.7vw,42px);letter-spacing:-.03em}.sec-h .lede{margin-top:14px;font-size:17.5px;color:var(--body)}
.center{margin-left:auto;margin-right:auto;text-align:center}
.reveal{opacity:0;transform:translateY(22px);transition:all .8s cubic-bezier(.16,1,.3,1)}.reveal.in{opacity:1;transform:none}

/* utility bar (Checkr-style, light) */
.util{border-bottom:1px solid var(--line)}
.util .wrap{display:flex;justify-content:flex-end;align-items:center;gap:24px;height:40px;font-size:13.5px}
.util a{color:var(--soft)}.util a:hover{color:var(--acc)}
.util a.cand{color:var(--acc);font-weight:500}

/* nav */
header{position:sticky;top:0;z-index:60;background:rgba(255,255,255,.78);backdrop-filter:blur(14px) saturate(150%)}
/* The bar runs on the wide container, the same width as the Checks catalogue
   and the Industries section, so the logo and the CTA stay tied to the page
   instead of to the viewport edge. The white bar background is still full
   width because `header` itself is not a .wrap. */
header .wrap{max-width:var(--maxw-wide)}
.nav{display:flex;align-items:center;justify-content:space-between;gap:28px;height:62px}
.brand{display:flex;align-items:center;gap:11px;font-weight:800;color:var(--ink);font-size:24px;letter-spacing:-.02em;flex:none}
.brand img{height:36px;width:auto}
.nav-links{display:flex;flex:1;justify-content:center;gap:14px;align-items:center}
.nav-links>li{list-style:none;position:relative}
.nav-links>li.has-mega{position:static}
.nav-links>li>a{display:flex;align-items:center;gap:7px;padding:9px 16px;font-size:14px;font-weight:500;color:var(--ink-2);border-radius:10px;transition:background .3s cubic-bezier(.4,0,.2,1),color .3s cubic-bezier(.4,0,.2,1)}
/* hover: tinted rectangle inside a white halo, with a blue glow around it */
.nav-links>li>a:hover{color:var(--acc);background:#EEF4FA}
.nav-links>li>a i{font-size:13px;opacity:.7}
/* 1280, not 1080: every item in here carries a 24px icon tile plus a
   9px gap from 31 Jul 2026, which is 33px the label no longer has. At
   1080 the five columns gave the longest label, "Pre-employment
   medical", 167px for a string that measures 169 - it wrapped to two
   lines on a two-pixel deficit. 1180 was still only 2px clear, which
   any font-loading difference would eat. 1280 leaves 18px and lands
   just inside the 1320px header container, so the panel now lines up
   with the bar instead of floating narrower than it. */
.mega{position:absolute;top:100%;left:50%;transform:translateX(-50%) translateY(8px);width:min(1280px,calc(100vw - 40px));background:var(--surface);border:1px solid var(--line-2);border-radius:20px;padding:30px 34px;opacity:0;visibility:hidden;transition:all .3s cubic-bezier(.16,1,.3,1);box-shadow:0 36px 74px -26px rgba(22,35,63,.3)}
.nav-links>li:hover .mega{opacity:1;visibility:visible;transform:translateX(-50%) translateY(10px)}
.mega.sol{width:min(1120px,calc(100vw - 40px))}
.mega .cols{display:grid;grid-template-columns:repeat(5,1fr);gap:28px}
.mega .cols.c4{grid-template-columns:repeat(4,1fr);gap:28px}
.mega .col h5{display:flex;align-items:flex-end;min-height:32px;font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--soft);padding-bottom:12px;margin-bottom:6px;border-bottom:1px solid var(--line)}
.mega .col a{display:flex;align-items:center;gap:9px;font-size:14px;color:var(--ink-2);padding:8px 12px;margin:0 -12px;border-radius:9px;font-weight:500;transition:background .3s cubic-bezier(.4,0,.2,1),color .3s cubic-bezier(.4,0,.2,1)}
.mega .col a:hover{color:var(--acc);background:#EEF4FA}
/* the icon tile. Structural, so it is NOT inside the homepage theme
   block - all 15 pages carry the same nav markup and the shell has to
   stay byte-identical. It reads --acc, so each page tints it with its
   own palette automatically. flex:none or the tile squashes when the
   label is long enough to fill the column. */
.mega .col a i,.dsub a i{flex:none;display:grid;place-items:center;width:24px;height:24px;border-radius:7px;background:var(--acc-soft);color:var(--acc);font-size:14px;transition:background .3s,color .3s}
.mega .col a:hover i,.dsub a:hover i{background:var(--acc);color:#fff}
.mega .mfoot{margin-top:24px;padding-top:20px;border-top:1px solid var(--line);text-align:center}

/* Panel width follows COLUMN COUNT, not which menu it is. .sol was the
   original four-column panel; Background checks joined it on 5 Aug 2026 when
   the two-item Specialised column came off and left it with four. At the base
   1280px, four columns give 282px each against labels that top out near 140,
   and the panel reads as half empty next to the Solutions one it sits beside. */
.mega.w4{width:min(1120px,calc(100vw - 40px))}

/* RESOURCES MENU - a description under every destination, not a bare label.
   Owner's instruction, 5 Aug 2026, against SpringVerify's and AuthBridge's
   resource menus. Three destinations only, so the panel is narrower than the
   other two and the columns are wide enough for a real line of copy.

   The rich item reuses the shape .login .drop already uses - tile, then a div
   holding a <b> and a <span> - rather than inventing a second one. What has to
   be restated is the alignment: .mega .col a centres its children, which is
   right for a one-line label and drops the icon to the middle of a three-line
   block here. */
.mega.res{width:min(920px,calc(100vw - 40px))}
.mega.res .cols{grid-template-columns:repeat(3,1fr);gap:20px}
.mega.res .mlab{font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--soft);padding-bottom:12px;margin-bottom:14px;border-bottom:1px solid var(--line)}
.mega.res .col a{align-items:flex-start;padding:12px;gap:12px}
.mega.res .col a i{width:30px;height:30px;font-size:16px;border-radius:9px;margin-top:1px}
.mega.res .col a b{display:block;font-size:15.5px;font-weight:700;color:var(--ink);letter-spacing:-.015em;transition:color .3s}
.mega.res .col a span{display:block;font-size:12.5px;font-weight:400;color:var(--soft);line-height:1.5;margin-top:4px}
/* .mega .col a:hover recolours the anchor, and <b> sets its own colour, so the
   heading would be the one thing on the row that does not respond to a hover. */
.mega.res .col a:hover b{color:var(--acc)}
.nav .right{display:flex;align-items:center;gap:10px;flex:none}
.nav .right .btn{padding:9px 17px;font-size:13.5px;border-radius:10px}
/* No box on the Log in control. Owner's instruction, 14 Aug 2026: it read as
   a second button competing with Talk to sales beside it. The padding stays,
   because it is what the .drop panel hangs off and what gives the control a
   hit area; only the border and the radius went. */
.login{position:relative}.login>a{display:flex;align-items:center;gap:6px;padding:8px 13px;font-size:13.5px;font-weight:600;color:var(--ink-2)}
.login .drop{position:absolute;top:100%;right:0;transform:translateY(8px);width:230px;background:var(--surface);border:1px solid var(--line-2);border-radius:14px;padding:8px;opacity:0;visibility:hidden;transition:all .25s;box-shadow:0 24px 50px -20px rgba(22,35,63,.2)}
.login:hover .drop{opacity:1;visibility:visible;transform:translateY(10px)}
.login .drop a{display:flex;gap:11px;padding:11px;border-radius:10px}.login .drop a:hover{background:var(--wash)}.login .drop i{color:var(--acc);font-size:19px}.login .drop b{color:var(--ink);font-size:13.5px;font-weight:600;display:block}.login .drop span{font-size:11.5px;color:var(--soft)}

/* mobile nav - drawer.
   Below 1040px the desktop links, the Log in dropdown and the Talk to sales
   button all leave the bar and the whole nav moves into a right-side drawer.

   THAT NUMBER TRACKS THE BAR'S CONTENTS AND HAS MOVED TWICE IN ONE DAY.
   It was 980 with five items. Resources made six and the bar stopped fitting:
   "Background checks" wrapped and the page scrolled sideways, so it went to
   1220. Platform and Trust then came off the bar on the owner's instruction,
   leaving four, and 1220 would have handed the drawer to laptops that have
   room to spare. It is 1040 now.

   MEASURE THE LINKS ON A WIDE VIEWPORT, NOT A NARROW ONE. The first attempt
   at the six-item number was taken at 1000px, where the label had ALREADY
   wrapped, so the list measured 690px and the answer came out 1145 - which
   still wrapped. A wrapped element reports the width it was squeezed into,
   not the width it wants. Unwrapped, "Background checks" is 184px against the
   133px it reports broken over two lines.

   Current arithmetic, measured at 1440px: the four items plus their three
   14px gaps are 557px. Add brand 103, the Log in and CTA block 232, the two
   28px .nav gaps and 64px of .wrap padding: 1012px of viewport required.
   1040 is that with 28px of slack, because a two-pixel margin is one
   font-loading difference away from the bug coming back - the same reason
   .mega is 1280 and not 1180. Re-measure on a wide viewport whenever an item
   is added or removed, or a label changes.

   No JavaScript: a checkbox drives the drawer, native <details> drives the
   sub-menus. Two reasons the markup sits where it does -
   1. The drawer and its scrim live AFTER </header>, not inside it. `header`
      carries backdrop-filter, which makes it the containing block for any
      position:fixed descendant, so a drawer nested inside it would be 62px
      tall instead of full height.
   2. The checkbox stays inside the bar next to the burger so it keeps a
      focus ring and a tab stop. Open state is therefore read with :has()
      from the root rather than a sibling combinator.
   Everything visual is inside the media query, so none of it can render on
   desktop and the input is display:none there - no phantom tab stop. */
.mnav-t,.burger,.drawer,.dscrim{display:none}
@media(max-width:1040px){
  /* Hiding the desktop links belongs HERE, with the burger that replaces
     them, not in the 980px block further down where it used to sit. Left
     there, raising this breakpoint would have shown the burger and the
     desktop links together for 170px of viewport width. */
  .nav-links{display:none}
  .nav .right{display:none}
  .mnav-t{display:block;position:absolute;width:1px;height:1px;opacity:0;margin:0}
  .burger{display:flex;flex-direction:column;justify-content:center;gap:5px;width:44px;height:44px;padding:0 9px;flex:none;cursor:pointer;-webkit-tap-highlight-color:transparent}
  .burger span{height:2.5px;border-radius:2px;background:var(--ink)}
  .mnav-t:focus-visible+.burger{outline:2px solid var(--acc);outline-offset:1px;border-radius:9px}
  .dscrim{display:block;position:fixed;inset:0;z-index:70;background:rgba(0,34,68,.45);opacity:0;visibility:hidden;transition:opacity .35s,visibility 0s .35s}
  .drawer{display:flex;flex-direction:column;position:fixed;top:0;right:0;bottom:0;z-index:80;width:min(88vw,400px);background:var(--surface);transform:translateX(100%);visibility:hidden;transition:transform .38s cubic-bezier(.32,.72,0,1),visibility 0s .38s;box-shadow:0 0 60px -12px rgba(22,35,63,.4)}
  html:has(.mnav-t:checked){overflow:hidden}
  html:has(.mnav-t:checked) .dscrim{opacity:1;visibility:visible;transition:opacity .35s}
  html:has(.mnav-t:checked) .drawer{transform:none;visibility:visible;transition:transform .38s cubic-bezier(.32,.72,0,1)}
  .dtop{display:flex;align-items:center;justify-content:space-between;padding:13px 12px 13px 22px;flex:none}
  .dx{display:grid;place-items:center;width:44px;height:44px;font-size:26px;color:var(--ink);cursor:pointer}
  .dnav{flex:1;overflow-y:auto;overscroll-behavior:contain;padding:4px 22px 24px}
  .dnav>a,.dnav>details>summary{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:17px 0;font-size:17px;font-weight:600;color:var(--ink);border-bottom:1px solid var(--line);cursor:pointer;list-style:none}
  .dnav>details>summary::-webkit-details-marker{display:none}
  .dnav>details>summary::after{content:"";flex:none;width:9px;height:9px;margin-right:5px;border-right:2px solid var(--soft);border-bottom:2px solid var(--soft);transform:rotate(-45deg);transition:transform .25s}
  .dnav>details[open]>summary::after{transform:rotate(45deg);margin-right:1px}
  .dsub{padding:2px 0 18px}
  .dsub h5{font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--soft);margin:16px 0 2px}
  .dsub h5:first-child{margin-top:8px}
  .dsub a{display:flex;align-items:center;gap:11px;padding:10px 0 10px 14px;font-size:15px;font-weight:500;color:var(--ink-2)}
  /* Resources carries its descriptions into the drawer too, so the phone menu
     says the same thing the desktop one does. Only three items, so the extra
     height costs nothing. Same restated alignment as .mega.res, same reason. */
  .dsub.res a{align-items:flex-start;padding:12px 0 12px 14px}
  .dsub.res a i{margin-top:1px}
  .dsub.res a b{display:block;font-size:15.5px;font-weight:700;color:var(--ink);letter-spacing:-.015em}
  .dsub.res a span{display:block;font-size:12.5px;font-weight:400;color:var(--soft);line-height:1.5;margin-top:3px}
  /* .dall already carried a trailing arrow <i>; it is not a leading
     icon tile, so it opts out of the sizing above. */
  .dsub a.dall i{width:auto;height:auto;background:none;color:inherit;border-radius:0;font-size:inherit}
  .dsub a.dall{display:inline-flex;align-items:center;gap:8px;margin-top:12px;color:var(--acc);font-weight:600;font-size:14.5px}
  .dfoot{flex:none;padding:16px 22px calc(16px + env(safe-area-inset-bottom));border-top:1px solid var(--line)}
  .dfoot .btn{width:100%;justify-content:center;height:52px;font-size:15.5px}
}

/* hero - centered */
/* min-height parks the hero so the trust claim below it lands just inside the
   fold - the collage and claim spacing both follow from this number.
   The 920px cap is what stops the hero growing without limit on a tall or
   zoomed-out viewport, where the copy and collage stay their fixed size and
   the rest of the block fills with white. 920 is exactly 1080dvh minus 160,
   so every screen up to 1080px tall renders precisely as before and the cap
   only engages past it. Not the same 920 as the `max-height:920px` query
   further down: that one shrinks the collage on short laptops and can never
   be in effect at the same time as this cap. */
.hero{position:relative;overflow:hidden;overflow:clip;min-height:min(calc(100dvh - 160px),920px);display:flex;align-items:center}
/* constellation field - navy marks on a white hero */
#field{position:absolute;inset:0;width:100%;height:100%;z-index:0;
  -webkit-mask-image:linear-gradient(180deg,#000 0%,#000 74%,rgba(0,0,0,.5) 90%,transparent 100%);mask-image:linear-gradient(180deg,#000 0%,#000 74%,rgba(0,0,0,.5) 90%,transparent 100%)}
/* white veil behind the copy block, plus an edge fade - keeps the dense field off the type */
.hero::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;background:radial-gradient(46% 30% at 50% 26%,rgba(255,255,255,.88) 0%,rgba(255,255,255,.5) 62%,transparent 84%)}
.hero .wrap{position:relative;z-index:2;width:100%;padding:112px 32px 12px;text-align:center}
.hero h1{font-size:clamp(28px,4.2vw,54px);letter-spacing:-.03em;margin:0 auto;font-weight:500;white-space:nowrap}
/* the rotating word pill. Standalone rather than scoped to .hero, because the
   check-page H1 uses the same component with a different word list. */
/* the border is 15% --elec on white, the same ratio the old #E3DAFF was to
   #3F00FF, so the pill keeps its weight against the headline. Re-derive it
   if --elec ever moves again rather than eyeballing a new tint. */
/* #D9D9F8 is 15% --elec on white, the same ratio #DDDDEA was to the navy, so
   the pill keeps its weight against the headline. Derived, not picked. */
.rot{display:inline-flex;align-items:center;justify-content:center;min-width:7.2em;color:var(--elec);border:1.5px solid #D9D9F8;background:var(--surface);border-radius:14px;padding:2px 16px;font-weight:600;box-shadow:0 6px 18px -8px rgba(0,0,205,.22)}
/* The word rolls, it does not fade. The one leaving goes out through the top of
   the pill while the next arrives from under the bottom, both on one curve, so
   the pair reads as a single movement rather than a swap. site.js builds this
   structure at runtime, so the markup stays one span with data-words on it.

   The clip is on `.rt` and not on `.rot`, deliberately. An INLINE-level box
   with clipped overflow can take its baseline from its own bottom edge rather
   than from the text in it, which drops the pill off the headline's baseline.
   `.rt` is a flex item, a block box, so that rule cannot reach it. Measured on
   the rendered homepage: neither position moves it in Chrome. Keep it here
   anyway - Chrome is not the only engine and that failure is silent.
   It does not touch .rot's box-shadow either: overflow clips descendants, and
   the shadow is drawn outside the border box by .rot itself.

   `.rw-sizer` is an invisible copy of the WIDEST word and it is load-bearing:
   every visible word is absolutely positioned and contributes no size, and
   below 640px min-width is 0, so without it the pill collapses to nothing. It
   is also what stops the pill resizing every 2.6s and shoving the rest of the
   headline sideways - `identity.html` has words on both sides of 7.2em. */
.rot .rt{position:relative;display:block;overflow:hidden;white-space:nowrap}
.rot .rw{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;transform:translateY(112%);transition:transform .62s cubic-bezier(.16,1,.3,1)}
.rot .rw.on{transform:none}
.rot .rw.up{transform:translateY(-112%)}
.rot .rw-sizer{visibility:hidden}
.hero .sub{font-size:16px;color:var(--soft);max-width:54ch;margin:18px auto 0}
.hero .cta{display:flex;gap:13px;justify-content:center;margin-top:24px;flex-wrap:wrap}
/* Checkr-style human collage */
/* Breaks out of the 1180 wrap so the four cards can run wider than the copy.
   The box ends up exactly 2x the calc term, so 48vw gave 96vw - a container
   that grew forever while the cards inside it stayed fixed at 1023px plus
   gaps. Clamped at 700, so the box tops out at 1400px. That is 1344px of
   content after the 28px padding, against 1287px of cards at the widest gap:
   57px of headroom. Do not lower the 700, .pcard is flex:0 1 auto and will
   start shrinking the photos instead of overflowing. Below a 1458px viewport
   48vw still wins, so every laptop renders as before. */
.collage{display:flex;gap:clamp(22px,4.4vw,88px);justify-content:center;align-items:flex-end;margin:30px calc(50% - min(48vw,700px)) 0;padding:16px 28px 34px;flex-wrap:nowrap}
.pcard{position:relative;border-radius:18px;border:1px solid var(--line);flex:0 1 auto;background:var(--wash);animation:floaty 11s ease-in-out infinite}
@keyframes floaty{0%,100%{transform:translateY(0)}50%{transform:translateY(-11px)}}
.pcard.a{animation-delay:0s}.pcard.b{animation-delay:1.4s}.pcard.c{animation-delay:.7s}.pcard.d{animation-delay:2s}
.pcard img{width:100%;height:100%;object-fit:cover;border-radius:inherit}
/* card c is the widest of the four, so a centred cover crop ate the headroom above her hair */
.pcard.c img{object-position:50% 12%}
.pcard.a{width:240px;height:271px}.pcard.b{width:259px;height:312px;margin-bottom:13px}.pcard.c{width:274px;height:283px}.pcard.d{width:250px;height:319px;margin-bottom:19px}
/* status card sits outside the photo, overhanging the edge - photo stays fully visible.
   a+b hang left, c+d hang right, so neighbouring chips never converge */
.pchip{position:absolute;bottom:-30px;z-index:3;background:var(--surface);border:1px solid var(--line-2);border-radius:13px;padding:10px 14px;display:flex;align-items:center;gap:9px;white-space:nowrap;text-align:left;box-shadow:0 2px 6px -2px rgba(0,34,68,.12)}
.pcard.a .pchip,.pcard.b .pchip{left:-28px}
.pcard.c .pchip,.pcard.d .pchip{right:-28px}
/* 2nd and 4th photo carry their chip at the top instead of the bottom */
.pcard.b .pchip{top:-26px;bottom:auto;left:-28px}
.pcard.d .pchip{top:-26px;bottom:auto;right:-28px}
/* 28px, up from 22. The four chips carry the generated check icons now, not
   Phosphor glyphs, and those are line art with echo ghosts and a glow outside
   the silhouette - the drawn mark is smaller than its box. Rendered at 22 the
   fingerprint ridges close into a blur. 28 is the floor where they survive,
   measured on the actual art, not chosen. The icons are white line work and
   are INVISIBLE on the white chip itself, so they have to stay inside this
   coloured tile. Do not move them out of it. */
.pchip .ic{width:28px;height:28px;border-radius:8px;background:var(--acc);color:#fff;display:grid;place-items:center;font-size:13px;flex:none;overflow:hidden}
.pchip .ic img{width:100%;height:100%;display:block}
.pchip .ic.b{background:var(--acc)}
.pchip b{font-size:12.5px;color:var(--acc);font-weight:600;display:block;line-height:1.25}.pchip span{font-size:10.5px;color:var(--acc);opacity:.72;line-height:1.35;display:block}
.imgnote{text-align:center;font-size:11.5px;color:var(--mute);margin-top:14px}

/* trusted band */
.trusted{padding:22px 0 26px;overflow:hidden;overflow:clip;background:linear-gradient(180deg,rgba(232,240,247,0) 0%,rgba(232,240,247,.5) 55%,var(--wash) 100%)}
/* interior pages: the homepage version ends on solid wash because a tinted
   section follows it there. Everywhere else it has to fade back out, or the
   band butts against white with a hard edge. Same claim, same marquee. */
.trusted.flat{padding:38px 0 44px;background:linear-gradient(180deg,rgba(232,240,247,0) 0%,rgba(232,240,247,.55) 50%,rgba(232,240,247,0) 100%)}
.trusted .claims{text-align:center;margin-bottom:52px}
.trusted .claims .a{font-size:34px;font-weight:500;color:var(--ink);letter-spacing:-.02em}.trusted .claims .a span{color:var(--ink)}

/* Smaller claim line and smaller logos on EVERY PAGE EXCEPT THE HOMEPAGE.
   Owner's instruction, 1 Aug 2026. `.trusted.flat` already means "not the
   homepage" - build.py hands the homepage `trusted` and all 49 others
   `trusted flat` - so this needs no new class and no per-page edit.
   The two mobile rules are restated on purpose. The base mobile sizes live
   in media queries at LOWER specificity (`.trusted .claims .a` is 0-3-0,
   `.marquee img` is 0-1-1), so the desktop rules here (0-4-0 and 0-3-1)
   would otherwise beat them inside those queries and a phone would render
   the desktop size. Same trap as `body.theme .trusted.flat` further down. */
.trusted.flat .claims .a{font-size:23px}
.trusted.flat .claims{margin-bottom:34px}
.trusted.flat .marquee img{height:30px}
@media(max-width:700px){.trusted.flat .marquee img{height:24px}}
@media(max-width:640px){.trusted.flat .claims .a{font-size:18px}.trusted.flat .claims{margin-bottom:24px}}
.marquee{display:flex;gap:56px;width:max-content;animation:scroll 34s linear infinite}
@keyframes scroll{to{transform:translateX(-50%)}}

section.band{padding:58px 0}
/* the washes fade in and out of the page rather than starting on a hard edge */
.band.tint{background:linear-gradient(180deg,var(--wash) 0%,rgba(232,240,247,.45) 62%,transparent 100%)}
.band.tint-in{background:linear-gradient(180deg,transparent 0%,rgba(232,240,247,.45) 45%,var(--wash) 100%)}
.band.tint-hold{background:linear-gradient(180deg,var(--wash) 0%,rgba(232,240,247,.5) 70%,transparent 100%)}

/* segmentation (industries) - above checks */
.tabs{display:flex;gap:26px;justify-content:center;margin-top:26px;flex-wrap:wrap;border-bottom:1px solid var(--line);padding-bottom:0}
.tab{padding:0 2px 16px;font-weight:600;font-size:16px;color:var(--soft);cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-1px;transition:all .25s}
.tab:hover{color:var(--acc)}
.tab.on{color:var(--ink);border-color:var(--acc)}
.panel{display:none;margin-top:30px}
.panel.on{display:block}
/* industries grid. Named .igrid, NOT .pgrid: the interior-page .pgrid further
   down this file is declared later and would win here, squashing the photo to
   a 320px thumbnail. Do not rename it back. */
/* 1.5:1, not 1.04:.96. The photo was as wide as the copy and the block read
   like a hero. Narrowing the photo column is also what shortens the panel:
   the copy is what sets the row height, and a wider copy column spends fewer
   lines on it. Roughly the OnGrid ratio. */
.igrid{display:grid;grid-template-columns:1.5fr 1fr;gap:56px;align-items:start}
.igrid h3{font-size:27px;letter-spacing:-.02em}
/* 70ch, not 54: the section runs on .wrap-wide, and a cap that stops short of
   the column just puts the height back as extra lines. Width is the point. */
.igrid .ilede{margin-top:12px;font-size:16px;color:var(--body);max-width:70ch}
.igrid .lk{display:inline-flex;align-items:center;gap:8px;color:var(--acc);margin-top:18px;font-weight:600;font-size:15px;transition:gap .25s}
.igrid .lk:hover{gap:13px}
/* the two sub-blocks: who gets checked, then what is usually ordered.
   16+12 above each rule, not 26+18: two of these sit in the column, so every
   pixel here is paid for twice. */
.isub{margin-top:16px;padding-top:12px;border-top:1px solid var(--line);font-size:11.5px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--soft)}
.iroles{margin-top:8px;font-size:15px;color:var(--body);line-height:1.6}
.ichecks{display:flex;flex-wrap:wrap;gap:7px;margin-top:10px}
.chk{font-size:13px;font-weight:600;color:var(--acc);background:var(--acc-soft);border:1px solid transparent;border-radius:999px;padding:6px 12px;line-height:1.15;transition:background .25s,border-color .25s}
/* hover is scoped to a.chk on purpose: chips whose check page does not exist
   yet stay <span>, and a span that lights up on hover reads as clickable. */
a.chk:hover{background:var(--surface);border-color:var(--line-b)}
/* the photo fills the column beside the copy, so the img cannot drive the row
   height. Once stacked it falls back to a fixed ratio, see the 980px query. */
/* fixed 3/2 landscape, top of the column, NOT stretched to the copy height.
   Stretching is what made the photo as tall as the panel and read as a hero
   image. The copy alone sets the row height now. See the 980px query for the
   stacked ratio. */
.pvis{border-radius:20px;overflow:hidden;overflow:clip;border:1px solid var(--line);position:relative;background:var(--wash);aspect-ratio:3/2}
.pvis img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.pvis::after{content:"";position:absolute;inset:0;background:linear-gradient(180deg,transparent 55%,rgba(0,70,135,.12))}
.proof{display:grid;grid-template-columns:repeat(3,1fr);gap:0;margin-top:30px;border-top:1px solid var(--line);padding-top:26px}
.proof div{padding:0 26px;border-right:1px solid var(--line)}.proof div:last-child{border-right:0}
.proof .t{font-size:17px;font-weight:700;color:var(--ink)}.proof .s{font-size:14.5px;color:var(--soft);margin-top:8px;line-height:1.5}

/* checks catalogue */
.wrap-wide{max-width:var(--maxw-wide)}
.cat{display:grid;grid-template-columns:repeat(4,1fr);gap:18px;margin-top:56px}
.tile{display:flex;flex-direction:column;align-items:center;text-align:center;gap:18px;padding:28px;border-radius:18px;background:var(--surface);border:1px solid var(--line)}
.tile i{width:64px;height:64px;border-radius:17px;background:var(--acc-soft);color:var(--acc);display:grid;place-items:center;font-size:32px}
.tile b{font-weight:600;color:#002D72;font-size:17px;line-height:1.35;min-height:46px;display:grid;place-items:center}
.cat-cta{margin-top:34px;text-align:center}

/* WHY - new unique design: big claim + evidence pillars */
.why{display:grid;grid-template-columns:1.05fr 1fr;gap:56px;align-items:center;margin-top:10px}
.why-l .big{font-size:clamp(64px,9vw,120px);font-weight:800;letter-spacing:-.05em;line-height:.9;color:var(--acc);font-family:var(--font)}
.why-l h2{font-size:clamp(26px,3.4vw,40px);letter-spacing:-.03em;margin-top:10px}
.why-l p{font-size:17px;color:var(--body);margin-top:16px;max-width:34ch}
.why-l .badges{display:flex;gap:10px;margin-top:24px;flex-wrap:wrap}
.why-l .badges span{font-size:13px;font-weight:600;color:var(--acc);background:var(--acc-soft);border-radius:999px;padding:7px 14px}
.pillars{display:flex;flex-direction:column;gap:12px}
.pillar{display:flex;gap:16px;align-items:flex-start;background:var(--surface);border:1px solid var(--line);border-radius:16px;padding:22px 24px;transition:transform .5s cubic-bezier(.32,.72,0,1),box-shadow .3s}
.pillar:hover{transform:translateX(4px);box-shadow:0 14px 30px -18px rgba(22,35,63,.22)}
.pillar .n{font-size:26px;font-weight:800;color:var(--acc);letter-spacing:-.03em;min-width:74px}
.pillar .m b{display:block;font-size:16.5px;font-weight:700;color:var(--ink)}.pillar .m span{font-size:14px;color:var(--soft);line-height:1.5}

/* WHY GVS cards - illustrated panel on top, copy under it, centred.
   Each card carries a generated illustration, images/why-w1..w6.jpg. They
   replaced the inline SVG scenes on 30 Jul 2026. The scenes are abstract
   product surfaces with no people and no lettering in them: a turnaround
   chart, a fan of checks closing into one package, a shield, a portal
   window, a stamped report, three packages. Any replacement must hold the
   same rules or the six stop reading as a set:
     - 1280x720, exactly 16:9, so the panel never crops them
     - logo palette only, and about 85% of each frame is white or pale blue
     - one small yellow accent per image, nothing more
     - no words in the artwork. At 378px wide, which is what the panel
       renders at on desktop, any label turns to mush. An earlier set carried
       "1 HOUR" and "ISO 27001" and both were illegible in the card.
   Working PNGs are in image-source/why-cards/, outside website/ so they are
   not deployed. */
.whyx{display:grid;grid-template-columns:repeat(3,1fr);gap:22px;margin-top:50px}
/* no hover lift, no shadow bloom, no art scale - the owner asked for cards
   that sit still. A border tint is the whole hover state. */
.wcard{background:var(--surface);border:1px solid var(--line);border-radius:22px;padding:12px 12px 28px;display:flex;flex-direction:column;transition:border-color .3s}
.wcard:hover{border-color:var(--sky-line)}
/* The jpgs are 16:9 as well, so cover crops nothing off them. The wash is
   kept as the placeholder that fills the panel while the image loads. */
.wviz{border-radius:17px;overflow:hidden;overflow:clip;aspect-ratio:16/9;box-shadow:inset 0 0 0 1px var(--sky-2);
  background:radial-gradient(105% 78% at 50% 4%,rgba(255,255,255,.96) 0%,rgba(255,255,255,0) 60%),
    linear-gradient(158deg,var(--sky-1) 0%,var(--sky-2) 54%,var(--sky-3) 100%)}
/* height:auto is load-bearing. The imgs carry width/height attributes so the
   panel reserves its space before the jpg arrives, and those attributes map
   to presentational hints. Leave height unset and the hint wins, the image
   renders at its intrinsic 720px and every card stretches to 378x720.
   height:100% does not fix it either: the parent's height comes from its own
   aspect-ratio, so a percentage height resolves to auto and lands back on
   720. Set height:auto and let aspect-ratio compute it from the width. */
.wviz img{width:100%;height:auto;aspect-ratio:16/9;object-fit:cover;display:block}
/* no kicker pill - the stat chips were removed 30 Jul 2026 on the owner's
   instruction. Title and copy only, centred. */
.wcard .txt{padding:24px 18px 0;text-align:center}
.wcard h3{font-size:19.5px;letter-spacing:-.02em;color:var(--navy)}
.wcard p{font-size:14.5px;color:var(--soft);margin-top:11px;line-height:1.58}
@media(max-width:980px){.whyx{grid-template-columns:1fr 1fr}}
@media(max-width:600px){.whyx{grid-template-columns:1fr}}
/* Four-up Why GVS, used by the sales page. The homepage runs six cards in the
   three-column default; four in that grid leaves a 3+1 orphan row.
   The two media rules are restated because `.whyx.w4` (0-2-0) beats plain
   `.whyx` (0-1-0) inside the queries above. Same rule as .fields.f4. */
.whyx.w4{grid-template-columns:repeat(4,1fr)}
@media(max-width:980px){.whyx.w4{grid-template-columns:1fr 1fr}}
@media(max-width:600px){.whyx.w4{grid-template-columns:1fr}}
/* portal preview */
.portal-shots{display:grid;grid-template-columns:1fr 1fr;gap:22px;margin-top:48px}
.pshot{border-radius:14px;overflow:hidden;overflow:clip;border:1px solid var(--line);box-shadow:0 26px 60px -32px rgba(22,35,63,.45);background:#fff}
.pshot .bar{display:flex;gap:6px;align-items:center;padding:11px 14px;background:#F2F5FA;border-bottom:1px solid var(--line)}
.pshot .bar span{width:10px;height:10px;border-radius:50%;background:#CFD8E6}
.pshot .bar span:nth-child(1){background:#FF5F57}.pshot .bar span:nth-child(2){background:#FEBC2E}.pshot .bar span:nth-child(3){background:#28C840}
.pshot img{width:100%;height:280px;object-fit:cover;object-position:left top;display:block}
@media(max-width:900px){.portal-shots{grid-template-columns:1fr}.pshot img{height:220px}}
/* trust - dark panel, copy on the left, certifications gridded on the right */
section.band.dark{background:var(--foot);padding:88px 0}
.band.dark h2{color:#fff}
.band.dark .lede{color:#C7D2EE}
.trust{display:grid;grid-template-columns:1.05fr 1fr;gap:64px;align-items:center}
.trust-l{max-width:34rem}
/* A link inside the navy panel inherits the paragraph's own colour and has no
   underline, so it is INVISIBLE as a link - same hex, same weight, no rule.
   The homepage panel has no link in it, which is why this never showed up.
   The sales page does. Underline, not a colour shift: nothing in the pale
   range on this navy keeps 4.5:1 against the copy it sits inside. */
.band.dark .trust-l a{color:#fff;font-weight:600;text-decoration:underline;text-underline-offset:3px}
.trust p{font-size:15.5px;color:#A5B3D8;margin-top:20px}
.certs{display:flex;gap:14px;justify-content:flex-end;align-items:center;flex-wrap:wrap}
/* the plate hugs each logo - equal heights, width follows the artwork */
.cert{background:#fff;border-radius:10px;display:grid;place-items:center;padding:5px}
.cert img{height:clamp(76px,7.6vw,112px);width:auto;object-fit:contain;mix-blend-mode:multiply}
@media(max-width:900px){.certs{gap:10px;justify-content:center}}

/* testimonials */
.vgrid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-top:48px}
.vc{background:linear-gradient(180deg,#fff 0%,rgba(25,25,112,.05) 55%,rgba(25,25,112,.13) 100%);border:1px solid var(--line);border-radius:18px;padding:28px;display:flex;flex-direction:column}
.vc .q{font-size:15.5px;color:var(--ink);line-height:1.6;flex:1}
.vc .who{display:flex;align-items:center;gap:12px;margin-top:22px;padding-top:20px;border-top:1px solid var(--line)}
/* object-position:top, not the 50% default. The sources are 200x268 portraits,
   so a centred square crop takes y 34-234 and slices the top of the hair off on
   all three. Top-aligned uses y 0-200, which is the headroom the shot already has. */
.vc .who img{width:46px;height:46px;border-radius:999px;object-fit:cover;object-position:50% 0}
.vc .who b{color:var(--ink);font-size:14.5px;font-weight:700;display:block}.vc .who span{font-size:12.5px;color:var(--soft)}

/* sales card (Business/Candidate toggle) */
.sales{padding:58px 0}
.salescard{display:grid;grid-template-columns:1fr 1.05fr;gap:0;border-radius:24px;overflow:hidden;overflow:clip;border:1px solid var(--line);box-shadow:0 40px 80px -50px rgba(22,35,63,.5)}
.sales-l{background:#191970;color:#fff;padding:52px 44px}
.sales-l h2{color:#fff;font-size:clamp(26px,3.2vw,38px);letter-spacing:-.03em}
.sales-l p{color:rgba(255,255,255,.86);font-size:16px;margin-top:14px}
.sales-l ul{list-style:none;margin-top:24px;display:flex;flex-direction:column;gap:14px}
.sales-l li{display:flex;gap:11px;align-items:center;font-size:15px;color:#EAF1FF}.sales-l li i{color:#fff;background:rgba(255,255,255,.18);border-radius:999px;width:24px;height:24px;display:grid;place-items:center;font-size:14px;flex:none}
.sales-r{background:var(--surface);padding:44px}

/* SALES PAGE (contact.html). The left panel goes light instead of navy, which
   is what both reference shots run: the form is the heavy element and the
   pitch sits on a wash beside it. This is a MODIFIER - .sales-l keeps all its
   layout, only colour is restated - so the homepage card is untouched.
   Every value reads a semantic token, so it re-colours with body.theme. */
.sales-l.lite{background:var(--wash);color:var(--body)}
.sales-l.lite h1{color:var(--ink);font-size:clamp(28px,3.4vw,40px);letter-spacing:-.03em;line-height:1.14}
.sales-l.lite h1 span{display:block;color:var(--acc)}
.sales-l.lite p,.sales-l.lite li{color:var(--body)}
.sales-l.lite li i{color:#fff;background:var(--acc)}
/* Bordered plates, not bare links. On a wash panel a plain text link is the
   one element with no edge and reads as unfinished next to the form. */
.lcontact{display:flex;flex-wrap:wrap;gap:12px;margin-top:30px}
/* 13.5px and the tighter padding are measured, not picked: the panel is
   ~455px of content at 1440px and the two chips at 14.5px came to ~497px, so
   the email chip wrapped onto its own row. At 13.5 they total ~425px and sit
   inline as the reference shots do. They still wrap below ~1200px, which is
   the intended fallback, not a failure. */
.lcontact a{display:inline-flex;align-items:center;gap:8px;border:1px solid var(--line-2);background:var(--surface);border-radius:11px;padding:10px 13px;font-size:13.5px;font-weight:600;color:var(--acc);transition:border-color .25s;white-space:nowrap}
.lcontact a i{font-size:16px}
.lcontact a:hover{border-color:var(--acc)}
/* There is no .phero above the split on this page, so the section itself pays
   the gap under the header. Do not add a hero here to get the spacing back. */
.salestop{padding-top:44px}
.sales-fh{font-size:22px;letter-spacing:-.02em;color:var(--ink);margin-bottom:20px}
.fnote{font-size:13px;color:var(--mute);margin-top:14px;line-height:1.55}
.fnote a{color:var(--acc);font-weight:600}
@media(max-width:640px){.lcontact a{width:100%}.sales-l.lite h1{font-size:28px}}
/* the form button used to be overridden to navy here while the rest of the
   site ran #3F00FF. --elec IS that navy now, so the override said nothing
   and was deleted. Layout-only rules for this button are in the 640px query. */
.toggle{display:flex;gap:6px;padding:5px;background:var(--wash);border-radius:12px;margin-bottom:24px}
.toggle button{flex:1;padding:11px;border-radius:9px;border:none;background:transparent;color:var(--soft);font-family:var(--font);font-weight:600;font-size:14.5px;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:8px;transition:all .25s}
.toggle button.on{background:var(--surface);color:var(--acc);box-shadow:0 2px 8px -3px rgba(22,35,63,.2)}
.field{margin-bottom:14px}
.field label{display:block;font-size:13px;font-weight:600;color:var(--ink-2);margin-bottom:6px}
.field input,.field select{width:100%;height:46px;border:1px solid var(--line-2);border-radius:11px;padding:0 14px;font-family:var(--font);font-size:15px;color:var(--ink);background:var(--surface);outline:none}
.field input:focus,.field select:focus{border-color:var(--acc);box-shadow:0 0 0 3px var(--acc-soft)}
.field input::placeholder{color:var(--mute)}
.frow{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.sales-r .btn-primary{width:100%;justify-content:center;margin-top:6px;height:48px}
.sform{display:none}.sform.on{display:block}
.candnote{background:var(--wash);border:1px solid var(--line-b);border-radius:12px;padding:16px 18px;font-size:14px;color:var(--body);display:flex;gap:11px;align-items:flex-start}
.candnote i{color:var(--acc);font-size:20px;margin-top:1px}

/* footer - blue, not navy/dark */
footer{background:var(--foot);color:#DCE7FF;padding:70px 0 34px}
.fgrid{display:grid;grid-template-columns:1.5fr 1fr 1fr 1fr 1fr;gap:32px}
.fgrid .brand{color:#fff;margin-bottom:14px}.fgrid .brand img{filter:brightness(0) invert(1)}.fgrid p{font-size:13.5px;color:#B7CBF7;max-width:22rem;line-height:1.6}
.fcol h4{font-size:12px;font-weight:700;letter-spacing:.04em;color:#A8C0F5;margin-bottom:15px}
.fcol a{display:block;font-size:14px;color:#DCE7FF;margin-bottom:10px}.fcol a:hover{color:#fff}
.fcontact{display:flex;gap:26px;flex-wrap:wrap;margin-top:44px;padding-top:26px;border-top:1px solid rgba(255,255,255,.18);font-size:13.5px;color:#DCE7FF}.fcontact b{color:#fff;font-weight:600}
.fbot{display:flex;justify-content:space-between;align-items:center;margin-top:26px;padding-top:22px;border-top:1px solid rgba(255,255,255,.18);font-size:12.5px;color:#B7CBF7;flex-wrap:wrap;gap:12px}
.fbot .legal{display:flex;gap:18px;flex-wrap:wrap}.fbot .legal a:hover{color:#fff}
.seals{display:flex;gap:10px}.seal{font-size:11px;color:#DCE7FF;border:1px solid rgba(255,255,255,.28);border-radius:8px;padding:6px 10px}

@media(max-width:980px){
  .util .wrap a:not(.cand){display:none}
  .collage{flex-wrap:wrap;gap:50px 14px;align-items:stretch;margin-inline:0;padding-inline:0}.pcard.a,.pcard.b,.pcard.c,.pcard.d{width:calc(50% - 7px);height:auto;aspect-ratio:4/5;margin-bottom:0;animation:none}
  .pcard.a .pchip,.pcard.b .pchip{left:-8px}.pcard.c .pchip,.pcard.d .pchip{right:-8px}.pchip{padding:8px 11px}
  .pcard.b .pchip,.pcard.d .pchip{top:auto;bottom:-30px}
  /* no viewport parking once the hero stops being a flex centre - the 112px
     of top padding it needed on desktop just strands the H1 down the page */
  .hero{min-height:0;display:block}
  .hero .wrap{padding:44px 32px 8px}
  .igrid,.trust,.vgrid,.why,.salescard{grid-template-columns:1fr}
  /* tabs wrap to two rows here. The 26px row gap stacks on top of each tab's
     16px padding-bottom and pushes the rows apart, so drop the row gap only. */
  .tabs{gap:0 22px}
  /* min-content of the form column computes ~7px wider than the card, and
     .salescard clips overflow, so the right edge of the form was being cut */
  .salescard>*{min-width:0}.proof{grid-template-columns:1fr;gap:20px}.proof div{border-right:0;padding:0}
  /* stacked: nothing stretches the photo any more, so give it a ratio back */
  .pvis{min-height:0;aspect-ratio:5/4}
  .cat{grid-template-columns:1fr 1fr}.frow{grid-template-columns:1fr}
  section.band,.sales{padding-top:68px;padding-bottom:68px}.fgrid{grid-template-columns:1fr 1fr}
}
/* laptop viewports - tighten the hero so both trust claims still clear the fold.
   threshold is 920px: above that the full-size collage still leaves room for the claims */
@media(min-width:981px) and (max-height:920px){
  .hero .wrap{padding:56px 32px 8px}
  .hero .sub{margin-top:14px}.hero .cta{margin-top:18px}
  .collage{margin-top:26px;padding:10px 28px 30px}
  .pcard.a{width:190px;height:214px}.pcard.b{width:204px;height:246px;margin-bottom:10px}
  .pcard.c{width:216px;height:223px}.pcard.d{width:197px;height:252px;margin-bottom:14px}
  .trusted .claims{margin-bottom:56px}
}
/* short windows - photos keep their size. the hero tightens around them and parks the block
   at the bottom, so what slack there is shows up as space above the headline */
@media(min-width:981px) and (max-height:790px){
  .hero{align-items:flex-end}
  .hero .wrap{padding:24px 32px 8px}
  .collage{margin-top:4px;padding-bottom:24px}
  .trusted .claims{margin-bottom:44px}
}
@media(max-width:640px){
  /* 32px of gutter on a 390px screen leaves 326px of column. 20px is the
     phone equivalent of the same optical margin. */
  .wrap{padding:0 20px}
  .hero .wrap{padding:20px 20px 4px}
  .hero h1{white-space:normal;font-size:clamp(29px,8.8vw,38px);font-weight:600}
  .rot{min-width:0;padding:0 11px;border-radius:11px}
  .hero .sub{font-size:16px;margin-top:12px;max-width:38ch}
  .hero .cta{margin-top:18px}
  .hero .cta .btn{width:100%;justify-content:center;height:52px;font-size:15.5px}

  /* collage - staggered two-column, not a grid. The big card and the small
     card swap sides between the rows and the right column starts lower, so
     the four photos read as a collage. Four equal halves read as a table.
     Widths are 56/44 minus half the gap, which leaves 2px of slack: at a
     flat 100% a rounding error wraps a card onto its own row.
     Row 2 runs both cards at the same height, as the reference does. Row 1
     does not, because the right card is the one carrying the drop.
     Whole collage has to finish inside the first screen. On a 390x844 phone
     Safari leaves ~690px, and the header, headline, lede and button spend
     ~340 of it. Anything that grows this block pushes row 2 under the fold,
     which is what the 56px row gap used to do. */
  .collage{gap:14px;padding:0 14px;margin-top:22px;align-items:flex-start;justify-content:flex-start}
  .pcard.a,.pcard.b,.pcard.c,.pcard.d{height:auto;margin-bottom:0}
  .pcard.a{width:calc(56% - 8px);aspect-ratio:1/1.01}
  /* b is portrait, not landscape. A 111px-tall crop left the driver in a
     strip with the chip over the rest of him. The drop is 22px rather than
     the reference's 45 because at this height a bigger one makes row 1
     taller than card a, and row 2 goes under the fold again. */
  .pcard.b{width:calc(44% - 8px);aspect-ratio:1/1.12;margin-top:22px}
  .pcard.c{width:calc(44% - 8px);aspect-ratio:1/1.045}
  .pcard.d{width:calc(56% - 8px);aspect-ratio:1/.814}
  /* f1 and f2 are 470x629 portraits. Cropped to near-square from the centre
     they lose 13% off the top, which takes the top of the head with it. */
  .pcard.a img,.pcard.c img{object-position:50% 10%}
  /* Chips sit INSIDE the photo and hang out sideways only. That is what the
     reference does, and it is why its two rows can sit 14px apart: a chip
     hanging below a card needs a 56px row gap to clear the row underneath,
     and that gap is what pushed row 2 off the first screen. Bottom of the
     card, never the top: the face is in the upper half of all four shots.
     max-width is the card plus both 12px overhangs, which is exactly what
     stops a chip reaching the card in the other column. Do not widen it.
     width:max-content is load-bearing. Absolutely positioned with only a
     left or a right offset, the chip shrink-to-fits against the card edge,
     so it wrapped to three lines on the two narrow cards and never reached
     its own max-width. max-content lets it size to the text first, then the
     max-width clamps it. */
  /* Chips straddle the bottom edge rather than sitting up inside the photo,
     and hang 16px out sideways. Both moves are to uncover the person: the
     chip now covers ~33px of card instead of ~55px.
     -6px is the whole budget for hanging below. The row gap is 14px, so a
     row-1 chip has 8px of clearance before it hits row 2. Do not increase
     it: the previous build hung chips 30px below, which needed a 56px row
     gap to clear, and that gap put row 2 under the fold.
     Only the padding, gap and icon shrink. Title stays at 11px - it is a
     label read at arm's length and 11px is the floor. */
  .pchip{padding:5px 8px;gap:6px;white-space:normal;width:max-content;max-width:calc(100% + 28px);bottom:-6px;top:auto}
  /* bottom is repeated on both of these because the 980 block sets
     `.pcard.b .pchip{bottom:-30px}` at higher specificity than a bare
     .pchip, so setting it once above does not reach b and d. */
  .pcard.a .pchip,.pcard.c .pchip{left:-16px;right:auto;bottom:-6px;top:auto}
  .pcard.b .pchip,.pcard.d .pchip{right:-16px;left:auto;bottom:-6px;top:auto}
  /* the longest title, "Address confirmed", runs 107px at 11.5px and the
     narrow card leaves it 106px. Everything here is buying back those few
     pixels so no chip title wraps: 24px of overhang is the hard ceiling,
     past it a chip reaches the card in the other column. */
  .pchip b{font-size:11px}.pchip span{font-size:10px}
  /* 22px, up from 17. Same reason as the desktop rule: these are the
     generated check icons now and 17px turned them to mush. The chip is
     anchored `bottom:-6px` here, so it grows UPWARD and the collage footprint
     and the fold calibration are unchanged - verified, not assumed. It does
     cover a little more of the card. */
  .pchip .ic{width:22px;height:22px;font-size:11px}

  /* the trust claim is a 34px display size built for a 1180px column */
  .trusted .claims .a{font-size:26px}
  .trusted .claims{margin-bottom:34px}
  /* 44px of card padding leaves 245px of form on a 390px screen */
  .sales-l{padding:34px 24px}
  .sales-r{padding:28px 24px}
  .tile{padding:22px 14px;gap:14px}
  .tile i{width:54px;height:54px;font-size:27px}
  .tile b{font-size:15.5px;min-height:42px}
  .svc .cta .btn,.ctaband .btn{width:100%;justify-content:center}
  /* 24px of link is not a thumb target. Padding grows the hit area to 44px,
     margin gives back what the padding added, so it does not move. */
  .igrid .lk{padding:10px 0;margin-top:16px}
}
@media(max-width:520px){.login{display:none}}
@media(prefers-reduced-motion:reduce){.reveal{opacity:1;transform:none;transition:none}.marquee,.pcard{animation:none}}

/* ---------------------------------------------------------------
   Interior pages (contact, about, trust, candidates, legal).
   Homepage sections live above. Same tokens, no new colours.
   --------------------------------------------------------------- */
.phero{padding:66px 0 40px;border-bottom:1px solid var(--line);background:linear-gradient(180deg,var(--wash-2),var(--paper))}
.phero .eyebrow{font-size:12px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--acc);margin-bottom:14px}
.phero h1{font-size:clamp(32px,4.6vw,50px);max-width:20ch}
.phero .lede{margin-top:16px;font-size:18px;color:var(--soft);max-width:56ch}
/* Hero with art beside the copy. Opt-in: `.phero.vis`, and the art goes in a
   .hvis after the .cta. Every other page keeps the single column it has now.

   THE ART IS NOT A CARD. No border, no radius, no shadow, no plate. Give it
   one and it becomes a picture pinned to the page instead of part of it.
   Two rules do the blending and both are needed:

   - `mix-blend-mode:multiply` drops the artwork's own pale ground out against
     the hero wash, so the image has no rectangle. Same trick `.cert img` uses
     on the trust band. It only works on art drawn on white or near-white; a
     photograph goes muddy under it, so hero art has to be the flat pale-ground
     illustration family (`images/why-w*.jpg`), not a photo.
   - the mask feathers all four edges to zero, so even the couple of percent of
     tint multiply leaves behind has nowhere to form an edge. It is TWO linear
     gradients intersected, not one radial. A radial big enough to keep the
     middle of the art opaque leaves its edge midpoints opaque too - measured
     on this page at 112%/106%, all four sat at 0.44 of the gradient and the
     rectangle was still visible. Crossed linears take each edge to 0 on its
     own axis while the centre stays at full strength.
     `mask-composite:intersect` is the standard; `-webkit-mask-composite:
     source-in` is the same thing for Safari before 15.4 and has to be spelt
     differently. Keep both.

   The copy column drops its own measure because the grid already gives it one.
   Below 900 the art stacks under the buttons at half strength: at phone width
   it is decoration, and full-strength art there pushes the CTA off the fold. */
.phero.vis>.wrap{display:grid;grid-template-columns:1.04fr .96fr;gap:44px;align-items:center}
.phero.vis h1{max-width:14ch}
.phero.vis .lede{max-width:46ch}
.hvis img{width:100%;height:auto;mix-blend-mode:multiply;
  -webkit-mask-image:linear-gradient(to right,transparent 0,#000 15%,#000 85%,transparent 100%),linear-gradient(to bottom,transparent 0,#000 13%,#000 87%,transparent 100%);
  mask-image:linear-gradient(to right,transparent 0,#000 15%,#000 85%,transparent 100%),linear-gradient(to bottom,transparent 0,#000 13%,#000 87%,transparent 100%);
  -webkit-mask-composite:source-in;mask-composite:intersect}
@media(max-width:900px){
  .phero.vis>.wrap{grid-template-columns:1fr;gap:18px}
  .phero.vis h1,.phero.vis .lede{max-width:none}
  /* Owner's rule, 31 Aug 2026: drop the hero art once it stacks - it read as a
     large decorative block under the copy on phones. Desktop keeps it. */
  .hvis{display:none}
}
/* Centred hero is the DEFAULT. Owner's rule, 18 Aug 2026: a hero with no art
   beside the copy is centred; a hero WITH art beside it stays left. Two hero
   kinds opt out below: `.phero.vis` (art beside the copy) and `.phero.art`
   (article masthead, kept left to sit with the left-aligned article body).
   `.phero.mid` still centres, it is just the default now.

   `text-align` alone does NOT centre this. `.phero h1` is 20ch and `.phero
   .lede` is 56ch, so both are narrow blocks sitting hard against the left of
   the wrap: the text centres inside a box that is itself off-centre. The auto
   margins are what move the boxes. Same reason `.btnrow` needs its own rule -
   it is a flex row and ignores text-align. */
.phero>.wrap{text-align:center}
.phero h1,.phero .lede{margin-inline:auto}
/* Hero button rows: some parts write `.cta`, some `.cta.btnrow`. `.btnrow` has
   flex+gap; bare `.cta` had none, so its two buttons touched (retail, staffing,
   it-ites, rcu ...). Give `.phero .cta` the same flex+gap. Check pages (.svc
   .cta) and the homepage (.hero .cta) already have it and are not .phero. */
.phero .cta{display:flex;gap:12px;flex-wrap:wrap;margin-top:18px}
.phero .cta,.phero .btnrow{justify-content:center}
.phero.vis>.wrap,.phero.art>.wrap{text-align:left}
.phero.vis h1,.phero.vis .lede,.phero.art h1,.phero.art .lede{margin-inline:0}
.phero.vis .cta,.phero.vis .btnrow,.phero.art .cta,.phero.art .btnrow{justify-content:flex-start}
.pbody{padding:54px 0 74px}
.pgrid{display:grid;grid-template-columns:1fr 320px;gap:56px;align-items:start}
.prose{max-width:70ch}
.prose h2{font-size:26px;margin:38px 0 12px}
.prose h2:first-child{margin-top:0}
.prose h3{font-size:18px;margin:26px 0 8px;color:var(--ink)}
.prose p{margin-bottom:14px;font-size:16px;line-height:1.72}
.prose ul,.prose ol{margin:0 0 16px 20px}
.prose li{margin-bottom:8px;font-size:16px;line-height:1.66}
.prose a{color:var(--acc);text-decoration:underline;text-underline-offset:3px}
.prose strong{color:var(--ink);font-weight:600}
.prose table{width:100%;border-collapse:collapse;margin:8px 0 20px;font-size:14.5px}
.prose th,.prose td{border:1px solid var(--line);padding:10px 12px;text-align:left;vertical-align:top}
.prose th{background:var(--wash-2);font-weight:600;color:var(--ink)}
.aside{position:sticky;top:96px;border:1px solid var(--line);border-radius:16px;padding:22px;background:var(--surface)}
.aside h4{font-size:13px;letter-spacing:.04em;text-transform:uppercase;color:var(--soft);margin-bottom:12px}
.aside p{font-size:14px;color:var(--soft);margin-bottom:14px;line-height:1.6}
.aside .btn{width:100%;justify-content:center}
.updated{font-size:13px;color:var(--mute);margin-top:8px}
.draft{border:1px solid #E0B400;background:#FFF9E6;border-radius:12px;padding:14px 16px;margin-bottom:26px;font-size:14px;color:#6B5200}
.draft b{color:#4A3900}
.cgrid{display:grid;grid-template-columns:1fr 1fr;gap:44px;align-items:start}
.cblock{border:1px solid var(--line);border-radius:16px;padding:26px;background:var(--surface)}
.cblock h3{font-size:17px;margin-bottom:6px}
.cblock p{font-size:14.5px;color:var(--soft);line-height:1.65;margin-bottom:14px}
.cblock a.big{font-size:19px;font-weight:600;color:var(--acc);display:block;margin-bottom:4px}
.vals{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;margin-top:8px}
.val{border:1px solid var(--line);border-radius:14px;padding:22px;background:var(--surface)}
.val i{color:var(--acc);font-size:22px;margin-bottom:10px;display:block}
.val h3{font-size:16px;margin-bottom:6px}
.val p{font-size:14px;color:var(--soft);line-height:1.6}
@media(max-width:900px){
  .pgrid,.cgrid{grid-template-columns:1fr;gap:32px}
  .aside{position:static}
  .vals{grid-template-columns:1fr}
}

/* ---------------------------------------------------------------
   Per-check service pages, plus the live-verification mockup that
   runs in every service hero and in the homepage middle section.
   The mockup is HTML, not an image: it stays sharp at any size and
   its colours stay in this file with everything else.
   --------------------------------------------------------------- */

/* live verification mockup */
.vwrap{position:relative;padding:22px 20px 26px}
.vdash{display:grid;grid-template-columns:.82fr 1.18fr;background:var(--surface);border:1px solid var(--line);border-radius:20px;overflow:hidden;overflow:clip;box-shadow:0 40px 84px -48px rgba(22,35,63,.6)}
/* flex column so .vfoot can be pushed to the bottom: side by side, the rail
   runs ~200px shorter than the detail panel and grid stretches it regardless */
.vl{padding:24px 22px;border-right:1px solid var(--line);background:var(--wash-2);display:flex;flex-direction:column}
.vl h3{font-size:16.5px;letter-spacing:-.02em;margin-bottom:12px}
.vfoot{margin-top:auto;display:flex;justify-content:space-between;gap:12px;padding-top:16px;font-size:11.5px;color:var(--soft)}
.vrow{display:flex;align-items:center;justify-content:space-between;gap:14px;padding:11px 0;border-bottom:1px solid var(--line)}
.vrow:last-child{border-bottom:0}
.vrow b{font-size:13.5px;font-weight:600;color:var(--acc)}
.vok{display:inline-flex;align-items:center;gap:5px;font-size:12.5px;font-weight:600;color:var(--ok);white-space:nowrap}
.vok i{font-size:15px}
.vr{padding:22px 24px}
.vtabs{display:flex;align-items:center;gap:22px;border-bottom:1px solid var(--line);margin-bottom:16px}
.vtabs span{padding-bottom:11px;font-size:14.5px;font-weight:600;color:var(--mute)}
.vtabs span.on{color:var(--acc);border-bottom:2px solid var(--acc);margin-bottom:-1px}
.vlive{margin-left:auto;margin-bottom:11px;display:inline-flex;align-items:center;gap:7px;font-size:12px;font-weight:600;color:var(--ok);background:var(--acc-soft);border-radius:999px;padding:5px 11px}
.vlive::before{content:"";width:7px;height:7px;border-radius:50%;background:var(--ok)}
.vcard{border:1px solid var(--line);border-radius:14px;padding:16px;display:flex;gap:14px;align-items:flex-start}
.vcard .vmeta{flex:1;min-width:0}
.vcard .k{font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--mute);margin-bottom:5px}
.vcard .n{font-size:19px;font-weight:700;color:var(--acc);letter-spacing:-.02em}
.vcard .vmeta p{font-size:12.5px;color:var(--soft);margin-top:5px;line-height:1.5}
.vcard img{width:62px;height:74px;border-radius:9px;object-fit:cover;flex:none}
.vgridf{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:12px}
.vf{border:1px solid var(--line);border-radius:11px;padding:11px 13px;min-width:0}
.vf .k{font-size:11.5px;color:var(--soft);margin-bottom:3px}
.vf .v{font-size:13.5px;font-weight:600;color:var(--ink);letter-spacing:-.01em;overflow-wrap:anywhere}
.vplace{margin-top:10px;background:var(--wash);border-radius:10px;padding:9px 13px;font-size:11.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--acc)}
/* the two status chips hang outside the card, as in the reference shot */
.fchip{position:absolute;z-index:3;display:flex;align-items:center;gap:9px;background:var(--surface);border:1px solid var(--line-2);border-radius:12px;padding:9px 13px;font-size:12.5px;font-weight:600;color:var(--ink);white-space:nowrap;box-shadow:0 10px 26px -14px rgba(0,34,68,.4)}
.fchip i{width:24px;height:24px;border-radius:7px;background:var(--acc-soft);color:var(--acc);display:grid;place-items:center;font-size:14px;flex:none}
/* the chips overhang the top and bottom edges, not the sides: the card is
   nearly as wide as its column, so a side-hung chip sits on top of the data */
.fchip.tl{top:0;left:-14px}
.fchip.br{bottom:2px;right:-14px}
@media(max-width:780px){.fchip{display:none}}
/* below 900 there is no room for two columns, so it stacks and runs tighter -
   at full padding the stacked card is nearly twice the height */
@media(max-width:900px){
  .vdash{grid-template-columns:1fr}
  .vl{border-right:0;border-bottom:1px solid var(--line);padding:18px 20px}
  .vl h3{margin-bottom:8px}
  .vrow{padding:9px 0}
  .vr{padding:18px 20px}
  /* narrow column: keep the tab label and status pill each on one line so
     the pill stays compact and the tab underline stays anchored */
  .vtabs{gap:10px;margin-bottom:14px}
  .vtabs span{white-space:nowrap}
  .vtabs .vlive{font-size:11px;padding:4px 9px;gap:6px}
  .vcard{padding:13px}
  .vcard img{width:54px;height:64px}
  .vgridf{grid-template-columns:1fr;gap:8px;margin-top:10px}
  .vf{padding:9px 11px}
  .vplace{margin-top:8px;padding:7px 11px}
}

/* service hero - centred copy, mockup full width beneath it.
   Side by side, the mockup gets a ~500px column and has to stack internally,
   which doubles its height and leaves the H1 stranded mid-screen. Giving it
   the full 1180 lets it stay horizontal, the way the reference does it. */
.svc{padding:36px 0 8px;background:linear-gradient(180deg,var(--wash-2),var(--paper))}
/* The breadcrumb and the check-type badge used to sit above the H1 and were
   removed on the owner's instruction. This padding is exactly the height they
   occupied (42+16 crumb, 52+16 badge), because the H1 was to stay put. */
/* 36px of .svc padding + 76 = 112, the same distance the homepage H1 sits
   below the top of its hero. Load-bearing: the breadcrumb and badge that used
   to fill this space are gone, so without it the H1 rides up. */
/* 54rem, not 50: at the homepage's 54px the longest headline ("Previous
   employment verification") measures 792px and would wrap in an 800px box.
   The lede is capped separately at 58ch, so widening this does not stretch it. */
.svc-copy{text-align:center;max-width:54rem;margin:0 auto;padding-top:76px}
/* identical treatment to the homepage "Verify __ with certainty." headline:
   same clamp, same weight 500. Do not restore the h1 default 700 here. */
.svc h1{font-size:clamp(28px,4.2vw,54px);letter-spacing:-.03em;font-weight:500}
/* no min-width override: the pill uses the base 7.2em, exactly as on the
   homepage. "Driving Licence" is the one word that grows it past that. */
.svc .lede{margin:16px auto 0;font-size:17.5px;color:var(--soft);max-width:58ch}
.svc .cta{display:flex;gap:12px;margin-top:24px;flex-wrap:wrap;justify-content:center}
.svc .vwrap{margin-top:34px}
@media(max-width:900px){.svc-copy{padding-top:44px}}

/* what's verified, and the sources behind it */
.fields{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-top:40px}
.fld{border:1px solid var(--line);border-radius:16px;padding:24px;background:var(--surface)}
.fld i{color:var(--acc);font-size:24px;display:block;margin-bottom:12px}
.fld b{display:block;font-size:16px;font-weight:700;color:#002D72;letter-spacing:-.01em}
.fld p{font-size:14.5px;color:var(--soft);margin-top:8px;line-height:1.55}
.srcnote{margin-top:26px;border-left:3px solid var(--acc);padding:4px 0 4px 18px;font-size:15.5px;color:var(--body);line-height:1.65}
.srcnote b{color:var(--ink)}

/* how it works - length matches the real fulfilment, never padded */
.steps{counter-reset:s;display:grid;gap:13px;margin-top:40px}
.step{counter-increment:s;display:flex;gap:18px;align-items:flex-start;background:var(--surface);border:1px solid var(--line);border-radius:16px;padding:22px 24px}
.step::before{content:counter(s,decimal-leading-zero);font-size:20px;font-weight:800;color:var(--acc);letter-spacing:-.03em;min-width:38px;line-height:1.35}
.step b{display:block;font-size:16.5px;font-weight:700;color:var(--ink)}
.step p{font-size:14.5px;color:var(--soft);margin-top:6px;line-height:1.55}
/* Pipeline: stages on a rail, then the ways a case can close.
   CODED, not a generated picture, and that is the decision rather than a
   shortcut. It carries labels, which a diffusion model cannot spell; it reads
   the tokens, so it re-colours with the theme instead of drifting off it; and
   each stage is a real link into the section that explains it, so the diagram
   is the page's jump nav as well as its map. An image could do none of that.
   No new colours: positive, negative and refer are told apart by the word and
   the glyph, because this palette has no green or red and is not getting one. */
.flow{display:grid;grid-template-columns:repeat(4,1fr);margin-top:42px}
.flow .fs{position:relative;padding:0 14px;text-align:center;color:inherit}
/* the rail is drawn per stage and clipped to half width on the end ones, so it
   stops dead at the first and last disc instead of running off the row */
.flow .fs::before{content:"";position:absolute;top:24px;left:0;right:0;height:2px;background:var(--line-2)}
.flow .fs:first-child::before{left:50%}
.flow .fs:last-child::before{right:50%}
/* --foot, not --elec: a numbered step marker is a plate, not an action. It
   read --elec only while the two held the same navy. See :root. */
.flow .fd{position:relative;z-index:1;width:50px;height:50px;margin:0 auto 15px;border-radius:999px;background:var(--foot);color:var(--paper);display:grid;place-items:center;font-size:23px}
.flow b{display:block;font-size:16px;color:var(--ink)}
.flow p{font-size:14px;color:var(--soft);margin-top:7px;line-height:1.55}
a.fs:hover b{color:var(--acc)}
.flowout{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;margin-top:36px}
.fo{border:1px solid var(--line);border-radius:14px;background:var(--surface);padding:20px 22px}
.fo i{font-size:21px;color:var(--acc);display:block;margin-bottom:11px}
.fo b{display:block;font-size:16px;color:var(--ink)}
.fo p{font-size:14px;color:var(--soft);margin-top:6px;line-height:1.55}
/* at two-up the rail has to re-end on the second and third stages, or it runs
   out of the right of row one and in from the left of row two */
@media(max-width:820px){
  .flow{grid-template-columns:1fr 1fr;row-gap:34px}
  .flow .fs:nth-child(2)::before{right:50%}
  .flow .fs:nth-child(3)::before{left:50%}
  .flowout{grid-template-columns:1fr}
}
@media(max-width:520px){
  .flow{grid-template-columns:1fr;row-gap:24px}
  .flow .fs::before{display:none}
}

/* the .tat block - a big navy plate carrying one exact turnaround number - was
   removed on 1 Aug 2026. The owner does not want a turnaround declared on the
   site. Turnaround is prose now, and it says the number is set when the package
   is scoped. Do not rebuild this component. */

/* consent note */
.dpdp{display:flex;gap:14px;align-items:flex-start;border:1px solid var(--line-b);background:var(--wash);border-radius:14px;padding:20px 22px;margin-top:40px}
.dpdp i{color:var(--acc);font-size:22px;flex:none;margin-top:1px}
.dpdp p{font-size:14.5px;color:var(--body);line-height:1.6}
.dpdp a{color:var(--acc);text-decoration:underline;text-underline-offset:3px}

/* related checks - never the page's own check */
.rel{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-top:40px}
.relc{display:block;border:1px solid var(--line);border-radius:16px;padding:24px;background:var(--surface);transition:transform .5s cubic-bezier(.32,.72,0,1),box-shadow .3s}
.relc:hover{transform:translateY(-3px);box-shadow:0 18px 36px -22px rgba(22,35,63,.3)}
.relc i{color:var(--acc);font-size:24px;display:block;margin-bottom:12px}
.relc b{display:block;font-size:16.5px;color:#002D72;font-weight:700}
.relc p{font-size:14px;color:var(--soft);margin-top:7px;line-height:1.5}
.relc .go{display:inline-flex;align-items:center;gap:7px;color:var(--acc);font-size:14px;font-weight:600;margin-top:14px}

/* Icon beside the heading, not above it. Owner's rule, 18 Aug 2026: every icon
   card whose icon sat on its own line above the title becomes a two-column
   grid - icon in a left gutter aligned to the title, title and body in the
   right column. Placed AFTER each component's own `i{margin-bottom}` rule so
   the margin reset wins the equal-specificity tie. `.typ` (centred cards) and
   the homepage (`.tile`/`.pchip`, different components) are left alone. */
.fld,.val,.fo,.relc{display:grid;grid-template-columns:auto 1fr;column-gap:13px;align-items:center}
.fld>i,.val>i,.fo>i,.relc>i{grid-column:1;grid-row:1;margin:0}
.fld>:not(i),.val>:not(i),.fo>:not(i),.relc>:not(i){grid-column:2;min-width:0}

/* FAQ - native <details>, no script */
.faq{margin-top:40px;border-top:1px solid var(--line)}
.faq details{border-bottom:1px solid var(--line)}
.faq summary{list-style:none;cursor:pointer;padding:22px 44px 22px 0;font-size:16.5px;font-weight:600;color:var(--ink);position:relative}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:"+";position:absolute;right:8px;top:18px;font-size:25px;font-weight:400;color:var(--acc);line-height:1}
.faq details[open] summary::after{content:"\2013"}
.faq .a{padding:0 44px 24px 0;margin-top:-4px;font-size:15.5px;color:var(--body);line-height:1.7}
.faq .a a{color:var(--acc);text-decoration:underline;text-underline-offset:3px}

/* closing band */
.ctaband{background:var(--foot);border-radius:24px;padding:48px 44px;text-align:center;margin-top:64px}
.ctaband h2{color:#fff;font-size:clamp(24px,3.2vw,34px)}
.ctaband p{color:#A5B3D8;font-size:16px;margin-top:12px;max-width:46ch;margin-inline:auto}
.ctaband .btn{margin-top:24px}
/* --elec is the footer navy now and this band IS that navy, so a primary
   button here would be the same colour as the panel it sits on and vanish.
   It inverts instead: white plate, navy type. This is the ONLY place on the
   site the primary button inverts, and it is the reason --elec and --foot
   are kept as two names. Runs on 10 check pages. */
.ctaband .btn-primary{background:#fff;color:var(--foot);box-shadow:0 2px 4px rgba(0,0,0,.16),0 12px 30px -10px rgba(0,0,0,.42)}
.ctaband .btn-primary:hover{background:#E8EDFB}

/* variable module: two real methods side by side (address) */
.compare{display:grid;grid-template-columns:1fr 1fr;gap:18px;margin-top:40px}
.cmp{border:1px solid var(--line);border-radius:18px;padding:28px;background:var(--surface)}
.cmp .tag{display:inline-block;font-size:11.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--acc);background:var(--acc-soft);border-radius:999px;padding:6px 12px;margin-bottom:14px}
.cmp h3{font-size:20px;letter-spacing:-.02em}
.cmp>p{font-size:14.5px;color:var(--soft);margin-top:10px;line-height:1.55}
.cmp ol{list-style:none;counter-reset:c;margin:18px 0 0;display:grid;gap:12px}
.cmp li{counter-increment:c;display:flex;gap:13px;font-size:14.5px;color:var(--body);line-height:1.5}
.cmp li::before{content:counter(c);flex:none;width:24px;height:24px;border-radius:50%;background:var(--acc-soft);color:var(--acc);font-size:12.5px;font-weight:700;display:grid;place-items:center}

/* variable module: genuine product variants (identity) */
.types{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-top:40px}
.typ{border:1px solid var(--line);border-radius:16px;padding:24px;background:var(--surface);text-align:center}
.typ i{width:56px;height:56px;border-radius:16px;background:var(--acc-soft);color:var(--acc);display:grid;place-items:center;font-size:27px;margin:0 auto 14px}
.typ b{display:block;font-size:16px;color:#002D72;font-weight:700}
.typ p{font-size:13.5px;color:var(--soft);margin-top:7px;line-height:1.5}

@media(min-width:901px) and (max-width:1080px){.types{grid-template-columns:1fr 1fr}}
@media(max-width:900px){.fields,.rel,.compare,.types{grid-template-columns:1fr}.ctaband{padding:36px 26px}}

/* client logo strip. Every PNG is padded to the same 44px canvas height and
   sized inside it by optical weight (tools/build-client-logos.py), so a
   square mark and a long wordmark read as equal. That padding is what lets
   `height` below control the strip: without it every logo renders 44px tall
   and the balancing is lost. Duration follows strip length, not taste - set
   it from the travel distance so both breakpoints scroll at the same 45px/s.
   Desktop travels ~4500px per loop, mobile ~3200px. No hover pause: it reads
   as the strip freezing on click, and on touch the state sticks. */
.marquee{gap:56px;align-items:center;animation-duration:100s}
.marquee img{height:44px;width:auto;flex:none}
/* logos fade in on the right and out on the left instead of appearing at a
   hard edge. The mask has to sit on this wrapper, not on .marquee: .marquee
   is ~9000px wide and animated, so a mask on it would scroll along with the
   logos instead of staying put at the viewport edges.
   The ramp is eased, not linear - the extra stops hold the logo faint for
   most of the fade so it surfaces well inside the edge rather than at it.
   Fade runs 16% of the width each side, 22% on mobile where 16% is too
   short a distance to read as a fade at all. 28%/34% was tried and rejected
   as too heavy: it dissolved three logos deep on each side. */
/* The window the strip scrolls through is a container, not the viewport. Left
   uncapped it ran the full width, so on a wide screen a logo entered 400px
   outside the page content, crossed 2200px of empty white and left again, and
   the strip read as unrelated to everything above it. Capped to the wide
   container it matches the header and the section below it, and about twelve
   logos are in view at once. The mask still fades at the window edges, which
   are now the content edges. */
.mrq{max-width:var(--maxw-wide);margin-inline:auto;overflow:hidden;overflow:clip;
  -webkit-mask-image:linear-gradient(90deg,transparent 0,rgba(0,0,0,.08) 5%,rgba(0,0,0,.32) 9%,rgba(0,0,0,.7) 13%,#000 16%,#000 84%,rgba(0,0,0,.7) 87%,rgba(0,0,0,.32) 91%,rgba(0,0,0,.08) 95%,transparent 100%);
  mask-image:linear-gradient(90deg,transparent 0,rgba(0,0,0,.08) 5%,rgba(0,0,0,.32) 9%,rgba(0,0,0,.7) 13%,#000 16%,#000 84%,rgba(0,0,0,.7) 87%,rgba(0,0,0,.32) 91%,rgba(0,0,0,.08) 95%,transparent 100%)}
@media(max-width:700px){.marquee{gap:36px;animation-duration:72s}.marquee img{height:34px}
  .mrq{-webkit-mask-image:linear-gradient(90deg,transparent 0,rgba(0,0,0,.08) 7%,rgba(0,0,0,.32) 13%,rgba(0,0,0,.7) 18%,#000 22%,#000 78%,rgba(0,0,0,.7) 82%,rgba(0,0,0,.32) 87%,rgba(0,0,0,.08) 93%,transparent 100%);
    mask-image:linear-gradient(90deg,transparent 0,rgba(0,0,0,.08) 7%,rgba(0,0,0,.32) 13%,rgba(0,0,0,.7) 18%,#000 22%,#000 78%,rgba(0,0,0,.7) 82%,rgba(0,0,0,.32) 87%,rgba(0,0,0,.08) 93%,transparent 100%)}}



/* ---------------------------------------------------------------
   HOMEPAGE THEME - index.html only, via `body.theme`.

   What survives here after the owner's review on 31 Jul 2026 is
   narrow, and that is the point. The palette in effect is:

     sea  #036695   the accent. Replaces #004687 on links, chips,
                    icons, tabs, tab rules and the check tiles.
     sky  #01a2d3   never drawn directly. Only its tints, --sky-1 to
                    --sky-4, on washes, hairlines and hovers.

   THERE IS NO YELLOW ON THIS PAGE. #FFEA00 / #FFF04D was tried three
   ways on 31 Jul 2026 - as the button colour, as the footer column
   headings, as the sales-card ticks and one hero chip tile - and the
   owner rejected every one of them. Do not reintroduce it anywhere
   without being asked for it by name.

   These are NOT overridden here. They are set in :root and apply to
   every page, not just this one:
     - `.btn-primary`  --elec, the logo blue, since 11 Aug 2026
     - `.rot`          --elec type on white, #D9D9F8 border
     - `.pchip .ic`    --elec, so the tile cannot drift from the button
     - `.band.dark`, `.sales-l`, `footer`   all --foot #191970
   `.nav-links` top-level links stay #1A1A1A, black not blue. That one
   was asked for twice and is not part of the navy change.
   --------------------------------------------------------------- */
body.theme{
  --sea:#036695;--sky:#01a2d3;
  --sea-d:#02405E;
  /* the existing semantic tokens, re-pointed. Every rule in this file
     already reads these, so most of the page re-colours from here.
     --elec and --foot are deliberately NOT re-pointed: --elec is the logo
     blue and --foot the navy in :root, both site-wide, not homepage
     decisions. Re-pointing either here would fork the homepage off. */
  --ink:#02405E;--body:#39505A;--soft:#4E6B79;--mute:#7C97A4;
  --acc:#036695;--acc-2:#02405E;--acc-soft:#E7F4FB;--acc-3:#01a2d3;--ok:#036695;
  --wash:#E7F4FB;--wash-2:#F2FAFD;
  --line:#DCEBF4;--line-2:#C6E3F2;--line-b:#CFE7F4;
}
/* hover tints, so the nav does not fall back to the old blue-grey
   #EEF4FA against the sky-tinted surfaces around it. The resting
   colour of a nav link is untouched - see the header note. */
body.theme .nav-links>li>a:hover,body.theme .mega .col a:hover{background:var(--sky-2)}
/* the mega-menu column headings, the drawer's copies of them, and the
   two industry sub-headings. Near-black, which is a neutral and
   spends no palette colour. The owner's explicit call. */
body.theme .mega .col h5,body.theme .dsub h5,body.theme .isub{color:#1A1A1A}
body.theme .dscrim{background:rgba(2,64,94,.5)}
/* the section washes hardcode the old wash rgb, so they have to be
   re-tinted with it or the fades run a different hue to --wash */
body.theme .trusted{background:linear-gradient(180deg,rgba(231,244,251,0) 0%,rgba(231,244,251,.5) 55%,var(--wash) 100%)}
body.theme .band.tint{background:linear-gradient(180deg,var(--wash) 0%,rgba(231,244,251,.45) 62%,transparent 100%)}
body.theme .band.tint-in{background:linear-gradient(180deg,transparent 0%,rgba(231,244,251,.45) 45%,var(--wash) 100%)}
body.theme .band.tint-hold{background:linear-gradient(180deg,var(--wash) 0%,rgba(231,244,251,.5) 70%,transparent 100%)}
body.theme .pvis::after{background:linear-gradient(180deg,transparent 55%,rgba(3,102,149,.12))}
/* check names and Why GVS card titles - both were hardcoded off-palette */
body.theme .tile b,body.theme .wcard h3{color:var(--ink)}
/* portal chrome. The bar keeps the sky tint. THE THREE DOTS ARE BACK TO THE
   macOS TRAFFIC LIGHTS - red, amber, green - on the owner's instruction,
   31 Jul 2026. That reverses the earlier call to tint them sky so the window
   did not borrow another brand's colours; the owner asked for the traffic
   lights by name. There is no rule here to do it: the base .pshot rule
   already carries #FF5F57 / #FEBC2E / #28C840 and this block simply no
   longer overrides it. Do not re-add a `span` override to "restore" them. */
body.theme .pshot .bar{background:var(--sky-1)}
body.theme .vc{background:linear-gradient(180deg,#fff 0%,rgba(1,162,211,.05) 55%,rgba(1,162,211,.14) 100%)}

/* THE TRUST BAND IS A BOX, NOT A FULL-BLEED STRIPE.
   Reversing the site's usual rule, on the owner's instruction after
   they zoomed the browser out: every other band on the page keeps its
   content in a 1180px column, so a navy that ran the full width of a
   2000px window read as a stripe painted across an otherwise narrow
   page. The colour now stops where the section's own content stops.
   The background therefore moves OFF the section and ONTO its .wrap,
   which is what carries the 1180px cap. The section keeps only the
   outer breathing room. Do not put `background` back on
   `section.band.dark` - that is the bug this fixes.
   NOTE: `footer` and the pale `.tint` washes are still full-bleed. */
body.theme section.band.dark{background:none;padding:34px 0}
/* 36px of side padding, not 56 or 48. Boxing the band is what broke
   the certification row: a .wrap normally pads 32px, so every pixel
   added here comes straight out of the 644px the three logos need,
   twice over. 56 left 617px and 48 left 638. 36 leaves 661. Raise it
   and the third logo drops to its own line. */
/* asymmetric on purpose: 72 top / 104 bottom. The owner asked for the
   heading 'a little up' after asking for a taller card, so the top pad
   came down 32px while the bottom held. The heading gap below absorbs
   16 of that back so the logo row does not ride up with it. */
body.theme .band.dark>.wrap{background:var(--foot);border-radius:24px;padding:72px 36px 104px}
/* the copy inside it. Brighter than the original #C7D2EE / #A5B3D8,
   which the owner could not read, but back in the navy tint family
   rather than the sky one now the panel is #191970 again. 13.8:1
   and 12.4:1 on navy. */
body.theme .band.dark .lede{color:#EDF2FF}
body.theme .trust p{color:#DCE4FA}
/* TRUST BAND LAYOUT: heading centred across the panel, copy left,
   certifications right. Set 31 Jul 2026 on the owner's instruction,
   against the AuthBridge reference they supplied.

   The H2 lives OUTSIDE `.trust-l` in the markup - that is the whole
   trick. Inside it, the heading is stuck in the same column as the
   paragraphs and cannot centre across the panel without dragging the
   copy centre with it. `.trust-row` then holds the two columns.

   `1fr auto`, not two fr units: the certification column is sized by
   its own contents (3 x 150 + 2 x 26 = 502px) and the copy takes what
   is left, 550px of the panel's 1108. Give the certs an fr and the row
   stops being self-correcting when a logo changes size. */
body.theme .trust{display:block;text-align:left}
body.theme .trust-h{max-width:none;text-align:center;margin-bottom:72px}
body.theme .trust-row{display:grid;grid-template-columns:1fr auto;gap:56px;align-items:center}
body.theme .trust-l{max-width:34rem;margin:0}
body.theme .trust p{max-width:none;margin-left:0;margin-right:0}

/* Certifications: three equal circles, no plate, no disc.
   The three source PNGs were re-cut on 31 Jul 2026 with their flat
   backgrounds keyed out to real alpha - white for ISO 9001 and MSME,
   BLACK for ISO 27001, which arrived flattened onto black rather than
   white. They are RGBA now, so nothing here needs a blend mode.
   The circle is the CONTAINER, not the artwork: the two ISO marks are
   circular already, MSME is a wide lockup, and a shared container is
   what makes the three read as a set. */
body.theme .certs{justify-content:flex-end;gap:26px;margin:0}
body.theme .cert{width:150px;height:150px;border-radius:50%;background:none;padding:0;display:grid;place-items:center;overflow:hidden}
body.theme .cert img{width:100%;height:100%;object-fit:contain;filter:none;mix-blend-mode:normal}
/* MSME sits on its OWN white disc, on the owner's instruction: the navy
   lockup was unreadable on the navy even with the halo, so it gets the
   same treatment ISO 27001 already has by design - a self-contained
   circular badge. Same 150px circle as the other two. No .cert-glow on
   it: a white halo on a white disc does nothing.

   6px of padding, not 12. The circle still clips the image, and MSME is
   a real 4:3 lockup rather than a circle drawn in a square canvas, so
   the geometry is a hard constraint: its artwork box is 412x301 of
   500x375, which puts the ends of the "MINISTRY OF MICRO, SMALL &
   MEDIUM ENTERPRISES" sub-line at the bounding-box corners. Those
   corners sit at (150-2P)/2 x 1.0205 from centre against a 75px radius,
   so P must be at least 1.5. 12px was safe but left the mark looking
   small beside the other two; 6px lands the corners at 70.5px, which is
   4.5px clear, and is as large as the lockup can go. Do not take it
   to 0. */
body.theme .cert-disc{background:#fff;padding:6px}
body.theme .cert-glow{padding:5px}
body.theme .cert.cert-glow img{filter:drop-shadow(0 0 1.5px rgba(255,255,255,.95)) drop-shadow(0 0 5px rgba(255,255,255,.45))}
/* the 104px of card padding is a desktop figure. Once the row stacks, the
   card is already twice as tall and 104 top and bottom reads as a hole. */
@media(max-width:900px){
  body.theme .band.dark>.wrap{padding:64px 26px;border-radius:20px}
  body.theme .trust-h{margin-bottom:34px}
  body.theme .trust-row{grid-template-columns:1fr;gap:36px}
  body.theme .trust-l{max-width:none}
  body.theme .certs{justify-content:center}
}
@media(max-width:640px){
  body.theme .band.dark>.wrap{padding:44px 20px;border-radius:16px}
  body.theme .cert{width:116px;height:116px}
  body.theme .certs{gap:16px}
}

/* CHECK CATALOGUE - full-bleed navy. Homepage only, 31 Jul 2026, revised
   twice the same day.

   FULL BLEED, NOT A BOX. The first build boxed this onto the .wrap with a
   24px radius by reusing `band dark`, copying the trust band. The owner
   rejected it: they want it running edge to edge like the footer. So this
   follows the site's NORMAL container rule instead - background on the
   SECTION, content in a .wrap - and the trust band goes back to being the
   single deliberate exception it always was.

   It therefore does NOT carry `dark` or `tint-in`. `dark` now means "boxed
   navy wrap" on this site and reusing it here meant overriding its own rules;
   `tint-in` only existed to carry a wash the navy now covers. The two colour
   lines `dark` supplied are restated below and that is all that is borrowed.

   FLAT NAVY, HARD EDGES, NO CONSTELLATION. A middle revision gave this
   section a gradient that faded in and out at both ends and a second copy of
   the hero's constellation canvas, masked to match. The owner removed both.
   What is here now is a flat `--foot` that starts and stops like the footer
   does. Do not reintroduce either without being asked for it by name.

   That removal is also why there is no `position:relative`, no `overflow`
   clip and no z-index on the .wrap any more: all three existed only to hold
   an absolutely positioned canvas that is gone. */
section.band.checks{padding:104px 0 112px;background:var(--foot)}
.band.checks h2{color:#fff}
.band.checks .lede{color:#EDF2FF}
/* THE TILES ARE OUTLINE ONLY. A visible boundary and no fill, so there is no
   colour step between the inside of a tile and the panel around it. The owner
   asked for this twice and rejected the filled version by name. Do not give
   these a background, not even a 5% white one. */
.band.checks .tile{background:transparent;border-color:rgba(255,255,255,.22);padding:22px 16px;gap:13px}
/* 42px, which is two lines at 15.5/1.35, not 40. "Previous employment
   verification" is the longest label and measures the full 232px of the label
   box, so it wraps where the other eleven do not. At a 40px floor that made
   row 1 163px tall against 161 for rows 2 and 3 - a 2px jog between rows for
   one label. The floor is now the wrapped height, so all twelve tiles land on
   163px whatever their line count. Re-measure this if the label font, the
   tile padding or the container width changes. */
.band.checks .tile b{color:#fff;font-size:15.5px;min-height:42px}
/* 62px, down from 84. The owner called the tiles too big and too wide. The
   section came off `wrap-wide` back to the 1180px `.wrap` in the same pass,
   which is what took a tile from 298px to 265. Do not raise the icon without
   widening the container again: it is the ratio of the two that read as
   oversized, not either on its own. */
.band.checks .tile img{width:62px;height:62px}
/* hover on a.tile only. Four of the twelve are div.tile because their check
   page does not exist yet, and a div that lights up reads as a broken link.
   Same rule as a.chk in the industries panels. */
.band.checks a.tile:hover{border-color:rgba(255,255,255,.55)}
@media(max-width:640px){
  .band.checks .tile img{width:52px;height:52px}
}
/* PORTAL - section 10% longer and the shots 10% larger, on the owner's
   instruction. Band padding 58px to 74 and the shot 280px to 308. Both had to
   move: the content sets most of this section's height, so padding alone does
   not shift it 10%. Measured on the page, not assumed. */
section.band.portal{padding:74px 0}
.portal .pshot img{height:308px}
@media(max-width:900px){.portal .pshot img{height:242px}}

/* the hero status chips are split on purpose, both set 31 Jul 2026:
   the icon tile takes the button's colour, the type takes sea. Vars
   rather than copies of the hex, so neither can drift from the button
   or from the rest of the accent colour. That paid off on 1 Aug 2026 -
   --elec went navy and the four tiles followed with no edit here. The
   check icons were generated on a #0B1263 ground, so they read cleaner
   on the navy tile than they did on the purple one. */
body.theme .pchip .ic,body.theme .pchip .ic.b{background:var(--elec)}
body.theme .pchip b,body.theme .pchip span{color:var(--sea)}

/* ===================================================================
   SITE-WIDE ROLL-OUT AND NEW PAGE COMPONENTS - 1 Aug 2026
   Owner's instruction: "Match the theme in every page." The homepage
   theme block above is now on all pages - every <body> carries
   class="theme" - so the tokens it re-points apply everywhere. Nothing
   in that block was rewritten; only the two rules below, which the
   roll-out newly exposed, and the components the new pages need.
   =================================================================== */

/* The interior pages run `trusted flat`, which fades the client strip
   OUT at the bottom because white follows it there. `body.theme .trusted`
   above is more specific than `.trusted.flat` (0-2-1 against 0-2-0), so
   the roll-out silently gave every interior page the homepage's
   fade-to-wash and left a hard wash-to-white seam. This restates the
   flat fade at theme specificity. Do not delete it. */
body.theme .trusted.flat{background:linear-gradient(180deg,rgba(231,244,251,0) 0%,rgba(231,244,251,.55) 50%,rgba(231,244,251,0) 100%)}

/* NAV ICONS - navy tile, neon glyph. Owner's instruction, 1 Aug 2026:
   the pale-blue tiles were generic, and the site already has a neon-on-navy
   icon language in images/checks/. --neon is the cyan those icons were
   generated with (#01C9FE, see PROGRESS) so the menu and the check
   catalogue speak the same way. Structural, so it sits outside the theme
   block: the nav markup is byte-identical on every page. */
:root{--neon:#01C9FE}
/* --foot, not --elec. These tiles are the "navy plate, neon glyph" the owner
   asked for on 1 Aug 2026 to match the check icons, and --neon is 7.63:1 on
   that navy against 5.73:1 on the logo blue. They read --elec only while the
   two held the same value; --foot is what was always meant. See :root. */
.mega .col a i,.dsub a i{background:var(--foot);color:var(--neon);box-shadow:0 0 0 1px rgba(1,201,254,.16),0 2px 8px -3px rgba(25,25,112,.5)}
.mega .col a:hover i,.dsub a:hover i{background:var(--neon);color:var(--foot);box-shadow:0 0 0 1px rgba(1,201,254,.4),0 0 12px -1px rgba(1,201,254,.55)}
.login .cd{font-size:12px}

/* Skip link. Off-screen until focused, which is the one place it has to
   be visible and is why it is not display:none. */
.skip{position:absolute;left:-9999px;top:0;z-index:99;background:var(--elec);color:#fff;padding:12px 18px;border-radius:0 0 10px 0;font-weight:600;font-size:14px}
.skip:focus{left:0}

/* utilities - these replaced 26 inline style="" attributes so the
   Content-Security-Policy can run style-src without 'unsafe-inline'.
   Do not reintroduce an inline style; add a utility here instead. */
.mt64{margin-top:64px}.mt40{margin-top:40px}.mt16{margin-top:16px}.mt14{margin-top:14px}
.mb0{margin-bottom:0}.mb22{margin-bottom:22px}
.oneup{grid-template-columns:1fr}.r24{border-radius:24px}
.btnrow{display:flex;gap:12px;margin-top:18px;flex-wrap:wrap}

/* DIRECTORY LAYOUT - all-checks.html and solutions.html.
   Sticky category rail on the left, category blocks on the right. The
   anchors work with JavaScript off; site.js only lights the current one. */
.dirwrap{display:grid;grid-template-columns:212px 1fr;gap:52px;align-items:start}
.dirnav{position:sticky;top:96px}
.dirnav h4{font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--soft);padding-bottom:12px;border-bottom:1px solid var(--line);margin-bottom:8px}
.dirnav a{display:flex;align-items:center;gap:10px;font-size:14.5px;font-weight:500;color:var(--body);padding:9px 12px;margin:0 -12px;border-radius:9px;transition:background .25s,color .25s}
.dirnav a i{font-size:16px;color:var(--acc-3);transition:color .25s}
.dirnav a:hover{background:var(--wash-2);color:var(--acc)}
.dirnav a.on{background:var(--acc-soft);color:var(--acc);font-weight:600}
.dirnav a.on i{color:var(--acc)}
.dircat{scroll-margin-top:96px}
.dircat+.dircat{margin-top:56px}
.dircat h2{font-size:25px;letter-spacing:-.025em}
.dircat>p{font-size:15.5px;color:var(--soft);margin-top:8px;max-width:64ch;line-height:1.6}
.dircat .rel{grid-template-columns:repeat(2,1fr);margin-top:24px}
@media(max-width:900px){
  .dirwrap{grid-template-columns:1fr;gap:26px}
  .dirnav{position:static;display:flex;flex-wrap:wrap;gap:8px}
  .dirnav h4{width:100%;margin-bottom:0}
  .dirnav a{margin:0;border:1px solid var(--line);padding:8px 13px}
  .dircat .rel{grid-template-columns:1fr}
}

/* LEADERSHIP was here and was deleted 5 Aug 2026 on the owner's instruction,
   along with the section in about-us.html and the images/leadership/ path it
   documented. There are no named people anywhere on this site. Do not
   reintroduce a leadership grid, a photo wall or a founder bio without being
   asked for it, and note that the site has no supplied names to fill one. */

/* TIMELINE - about-us.html. One column, the rule drawn on the list rather
   than on each row, so adding an entry needs no arithmetic. */
.tline{margin-top:40px;border-left:2px solid var(--line);padding-left:0}
.tev{position:relative;padding:0 0 30px 30px}
.tev:last-child{padding-bottom:0}
.tev::before{content:"";position:absolute;left:-7px;top:6px;width:12px;height:12px;border-radius:50%;background:var(--acc);box-shadow:0 0 0 4px var(--paper)}
.tev .yr{font-size:13px;font-weight:700;letter-spacing:.08em;color:var(--acc);text-transform:uppercase}
.tev b{display:block;font-size:17px;color:var(--ink);margin-top:5px}
.tev p{font-size:14.5px;color:var(--soft);margin-top:6px;line-height:1.6;max-width:60ch}

/* NUMBER ROW - about-us.html and the solution pages. */
.statrow{display:grid;grid-template-columns:repeat(4,1fr);gap:18px;margin-top:40px}
.stat{border:1px solid var(--line);border-radius:16px;background:var(--surface);padding:26px 24px}
.stat .n{font-size:clamp(30px,4vw,42px);font-weight:800;letter-spacing:-.04em;color:var(--acc);line-height:1}
.stat b{display:block;font-size:15.5px;color:var(--ink);margin-top:12px}
.stat p{font-size:13.5px;color:var(--soft);margin-top:5px;line-height:1.5}
@media(max-width:900px){.statrow{grid-template-columns:repeat(2,1fr)}}

/* two-column body used by the solution and industry pages */
.splitr{display:grid;grid-template-columns:1.15fr 1fr;gap:52px;align-items:center;margin-top:8px}
.splitr img{border-radius:20px;width:100%;height:100%;max-height:400px;object-fit:cover}
.splitr.full{grid-template-columns:1fr 1.35fr;align-items:center}
.splitr.full img{height:auto;max-height:none;object-fit:contain}
.splitr h2{font-size:clamp(26px,3.4vw,38px);letter-spacing:-.03em}
.splitr p{font-size:16.5px;color:var(--body);margin-top:14px;line-height:1.65}
@media(max-width:900px){.splitr,.splitr.full{grid-template-columns:1fr;gap:28px}}

/* a plain link list, used on sitemap.html and the resource pages */
.lgrid{display:grid;grid-template-columns:repeat(4,1fr);gap:34px;margin-top:32px}
.lgrid h3{font-size:14px;letter-spacing:.02em;color:var(--ink);padding-bottom:10px;border-bottom:1px solid var(--line);margin-bottom:10px}
.lgrid a{display:block;font-size:14.5px;color:var(--body);padding:6px 0;transition:color .2s}
.lgrid a:hover{color:var(--acc)}
@media(max-width:900px){.lgrid{grid-template-columns:repeat(2,1fr)}}
@media(max-width:520px){.lgrid{grid-template-columns:1fr}}

/* three and two column variants of the existing .fields grid, so a page
   with four or two points does not leave a ragged last row */
.fields.f2{grid-template-columns:repeat(2,1fr)}
.fields.f4{grid-template-columns:repeat(4,1fr)}
@media(max-width:900px){.fields.f4{grid-template-columns:repeat(2,1fr)}
  /* .f2 had no mobile rule, and `.fields` (0-1-0) cannot reach it from the
     query above: .fields.f2 is 0-2-0 and wins. Harmless while every f2 card
     held ordinary prose, whose min-content is one word. It stopped being
     harmless when the address cards went in on 14 Aug 2026: an email address
     is one unbreakable 240px token, so `1fr` (= minmax(auto,1fr)) cannot
     shrink the column below about 289px and two of them overflow a 346px
     phone wrap. One column below 900px, which is what .fields, .rel, .compare
     and .types all do. */
  .fields.f2{grid-template-columns:1fr}}
/* Phones: the two-up above is too narrow for the icon-beside-heading cards, so
   a long title wraps to one word per line. One column below 640px. */
@media(max-width:640px){.fields.f4{grid-template-columns:1fr}}

/* Honeypot. Off-screen rather than display:none on purpose: a bot that
   filters on display:none skips it, and a field it never sees is the whole
   point. aria-hidden and tabindex=-1 keep it away from assistive tech and
   from the keyboard tab order. */
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

/* A .stat whose value is a word rather than a figure. "ISO 27001" wrapped to
   two lines between 900px and 1180px at the numeric size, which dropped that
   card's label below the other three in the row. */
.stat .n.sm{font-size:clamp(21px,2.4vw,28px);white-space:nowrap}

/* A .prose table cell holding a bare domain has no break opportunity in it, so
   the table sets its own minimum width and takes the page with it: cookies.html
   overflowed a 390px phone by 98px on "fonts.googleapis.com, fonts.gstatic.com".
   Breaking inside the cell rather than making the table scroll keeps it
   readable and needs no wrapper element.

   THE DEFAULT IS break-word, AND .brk OPTS IN TO anywhere. It was `anywhere`
   everywhere until 5 Aug 2026, and that is a stronger rule than it looks:
   `anywhere` counts a single CHARACTER as a cell's min-content width, so auto
   table layout is free to starve a column to nothing. On a tight table it
   does exactly that, and ordinary words then break mid-syllable. Measured on
   the twenty article tables added that day: 15 breaks across 6 tables, with
   "Previous employment" rendering as "Previous employ / ment" in a 63px
   column. `break-word` breaks a long token just the same but keeps
   min-content at the widest WORD, so a label column cannot collapse below one.
   Re-measured after the change: 0 mid-word breaks, no table wider than its
   726px column.

   Which leaves the one case `anywhere` was written for, so cookies.html's
   table carries .brk. Verified in a 350px column: 350px with .brk, 469px
   without it, which is the original 390px-phone overflow returning. Put .brk
   on any future table holding a URL, a hash or another unbreakable token. */
.prose td,.prose th{overflow-wrap:break-word}
.prose table.brk td,.prose table.brk th{overflow-wrap:anywhere}

/* Inline form result. site.js posts the lead in the background and writes the
   outcome here, so the visitor never navigates to api/lead.php. With
   JavaScript off the form posts normally and lead.php redirects instead. */
.formmsg{margin-top:16px;border-radius:12px;padding:14px 16px;font-size:14.5px;line-height:1.55;border:1px solid}
.formmsg.ok{background:var(--wash);border-color:var(--line-b);color:var(--ink)}
.formmsg.ok b{color:var(--ink)}
.formmsg.bad{background:#FFF4E5;border-color:#FFD8A8;color:#7A4A00}
.formmsg a{color:inherit;text-decoration:underline;text-underline-offset:3px;font-weight:600}

/* ---------------------------------------------------------------
   RESOURCES - the three listings (blog, research, latest news) and the
   article template under all twenty posts. Added 5 Aug 2026.

   Most of an article page is not new and deliberately so. The two-column
   body is the .pgrid the check pages already use, the sticky right rail
   is .aside, the copy is .prose, the closing questions are .faq, the
   related posts are .rel/.relc and the sign-off is .ctaband. The three
   listings reuse .dirwrap/.dirnav/.dircat from all-checks.html, and the
   rail in site.js is already generic - it observes whatever its own
   anchors point at - so no JavaScript changed for any of this.

   What is genuinely new is five things: the listing card, the article
   masthead, a contents rail, a pull-out note and a takeaways plate.
   --------------------------------------------------------------- */

/* The listing card. Typographic on purpose: there are no post images
   and none are planned, so the card leads on its category and its
   headline. Twenty stock photographs of people shaking hands is what
   every competitor does and it tells a reader nothing. The tile is the
   navy-plate-with-neon-glyph the nav and the check catalogue already
   run, so the card belongs to the same family without new artwork.
   flex column with `margin-top:auto` on the footer row, so cards in a
   row keep their read-time line on one baseline whatever the dek does. */
.posts{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-top:36px}
.posts.p2{grid-template-columns:repeat(2,1fr)}
.postc{display:flex;flex-direction:column;border:1px solid var(--line);border-radius:16px;padding:24px;background:var(--surface);transition:transform .5s cubic-bezier(.32,.72,0,1),box-shadow .3s,border-color .3s}
.postc:hover{transform:translateY(-3px);box-shadow:0 18px 36px -22px rgba(22,35,63,.3);border-color:var(--line-2)}
.postc .pk{display:flex;align-items:center;gap:10px;margin-bottom:15px}
/* --foot, not --elec: same navy-plate-and-neon-glyph pair as the menu tiles. */
.postc .pk i{flex:none;display:grid;place-items:center;width:30px;height:30px;border-radius:9px;background:var(--foot);color:var(--neon);font-size:16px;box-shadow:0 0 0 1px rgba(1,201,254,.16),0 2px 8px -3px rgba(25,25,112,.5)}
.postc .pk span{font-size:11px;font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--acc)}
.postc b{display:block;font-size:17.5px;line-height:1.32;letter-spacing:-.02em;color:#002D72;font-weight:700}
.postc p{font-size:14px;color:var(--soft);margin-top:9px;line-height:1.55}
.postc .rt{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:auto;padding-top:18px;font-size:12.5px;color:var(--mute);font-weight:500}
.postc .rt .go{display:inline-flex;align-items:center;gap:6px;color:var(--acc);font-size:13.5px;font-weight:600}
.postc:hover .rt .go{gap:9px}

/* The one featured card, at the top of a listing. Same card, wider and
   with the headline stepped up - not a second component. */
.postc.feat{padding:32px 34px}
.postc.feat b{font-size:clamp(22px,2.6vw,29px);line-height:1.2;max-width:22ch}
.postc.feat p{font-size:15.5px;margin-top:12px;max-width:64ch}

/* Article masthead. .phero does the eyebrow, the H1 and the lede already;
   the only thing wrong for an article is the 20ch H1 cap, which was
   measured for a four-word page title and puts a real headline on five
   lines. 26ch at a smaller clamp holds a 70-character headline to two. */
.phero.art h1{max-width:26ch;font-size:clamp(29px,3.9vw,44px)}
.phero.art .lede{max-width:64ch}
.artmeta{display:flex;flex-wrap:wrap;align-items:center;gap:10px 22px;margin-top:22px;font-size:13.5px;color:var(--soft);font-weight:500}
.artmeta span{display:inline-flex;align-items:center;gap:7px}
.artmeta i{color:var(--acc);font-size:16px}

/* Contents rail, inside .aside. Anchors only - there is no scroll-spy on
   an article and it does not need one; the rail is short enough to read
   in one look. .aside is already sticky and already styles its h4. */
.toc{margin-bottom:20px}
.toc h4{margin-bottom:10px}
.toc a{display:block;padding:7px 10px;margin:0 -10px;border-radius:8px;font-size:13.5px;line-height:1.4;color:var(--soft);border-left:2px solid transparent;transition:background .25s,color .25s,border-color .25s}
.toc a:hover{background:var(--wash-2);color:var(--acc);border-left-color:var(--acc)}

/* Pull-out note inside .prose. One rule, one plate, no icon: it is the
   same left-ruled shape as .srcnote so an article does not invent a
   second way of saying "read this bit twice". */
.callout{border-left:3px solid var(--acc);background:var(--wash-2);border-radius:0 12px 12px 0;padding:18px 22px;margin:26px 0}
.callout p{margin:0;font-size:15px;line-height:1.65;color:var(--ink)}
.callout p+p{margin-top:10px}
.callout b{color:var(--ink)}

/* Takeaways plate. Sits directly under the H1's section, before the first
   H2, so a reader who is not going to read 1,800 words still leaves with
   the argument. Not a summary of the page: the four lines are the four
   things worth acting on.

   The label is a <b> and not a heading. Inside .prose a real <h2> would
   join the document outline and the contents rail as a section that is
   not one, and an <h4> under the H1 skips a level for a screen reader. */
.takeaways{border:1px solid var(--line-b);background:var(--wash-2);border-radius:16px;padding:24px 26px;margin-bottom:34px}
.takeaways b{display:block;font-size:12px;font-weight:700;letter-spacing:.11em;text-transform:uppercase;color:var(--acc);margin:0 0 14px}
.takeaways ul{margin:0;list-style:none;display:grid;gap:11px}
.takeaways li{position:relative;padding-left:26px;font-size:15px;line-height:1.6;color:var(--ink);margin:0}
.takeaways li::before{content:"";position:absolute;left:2px;top:.55em;width:8px;height:8px;border-radius:50%;background:var(--acc)}

/* Listing inside a category block on blog.html. Two-up, matching the way
   .dircat already narrows .rel to two columns in the same rail layout. */
.dircat .posts{grid-template-columns:repeat(2,1fr);margin-top:24px}

@media(max-width:900px){
  .posts,.posts.p2,.dircat .posts{grid-template-columns:1fr}
  .toc{display:none}
}

/* =============================================================
   "How it works" motion piece. Promoted from trials/ to the live
   site. The whole timeline is CSS: one loop, driven by delays and
   keyframe windows on a single clock (--cyc). site.js only starts
   and parks it. Full timing model is documented in the first block
   below. Nothing here was in site.css before.
   ============================================================= */
/* "How it works" motion piece - TRIAL. Nothing here is in site.css yet.

   ONE loop drives everything. There is no JavaScript timeline and no per-step
   state: every moving part is a CSS animation of the same duration, and the
   choreography is nothing but different delays and different keyframe windows
   on that one clock. Three consequences worth knowing before editing:
     - Nothing can drift out of sync, because there is only one clock.
     - Every keyframe must END where it STARTS, or the loop visibly jumps at
       the seam.
     - To retime the piece, change --cyc. Everything is a percentage of it.

   Colours are site tokens only. The three window dots are the traffic lights
   .pshot already draws.

   ------------------------------------------------------------------------
   THE ONE NUMBER THAT MAKES THIS WORK ON 21 PAGES: 57.143%.

   Pages carry three, four or five steps. If each step took an equal share of
   the loop, the stage would have to be re-timed for each of those three
   cases - eighteen keyframes rewritten, three times over, and three panels
   that tick at three different speeds sitting one click apart.

   So the split is held instead. The STAGE always owns 0 -> 57.143% of the
   loop and the REPORT CARD always owns 57.143% -> 100%, whatever the step
   count. Only the RAIL divides its share differently, and only its own six
   keyframes change. Every stage timing below is therefore byte-identical on
   all 21 pages, and the panel behaves the same everywhere.

   What changes per step count is --cyc, so a lead step stays long enough to
   read:

     steps | --cyc    | lead step        | report card
       3   | 15.12s   | 28.571%  4.32s   | 42.857%  6.48s
       4   | 18.90s   | 19.048%  3.60s   | 42.857%  8.10s
       5   | 22.40s   | 14.286%  3.20s   | 42.857%  9.60s

   Set the count with .wt.n3 / .n4 / .n5. The LAST step is the report card on
   every one of them and shares one set of keyframes (wtNum3 / wtTitle3 /
   wtCap3), because its 42.857% share never moves.
   ------------------------------------------------------------------------ */

.wt{
  --cyc:15.12s;           /* three steps. Built at 9s, slowed 20% twice to
                             12.96s, then the tail extended 50% for the report
                             card: 12.96 + 2.16 = 15.12 */
  --row:calc(var(--cyc)*.02857);   /* stagger between document rows. Derived,
                             not typed: .432s at 15.12s, which is what it was
                             when it was an absolute value that had to be
                             moved by hand every time the loop changed */
  display:grid;grid-template-columns:1fr 1.12fr;gap:56px;align-items:start;
}
.wt.n4{--cyc:18.9s}
.wt.n5{--cyc:22.4s}

/* Two classes, both set by the observer, and the DEFAULT is running: if the
   script never loads the piece still plays rather than sitting frozen.
   `pause` parks it off screen. `reset` is held for one frame on the way back
   in - `animation:none` cancels every animation, so removing it starts the
   loop again at 00:00 instead of resuming mid-story. Play-state alone cannot
   do that: pausing keeps the clock exactly where it stopped. */
.wt.pause :where(i,span,div,li,b){animation-play-state:paused}
.wt.reset :where(i,span,div,li,b){animation:none!important}

/* ---- left column: heading, lede, the steps ----------------------------- */
.wt-side .lede{margin-top:14px}
.wt-rail{position:relative;list-style:none;margin:30px 0 0;padding:0;display:grid;gap:2px}
.wt-rail::before,.wt-fill{content:'';position:absolute;left:18px;top:26px;bottom:26px;width:2px;border-radius:2px}
.wt-rail::before{background:var(--line-2)}
.wt-fill{bottom:auto;height:0;background:var(--elec);animation:wtFill var(--cyc) linear infinite}
/* The line fades out full rather than snapping back to zero. A height that
   collapses in view is the loop showing its seam. Same for the progress bar. */
@keyframes wtFill{0%{height:0;opacity:1}94%{height:100%;opacity:1}98%,100%{height:100%;opacity:0}}

.wt-step{display:flex;gap:15px;align-items:flex-start;padding:9px 0 13px}
.wt-n{position:relative;z-index:1;flex:0 0 38px;height:38px;border-radius:50%;
  border:2px solid var(--line-2);background:var(--surface);display:grid;place-items:center;
  font-size:12.5px;font-weight:700;color:var(--mute);letter-spacing:-.02em;
  animation:wtNum var(--cyc) infinite}
.wt-tx b{display:block;font-size:16.5px;font-weight:700;color:var(--mute);line-height:1.3;
  animation:wtTitle var(--cyc) infinite}
.wt-tx span{display:block;font-size:14.5px;line-height:1.55;color:var(--soft);margin-top:5px}

/* THE RAIL, per step count. `.wt-fill` is the rail's first child, so step k is
   nth-child(k+1). Lead steps share one keyframe offset by their start; the
   LAST step always uses the *3 pair, because its share is 42.857% on every
   variant. */
.wt.n3 .wt-step:nth-child(3) :where(.wt-n,b){animation-delay:calc(var(--cyc)*.28571)}
.wt.n4 .wt-step:nth-child(3) :where(.wt-n,b){animation-delay:calc(var(--cyc)*.19048)}
.wt.n4 .wt-step:nth-child(4) :where(.wt-n,b){animation-delay:calc(var(--cyc)*.38095)}
.wt.n5 .wt-step:nth-child(3) :where(.wt-n,b){animation-delay:calc(var(--cyc)*.14286)}
.wt.n5 .wt-step:nth-child(4) :where(.wt-n,b){animation-delay:calc(var(--cyc)*.28571)}
.wt.n5 .wt-step:nth-child(5) :where(.wt-n,b){animation-delay:calc(var(--cyc)*.42857)}
/* the last step, wherever it falls */
.wt.n3 .wt-step:nth-child(4) :where(.wt-n,b),
.wt.n4 .wt-step:nth-child(5) :where(.wt-n,b),
.wt.n5 .wt-step:nth-child(6) :where(.wt-n,b){animation-delay:calc(var(--cyc)*.57143)}
.wt.n3 .wt-step:nth-child(4) .wt-n,
.wt.n4 .wt-step:nth-child(5) .wt-n,
.wt.n5 .wt-step:nth-child(6) .wt-n{animation-name:wtNum3}
.wt.n3 .wt-step:nth-child(4) .wt-tx b,
.wt.n4 .wt-step:nth-child(5) .wt-tx b,
.wt.n5 .wt-step:nth-child(6) .wt-tx b{animation-name:wtTitle3}
/* lead steps on the four- and five-step variants run a shorter window */
.wt.n4 .wt-n{animation-name:wtNum4}
.wt.n4 .wt-tx b{animation-name:wtTitle4}
.wt.n5 .wt-n{animation-name:wtNum5}
.wt.n5 .wt-tx b{animation-name:wtTitle5}

@keyframes wtNum{
  0%{border-color:var(--elec);background:var(--elec);color:#fff}
  28.57%{border-color:var(--elec);background:var(--elec);color:#fff}
  29.5%,100%{border-color:var(--line-2);background:var(--surface);color:var(--mute)}
}
@keyframes wtNum4{
  0%{border-color:var(--elec);background:var(--elec);color:#fff}
  19.05%{border-color:var(--elec);background:var(--elec);color:#fff}
  19.98%,100%{border-color:var(--line-2);background:var(--surface);color:var(--mute)}
}
@keyframes wtNum5{
  0%{border-color:var(--elec);background:var(--elec);color:#fff}
  14.29%{border-color:var(--elec);background:var(--elec);color:#fff}
  15.22%,100%{border-color:var(--line-2);background:var(--surface);color:var(--mute)}
}
@keyframes wtNum3{
  0%{border-color:var(--elec);background:var(--elec);color:#fff}
  42.86%{border-color:var(--elec);background:var(--elec);color:#fff}
  43.8%,100%{border-color:var(--line-2);background:var(--surface);color:var(--mute)}
}
@keyframes wtTitle{0%,28.57%{color:var(--ink)}29.5%,100%{color:var(--mute)}}
@keyframes wtTitle4{0%,19.05%{color:var(--ink)}19.98%,100%{color:var(--mute)}}
@keyframes wtTitle5{0%,14.29%{color:var(--ink)}15.22%,100%{color:var(--mute)}}
@keyframes wtTitle3{0%,42.86%{color:var(--ink)}43.8%,100%{color:var(--mute)}}

/* ---- right column: the window ------------------------------------------ */
.wt-win{border:1px solid var(--line);border-radius:18px;background:var(--surface);overflow:hidden;
  box-shadow:0 30px 70px -40px rgba(0,34,68,.45)}
.wt-top{display:flex;align-items:center;gap:7px;padding:11px 16px;background:var(--wash-2);border-bottom:1px solid var(--line)}
.wt-top .d{width:10px;height:10px;border-radius:50%;background:#FF5F57}
.wt-top .d:nth-child(2){background:#FEBC2E}
.wt-top .d:nth-child(3){background:#28C840}
.wt-top em{margin-left:10px;font-style:normal;font-size:12.5px;color:var(--soft)}
.wt-prog{height:2px;background:var(--line)}
.wt-prog i{display:block;height:100%;width:0;background:var(--elec);animation:wtProg var(--cyc) linear infinite}
@keyframes wtProg{0%{width:0;opacity:1}92%{width:100%;opacity:1}97%,100%{width:100%;opacity:0}}

/* The rows set the height and the min-height is the REPORT CARD's floor, not
   a measurement of the rows. The card is absolutely positioned over them, so
   it contributes nothing; below about 296px it would be clipped. Everything
   above that is whatever the row count comes to. This is what lets a three-row
   and a five-row page share one rule instead of carrying a measured height
   each - the version before this had one, and it had to be re-measured on
   every page that added a row. */
.wt-stage{position:relative;min-height:296px;padding:16px 22px}

/* One pass of light across the panel at the top of each loop. Low opacity on
   purpose: it should register as the case waking up, not as a shine effect. */
.wt-sweep{position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(100deg,transparent 38%,var(--sky-2) 50%,transparent 62%);
  opacity:0;animation:wtSweep var(--cyc) ease-out infinite}
@keyframes wtSweep{0%{transform:translateX(-60%);opacity:0}5.14%{opacity:.75}18.86%{transform:translateX(60%);opacity:0}100%{opacity:0}}

/* consent line ----------------------------------------------------------- */
.wt-consent{display:flex;align-items:center;gap:10px;padding:9px 12px;border-radius:11px;
  background:var(--wash-2);font-size:13.5px;color:var(--soft);
  opacity:0;animation:wtConsent var(--cyc) infinite}
.wt-consent b{color:var(--ink);font-weight:600}
.wt-consent i.ph{font-size:17px;color:var(--acc)}
.wt-consent .wt-st{margin-left:auto}
@keyframes wtConsent{
  0%{opacity:0;transform:translateY(-8px)}
  5.14%,54.86%{opacity:1;transform:none}
  60%,100%{opacity:0;transform:translateY(-8px)}
}

/* document rows ---------------------------------------------------------- */
/* The three column labels. They are the cheapest explanation in the piece:
   with them, one row reads as a sentence - Aadhaar, checked against UIDAI,
   matched - and a reader who has never bought a background check knows what
   the panel is doing without being told. */
.wt-head{display:grid;grid-template-columns:22px 1fr auto 118px;gap:12px;align-items:center;
  padding:14px 2px 7px;font-size:10.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--mute);opacity:0;animation:wtRow var(--cyc) infinite}
.wt-head span:last-child{text-align:right}

.wt-row{position:relative;display:grid;grid-template-columns:22px 1fr auto 118px;gap:12px;align-items:center;
  padding:11px 2px;border-bottom:1px solid var(--line);font-size:14px;
  opacity:0;animation:wtRow var(--cyc) infinite}
/* Counted with sibling combinators, NOT :nth-of-type. The rows are not the
   first divs in the stage - the consent line is - so :nth-of-type(2) selects
   row ONE and the whole stagger lands one row out. That bug was live and
   measurable: row 1 ran 300ms late and row 4 ran first.
   --i is an inherited property, so the bar, the chips and the ping inside a
   row pick their delay up from the row without being counted again.
   Five deep, because the widest page in the set runs five rows. */
:is(.wt-row,.wt-ev){--i:0}
:is(.wt-row,.wt-ev)+:is(.wt-row,.wt-ev){--i:1}
:is(.wt-row,.wt-ev)+:is(.wt-row,.wt-ev)+:is(.wt-row,.wt-ev){--i:2}
:is(.wt-row,.wt-ev)+:is(.wt-row,.wt-ev)+:is(.wt-row,.wt-ev)+:is(.wt-row,.wt-ev){--i:3}
:is(.wt-row,.wt-ev)+:is(.wt-row,.wt-ev)+:is(.wt-row,.wt-ev)+:is(.wt-row,.wt-ev)+:is(.wt-row,.wt-ev){--i:4}
.wt-row>i.ph{font-size:17px;color:var(--acc)}
.wt-row .nm{color:var(--ink);font-weight:600}

/* The document and the authority that issued it, with the query crossing
   between them. This is the one thing in the panel that says the answer comes
   from somewhere else, so it is drawn rather than described: a dashed track,
   a dot that travels it, and a name that lights when the dot arrives. */
.wt-src{display:flex;align-items:center;gap:9px;font-size:12.5px}
.wt-track{position:relative;width:46px;height:1px;flex:none;
  background:repeating-linear-gradient(90deg,var(--line-2) 0 4px,transparent 4px 9px)}
.wt-src em{font-style:normal;color:var(--mute);white-space:nowrap;animation:wtSrc var(--cyc) infinite}
@keyframes wtSrc{0%,34.29%{color:var(--mute)}39.43%,54.86%{color:var(--acc-2)}60%,100%{color:var(--mute)}}
@keyframes wtRow{
  0%{opacity:0;transform:translateY(10px)}
  6.86%,54.86%{opacity:1;transform:none}
  60%,100%{opacity:0;transform:translateY(-6px)}
}

/* The three states share one box and cross-fade in it, so the row never
   changes width and nothing to its left moves. */
.wt-sts{position:relative;height:28px}
.wt-sts>*{position:absolute;right:0;top:50%;transform:translateY(-50%)}
.wt-bar{width:92px;height:5px;border-radius:3px;background:var(--wash);overflow:hidden;
  animation:wtBarBox var(--cyc) infinite}
.wt-bar i{display:block;height:100%;width:0;background:var(--acc-3);border-radius:3px;
  animation:wtBar var(--cyc) cubic-bezier(.3,.7,.3,1) infinite}
@keyframes wtBarBox{0%,6%{opacity:0}7.71%,25.71%{opacity:1}28.29%,100%{opacity:0}}
@keyframes wtBar{0%,7.71%{width:0}23.14%,100%{width:100%}}

.wt-st{display:inline-flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600;
  padding:5px 10px;border-radius:999px;white-space:nowrap;opacity:0}
.wt-st.go{background:var(--acc-soft);color:var(--acc-2);animation:wtGo var(--cyc) infinite}
.wt-st.flag{border:1px solid var(--line-2);color:var(--soft);animation:wtGo var(--cyc) infinite}
.wt-st.wait{background:var(--wash-2);color:var(--mute);animation:wtWait var(--cyc) infinite}
.wt-st.wait i{width:6px;height:6px;border-radius:50%;background:currentColor;animation:wtPulse 1s ease-in-out infinite}
/* An employer who does not answer. It occupies the "Asking" slot rather than
   adding a fourth beat to the sequence: there is no room in the window for
   four, and the escalation is already spelled out in the source column beside
   it and in the step it belongs to. */
.wt-st.wait.warn{background:#FDF3E3;color:#8A6524}
.wt-st.sm{padding:4px 9px;font-size:12px}
.wt-consent .wt-st.go{animation:none;opacity:1}
@keyframes wtWait{0%,28.29%{opacity:0}30%,37.71%{opacity:1}40.29%,100%{opacity:0}}
@keyframes wtGo{
  0%,39.43%{opacity:0;transform:translateY(-50%) scale(.8)}
  42.86%,54.86%{opacity:1;transform:translateY(-50%) scale(1)}
  60%,100%{opacity:0;transform:translateY(-50%) scale(1)}
}
@keyframes wtPulse{50%{opacity:.25}}

/* The query itself, travelling the dashed track from the document to the
   authority. Sized in px against the 46px track rather than in percent, so it
   lands ON the name and not past it. */
.wt-ping{position:absolute;top:50%;left:0;width:7px;height:7px;margin-top:-3.5px;border-radius:50%;
  background:var(--acc);box-shadow:0 0 0 4px rgba(0,70,135,.14);opacity:0;
  animation:wtPing var(--cyc) cubic-bezier(.5,0,.5,1) infinite}
@keyframes wtPing{
  0%,30%{opacity:0;transform:translateX(0)}
  31.71%{opacity:1;transform:translateX(0)}
  37.71%{opacity:1;transform:translateX(39px)}
  40.29%,100%{opacity:0;transform:translateX(39px)}
}

/* ---- the field-visit stage --------------------------------------------- */
/* A second stage, for the checks where nobody queries a database: an associate
   stands at a door. The four-column grammar above - document, checked against,
   result - is what makes those rows readable, and it says nothing true about a
   visit, so this drops the middle column and puts the detail under the line
   instead. Everything else is shared: same window, same rail, same report
   card, same keyframes. There is no new timing here at all, which is the point
   of having two stages rather than two components. */
.wt-ev{display:grid;grid-template-columns:22px 1fr auto;gap:4px 12px;align-items:center;
  padding:11px 2px;border-bottom:1px solid var(--line);font-size:14px;
  opacity:0;animation:wtRow var(--cyc) infinite}
.wt-ev>i.ph{grid-row:1/3;font-size:17px;color:var(--acc)}
.wt-ev .nm{color:var(--ink);font-weight:600}
.wt-ev .sub{grid-column:2;font-size:12.5px;color:var(--mute);animation:wtSrc var(--cyc) infinite}
.wt-ev .wt-sts{grid-column:3;grid-row:1/3;width:118px}
/* `.wt.field`, not `.wt-field`: the modifier sits on the component, beside n3
   and n5. Written as `.wt-field` first, which matched nothing, and the head
   silently kept its four-column track while the rows below it ran three - so
   the "Recorded" label sat 130px left of the chips it labels. */
.wt.field .wt-head{grid-template-columns:22px 1fr 118px}

/* the report, over the rows ---------------------------------------------- */
/* Pinned top AND bottom, so the card covers the whole stage. The rows fade out
   on their own stagger and the last one is still going 500ms after the report
   has arrived; covering them is cheaper than trying to make a staggered exit
   finish on an unstaggered cue. It also gives the card a footer to stamp. */
.wt-report{position:absolute;left:22px;right:22px;top:16px;bottom:16px;
  display:flex;flex-direction:column;
  border:1px solid var(--line);border-radius:14px;overflow:hidden;background:var(--surface);
  opacity:0;animation:wtReport var(--cyc) cubic-bezier(.2,.8,.3,1) infinite}
/* The card is pinned top and bottom, so its height is the stage's, not its
   content's - and the stage is as tall as the rows underneath, which differ per
   page. Centring the fields turns the leftover into padding above and below
   instead of a hole between the last field and the stamp. It is a no-op on a
   page whose rows happen to fill the card. This is what lets a four-row page
   and a five-row page share one card. */
.wt-report .bd{flex:1;display:flex;flex-direction:column;justify-content:center}
/* The last step's window, 57.143% to 100%, is half again as long as a lead
   step on every variant. The card takes the same time to arrive as it always
   did and then simply stands there longer: it is the only thing in the piece
   anyone reads rather than watches. */
@keyframes wtReport{
  0%,58.86%{opacity:0;transform:translateY(24px)}
  66.57%,92.29%{opacity:1;transform:none}
  98.71%,100%{opacity:0;transform:translateY(-10px)}
}
.wt-report .hd{display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:13px 16px;background:var(--elec);color:#fff}
.wt-report .hd b{font-size:14px}
.wt-report .hd span{font-size:12px;opacity:.8}
.wt-report .bd{padding:2px 16px}
.wt-fld{display:flex;justify-content:space-between;gap:16px;padding:10px 0;
  border-bottom:1px solid var(--line);font-size:13.5px;
  opacity:0;animation:wtFld var(--cyc) infinite}
.wt-fld:nth-child(2){animation-delay:.24s}
.wt-fld:nth-child(3){animation-delay:.48s}
.wt-fld:nth-child(4){animation-delay:.72s}
.wt-fld:last-child{border-bottom:0}
.wt-fld span{color:var(--soft)}
.wt-fld b{color:var(--ink);font-weight:600;font-variant-numeric:tabular-nums}
@keyframes wtFld{0%,62.71%{opacity:0;transform:translateX(-10px)}69.14%,91%{opacity:1;transform:none}97.43%,100%{opacity:0}}
.wt-stamp{display:flex;align-items:center;gap:8px;padding:11px 16px;background:var(--wash-2);
  border-top:1px solid var(--line);font-size:12.5px;color:var(--soft);
  opacity:0;animation:wtStamp var(--cyc) cubic-bezier(.2,.8,.3,1.3) infinite}
.wt-stamp i{font-size:15px;color:var(--acc)}
@keyframes wtStamp{0%,79.43%{opacity:0;transform:scale(.94)}84.57%,92.29%{opacity:1;transform:none}97.43%,100%{opacity:0}}

/* ---- the caption ------------------------------------------------------- */
/* It sits OUTSIDE the window, deliberately. A real portal does not narrate
   itself, so the moment this line goes inside the frame the panel stops
   reading as a product and starts reading as an advert. Outside it, it is a
   caption on a figure, which is what it is.
   The rail says what the step is called. This says what is happening and to
   whom, in the plainest words available. Neither repeats the other.

   Grid-stacked rather than absolutely positioned inside a measured height.
   Every caption sits in cell 1/1, so the block is as tall as its longest line
   and no page needs its own height. The version before this carried a 46px
   desktop and a 66px mobile figure that had to be re-measured whenever a
   caption was rewritten. */
.wt-cap{display:grid;margin-top:16px}
.wt-cap span{grid-area:1/1;display:flex;gap:10px;align-items:flex-start;
  font-size:14.5px;line-height:1.45;color:var(--soft);opacity:0;
  animation:wtCap var(--cyc) infinite}
.wt-cap b{flex:none;font-size:11.5px;font-weight:700;letter-spacing:.04em;color:var(--elec);
  background:var(--acc-soft);border-radius:6px;padding:3px 7px;margin-top:1px}
/* Same delays as the rail beside it, and the same last-step exception. */
.wt.n3 .wt-cap span:nth-child(2){animation-delay:calc(var(--cyc)*.28571)}
.wt.n4 .wt-cap span:nth-child(2){animation-delay:calc(var(--cyc)*.19048)}
.wt.n4 .wt-cap span:nth-child(3){animation-delay:calc(var(--cyc)*.38095)}
.wt.n5 .wt-cap span:nth-child(2){animation-delay:calc(var(--cyc)*.14286)}
.wt.n5 .wt-cap span:nth-child(3){animation-delay:calc(var(--cyc)*.28571)}
.wt.n5 .wt-cap span:nth-child(4){animation-delay:calc(var(--cyc)*.42857)}
.wt.n4 .wt-cap span{animation-name:wtCap4}
.wt.n5 .wt-cap span{animation-name:wtCap5}
.wt.n3 .wt-cap span:nth-child(3),
.wt.n4 .wt-cap span:nth-child(4),
.wt.n5 .wt-cap span:nth-child(5){animation-delay:calc(var(--cyc)*.57143);animation-name:wtCap3}
@keyframes wtCap{
  0%{opacity:0;transform:translateY(7px)}
  3.43%,25.71%{opacity:1;transform:none}
  28.57%,100%{opacity:0;transform:translateY(-5px)}
}
@keyframes wtCap4{
  0%{opacity:0;transform:translateY(7px)}
  2.29%,17.14%{opacity:1;transform:none}
  19.05%,100%{opacity:0;transform:translateY(-5px)}
}
@keyframes wtCap5{
  0%{opacity:0;transform:translateY(7px)}
  1.71%,12.86%{opacity:1;transform:none}
  14.29%,100%{opacity:0;transform:translateY(-5px)}
}
/* The last caption holds as long as the card it describes. */
@keyframes wtCap3{
  0%{opacity:0;transform:translateY(7px)}
  3.43%,40%{opacity:1;transform:none}
  42.86%,100%{opacity:0;transform:translateY(-5px)}
}

/* THE ROW STAGGER, and it has to be the last animation declaration in the
   file. Every rule above uses the `animation` shorthand, and the shorthand
   resets animation-delay to 0 - so a delay written before them is silently
   thrown away. That was live: the rows were staggered but their bars, chips
   and pings all fired together, which is exactly the tell that four sources
   are being asked at once rather than in turn.
   `:is()` and not `:where()`: :where() contributes no specificity, so
   `.wt-row :where(.wt-st)` is 0-1-0 and loses to `.wt-st.go` at 0-2-0
   wherever it sits in the file. :is() takes its most specific argument, so
   these match the specificity the single-class version had. */
:is(.wt-row,.wt-ev),
:is(.wt-row,.wt-ev) .wt-bar,
:is(.wt-row,.wt-ev) .wt-bar i,
:is(.wt-row,.wt-ev) .wt-st,
:is(.wt-row,.wt-ev) .sub,
.wt-ping{animation-delay:calc(var(--i,0)*var(--row))}

/* ---- breakpoints -------------------------------------------------------- */
@media(max-width:980px){
  .wt{grid-template-columns:1fr;gap:30px}
}
@media(max-width:560px){
  .wt-stage{padding:14px 14px}
  .wt-report{left:14px;right:14px;top:14px}
  /* Four columns do not fit a phone. The authority drops under the document
     name, which keeps the pairing that does the explaining, and the dashed
     track goes: at this width it would be 20px of dots and read as a dash. */
  .wt-row{grid-template-columns:20px 1fr 104px;gap:4px 9px;font-size:13.5px}
  .wt-row .nm{grid-column:2}
  .wt-src{grid-column:2;grid-row:2;font-size:11.5px}
  .wt-track{display:none}
  .wt-src::before{content:'\2192';color:var(--mute);margin-right:-3px}
  .wt-sts{grid-column:3;grid-row:1/3}
  .wt-ev{grid-template-columns:20px 1fr 104px;gap:3px 9px;font-size:13.5px}
  .wt-ev .wt-sts{width:104px}
  .wt-head{grid-template-columns:20px 1fr 104px;gap:9px;font-size:9.5px}
  .wt-head span:nth-child(3){display:none}
  .wt.field .wt-head span:nth-child(3){display:block}
  .wt.field .wt-head{grid-template-columns:20px 1fr 104px}
  .wt-bar{width:74px}
  .wt-st{font-size:11.5px;padding:4px 8px}
  .wt-cap{font-size:14px}
}

/* Reduced motion: no loop at all. The panel holds the finished case, which is
   the state that carries the meaning, and the rail shows all the steps
   already lit rather than one. */
@media(prefers-reduced-motion:reduce){
  .wt :where(i,span,div,li,b){animation:none!important}
  .wt-fill{height:100%}
  .wt-prog i{width:100%}
  .wt-n{border-color:var(--elec);color:var(--elec)}
  .wt-tx b{color:var(--ink)}
  .wt-sweep,.wt-consent,.wt-row,.wt-ev,.wt-head,.wt-ping,.wt-bar,.wt-st.wait{display:none}
  /* `display:block` on the parent, NOT `position:static` on the children. The
     captions used to be absolutely positioned and static was what released
     them; they are grid-stacked in cell 1/1 now, and grid-area is only obeyed
     by a grid item, so dropping the parent out of grid is what un-stacks them.
     Left as `position:static` this silently printed all five captions on top of
     each other, and only for the readers who cannot see the animation. */
  .wt-cap{display:block}
  .wt-cap span{opacity:1;transform:none}
  .wt-cap span+span{margin-top:8px}
  .wt-report{opacity:1;transform:none;position:static}
  .wt-fld{opacity:1;transform:none}
  .wt-stamp{opacity:1;transform:none}
  .wt-stage{min-height:0}
}


/* about-us: "What we do" split heading over a tinted copy panel (added 2026-08) */
.wwd-top h2{font-size:clamp(28px,3.7vw,40px);letter-spacing:-.03em}
.wwd-top .lede{margin-top:16px;max-width:60ch;font-size:17.5px;line-height:1.55;color:var(--body)}
.wwd-rule{border:0;border-top:1px solid var(--line);margin:30px 0}
.wwd-split{display:grid;grid-template-columns:1.12fr .88fr;gap:44px;align-items:stretch}
.wwd-media{position:relative;min-height:340px;border-radius:20px;overflow:hidden}
.wwd-media img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center 28%}
.wwd-text h2{font-size:clamp(22px,2.7vw,30px);letter-spacing:-.02em}
.wwd-text p{margin-top:15px;font-size:16.5px;color:var(--body);line-height:1.65}
.wwd-lab{color:var(--ink);margin-bottom:10px}
@media(max-width:900px){
  .wwd-split{grid-template-columns:1fr;gap:24px}
  .wwd-media{min-height:0;aspect-ratio:16/10}
}
