/* Shared v2 stylesheet, extracted from v2-home and owned by tools/build_v2.py.
   Every v2 page links this; nothing has its own <style> block, which is how the
   live site ended up with 102 font sizes and 1,612 dead rules across 137 copies. */
/* -------- hero -------- */
.v2hero{padding:58px 0 46px}
.v2hero-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:56px;align-items:center}
.v2slogan{font-size:17px;font-weight:700;color:var(--blue);margin:0 0 16px;letter-spacing:-.01em}
/* THE TYPED SLOT (Abu, 31 Oct: bring back the cycling word). Lifted from the
   shipped index.html and rescoped from .hero-slogan to .v2slogan.
   THE SLOT CANNOT CHANGE HEIGHT, which is the whole reason for the hidden
   sizer spans: every word sits in the SAME inline-grid cell (grid-area:1/1),
   so the cell is as wide as the widest word and the line never reflows. This
   is the rule the 17 Sep eyebrow episode set -- motion must never change the
   height of anything above the fold -- and it is why the words are not simply
   swapped in place.
   THE GREEN LINE IS DRAWN INSIDE THE GLYPH BOX (background-position 90%, no
   padding on the span). A padding-bottom here made the line one pixel taller
   with text than without, which is the documented trap.
   #12A06A LITERAL: this stylesheet has no --green token, same as the live US
   page, which uses the BD green's value directly. */
/* <em> IS NOT EMPHASIS HERE, it is the hook the live page uses to colour the
   second clause, so it must not be italic. MISSED ON THE FIRST CUT and caught
   by looking at the render: I lifted every rule NAMED after this component
   (.tw, .tw-w, .typed) and not the one that merely REACHES it, so "Smooth
   operations." shipped in browser-default italic beside an upright "Clean
   finances." Live carries .hero-home .hero-slogan em{font-style:normal}.
   THE RULE: when lifting a component, take every rule that lands on its
   elements, not just the ones that share its name.
   Colour is deliberately NOT copied: live sets the slogan in muted ink with a
   blue <em>, while this preview sets the whole slogan in blue by its own
   design. Only the italic was a defect. */
.v2hero .v2slogan em{font-style:normal}
.v2hero .v2slogan .tw{display:inline-grid;text-align:left;vertical-align:baseline}
.v2hero .v2slogan .tw>span{grid-area:1/1;white-space:nowrap}
.v2hero .v2slogan .tw .tw-w{visibility:hidden}
.v2hero .v2slogan .tw .typed{position:relative;justify-self:start;color:#12A06A;
  background:linear-gradient(#12A06A,#12A06A) no-repeat 0 90%/100% .08em}
.v2hero .v2slogan .tw .typed.out{transition:opacity .18s ease;opacity:0}
/* The navy hero variant needs a lighter green: #12A06A on navy is nearly black. */
.v2hero-navy .v2slogan .tw .typed{color:#3FD39A;
  background-image:linear-gradient(#3FD39A,#3FD39A)}
.v2hero h1{font-size:clamp(34px,4.3vw,56px);margin:0 0 18px;max-width:14ch}
/* The local pages lead with the search phrase, so their H1 is about 65
   characters where the rest of the site runs short. A 14ch measure broke
   that into FIVE lines at every width, 316px tall at 1280. The keyword-first
   wording is a standing decision, so the measure gives way, not the copy. */
.v2hero h1.h1-wide{max-width:22ch}
/* Contact leads straight into the form, so it does not want the full band
   rhythm between the hero and the fields. Measured 1 Nov: the hero's own 46px
   bottom plus this 28px put 105px of empty white between the last hero word
   and the tab row, which is the "unnecessary space before the Contact form".
   Both sides come down; :has() scopes the hero change to this one page, and
   where it is unsupported the hero simply keeps its standard padding.
   band-tight matches contact.html alone, so these rules reach no other page. */
.v2hero + .band.band-tight{padding-top:8px}
.v2hero:has(+ .band.band-tight){padding-bottom:24px}
@media(max-width:768px){
  .v2hero + .band.band-tight{padding-top:6px}
  .v2hero:has(+ .band.band-tight){padding-bottom:16px}
}
.v2hero h1 em{font-style:normal;color:var(--blue)}
.v2hero .lead{max-width:52ch;margin-bottom:26px}
.v2segs{display:flex;flex-wrap:wrap;gap:8px 20px;margin-top:24px;font-size:13px;font-weight:600}
.v2segs a{color:var(--muted);text-decoration:none;border-bottom:1px solid var(--line);padding-bottom:2px}
.v2segs a:hover{color:var(--blue);border-color:var(--blue-pale)}
.v2hero svg{width:100%;height:auto;display:block}

/* -------- thin logo strip -------- */
.v2trust{padding:26px 0 30px;border-top:1px solid var(--line-soft);border-bottom:1px solid var(--line-soft)}
.v2trust p{text-align:center;font-size:13px;color:var(--quiet);margin:0 0 18px}

/* -------- services -------- */
.v2svc{display:grid;grid-template-columns:repeat(6,1fr);gap:16px;margin-top:30px;grid-auto-rows:1fr}
.v2svc>article{grid-column:span 2;border:1px solid var(--line);border-radius:var(--radius);
  background:#fff;padding:24px 22px;display:flex;flex-direction:column;
  transition:border-color var(--t) var(--ease),box-shadow var(--t) var(--ease)}
.v2svc>article:nth-child(4){grid-column:2/4}
.v2svc>article:nth-child(5){grid-column:4/6}
.v2svc>article:hover{border-color:var(--line-hover);box-shadow:var(--shadow)}
.v2svc svg{display:block;width:96px;height:64px;margin:0 0 16px}
.v2svc h3{font-size:19px;margin:0 0 8px}
.v2svc .out{font-size:14.5px;font-weight:700;color:var(--ink);margin:0 0 8px}
.v2svc p{font-size:14px;color:var(--muted);margin:0 0 16px;line-height:1.55}
.v2svc .link{margin-top:auto}

/* -------- portal -------- */
/* FULL RAIL, not a narrow column. At .85fr/1.15fr the shot was 529px and the
   snippets, which render near their natural 273 and 357px, came out at 49% and
   58% of it -- against the 23-27% the recorded rule asks for, and the live
   system would not float them below 880px at all, it stacks them. Widening the
   frame fixes the PROPORTION and the LEGIBILITY at the same time: at ~1000px the
   same snippets are 26% and 31% AND still render at natural size. */
.v2portal{display:block}
.v2portal-intro{max-width:var(--rail-intro);margin-bottom:30px}
.v2portal ul{list-style:none;padding:0;margin:20px 0 22px;display:grid;grid-template-columns:repeat(3,1fr);gap:0 30px}
.v2portal li{font-size:14.5px;color:var(--body);padding:11px 0;border-top:2px solid var(--blue-pale)}
.v2frame{border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;background:#fff;
  box-shadow:var(--shadow)}
.v2frame-bar{display:flex;align-items:center;gap:6px;padding:10px 14px;border-bottom:1px solid var(--line);
  background:var(--bg-alt)}
.v2frame-bar i{width:9px;height:9px;border-radius:50%;background:var(--dot);display:block}
.v2frame-bar span{margin-left:8px;font-size:11.5px;color:var(--quiet);font-weight:600}
/* height:auto IS LOAD-BEARING. Without it the img's height ATTRIBUTE wins over the
   width:100%, so the frame rendered 1350px tall at every viewport -- 585x1350 at
   desktop and 352x1350 on a phone, for an image whose real ratio is 1591x1170.
   That single missing declaration was 26% of the page height. The attributes below
   are the shot's TRUE natural size; a wrong pair here reintroduces the bug. */
.v2frame img{width:100%;height:auto;display:block}
.v2cap{font-size:12.5px;color:var(--quiet);margin:11px 0 0;line-height:1.5}

/* ---- popped-out snippets ------------------------------------------------
   The wrapper is padded so a snippet can sit PARTLY outside the frame without
   leaving the column; without that padding the composition clips. The frame is
   dimmed only while snippets float, which is the recorded rule: dimming exists
   so a callout can win, so with no callouts it would just be serving a washed
   out screenshot. Saturation and opacity rather than blur, because at 1.6px of
   blur the screen's own numbers stop being readable, which costs the frame the
   one thing it is there for. */
.v2pf{position:relative;padding:0 86px 0 76px}
.v2pf .v2frame img{filter:saturate(.62) opacity(.84);transition:filter var(--t) var(--ease)}
.v2pf:hover .v2frame img{filter:none}
.v2shot{position:relative;display:block;line-height:0}
.v2lead{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible}
.v2dot{position:absolute;width:9px;height:9px;margin:-4.5px 0 0 -4.5px;border-radius:50%;
  background:var(--blue);box-shadow:0 0 0 3px rgba(28,95,204,.2);pointer-events:none}
.v2snipwrap{display:contents}
.v2snip{position:absolute;margin:0;border-radius:9px;overflow:hidden;background:#fff;
  border:1px solid var(--line);box-shadow:0 14px 34px -12px rgba(10,31,68,.34);
  transition:transform var(--t) var(--ease),box-shadow var(--t) var(--ease)}
.v2snip img{display:block;width:100%;height:auto}
.v2snip:hover{transform:translateY(-3px);box-shadow:0 20px 44px -12px rgba(10,31,68,.4)}
.v2snip-a{width:26%;right:0;top:12%}
.v2snip-b{width:31%;left:0;bottom:8%}

/* -------- founder, the one navy band -------- */
.v2founder{background:var(--ink);color:#fff;padding:var(--band) 0}
.v2founder-grid{display:grid;grid-template-columns:260px 1fr;gap:52px;align-items:center;
  max-width:960px;margin:0 auto}
.v2founder img{width:100%;aspect-ratio:1/1;object-fit:cover;border-radius:var(--radius);display:block}
.v2founder h2{color:#fff;font-size:clamp(24px,2.6vw,33px);margin:0 0 16px}
.v2founder p{color:var(--on-dark-soft);font-size:15.5px;max-width:58ch;margin:0 0 20px}
.v2fname{display:flex;justify-content:space-between;font-size:12.5px;color:var(--on-dark-soft);
  margin-top:13px;padding-top:12px;border-top:1px solid var(--on-dark-line)}
.v2fname b{color:#fff}
/* WRAP IS LOAD-BEARING. Three facts at a fixed 44px gap measured 327px inside a
   284px container at 320px wide, which is 25px of horizontal page overflow. The
   fault predates hero B and only appears below 390, which is why a sweep that
   stopped at 390 never saw it. */
.v2facts{display:flex;flex-wrap:wrap;gap:22px 44px;border-top:1px solid var(--on-dark-line);padding-top:20px;margin-top:4px}
.v2facts strong{display:block;font-size:27px;color:#fff;letter-spacing:-.03em;line-height:1.1}
.v2facts span{font-size:12.5px;color:var(--on-dark-soft)}

/* -------- industries -------- */
.v2ind{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px;margin-top:28px}
.v2ind article{border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;background:#fff;
  display:flex;flex-direction:column;transition:border-color var(--t) var(--ease)}
.v2ind article:hover{border-color:var(--line-hover)}
.v2ind .art{aspect-ratio:1.6/1;background:#EEF3F8}
.v2ind .art svg{width:100%;height:100%;display:block}
.v2ind .in{padding:16px 16px 18px;display:flex;flex-direction:column;flex:1}
.v2ind h3{font-size:16px;margin:0 0 6px}
.v2ind p{font-size:13px;color:var(--muted);margin:0 0 13px;line-height:1.5}
.v2ind .link{margin-top:auto;font-size:13px}

/* -------- steps -------- */
.v2steps{display:grid;grid-template-columns:repeat(3,1fr);gap:40px;margin-top:30px}
.v2steps svg{width:30px;height:30px;display:block;margin:0 0 15px}
.v2steps h3{font-size:18px;margin:0 0 8px}
.v2steps p{font-size:14px;color:var(--muted);margin:0;line-height:1.55}

/* -------- pricing entry: table visible, questionnaire collapsed -------- */
.v2price{display:grid;grid-template-columns:repeat(3,1fr);gap:0;margin-top:26px;
  border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}
.v2price>div{padding:20px 22px;border-right:1px solid var(--line-soft)}
.v2price>div:last-child{border-right:0}
.v2price b{display:block;font-size:13px;color:var(--quiet);font-weight:700;margin-bottom:7px}
.v2price strong{display:block;font-size:21px;color:var(--ink);letter-spacing:-.02em;margin-bottom:5px}
.v2price span{font-size:13px;color:var(--muted);line-height:1.5;display:block}
.v2est{margin-top:18px;border:1px solid var(--line);border-radius:var(--radius);background:#fff}
.v2est summary{display:flex;align-items:center;justify-content:space-between;gap:16px;cursor:pointer;
  list-style:none;padding:17px 22px;font-weight:700;color:var(--ink);font-size:15.5px;min-height:24px}
.v2est summary::-webkit-details-marker{display:none}
.v2est summary::after{content:'+';font-size:22px;font-weight:700;color:var(--blue)}
.v2est[open] summary::after{content:'\2212'}
.v2est summary:focus-visible{outline:2px solid var(--blue);outline-offset:-2px;border-radius:var(--radius)}
.v2est .body{padding:4px 22px 22px;border-top:1px solid var(--line-soft)}
.v2est .body p{font-size:14px;color:var(--muted);margin:14px 0 0}
.v2q{margin:16px 0 0}
.v2q b{display:block;font-size:14px;color:var(--ink);margin-bottom:9px}
.v2opts{display:flex;flex-wrap:wrap;gap:8px}
.v2opts span{border:1px solid var(--line);border-radius:999px;padding:7px 14px;font-size:13px;
  color:var(--body);background:#fff}
.v2opts span:first-child{border-color:var(--blue);color:var(--blue);font-weight:700}

/* -------- flags -------- */
.flag{border:1px dashed var(--flag-rule);background:var(--flag-bg);border-radius:var(--radius-sm);
  padding:14px 17px;margin:18px 0 0}
.flag p{font-size:13.5px;margin:0;color:var(--body);max-width:84ch}
.flag b{color:var(--flag)}

/* -------- closing -------- */
.v2close{background:var(--bg-alt);padding:var(--band) 0}
/* The closing band was the one section that kept its desktop padding on a
   phone, 72px against the 44px every other band drops to. */
@media(max-width:768px){ .v2close{padding:var(--band-sm) 0} }
.v2close-grid{display:flex;align-items:center;justify-content:space-between;gap:44px;flex-wrap:wrap}
.v2close h2{margin:0 0 10px;max-width:17ch}
.v2close p{margin:0;max-width:46ch}

@media(max-width:1000px){
  .v2hero-grid{grid-template-columns:1fr;gap:34px}
  .v2portal{grid-template-columns:1fr;gap:30px}
  .v2ind{grid-template-columns:repeat(2,1fr)}
  .v2svc{grid-template-columns:repeat(4,1fr)}
  .v2svc>article:nth-child(4){grid-column:auto/span 2}
  .v2svc>article:nth-child(5){grid-column:2/4}
  .v2steps{grid-template-columns:1fr;gap:26px}
  .v2founder-grid{grid-template-columns:200px 1fr;gap:32px}
}
/* UNDER 780 THE POPPED-OUT LAYER GOES, not the frame. This is the recorded rule
   and the reason is that the phone frame already shows those tiles at full size,
   so a floating crop of one is a duplicate of something two inches away. The dim
   comes off with them, since it exists only so a callout can win. */
/* Below 880 the frame is too narrow to float anything beside it, which is the
   threshold the live system already uses. Snippets drop under the frame in a row. */
@media(max-width:880px){
  .v2pf{padding:0}
  .v2snip{position:static;width:auto}
  .v2snip-a,.v2snip-b{inset:auto}
  .v2snipwrap{display:flex;gap:12px;margin-top:14px}
  .v2snipwrap .v2snip{flex:1 1 0;min-width:0}
  .v2lead,.v2dot{display:none}
  .v2pf .v2frame img{filter:none}
}
@media(max-width:780px){
  .v2snip,.v2lead,.v2dot{display:none}
  .v2pf{padding:0}
  .v2pf .v2frame img{filter:none}
}
@media(max-width:720px){
  .v2hero{padding:34px 0 30px}
  .v2svc{grid-template-columns:1fr;grid-auto-rows:auto}
  .v2svc>article,.v2svc>article:nth-child(4),.v2svc>article:nth-child(5){grid-column:1/-1}
  /* TWO columns on a phone, not one. Four full-width illustrations stacked were
     880px of artwork and made industries 18% of the phone page. At 2x2 the art is
     170x106, which these simple bar shapes still read at. */
  .v2ind{grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}
  .v2ind h3{font-size:14.5px}
  .v2ind p{font-size:12px}
  .v2price{grid-template-columns:1fr}
  .v2price>div{border-right:0;border-bottom:1px solid var(--line-soft)}
  .v2price>div:last-child{border-bottom:0}
  .v2founder-grid{grid-template-columns:1fr;gap:26px}
  .v2founder img{max-width:220px}
  .v2facts{gap:18px 28px}
}

/* ============================================================================
   Added by tools/build_v2.py's page set: chrome and blocks the home page did
   not need. Same tokens, same rhythm, nothing new invented.
   ========================================================================== */
/* The menu button. Hidden where the full nav fits. 44px square, because this is
   a control a person operates, which is the exception the 19 Sep tap-target
   decision reserves full size for. */
.v2nav-in{position:relative}
/* NAV DROPDOWNS. Base rules sit here, above the media queries that change
   them: twice today a base rule appended after its media query silently
   disabled the thing it was meant to style. */
.v2dd{position:relative;display:inline-flex}
.v2dd-t{display:inline-flex;align-items:center;gap:5px;background:none;border:0;padding:0;
  font:inherit;color:var(--body);font-weight:600;cursor:pointer}
.v2dd-t:hover{color:var(--blue)}
/* WCAG 2.2 AA asks for 24x24 CSS px on a target. The nav links sat at
   22.67px, which is line-height 1.62 on 14px with no padding: one pixel
   and a bit short, on every page. A min-height floor costs nothing because
   the header's own padding already sets its 69px height. */
#v2menu a,.v2dd-t{padding-top:1px;padding-bottom:1px;min-height:24px;box-sizing:border-box}
.v2dd-t[aria-current="page"]{color:var(--blue)}
.v2dd-c{width:0;height:0;border-left:4px solid transparent;border-right:4px solid transparent;
  border-top:5px solid currentColor;transition:transform .16s ease}
.v2dd-t[aria-expanded="true"] .v2dd-c{transform:rotate(180deg)}
.v2dd-m{display:none;position:absolute;top:calc(100% + 12px);left:-14px;min-width:232px;
  background:#fff;border:1px solid var(--line-soft);border-radius:12px;padding:6px;
  box-shadow:0 20px 40px -18px rgba(10,31,68,.3);z-index:30}
.v2dd-m.open{display:block}
.v2dd-m a{display:block;padding:9px 12px;border-radius:8px;font-size:14px;
  color:var(--body);text-decoration:none;white-space:nowrap}
.v2dd-m a:hover{background:var(--g50,#F4F7FB);color:var(--blue)}
/* Operations: cards that carry a bullet list, and the two engagement modes.
   .v2covers-rich is .v2covers with room for the list underneath. */
.v2covers-rich article ul,.v2modes article ul{list-style:none;margin:12px 0 0;padding:0}
.v2covers-rich article li,.v2modes article li{position:relative;padding-left:16px;margin-bottom:7px;
  font-size:13.5px;line-height:1.6;color:var(--muted)}
.v2covers-rich article li:before,.v2modes article li:before{content:"";position:absolute;left:0;top:8px;
  width:5px;height:5px;border-radius:50%;background:var(--blue)}
.v2modes{display:grid;grid-template-columns:1fr 1fr;gap:22px}
.v2modes article{background:#fff;border:1px solid var(--line);border-radius:14px;padding:22px 22px 20px}
.v2modes article h3{margin:0 0 4px;font-size:17px;letter-spacing:-.01em}
.v2modes article .out{margin:0 0 10px;font-size:13px;font-weight:600;color:var(--blue)}
.v2modes article p{margin:0;font-size:14px;line-height:1.65;color:var(--muted)}
@media(max-width:820px){ .v2modes{grid-template-columns:1fr} }
.v2menu-portal{display:none}
.v2burger{display:none;width:44px;height:44px;min-width:44px;flex:0 0 44px;margin-left:auto;align-items:center;justify-content:center;
  flex-direction:column;gap:4px;background:none;border:0;cursor:pointer;padding:0}
.v2burger span{display:block;width:20px;height:2px;background:var(--ink);border-radius:2px;
  transition:transform .18s ease,opacity .18s ease}
.v2burger[aria-expanded="true"] span:nth-child(1){transform:translateY(6px) rotate(45deg)}
.v2burger[aria-expanded="true"] span:nth-child(2){opacity:0}
.v2burger[aria-expanded="true"] span:nth-child(3){transform:translateY(-6px) rotate(-45deg)}
.v2nav{border-bottom:1px solid var(--line-soft);background:#fff;position:sticky;top:0;z-index:20}
.v2nav-in{display:flex;align-items:center;gap:26px;height:68px}
.v2brand{font-weight:800;font-size:18px;color:var(--ink);text-decoration:none;letter-spacing:-.02em}
.v2nav nav{display:flex;gap:22px;margin-left:auto;font-size:14px;font-weight:600}
.v2nav nav a{color:var(--body);text-decoration:none}
.v2nav nav a:hover{color:var(--blue)}
.v2nav nav a[aria-current="page"]{color:var(--blue)}
.v2heroart svg{width:100%;max-width:320px;height:auto;display:block;margin-left:auto}

.v2covers{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px;grid-auto-rows:1fr}
.v2covers article{border:1px solid var(--line);border-radius:var(--radius);padding:20px 18px;background:#fff}
.v2covers h3{font-size:16px;margin:0 0 7px}
.v2covers p{font-size:13.5px;color:var(--muted);margin:0;line-height:1.55}
/* Three across, for the six-item sets on the vertical and local pages. Six in
   the four-column default leaves an orphan row of two. grid-auto-rows:1fr is
   inherited from .v2covers, which is what keeps every card in a row the same
   height when one paragraph runs long -- the fault the hub cards shipped with. */
.v2covers-3{grid-template-columns:repeat(3,minmax(0,1fr))}
/* A quiet label over the card title. It names the category the card belongs to
   and the h3 does not, which is the test the 21 Sep eyebrow sweep set. */
.v2kick{font-size:10.5px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;
  color:var(--blue);margin:0 0 5px}
/* The "no lock-in" row: four short assurances, no cards, separated by rules. */
.v2assure{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:0;margin-top:var(--stack)}
.v2assure div{padding:0 18px;border-left:1px solid var(--line)}
.v2assure div:first-child{padding-left:0;border-left:0}
.v2assure h3{font-size:14px;margin:0 0 5px}
.v2assure p{font-size:13px;color:var(--muted);margin:0;line-height:1.5}
/* RELATED SERVICES / RELATED READING, the carried blocks.
   These rules used to be copied per page out of the old page's head, which
   broke twice over. Re-porting an already-ported page found no head rules to
   copy and emitted none, so the block rendered as bare underlined links; and
   the copied rules referenced --g100, --g900 and --fd, which the v2 token set
   does not define, so the border, the title colour and the title font were
   INVALID declarations even where the copy did land. Defining them here ends
   both: the pattern is shared by nine pages and does not belong in a per-page
   copy. */
.related-sec{padding:var(--band) 0;background:var(--bg-alt)}
/* The carried markup wraps itself in .con, which was defined only in the same
   per-page copy. Undefined, the block ran full-bleed with no gutter. */
.con{max-width:var(--rail);margin:0 auto;padding:0 var(--gutter)}
.rel3{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;max-width:940px;
  margin:0 auto;grid-auto-rows:1fr}
.related-card{background:#fff;border:1px solid var(--line);border-radius:var(--radius-sm);
  padding:14px 16px;display:flex;flex-direction:column;gap:6px;text-decoration:none;
  transition:border-color .2s ease,box-shadow .2s ease,transform .2s ease}
.related-card:hover{border-color:var(--line-hover);box-shadow:var(--shadow);transform:translateY(-2px)}
.related-card h3{font-size:13.5px;font-weight:700;color:var(--ink);margin:0;line-height:1.35}
.related-arrow{font-size:12.5px;font-weight:700;color:var(--blue);margin-top:auto}
@media(max-width:760px){
  .rel3{grid-template-columns:none;grid-auto-flow:column;grid-auto-columns:80%;
    overflow-x:auto;scroll-snap-type:x mandatory;padding:2px 2px 10px;scrollbar-width:thin}
  .rel3>*{scroll-snap-align:start;margin:0}
}
/* A single line of regional context under a page's FAQ. It exists so the core
   service pages link to the regional pages in BODY copy: before this, only
   services.html did, and the regional pages were reachable from the chrome
   nowhere (Abu took them out of the footer on 20 Sep as "only seo link").
   Deliberately one quiet sentence, not a location directory. */
/* Its own padding, NOT .band's 72px: one sentence in a full band renders as a
   line of text floating in a void, which is what the first cut did. */
.s-region{padding:30px 0 34px}
.v2region{margin:0;font-size:14px;color:var(--muted);max-width:var(--rail-prose);line-height:1.6}
.v2region a{color:var(--blue);font-weight:600}
@media(max-width:768px){ .s-region{padding:22px 0 26px} }
/* Two-column bullet list for the "built for" sets. */
.v2fits{list-style:none;margin:var(--stack) 0 0;padding:0;display:grid;
  grid-template-columns:1fr 1fr;gap:9px 28px;max-width:var(--rail-prose)}
.v2fits li{position:relative;padding-left:17px;font-size:14px;color:var(--muted);line-height:1.55}
.v2fits li:before{content:"";position:absolute;left:0;top:8px;width:6px;height:6px;
  border-radius:50%;background:var(--blue)}

.v2delivery{display:flex;justify-content:space-between;gap:20px;flex-wrap:wrap;
  border-top:1px solid var(--line);margin-top:26px;padding-top:18px;font-size:13px;color:var(--muted)}
.v2delivery a{color:var(--blue)}
.v2areas{font-size:15px;color:var(--body);max-width:70ch;line-height:1.9}

.v2contact{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);gap:44px;align-items:start}
.v2form{border:1px solid var(--line);border-radius:var(--radius);padding:24px;background:#fff}
.v2form h2{font-size:var(--h4);margin:0 0 16px}
.v2form label{display:block;font-size:12.5px;font-weight:700;color:var(--ink);margin:0 0 14px}
.v2form label span{font-weight:400;color:var(--muted)}
.v2form input,.v2form textarea{display:block;width:100%;margin-top:6px;font:inherit;font-size:14px;
  color:var(--ink);background:#fff;border:1px solid var(--line);border-radius:var(--radius-sm);
  padding:10px 12px;min-height:44px}
.v2form textarea{min-height:104px;resize:vertical;line-height:1.5}
.v2form input:focus-visible,.v2form textarea:focus-visible{outline:2px solid var(--blue);outline-offset:1px}
.v2sent{margin:12px 0 0;padding:11px 13px;border-radius:var(--radius-sm);background:var(--blue-wash);
  border:1px solid var(--blue-pale);font-size:13px;color:var(--ink)}
.v2next{margin:0;padding:0;list-style:none;counter-reset:n}
.v2next li{counter-increment:n;padding:14px 0;border-top:1px solid var(--line-soft);
  display:grid;grid-template-columns:26px minmax(0,1fr);gap:12px}
.v2next li::before{content:counter(n) '.';font-size:14px;font-weight:800;color:var(--blue)}
.v2next h3{font-size:14.5px;margin:0 0 3px}
.v2next p{font-size:13.5px;color:var(--muted);margin:0;line-height:1.55}
.v2reach{margin-top:var(--stack)}
.v2reach h2{font-size:var(--h4);margin:0 0 10px}
.v2reach a{display:block;margin-bottom:7px}

/* The footer was pure white against mostly-white pages, so it read as more of
   the page rather than the end of it. --blue-wash is deliberately NOT --bg-alt:
   index, contact and about all put an F5F7FC band directly above the footer, and
   tinting it the same would put two identical tones against each other. */
.v2foot{border-top:1px solid var(--line);padding:42px 0 22px;background:var(--blue-wash)}
/* The copyright line broke mid-separator on a phone: "(c) 2026 Finsightic -"
   then "Privacy", then a lone "-" on its own line, then "Terms". Each
   separator is now glued to the link it introduces, so the line wraps BETWEEN
   groups and never after a middot. */
.v2foot-b2 .nw{white-space:nowrap;display:inline;margin-bottom:0}
.v2foot-grid{display:grid;grid-template-columns:1.5fr repeat(3,1fr);gap:36px}
.v2foot-b{font-weight:800;font-size:17px;color:var(--ink);margin:0 0 10px;letter-spacing:-.02em}
.v2foot p{font-size:12.5px;color:var(--muted);max-width:30ch;margin:0}
.v2foot h2{font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:var(--quiet);margin:0 0 12px}
/* 24px is the WCAG 2.2 AA tap target and the standard this site chose on
   19 Sep (44px is Apple's guideline, not the standard). These links were
   rendering 164x21 -- three pixels under, on every page, because the v2
   footer never set a height and 13px text gives a 21px line box. */
.v2foot a{display:block;min-height:24px;font-size:13px;color:var(--body);text-decoration:none;margin:0 0 8px}
.v2foot a:hover{color:var(--blue)}
.v2foot-b2{display:flex;justify-content:space-between;gap:20px;font-size:12px;color:var(--quiet);
  border-top:1px solid var(--line-soft);padding-top:16px;margin-top:30px}
.v2foot-b2 span:first-child{max-width:74ch}
/* Privacy and Terms live on the footer's bottom line. They were dropped
   when the v2 footer replaced the old one, which left the privacy policy
   unreachable from 156 of 157 pages on a site that runs GA4 and sets
   cookies. Quiet, but a real link, not grey text. */
/* `.v2foot a{display:block}` styles the four link COLUMNS, and it was also
   hitting Privacy and Terms in the bottom bar, so each sat on its own line
   with the separator stranded above it. Equal specificity, so this later rule
   wins. Vertical padding keeps the hit area at ~26px without a block box. */
.v2foot-b2 a{color:inherit;text-decoration:underline;text-underline-offset:2px;display:inline;min-height:0;margin:0;padding:4px 0}
.v2foot-b2 a:hover{color:var(--blue)}

@media(max-width:1000px){
  .v2covers{grid-template-columns:repeat(2,minmax(0,1fr))}
  .v2covers-3{grid-template-columns:repeat(2,minmax(0,1fr))}
  /* The assurance row's dividers are vertical rules between columns, so when
     it wraps to two columns the left border has to come off the new first
     column of each row or a rule floats in the middle of nothing. */
  .v2assure{grid-template-columns:1fr 1fr;gap:20px 0}
  .v2assure div:nth-child(odd){padding-left:0;border-left:0}
  .v2contact{grid-template-columns:1fr;gap:30px}
  .v2foot-grid{grid-template-columns:1fr 1fr;gap:26px}
  /* THE NAV USED TO JUST VANISH HERE. display:none with nothing to replace it
     meant that below 1000px -- every phone and most tablets -- the six main
     links were unreachable and there was no menu button: a visitor on a phone
     could reach Client portal and Book a call and nothing else. The links now
     collapse into a panel behind the button. */
  .v2nav nav{display:none;position:absolute;left:0;right:0;top:100%;margin-left:0;
    flex-direction:column;gap:0;background:#fff;border-bottom:1px solid var(--line-soft);
    box-shadow:0 18px 34px -18px rgba(10,31,68,.28);padding:6px 0 10px}
  .v2nav nav.open{display:flex}
  .v2nav nav a{padding:13px 20px;border-top:1px solid var(--line-soft);font-size:15px}
  .v2nav nav a:first-child{border-top:0}
  .v2burger{display:inline-flex}
  /* The burger was the second child with margin-left:auto, so it landed between
     the brand and the Portal link: measured at x157-201 on a 390px bar, which is
     the middle. The brand now takes the auto margin and the button is ordered
     last, so the bar reads brand ... Portal, Book a call, menu. Done with order
     rather than by moving it in the DOM, because the button must stay next to
     the nav it controls for focus to land in the panel it opens. */
  .v2brand{margin-right:auto}
  .v2burger{margin-left:0;order:9}
  /* In the panel there is no room for a second layer of hiding, so a group's
     items are simply listed under its label, indented. The disclosure button
     becomes a plain heading and stops being a control. */
  /* ACCORDION, NOT A FLAT LIST (Abu, 2 Nov 2026: "they should stay collapsed,
     and once a specific thing clicked, it should expand. And when another
     category clicked, that will expand, but the other one will close").
     The panel used to list all 18 links at once with the group label as a dead
     heading (pointer-events:none). Now the label is the control it already is
     in markup -- it has always been a <button> with aria-expanded and
     aria-controls -- the chevron comes back and turns, and the script's
     existing closeAll() gives the one-open-at-a-time behaviour for free. */
  .v2dd{display:block}
  .v2dd-t{width:100%;padding:13px 20px;border-top:1px solid var(--line-soft);
    font-size:15px;justify-content:space-between}
  .v2dd-c{display:block;margin-right:2px;transition:transform .18s ease}
  .v2dd-m{display:none;position:static;border:0;border-radius:0;
    padding:0;box-shadow:none;min-width:0}
  .v2dd-m.open{display:block}
  /* THIS RULE NEVER APPLIED. `.v2nav nav a{padding:13px 20px}` three lines up
     scores 0,1,2 and `.v2dd-m a` scores 0,1,1, so the top-level padding won and
     every child of a group sat at the same 20px as its own heading: measured
     padding-left 20px and font-size 15px on all 11 sub-items, where 34px and
     14.5px were intended. The panel read as one flat list of 18 links with no
     hierarchy at all. Scoped through .v2nav nav so it outranks the rule it was
     always meant to override, and the indent goes to 36px because 14px of
     difference is not legible as a level on a phone. */
  .v2nav nav .v2dd-m a{padding:11px 20px 11px 36px;font-size:14.5px;
    color:var(--muted,#55618C);white-space:normal;border-top:1px solid var(--line-soft)}
  /* the group heading owns the rule above its first child */
  .v2nav nav .v2dd-m a:first-child{border-top:1px solid var(--line-soft)}
  /* The bar needed 438px of content in 390px of space once the button was
     added. Client portal moves INTO the panel rather than off the site, and
     the gap tightens; the brand and the primary CTA stay on the bar. */
  .v2menu-portal{display:block}
}
@media(max-width:720px){
  .v2covers{grid-template-columns:1fr;grid-auto-rows:auto}
  .v2covers-3{grid-template-columns:1fr}
  .v2assure{grid-template-columns:1fr;gap:16px 0}
  .v2assure div{padding-left:0;border-left:0}
  .v2fits{grid-template-columns:1fr}
  .v2heroart{display:none}
  .v2foot-b2{display:block}
  .v2foot-b2 span{display:block;margin-bottom:8px}
}

/* ---- second batch: verticals, technical topics, blog post ---------------- */
.v2heroart-wide svg{max-width:420px}
.v2wrong{list-style:none;margin:0;padding:0;max-width:72ch;display:grid;gap:0}
.v2wrong li{font-size:15px;color:var(--body);padding:13px 0 13px 26px;position:relative;
  border-bottom:1px solid var(--line-soft);line-height:1.55}
.v2wrong li::before{content:'';position:absolute;left:0;top:20px;width:12px;height:2px;background:var(--blue-pale)}
.v2chips{display:flex;flex-wrap:wrap;gap:9px}
.v2chip{display:inline-flex;align-items:center;min-height:38px;padding:0 16px;border-radius:999px;
  border:1px solid var(--line);background:#fff;font-size:13.5px;font-weight:600;color:var(--body);
  text-decoration:none;transition:border-color var(--t) var(--ease),color var(--t) var(--ease)}
.v2chip:hover{border-color:var(--blue-pale);color:var(--blue)}
.v2byline{font-size:13px;color:var(--quiet);margin-top:18px}
.v2article{max-width:var(--rail-prose)}
.v2article h2{font-size:22px;margin:30px 0 10px}
.v2article h2:first-child{margin-top:0}
.v2article p{font-size:16.5px;line-height:1.72;color:var(--body)}
.v2cta-inline{margin:32px 0 0;padding:20px 22px;border:1px solid var(--line);border-left:3px solid var(--blue);
  border-radius:var(--radius-sm);background:var(--bg-alt)}
.v2cta-inline p{font-size:14.5px;margin:0 0 13px}

/* ---- client portal entry -------------------------------------------------
   A QUIET LINK, NOT A SECOND BUTTON. Two filled buttons side by side in the
   nav would make an existing client's login compete with a prospect's first
   action, and the prospect is who the marketing site is for. This is the
   convention in the category: Kruze and Bookkeeper360 both put the client
   login as plain nav text beside their primary CTA. It still clears the 24px
   WCAG 2.2 AA target. */
.v2login{font-size:13.5px;font-weight:600;color:var(--body);text-decoration:none;
  display:inline-flex;align-items:center;min-height:24px;padding:6px 2px;white-space:nowrap;
  /* A HAIRLINE, because without it this reads as a seventh nav page rather than
     the one entrance meant for people who are already clients. The divider is
     the whole signal; the link itself stays quiet. */
  margin-left:8px;padding-left:20px;border-left:1px solid var(--line)}
/* .v2login is a flex container, so its children are flex items and default to
   display:block. Without an explicit default BOTH labels render and the link
   reads "Client portalPortal" -- which is exactly what the first cut shipped
   into the working tree, because the desktop rule had been written inside a
   max-width media query and never matched at desktop at all. */
.v2login-l{display:inline}.v2login-s{display:none}
.v2login:hover{color:var(--blue)}
.v2login:focus-visible{outline:2px solid var(--blue);outline-offset:3px;border-radius:5px}
@media(max-width:1000px){
  /* the main nav hides here; the portal link stays, because a client arriving
     on a phone to check their numbers is exactly who still needs it */
  .v2login{margin-left:auto;padding-left:2px;border-left:0}
}
@media(max-width:420px){ .v2login{font-size:12.5px} }

/* ---- home pricing, one line ---------------------------------------------- */
.v2priceline{display:flex;align-items:center;justify-content:space-between;gap:38px;flex-wrap:wrap}
.v2priceline h2{margin:0 0 9px;max-width:18ch}
.v2priceline p{font-size:15px;color:var(--muted);margin:0;max-width:62ch}
.v2priceline .btn{flex:0 0 auto}

/* ---- the one real human, used where the promise is made ------------------
   Not a stock portrait and not a generated one. Across the eight firms read on
   31 Oct, every people photograph that works is a NAMED individual with a
   credential; that is the only job photography does in this category, and it is
   the job an invented face cannot do. So the answer is more of the one that is
   real, placed where it earns its keep: beside "a person reads it". */
.v2person{display:flex;align-items:center;gap:14px;margin:0 0 var(--stack);
  padding-bottom:18px;border-bottom:1px solid var(--line-soft)}
.v2person img{width:62px;height:62px;border-radius:50%;object-fit:cover;flex:0 0 auto}
.v2person b{display:block;font-size:15px;color:var(--ink);line-height:1.3}
.v2person span{display:block;font-size:12.5px;color:var(--muted);margin-top:3px}
@media(max-width:720px){ .v2priceline{gap:20px} .v2priceline .btn{width:100%;justify-content:center} }

/* ---- hero B: the questions you are carrying ------------------------------
   Replaces the fork chart. The objection it answers, in Abu's words about the
   industry icons, was "they look like just financial charts" -- and the hero was
   the same object four times the size, on the first screen, saying "accounting"
   to a reader who already pays a bookkeeper and a CPA.

   REAL TYPE, NOT A DRAWING. Every word here is markup, so it reads at any size,
   can be translated (which the BD port would need), and changing a question is a
   text edit rather than a redraw. It also means no fake UI: these are cards
   carrying sentences, not an imitation of software. */
.qstack{position:relative;padding:14px 0 0}
.qc{border-radius:14px;padding:18px 22px}
.qc-b{background:#DCE6F3;margin:0 26px 0 14px;color:#51627A;font-size:15px;font-weight:600}
.qc-m{background:#C0D8F8;margin:-6px 12px 0 28px;color:#2A4467;font-size:15.5px;font-weight:600}
.qc-f{background:#fff;border:2px solid var(--blue);box-shadow:0 18px 44px -18px rgba(10,31,68,.4);
  margin:-6px 0 0;padding:22px 24px}
.qc-f b{display:block;font-size:20px;color:var(--ink);letter-spacing:-.02em;line-height:1.3}
.qc-f hr{border:0;border-top:1px solid var(--line-soft);margin:15px 0 13px}
.qc-ans{display:flex;align-items:center;gap:12px}
.qc-ans span{font-size:15px;color:var(--body);line-height:1.45}
.qc-ans i{flex:0 0 auto;width:38px;height:38px;border-radius:10px;background:var(--blue-wash);
  display:grid;place-items:center}
.qc-ans svg{width:19px;height:19px}
@media(max-width:720px){
  .qstack{padding-top:6px}
  .qc{padding:15px 18px}
  .qc-b,.qc-m{font-size:14px}
  .qc-f{padding:19px 20px}
  .qc-f b{font-size:18px}
  .qc-ans span{font-size:14px}
}

/* ============================================================================
   HERO VARIANTS — full pages, so each option can be judged IN CONTEXT.
   The stacked-questions hero looked right in a comparison strip and wrong on
   the page; a strip is not a page, and that is the whole reason these exist.
   ========================================================================== */
.vswitch{position:sticky;top:0;z-index:40;display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  padding:9px 18px;background:var(--ink);color:var(--on-dark-soft);font-size:12.5px}
.vswitch span{font-weight:800;letter-spacing:.07em;text-transform:uppercase;font-size:10.5px;margin-right:4px}
.vswitch a{color:var(--on-dark-soft);text-decoration:none;padding:5px 11px;border-radius:999px;
  border:1px solid var(--on-dark-line);min-height:24px;display:inline-flex;align-items:center;white-space:nowrap}
.vswitch a:hover{border-color:var(--on-dark-line-hover);color:#fff}
.vswitch a.on{background:#fff;color:var(--ink);border-color:#fff;font-weight:700}
/* PREVIEW ONLY. The review preview puts a 41px .vswitch bar above the nav; the
   published site has none (0 of 157 pages carry .vswitch). Unscoped, these two
   rules shipped to production and were the whole reason the header "disappeared"
   on scroll: a 41px offset on desktop, and position:static under 720px which
   cancels sticky outright. Scoped to the preview so both sides behave. */
body:has(.vswitch) .v2nav{top:41px}

/* A and D: one column, headline takes the full width */
.v2hero-solo .wrap{display:block}
.v2hero-solo h1{max-width:17ch;font-size:clamp(34px,5vw,62px)}
.v2hero-solo .lead{max-width:58ch;font-size:18px}

/* B: the founder */
.f-port{position:relative;max-width:300px;margin-left:auto}
.f-port img{width:100%;aspect-ratio:1/1;object-fit:cover;border-radius:18px;display:block}
.f-name{margin-top:14px}
.f-name b{display:block;font-size:16px;color:var(--ink)}
.f-name span{display:block;font-size:13px;color:var(--muted);margin-top:2px}
/* bottom clears the name row; at 26px the card covered "Abu Bakar" entirely */
.f-quote{position:absolute;left:-54px;bottom:78px;background:#fff;border:1px solid var(--line);
  border-left:3px solid var(--blue);border-radius:10px;padding:14px 16px;max-width:255px;
  box-shadow:0 16px 38px -16px rgba(10,31,68,.34);font-size:13.5px;color:var(--body);line-height:1.5}

/* D: navy first screen only. The client logos CANNOT come with it - they are
   colour marks drawn for a white ground, which is why the 17 Sep note called the
   institutional direction "not a repaint". They stay on the white band below. */
.v2hero-navy{background:var(--ink)}
.v2hero-navy h1{color:#fff}
.v2hero-navy h1 em{color:#8FBAF5}
.v2hero-navy .v2slogan{color:#8FBAF5}
.v2hero-navy .lead{color:var(--on-dark-soft)}
.v2hero-navy .btn-s{background:transparent;color:#fff;border-color:var(--on-dark-line)}
.v2hero-navy .v2segs a{color:var(--on-dark-soft);border-color:var(--on-dark-line)}
.v2facts-hero{display:flex;flex-wrap:wrap;gap:22px 46px;margin-top:34px;padding-top:26px;
  border-top:1px solid var(--on-dark-line)}
.v2facts-hero strong{display:block;font-size:30px;color:#fff;letter-spacing:-.03em;line-height:1.1}
.v2facts-hero span{font-size:12.5px;color:var(--on-dark-soft)}

@media(max-width:900px){ .f-port{margin:0 auto}
  .f-quote{position:static;max-width:none;margin-top:14px} }
@media(max-width:720px){ .vswitch{padding:8px 14px;gap:6px;font-size:11.5px}
  .vswitch a{padding:4px 9px} body:has(.vswitch) .v2nav{top:unset;position:static} }

/* ============================================================================
   HERO E — the fit-finder. THE POINT IS THAT IT IS NOT A PICTURE.
   Five rounds of hero imagery failed for the same reason each time: a drawn
   object on a first screen acquires a meaning nobody chose (a chart said
   "accounting", stacked cards said "support chat"). A control cannot do that,
   because it announces what it is by being used.
   It is also not new. finsightic.com ALREADY runs this widget in its hero; the
   v2 rebuild had replaced a working conversion mechanism with decoration.
   ========================================================================== */
.v2fit{border:1px solid var(--line);border-radius:var(--radius);background:#fff;
  box-shadow:0 18px 44px -24px rgba(10,31,68,.34);overflow:hidden}
.v2fit-head{display:flex;align-items:center;gap:9px;padding:13px 20px;
  border-bottom:1px solid var(--line-soft);background:var(--bg-alt);
  font-size:12.5px;font-weight:700;color:var(--muted)}
.v2fit-dot{width:8px;height:8px;border-radius:50%;background:var(--blue);flex:0 0 auto}
.v2fit-body{padding:20px}
.v2fit-q{font-size:17px;font-weight:700;color:var(--ink);margin:0 0 15px;line-height:1.35}
.v2fit-opts{display:flex;flex-direction:column;gap:7px}
/* AN AUTHOR display RULE BEATS THE hidden ATTRIBUTE. The UA stylesheet sets
   [hidden]{display:none}, but display:flex above it wins on specificity, so the
   script set hidden and the five size options stayed on screen underneath the
   result. Found by looking at the rendered card; the script was correct. Any
   element given an explicit display needs this twin if anything toggles hidden. */
.v2fit-opts[hidden]{display:none}
.v2fit-opt{text-align:left;font:inherit;font-size:14px;color:var(--body);background:#fff;
  border:1px solid var(--line);border-radius:var(--radius-sm);padding:11px 14px;min-height:44px;
  cursor:pointer;transition:border-color var(--t) var(--ease),background var(--t) var(--ease),color var(--t) var(--ease)}
.v2fit-opt:hover{border-color:var(--blue-pale);background:var(--blue-wash);color:var(--ink)}
.v2fit-opt:focus-visible{outline:2px solid var(--blue);outline-offset:2px}
.v2fit-res{padding:2px 0 0}
.v2fit-rl{font-size:11px;font-weight:800;letter-spacing:.09em;text-transform:uppercase;
  color:var(--quiet);margin:0 0 6px}
.v2fit-rv{font-size:21px;font-weight:800;color:var(--ink);letter-spacing:-.02em;margin:0 0 7px}
.v2fit-rd{font-size:14px;color:var(--muted);margin:0 0 16px;line-height:1.5}
.v2fit-again{display:block;margin:12px 0 0;padding:6px 0;min-height:24px;border:0;background:none;
  font:inherit;font-size:13px;color:var(--muted);text-decoration:underline;cursor:pointer}
.v2fit-again:hover{color:var(--blue)}
.v2fit-steps{display:flex;gap:6px;margin-top:17px}
.v2fit-steps i{height:3px;flex:1;border-radius:2px;background:var(--line)}
.v2fit-steps i.on{background:var(--blue)}
@media(max-width:720px){ .v2fit-body{padding:17px} .v2fit-q{font-size:16px} }

/* ---- the fit-finder as a band, not a hero card --------------------------
   It lost the hero slot to the portal and kept its job. Two columns so the
   question card does not run the full rail, which would make a 30-second tool
   look like a form. */
.v2fitband{display:grid;grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr);gap:46px;align-items:center}
.v2fitband-t h2{margin:0 0 12px;max-width:16ch}
.v2fitband-t p{font-size:15.5px;color:var(--muted);margin:0;max-width:46ch}
@media(max-width:860px){ .v2fitband{grid-template-columns:1fr;gap:26px} }

/* TRIED AND REVERTED. Putting the cash screen in the lower section meant flipping
   the callout seats to its anchors (top-left 29.7/21.1, bottom-right 82/82), and
   the result hung the "Months covered" card outside the frame and over the
   section's own paragraph. The seats here are built around the OVERVIEW's
   geometry. Fixed by moving the cash screen to the HERO as a plain frame with no
   callouts, which needs no geometry at all, and leaving this section on the
   overview it was measured for. Two different screens, one set of proven seats. */

/* ---- hero: four popped sections, no frame ------------------------------
   TRIED AND SUPERSEDED: a cropped band of the whole screen with one callout.
   It fixed legibility and was still a picture of a dashboard. These are four
   element screenshots shown at or near the size the shoot script cut them,
   which is why every figure reads: a 226px tile rendered at 226px is sharp,
   the same tile inside a downscaled 1591px screen is four pixels of type.

   WIDTHS ARE PERCENTAGES OF THE COLUMN, chosen so each card lands near its
   native size in a ~500px column: 61% of 500 is 305 against a native 306.
   Set in px instead and they would be wrong at every other width. */
.v2pop{position:relative;aspect-ratio:1/.84;margin:0}
/* BORDERLESS, BUT NOT EDGELESS (Abu, 31 Oct: "if we do borderless pop-up would
   be even better"). He is right, and the reason is the product rather than
   taste: OS 0693 made the client portal one white sheet with the figures
   sitting on it directly, and every one of these PNGs is pure white at all four
   edges. The 1px line and the 11px radius were a card WE drew around a crop of
   a screen that has no cards in it.
   TAKING THE SHADOW OFF AS WELL WAS TRIED AND FAILED, and the failure shows up
   on one ground only. The service pages sit on #F5F7FC, where a white crop
   reads on its own; BOTH HEROES SIT ON WHITE, and there, with no border and no
   shadow, the pieces dissolved -- "12.0 mo", "6 of 6" and "Day 8" stopped being
   lifted pieces of a screen and became loose text on the page, which is the
   opposite of a pop-up. So the line goes and the elevation stays: the shadow
   says lifted, the absence of a border says we drew nothing.
   SQUARE CORNERS ON PURPOSE. A radius is the other half of the card signal, and
   these are rectangles cut out of a screen.
   THE SHADOW IS TWO LAYERS, not one. The old single downward shadow
   (0 18px 40px -16px) left the TOP edge of each piece with nothing at all on a
   white ground; the 1px contact layer is what makes the top read. */
.v2pop-c{position:absolute;margin:0;overflow:hidden;background:#fff;
  box-shadow:0 1px 3px rgba(10,31,68,.10),0 20px 46px -14px rgba(10,31,68,.28);
  transition:transform var(--t) var(--ease),box-shadow var(--t) var(--ease)}
.v2pop-c img{display:block;width:100%;height:auto}
/* STOPGAP FOR A CUT ASSET, AND IT EXPIRES ITSELF.
   snip-portal-cash-2 is sliced on its left edge: the shoot window took a
   CARD-type snippet with the TILE rule (the 12px-left shift recorded in
   CLAUDE.md, which is empty air on a tile and the first glyph on a card), so a
   month label arrives as a bare "'27". Measured: the orphan ink runs css 0.3 to
   18.7 of a 305px card and the first clear column is 19.0, so 20px removes it
   with a pixel to spare. That is a 6.6% upscale on a 3x asset, still far above
   native. The chart's marker, floor line and Jun/Sep labels all survive.
   THIS IS NOT THE FIX. The fix is re-cutting the snippet with proper left air,
   which needs the OS, which is unreachable (assets are shotAt 2026-09-25). The
   selector names the exact content hash on purpose: when that snippet is
   re-shot the filename changes, this rule stops matching, and the real crop
   takes over with nothing stale left behind. If it comes back still cut, it
   comes back VISIBLY rather than silently patched.
   PRE-EXISTING, not caused by the borderless change -- but borderless is why it
   had to be dealt with: a 1px line reads as "this card clips its content", and
   with the line gone the same slice reads as a broken image.
   THE CLIP IS A PERCENTAGE, NOT PIXELS, and getting that wrong is why the first
   attempt left a dark remnant. The card renders this 305px asset at 270px in
   the hero and 355px on a service page, so a flat 20px clip removed 22px of the
   image in one place and 17px in the other -- a fixed-pixel clip on a SCALED
   image does not remove a fixed amount OF THE IMAGE. 8.3% of the figure clips
   7.66% of the picture at every size, which is 23.4px of the 305px original
   against ink that ends at 19: four pixels of air, everywhere. */
.v2pop-c img[src$="snip-portal-cash-2.e2cfc5ad.png"]{
  width:108.3%;max-width:none;margin-left:-8.3%}
.v2pop-c:hover{transform:translateY(-4px);
  box-shadow:0 2px 5px rgba(10,31,68,.12),0 28px 58px -14px rgba(10,31,68,.36);z-index:5}
/* THE TILES CLIP CORNERS, NOT CONTENT. The first arrangement overlapped them
   across the middle of the chart and buried the one card with something to read
   in it: the projection line was half covered and "Sep '27" was cut mid-label.
   A collage that hides its own evidence is worse than no collage. The chart now
   holds the left, the three tiles stack down the right, and they meet in a 2 to
   4% seam that crosses nothing but the chart's margin. */
.v2pop-chart {left:0;  top:14%; width:56%; z-index:1}
.v2pop-months{right:0; top:0;   width:46%; z-index:3}
.v2pop-rec   {right:0; top:35%; width:46%; z-index:2}
.v2pop-closed{right:4%;top:70%; width:46%; z-index:4}
.v2pop-cap{margin-top:16px}

/* THE STACK BREAKPOINT IS THE HERO COLUMN BREAKPOINT, ONE DECISION NOT TWO.
   .v2hero-grid collapses to a single column at 1000px. With the stack still at
   900 the collage spent 100px of width in a full-width column, rendering every
   card at up to 581px against natives of 226 to 306. Same class of fault as the
   <picture> breakpoint that disagreed with the layout breakpoint on 23 Sep. */
@media(max-width:1000px){
  /* A STACK, NOT A COLLAGE. Overlapping cards need width to read; below this the
     column is too narrow and they start covering each other's figures, which is
     the one thing these cards exist to show. */
  .v2pop{aspect-ratio:auto;display:grid;grid-template-columns:1fr 1fr;gap:12px;
    justify-items:center}
  .v2pop-c{position:static;width:auto}
  /* NEVER ABOVE NATIVE. Stacked, the grid handed the chart the full column and it
     rendered 852px against a native 306 -- a 2.8x upscale, which throws away the
     one property these cards have. An element screenshot is sharp at the size it
     was cut and soft at any size above it, so each card is capped at its own. */
  .v2pop-chart{grid-column:1/-1;max-width:306px}
  .v2pop-months{max-width:226px}
  .v2pop-rec,.v2pop-closed{max-width:273px}
  .v2pop-c:hover{transform:none}
}
@media(max-width:520px){ .v2pop{grid-template-columns:1fr} }

/* The highlight is at its NATIVE 226px css width or close to it, which is why it
   reads crisply: it is an element screenshot shown at the size it was cut, not a
   downscale of the whole screen. */
.v2hilite{position:absolute;right:0;bottom:0;width:58%;max-width:268px;margin:0;
  border-radius:10px;overflow:hidden;background:#fff;border:1px solid var(--line);
  box-shadow:0 16px 38px -14px rgba(10,31,68,.38)}
.v2hilite img{display:block;width:100%;height:auto}
@media(max-width:900px){
  /* on a phone the band is already legible at full width, and a crop of a tile
     sitting over the tile it came from is the duplication the snippet note flags */
  .v2hilite,.v2crop-dot,.v2crop-lead{display:none}
  .v2heroshot{padding-bottom:0}
}

/* ---- service pages: two popped cards instead of a shrunken screen --------
   Measured before changing: the full frames rendered 908px against a 1591px
   native, so every figure sat at 57% and the one thing a product shot exists
   to show was half size. Two element screenshots at or near native read; a
   whole screen at 57% does not.

   TWO COLUMNS, NOT FULL RAIL. A popped pair capped at native inside a 1,072px
   rail would be two small cards adrift in a wide band. A text column beside
   them gives roughly 480px, which is where a 306px card sits comfortably. */
.v2portal2{display:grid;grid-template-columns:minmax(0,.95fr) minmax(0,1.05fr);gap:48px;align-items:center}
.v2portal2 .v2cap{margin-top:var(--stack-sm)}
.v2pop2{position:relative;aspect-ratio:1/.72}
.v2pop2 .v2pop-c{position:absolute}
/* capped at native: an element screenshot is sharp at the size it was cut and
   soft above it, which is the whole reason to use cuts rather than a screen */
.v2p2-1{left:0;   top:6%;  width:66%; max-width:357px; z-index:1}
/* inset rather than flush: a popped card sitting exactly on the container edge
   reads as clipped even when it is not, which a capture made obvious */
.v2p2-2{right:3%; bottom:4%;width:56%; max-width:306px; z-index:2}
@media(max-width:1000px){
  .v2portal2{grid-template-columns:1fr;gap:28px}
  .v2pop2{aspect-ratio:auto;display:flex;flex-wrap:wrap;gap:14px;justify-content:center}
  .v2pop2 .v2pop-c{position:static;width:auto;flex:0 1 auto}
}

/* ---- very narrow screens: a long CTA label must not push the page ---------
   Found on the BD home at 320px: "Request a free accounts review" is a long
   label, .btn carries 26px of padding each side and does not wrap, so the
   button ran 12px past the viewport. BD labels are longer than the US ones in
   both languages, which is why this surfaced there first and not on /. */
@media(max-width:380px){
  .btn{padding:0 16px;font-size:14px}
  .v2hero .actions .btn,.v2close .actions .btn{width:100%;justify-content:center}
  /* THE NAV BUTTON WAS THE ACTUAL CULPRIT, not the hero CTA. My first rule
     targeted .v2hero .actions .btn and the overflow was .v2nav .btn, so the
     fix moved 12px to 8px and I nearly shipped it as done. The nav wraps to a
     second line here instead of forcing the page wider. */
  .v2nav-in{flex-wrap:wrap;height:auto;padding:10px 0;gap:8px 14px}
  .v2nav .btn{padding:0 12px;font-size:13px}
  .v2login{padding-left:0;border-left:0;margin-left:0}
}

/* THE LOGOS. The v2 chrome shipped a text wordmark and the port replaced
   logo.png and logo-footer.png with the word "Finsightic" on 24 live pages
   before Abu caught it on /about. The images are restored by tools/port_v2.py,
   and these are the rules that used to live in each page's own inlined
   stylesheet -- the one the port removes. Without them the footer logo renders
   at its attribute size, 200x60, instead of the 28px tall it has always been.
   width:auto is load-bearing: the width attribute is only the aspect hint, so
   setting height alone without it squashes the mark. */
.v2nav .logo-img{height:38px;width:auto;display:block}
.v2foot .ft-logo{height:28px;width:auto;display:block}
.v2foot .ft-brand-link{display:inline-block;margin-bottom:10px}

/* The band that replaced the price table on every page but /pricing. */
.v2plink{text-align:center}
.v2plink h2{margin:0 0 10px}
.v2plink .sec-intro{max-width:620px;margin:0 auto 20px}
@media(max-width:700px){
  .v2nav-in{gap:12px}
  /* The portal link now SHOWS on a phone. Measured at 390 and 360: the header bar
   has 73px and 60px free, "Client portal" needs 78px and does not fit, "Portal"
   needs 38px and does. Both strings live in the markup and swap in CSS, with the
   full name on aria-label, so a screen reader never hears the abbreviation.
   Same pattern as the portal tab labels. */
.v2login{display:inline-flex;align-items:center;min-height:24px;margin-left:0;padding-left:0;border-left:0}
.v2login-l{display:none}.v2login-s{display:inline}
  /* The header CTA wrapped to two lines once the portal link took its width:
     109px box, 18px padding each side, 73px of text area for a label needing
     ~80px. nowrap keeps it on one line and the row still fits (361 of 390). */
  /* The header CTA was full size on the common phone widths: the compact rule
     only started at 380px, so 390 and 412 rendered 110x38 at 14px with 18px
     padding, which is 28% of the whole bar and the same height as the logo.
     The compact treatment now covers the phone range, where the burger is
     showing and space is tight: 24% of the bar and clearly below the logo,
     still far above the 24px target. */
  .v2nav .btn{white-space:nowrap;padding:0 14px;font-size:13px;height:34px;min-height:34px}
}

/* Under 340px the brand, burger, portal link and CTA total 338px and the header
   breaks to two rows. The portal link is the one that is also in the menu, so it
   is the one that goes; measured single-row at 360 and 390, which is where the
   phones actually are. MUST STAY LAST: the max-width:700 block sets
   .v2login{display:inline-flex} at equal specificity, so source order decides. */
@media(max-width:340px){ .v2login{display:none} }
