/* ============================================================================
   Truvenn Advisors — design system
   Type:   Barlow (display + body), Barlow Condensed (labels), Newsreader (clauses)
   Colour: a navy ramp. Orange is the logo's alone — it is not a UI colour.
           Emphasis comes from weight, rule and depth, never from hue.
           Photography is never tinted.
   ========================================================================== */

/* ---------------------------------------------------------------- fonts -- */
@font-face{font-family:'Barlow';font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/barlow-400-latin.woff2") format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Barlow';font-style:normal;font-weight:400;font-display:swap;
  src:url("fonts/barlow-400-latin-ext.woff2") format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'Barlow';font-style:normal;font-weight:500;font-display:swap;
  src:url("fonts/barlow-500-latin.woff2") format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Barlow';font-style:normal;font-weight:500;font-display:swap;
  src:url("fonts/barlow-500-latin-ext.woff2") format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'Barlow';font-style:normal;font-weight:700;font-display:swap;
  src:url("fonts/barlow-700-latin.woff2") format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Barlow';font-style:normal;font-weight:700;font-display:swap;
  src:url("fonts/barlow-700-latin-ext.woff2") format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'Barlow Condensed';font-style:normal;font-weight:600;font-display:swap;
  src:url("fonts/barlow-condensed-600-latin.woff2") format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Barlow Condensed';font-style:normal;font-weight:600;font-display:swap;
  src:url("fonts/barlow-condensed-600-latin-ext.woff2") format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'Newsreader';font-style:normal;font-weight:400 500;font-display:swap;
  src:url("fonts/newsreader-latin.woff2") format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:'Newsreader';font-style:normal;font-weight:400 500;font-display:swap;
  src:url("fonts/newsreader-latin-ext.woff2") format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:'Newsreader';font-style:italic;font-weight:400 500;font-display:swap;
  src:url("fonts/newsreader-italic-latin.woff2") format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}

/* --------------------------------------------------------------- tokens -- */
:root{
  /* Navy ramp. Orange was retired as a UI colour: at the sizes the site uses it
     it read as a tint over the whole page rather than as a signal, which is the
     machine-generated look this design exists to avoid. Orange now survives in
     one place only — the logo, where it is the brand's own. Emphasis is carried
     by weight, rule and depth of navy instead of by hue. */
  --ink:#0D1922;
  --deep:#0D1922;
  --paper:#FFFFFF;
  --mist:#F2F5F7;
  --line:#DCE3E9;
  --line-strong:#0D1922;

  /* --ink-2 is for heading and bold text set on paper — --ink/--deep stay
     #0D1922 because that same value is also the fill behind every dark band,
     and needs to stay genuinely dark for white text to sit on it. --ink-2 is
     lighter and less blue-dominant than --ink: chosen 2026-08-23 after --ink
     at weight 700 read as colder and heavier than intended for body-adjacent
     headings. Never used as a fill. */
  --ink-2:#2B313A;

  /* --steel carries text on white (#2C4459 is ~9.8:1, comfortably AA).
     --steel-2 is for focus rings and hovers, --steel-3 only ever sits on a
     dark band, where it is the light end of the ramp. --slate is muted body. */
  --steel:#2C4459;
  --steel-2:#44637D;
  --steel-3:#84A2BC;
  --slate:#54646F;
  /* Body copy sitting on a dark band. Light enough for AA on --deep without
     going pure white, which would compete with the headings. */
  --on-dark:#B9C6D0;

  /* Type families. Three roles, no exceptions:
     display = everything structural, label = tracked uppercase eyebrows,
     serif   = the written commitments only. */
  --display:'Barlow',system-ui,-apple-system,sans-serif;
  --label:'Barlow Condensed','Barlow',system-ui,sans-serif;
  --serif:'Newsreader',Georgia,serif;

  /* Type scale. Every size on the site comes from this list — nothing sets a
     font-size in a page. Fluid steps interpolate between the mobile and
     desktop values, so a heading is never tuned per breakpoint. */
  --text-3xl:clamp(2.5rem,4.6vw,5.3rem);   /* h1                    */
  --text-2xl:clamp(1.75rem,2.6vw,3.1rem);  /* h2, section titles    */
  --text-xl:clamp(1.25rem,1.6vw,1.7rem);   /* h3, card titles       */
  --text-lg:clamp(1.18rem,0.85rem + 0.37vw,1.44rem);   /* h4            */
  /* The lede's old ceiling was 20.16px, reached at 1391px. Once --text-base
     scaled past 19px the two nearly met and the lede stopped reading as a
     lede, so its ceiling moves with them. */
  --text-md:clamp(1.05rem,0.95rem + 0.343vw,1.5rem);   /* lede  20→24    */
  /* The small steps are fluid too, and that is not cosmetic. They used to be
     fixed rem while the headings scaled, so on a 2560 display h1 was 85px and
     a .label was still 13px — a 6.5:1 ratio at which Barlow Condensed loses
     its character entirely and reads as the wrong typeface rather than as a
     small one. Each ramp is tuned to leave the value unchanged at 1440 and
     reach roughly +22% at 2560, so nothing shifts on a laptop. */
  --text-base:clamp(1rem,0.72rem + 0.31vw,1.22rem);    /* body     16→19 */
  --text-sm:clamp(0.9375rem,0.67rem + 0.30vw,1.14rem); /* 2nd body 15→18 */
  --text-xs:clamp(0.875rem,0.63rem + 0.28vw,1.07rem);  /* captions 14→17 */
  --text-2xs:clamp(0.8125rem,0.58rem + 0.26vw,0.99rem);/* labels   13→16 */

  --lh-tight:1.08;
  --lh-snug:1.55;
  --lh-body:1.62;
  --track-tight:-.035em;
  --track-snug:-.025em;
  --track-label:.16em;

  /* Spacing scale */
  --space-1:4px;
  --space-2:8px;
  --space-3:14px;
  --space-4:20px;
  --space-5:28px;
  --space-6:40px;
  --space-7:56px;

  /* --wrap is the content measure, not the page width, and it is relative:
     the layout takes the full width of its container and only stops at a
     ceiling. Below that ceiling nothing about the page is a fixed pixel width.

     `100%` rather than `vw`: a percentage resolves against the containing
     block, which already excludes the scrollbar, so this cannot overflow on
     platforms with classic scrollbars the way a `100vw` width would. It also
     means there is no wasted margin at all below the ceiling — the gutter is
     the only inset, which matters most on a phone.

     Widening costs nothing in image quality — every photo is sized by
     `aspect-ratio` + `object-fit:cover`, so it scales rather than re-crops —
     and nothing in readability, because every prose block is capped in `ch`
     (.lede, .clause, .block p, .measure). The surplus goes to the grid.

     The ceiling is set for a 2560px display, the widest common desktop, so
     that a maximised window still fills. It is not a design maximum — the
     grids below reflow their column count and every text block is capped in
     `ch`, so the page holds together at this width rather than merely fitting
     into it. */
  --wrap:min(100%,2240px);
  --gutter:clamp(24px,4.5vw,96px);
  /* Two adjacent sections each contribute a --band, so this value is doubled
     wherever they meet. At the old 132px ceiling that put 264px between the
     practices grid and the band below it — nearly a fifth of a 1440px screen,
     and it read as the page coming apart rather than as rhythm. */
  --band:clamp(48px,4.6vw,92px);

  /* Masthead height lives here because the sticky aside on the services page
     has to clear it. Hardcoding the offset means the two drift apart the next
     time the logo changes size — which is exactly what happened: 112px was
     sized around a 72px lockup, and the venn lockup measures 47px, which left
     32px of air a side instead of the ~20px the bar was tuned for. */
  --mast:96px;
}

/* ---------------------------------------------------------------- reset -- */
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{
  margin:0;background:var(--paper);color:var(--ink);
  font-family:var(--display);font-size:var(--text-base);line-height:var(--lh-body);
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
}
img{display:block;max-width:100%}
h1,h2,h3,h4{margin:0;font-weight:500;color:var(--ink-2);letter-spacing:var(--track-snug);line-height:var(--lh-tight)}
p{margin:0}
ul{margin:0;padding:0;list-style:none}
/* <figure> carries a UA default of `margin: 1em 40px`. Every .shot is a figure,
   so without this reset each photograph is silently inset by 40px a side. It
   went unnoticed until the hero began bleeding to the screen edge. */
figure{margin:0}
/* Same trap as <figure>: <dl> carries a UA `margin: 1em 0`. .facts is the only
   dl on the site, and inside .diagnostic-panel that phantom 16px was reading as
   deliberate spacing above the hairline. Reset it and let the components set
   their own. */
dl{margin:0}
a{color:inherit}
:focus-visible{outline:2px solid var(--steel-2);outline-offset:3px}
::selection{background:var(--ink);color:#fff}

.wrap{max-width:var(--wrap);margin:0 auto;padding-inline:var(--gutter)}
.skip{position:absolute;left:-9999px;top:0;z-index:100;background:var(--deep);color:#fff;padding:12px 18px}
.skip:focus{left:0}

h1{font-size:var(--text-3xl);letter-spacing:var(--track-tight);line-height:1.03}
h2{font-size:var(--text-2xl)}
h3{font-size:var(--text-xl)}
h4{font-size:var(--text-lg);letter-spacing:-.018em}

/* --------------------------------------------------------------- labels -- */
.label{
  font-family:var(--label);font-weight:600;font-size:var(--text-2xs);
  letter-spacing:var(--track-label);text-transform:uppercase;color:var(--steel);
}
.label-quiet{color:var(--slate)}
/* A real heading carrying label weight. Some sections need to exist in the
   document outline without taking a 50px h2 — the practice index is one: it is
   a navigational block, not a content section. Style the heading down rather
   than replace it with a <p class="label">, which is what left that section
   with no heading at all until Aug 2026. Never reach for visually-hidden text
   here; the heading should be visible, just quiet. */
h2.h-label,h3.h-label{
  font-family:var(--label);font-weight:600;font-size:var(--text-2xs);
  letter-spacing:var(--track-label);text-transform:uppercase;color:var(--slate);
}
.label-sm{font-size:clamp(0.75rem,0.54rem + 0.24vw,0.92rem)}
.lede{font-size:var(--text-md);line-height:var(--lh-snug);color:var(--slate);max-width:60ch}
/* No cap: its one use (careers.html's "no open roles" message) is a lone
   paragraph in a single-column section with nothing beside it to run under. */
.prose-lead{font-size:var(--text-md)}
.measure{max-width:54ch}

/* Links inside running text. `a{color:inherit}` above keeps navigation and
   cards from turning orange, so prose links have to opt back in. */
.link-signal,
.rule-item a,.prose-lead a,.block p a,.clause p a,.lede a{
  color:var(--steel);text-decoration:underline;text-underline-offset:3px;
}
.link-signal:hover,.rule-item a:hover,.prose-lead a:hover{color:var(--steel-2)}

/* -------------------------------------------------------------- buttons -- */
.btn{
  display:inline-flex;align-items:center;gap:10px;
  font-family:var(--display);font-weight:600;font-size:var(--text-sm);line-height:1;
  padding:14px 22px;text-decoration:none;border:1px solid var(--ink);
  background:var(--ink);color:#fff;cursor:pointer;
  transition:background .18s ease,border-color .18s ease,color .18s ease;
}
.btn:hover{background:var(--steel);border-color:var(--steel)}
.btn .arw{transition:transform .18s ease}
.btn:hover .arw{transform:translateX(3px)}
.btn-outline{background:transparent;border-color:var(--line-strong);color:var(--ink)}
.btn-outline:hover{background:var(--ink);border-color:var(--ink);color:#fff}
.btn-ondark{background:transparent;border-color:rgba(255,255,255,.4);color:#fff}
.btn-ondark:hover{background:#fff;border-color:#fff;color:var(--deep)}
/* A solid .btn on a dark band is --ink on --deep — the same navy — so it
   renders as an invisible box behind white text. That shipped on all five
   .band-cta sections. Applying the treatment from the band rather than relying
   on the page to remember `.btn-ondark` means it cannot regress. */
.band-dark .btn:not(.btn-outline){
  background:transparent;border-color:rgba(255,255,255,.4);color:#fff;
}
.band-dark .btn:not(.btn-outline):hover{background:#fff;border-color:#fff;color:var(--deep)}

.arwlink{
  font-family:var(--display);font-weight:600;font-size:var(--text-sm);color:var(--steel);
  text-decoration:none;display:inline-flex;align-items:center;gap:8px;
  border-bottom:1px solid var(--ink);padding-bottom:2px;
}
.arwlink .arw{transition:transform .18s ease}
.arwlink:hover .arw{transform:translateX(4px)}

/* ------------------------------------------------------------- masthead -- */
.masthead{position:sticky;top:0;z-index:60;background:var(--paper);border-bottom:1px solid var(--line)}
/* The masthead is the one band that ignores --wrap: it spans the full viewport
   and takes only the gutter, so the logo sits at the left edge of the page and
   the nav at the right. Capping it at --wrap left it visibly inset from the
   rule beneath it on a wide screen, which read as a narrower site rather than
   as alignment. The trade is that the logo no longer lines up with the content
   column below — that is accepted deliberately; the masthead reads as chrome,
   not as the first row of the grid. */
.masthead .wrap{display:flex;align-items:center;gap:28px;min-height:var(--mast);max-width:none}
/* ---- the brand lockup ---------------------------------------------------

   The mark is SVG and the wordmark is live text, not a flattened image. That
   is what lets the horizontal lockup and the footer stamp share one set of
   type rules, and it is why there is no longer a declared width/height to
   drift out of sync with the artwork (the old footer PNG claimed 330x115 for
   a 315x101 file and reserved a box 15px too narrow on every page load).

   These sizes are deliberately NOT on the fluid type scale. A wordmark is
   artwork: it holds a fixed proportion and steps at a breakpoint, the way
   .brand img{height:72px} always did. Scaling it with the viewport would let
   the mark and the type drift against each other. --track-brand is the
   wordmark's own tracking; it is wider than --track-label because the venn
   sets a wide, open rhythm the name has to match. */
.brand{
  /* One size drives the whole lockup, so the mark, the name and the descriptor
     hold their proportions to each other while the lockup as a whole tracks the
     page. It was briefly a fixed 30px, which is the trap: everything around it
     is fluid, so between 1280 and 2560 the name went from 1.88x the nav to
     1.54x — the brand quietly lost a fifth of its presence on the widest
     screens, which are the ones it most needs to hold. This clamp follows the
     same curve as --text-base, scaled to keep that ratio at ~1.85 throughout. */
  --brand:clamp(30px,21.3px + 0.573vw,36px);
  margin-right:auto;display:flex;align-items:center;flex-shrink:0;
  gap:calc(var(--brand) * .44);text-decoration:none;color:var(--ink);
}
.brand-mark{height:calc(var(--brand) * 1.47);width:auto}
.brand-name{display:flex;flex-direction:column;align-items:center}
/* .05em, chosen against the nav at its widest size. Uppercase Barlow Bold at
   .10em read airy rather than confident; below .03em the N-N pair starts to
   fuse and the name reads compressed. .05em is the dense end of the usable
   range. The descriptor keeps its own much wider tracking — it is a label, not
   part of the name. */
.brand-word{
  font-size:var(--brand);font-weight:700;line-height:1;letter-spacing:.05em;
  text-transform:uppercase;color:var(--ink);
}
.brand-sub{
  font-size:calc(var(--brand) * .40);font-weight:500;line-height:1;
  letter-spacing:.38em;text-transform:uppercase;color:var(--steel);
  margin-top:calc(var(--brand) * .2);
}
/* The name is set in sentence case in the markup and uppercased here on
   purpose: a screen reader says "Truvenn", not "T R U V E N N". */
.mainnav{display:flex;align-items:center;gap:30px}
.mainnav a{
  text-decoration:none;font-size:var(--text-base);font-weight:500;color:var(--ink);
  padding-block:4px;position:relative;white-space:nowrap;
}
.mainnav a::after{
  content:"";position:absolute;left:0;right:0;bottom:-2px;height:2px;background:var(--ink);
  transform:scaleX(0);transform-origin:left;transition:transform .2s ease;
}
.mainnav a:hover::after{transform:scaleX(1)}
.mainnav a[aria-current="page"]{font-weight:600}
.mainnav a[aria-current="page"]::after{transform:scaleX(1)}
.navtoggle{display:none}
.burger{display:none}

/* ----------------------------------------------------------------- hero -- */
.hero{padding-top:clamp(52px,7vw,92px);padding-bottom:calc(var(--band) * .5)}
/* Interior pages open on a dark band. It gives the sections below it something
   to resolve against, and it is the only full-width surface on the site — the
   navy does the work orange used to do, at a scale where a colour can carry
   weight without reading as a tint. The split hero opts out: it has a
   photograph doing the same job. */
/* Two columns, not one. Set as a single stack the band ran 582px tall with 63%
   of its width empty — on a 1440px-high screen that is 40% of the first screen
   given to a mostly blank navy rectangle. The headline now takes the left
   column and the lede sits beside it, bottom-aligned, which uses the width and
   takes roughly a third off the height. All three pages that use this hero
   (services, industries, insights) have exactly label/h1/lede as the children
   of .wrap, so the placement below needs no wrapper. */
.hero:not(.hero-split):not(.hero-bleed){
  background:var(--deep);color:#fff;
  /* Less above than below, so the block sits high in the band rather than
     centred in it — the band is a masthead, and a masthead's weight belongs at
     the top. The top step is ~20% under the bottom one. */
  padding-block:clamp(38px,4.8vw,60px) clamp(48px,6vw,76px);
}
/* align-items:start, not end. Bottom-aligning the two columns made the offset
   between the headline's first line and the lede's depend on how many lines the
   lede happened to have — 38px on services, 75px on industries, 25px on
   insights. An inconsistent relationship is what reads as "not aligned"; a
   shared top edge is stable on every page. */
.hero:not(.hero-split):not(.hero-bleed) .wrap{
  display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);
  column-gap:clamp(32px,5vw,80px);align-items:start;
}
.hero:not(.hero-split):not(.hero-bleed) .label{grid-column:1;grid-row:1}
.hero:not(.hero-split):not(.hero-bleed) h1{grid-column:1;grid-row:2;color:#fff;font-weight:500;max-width:none}
.hero:not(.hero-split):not(.hero-bleed) .lede{
  grid-column:2;grid-row:2;color:var(--on-dark);max-width:46ch;margin-top:0;
}
.hero:not(.hero-split):not(.hero-bleed) .label{color:var(--steel-3)}
.hero:not(.hero-split):not(.hero-bleed) .hero-facts{border-top-color:rgba(255,255,255,.22);color:var(--on-dark)}
/* One step up from the default label. A hero eyebrow sits under an 85px
   headline, and at --text-2xs it was too quiet to register as part of the
   composition. Scoped to .hero so every page opens the same way and the
   smaller labels elsewhere (footer legal, .block h3) are untouched. */
.hero .label{font-size:var(--text-sm)}
.hero h1{margin:18px 0 0;max-width:17ch}
.hero .lede{margin-top:24px}
.hero-actions{display:flex;flex-wrap:wrap;gap:14px;margin-top:32px}
/* These are claims about the firm, not captions, so they carry --steel at 500
   rather than muted --slate. At 15px in --slate they were legible by the
   contrast metric and still hard to actually read. */
.hero-facts{
  display:flex;flex-wrap:wrap;gap:12px 30px;margin-top:40px;padding-top:16px;
  border-top:1px solid var(--line);font-size:var(--text-sm);font-weight:500;
  color:var(--steel);
}
/* The split hero sits on the same grid as every section below it: the text
   column starts on the --wrap line and the photograph ends on it. An earlier
   version bled the photo to the right edge of the screen; it read as a crop
   running off the page rather than as a deliberate edge, so the hero was put
   back inside the measure.

   The row height is driven by the text, not by the viewport. Pinning it to
   100svh left the copy floating in the middle of an 820px band with ~165px of
   dead space under it, which is what pushed the first section so far down the
   page. Now the text column sets the height and the photo matches it. */
/* The hero fills the first screen. Note this is NOT the earlier 100svh hero
   that was removed: that one left 490px of copy centred in an 820px band
   because the image was silently driving the height (see the note on the img
   below). Here the text sets its own padding and the photo fills the column,
   so the height is occupied rather than padded. There is no bottom padding —
   the section that follows brings a full --band of its own, and stacking the
   two is what made the first section look adrift.

   min-height, not height: if the copy is ever longer than the screen the hero
   simply grows. Nothing is ever clipped. */
.hero-split{padding-block:0}
.hero-split .wrap{
  display:grid;grid-template-columns:minmax(0,0.95fr) minmax(0,1fr);
  gap:clamp(32px,5vw,72px);align-items:stretch;
  min-height:calc(100svh - var(--mast));
}
.hero-split .wrap > :first-child{
  align-self:center;padding-block:clamp(40px,5vw,80px);
}
/* No ch cap, and text-wrap:balance instead. A hard cap forces a break point
   that is right at one width and wrong at every other; balance lets the
   browser choose, and never leaves the orphan ("…acted / on.") that a 17ch cap
   produced. The headline needs 936px to set on one line, so it does exactly
   that above roughly 2400px and wraps to two balanced lines below — at 1920px
   it is 166px short, so one line is not reachable there at any sane size. */
.hero-split h1{max-width:none;text-wrap:balance}
/* The photo fills the row rather than declaring a ratio, so its height tracks
   the text beside it — this is the ONE image on the site whose ratio is not
   fixed. object-fit:cover still applies, so it crops instead of distorting.

   The img is absolutely positioned for a reason. As a normal flow child,
   `height:100%` cannot resolve — the figure is sized by the grid's stretch, so
   the percentage has no definite basis — and the image silently falls back to
   its intrinsic 3:2, which then becomes the tallest thing in the row and sets
   the height itself. Taking it out of flow leaves the text as the only item
   that contributes height, which is the whole point. The min-height on the
   figure is a floor for short copy, not the usual case. */
.hero-split .shot{position:relative;height:100%;min-height:clamp(300px,26vw,460px);border:0}
.hero-split .shot img{
  position:absolute;inset:0;width:100%;height:100%;
  aspect-ratio:auto;object-fit:cover;
}

/* ----------------------------------------------------- full-bleed hero --
   The home page opens on the photograph itself, with the copy over it.

   THIS IS THE ONE PLACE A PHOTOGRAPH IS TREATED, and it is a deliberate,
   bounded exception to principle 2 — see "Photography stays documentary" in
   DESIGN.md, which records the exception and its limits. It applies to the home
   hero and nowhere else. Every other image on the site runs in its own colour.

   The scrim is a gradient rather than a flat wash because the copy occupies the
   left of the frame only: it is dense enough to carry text where the text is
   (85-94% at the left, measured 9.4:1 for white on the worst pixel under the
   headline) and thins to 30% at the right, where the photograph is left to read
   as a photograph. That asymmetry is the point — a flat scrim heavy enough for
   the copy would dim the whole picture.

   The consequence, and the reason this comment exists: the right of the frame
   is NOT safe for text. White there measures ~2.0:1. If copy is ever widened,
   centred, or moved across this hero, the gradient has to be re-cut for it.
   Contrast here is a function of position, not a constant. */
.hero-bleed{
  position:relative;overflow:hidden;display:flex;align-items:center;color:#fff;
  min-height:calc(100svh - var(--mast));
}
/* A real <img>, not a background-image: it keeps width/height (so the layout
   does not shift) and fetchpriority (it is the LCP element). alt is empty
   because the headline over it carries the meaning — it is not content. */
.hero-bleed .hero-bg{
  position:absolute;inset:0;z-index:0;
  width:100%;height:100%;aspect-ratio:auto;object-fit:cover;
}
.hero-bleed::before{
  content:"";position:absolute;inset:0;z-index:1;
  background:linear-gradient(95deg,
    rgba(13,25,34,.94) 0%, rgba(13,25,34,.72) 52%, rgba(13,25,34,.30) 100%);
}
.hero-bleed .wrap{position:relative;z-index:2;width:100%}
/* The measure that keeps the copy inside the dense end of the gradient. */
.hero-bleed .wrap > div{max-width:62ch}
.hero-bleed h1{color:#fff;font-weight:500;max-width:none;text-wrap:balance}
.hero-bleed .label{color:var(--steel-3)}
.hero-bleed .lede{color:var(--on-dark)}
.hero-bleed .hero-facts{color:var(--on-dark);border-top-color:rgba(255,255,255,.25)}
.hero-bleed .hero-facts span{white-space:nowrap}
/* A solid .btn here would be --ink over a dark scrim. Inverted, as on the
   navy bands: white is the primary action, outline is the secondary. */
.hero-bleed .btn{background:#fff;border-color:#fff;color:var(--ink)}
.hero-bleed .btn:hover{background:var(--steel-3);border-color:var(--steel-3);color:var(--ink)}
.hero-bleed .btn-outline{background:transparent;border-color:rgba(255,255,255,.55);color:#fff}
.hero-bleed .btn-outline:hover{background:rgba(255,255,255,.14);border-color:#fff;color:#fff}

/* ---------------------------------------------------------------- shots --
   Photography runs untinted: a hairline frame and a slow scale on hover, so
   images read as documentary rather than decorative. */
.shot{overflow:hidden;background:var(--mist);border:1px solid var(--line)}
/* height:auto is required, not cosmetic. Every <img> carries width/height
   attributes (so the browser can reserve space and avoid layout shift), and
   those map to a presentational `height: 853px`. Without height:auto that hint
   wins, aspect-ratio is ignored, and every photograph renders portrait. */
.shot img{
  width:100%;height:auto;aspect-ratio:3/2;object-fit:cover;
  transition:transform .6s cubic-bezier(.2,.7,.3,1);
}
.shot:hover img{transform:scale(1.03)}

/* -------------------------------------------------------------- section -- */
.section{padding-block:calc(var(--band) * .5)}
.section-mist{background:var(--mist)}
.section-head{
  display:flex;align-items:baseline;justify-content:space-between;gap:24px;
  flex-wrap:wrap;padding-bottom:16px;border-bottom:2px solid var(--line-strong);
  margin-bottom:clamp(26px,3vw,40px);
}
.section-head p{font-size:var(--text-sm);color:var(--slate)}

/* A numeral and the title it indexes, set on one line — "01 Strategy &
   Transformation". This is the idiom .index-row already uses on the services
   page, so the same five practices are now indexed the same way in both
   places. Baseline, not centre: the numeral is about two-thirds the title's
   size, and centring would leave it floating above the title's baseline
   instead of sitting on it. */
.num-title{display:flex;align-items:baseline;gap:.6em;flex-wrap:wrap}
.num-title > :is(h3,h4,h5){margin:0}

/* ---- the spine device: a navy left rule flags a stated position -------- */
.rule-list{display:grid;gap:26px}
.rule-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}
.rule-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}
.rule-item{border-left:2px solid var(--ink);padding-left:18px}
/* Same treatment as .card-num, deliberately: the page should have one way of
   numbering, not two. This was a fixed 1.6rem — larger than the h5 it labels,
   so the marker outshouted the step name. A numeral here is an index, and the
   left rule is already carrying the emphasis. */
.rule-item .step{
  font-family:var(--label);font-size:var(--text-sm);
  letter-spacing:var(--track-label);color:var(--steel);line-height:1;
}
/* The h4's own margins are zeroed inside .num-title, so the spacing moves to
   the wrapper. */
.rule-item h4{margin:6px 0 8px}
.rule-item .num-title{margin:0 0 8px}
.rule-item h5{margin:0 0 4px;font-size:var(--text-base);font-weight:700;letter-spacing:-.01em}
.rule-item p{font-size:var(--text-sm);color:var(--slate);max-width:46ch}
/* The 46ch cap is sized for a grid column (.rule-cols-3/4), where the column
   is already narrower than that. In a plain .rule-list — one item spanning
   the full width, as on the Insights publication-standard section — the same
   cap runs well inside the space the item actually has. */
.rule-list:not(.rule-cols-3):not(.rule-cols-4) .rule-item p{max-width:none}

/* ---------------------------------------------------------------- cards -- */
.cardgrid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:20px}
.cardgrid-2{grid-template-columns:repeat(2,minmax(0,1fr))}
.card{
  display:flex;flex-direction:column;gap:10px;
  border:1px solid var(--line);background:var(--paper);
  padding:24px;text-decoration:none;color:inherit;
  transition:border-color .2s ease,transform .2s ease;
}
a.card:hover{border-color:var(--ink);transform:translateY(-3px)}
/* .card-num is for a numeral ("01"); .card-kicker is for a word ("Financial
   services") and is uppercased so it reads as a label rather than a stray
   letterspaced sentence. */
/* --text-sm, matching .index-num on the services page: the two are the same
   device indexing the same five practices, so they take the same size. */
.card-num,.card-kicker{
  font-family:var(--label);font-size:var(--text-sm);
  letter-spacing:var(--track-label);color:var(--steel);
}
.card-kicker{text-transform:uppercase}
.card h3{margin:0}
.card p{font-size:var(--text-sm);color:var(--slate);flex:1;max-width:52ch}
.card .arwlink{border:0;padding:0;font-size:var(--text-xs);align-self:flex-start}
.card-media{margin:-24px -24px 6px;border-bottom:1px solid var(--line)}
.card-media img{width:100%;height:auto;aspect-ratio:16/9;object-fit:cover}
.card-meta{font-size:var(--text-2xs);color:var(--slate);font-family:var(--label);letter-spacing:.06em;text-transform:uppercase}
/* Not a sixth practice — an aside for a reader who cannot place their problem
   in one of the five. It occupies the grid's last cell because that cell would
   otherwise be empty, but it must not outweigh the services beside it: no
   numeral, a step down the type scale, and no flex:1 on its paragraph. That
   stretch is right for a card with three lines of body copy; with one line it
   opened a void between the text and the link that read as a broken layout. */
.card-invite{background:var(--mist);border-color:var(--mist);justify-content:center}
.card-invite h3{font-size:var(--text-lg)}
.card-invite p{flex:none}

/* --------------------------------------------------- services: contents -- */
/* The title column is capped, not a half-share of the row. At `1fr 1fr` each
   column was ~946px on a 2560 screen while the text filled a third of it, so
   the title ended at 648px and its audience did not start until 1306 — a 658px
   void between two things that describe each other. The eye cannot bind them
   across that. A capped title column puts the audience directly after the
   longest practice name at every width.

   The arrow stays at the row's right edge: a terminal chevron on a full-width
   row with a hover fill is the conventional affordance, and the fill is what
   binds it to the rest of the row. */
.index-row{
  display:grid;
  grid-template-columns:64px clamp(240px,26vw,460px) minmax(0,1fr) 32px;
  align-items:center;gap:20px;padding:20px 0;border-bottom:1px solid var(--line);
  text-decoration:none;color:inherit;transition:background .18s ease,padding-inline .18s ease;
}
.index-row:hover{background:var(--mist);padding-inline:16px}
.index-num{font-family:var(--label);font-size:var(--text-sm);letter-spacing:.1em;color:var(--slate)}
/* Same weight/colour move as h1-h4: this is bold text on paper doing a
   heading's job (the practice's name), and it was still at the old --ink/700
   pairing the headings moved off of. */
.index-name{font-size:var(--text-xl);font-weight:500;color:var(--ink-2);letter-spacing:-.02em}
.index-for{font-size:var(--text-sm);color:var(--slate);max-width:44ch}
.index-row .arw{justify-self:end;color:var(--steel);transition:transform .18s ease}
.index-row:hover .arw{transform:translateX(4px)}

/* -------------------------------------------------- services: practices -- */
.practice{padding-block:var(--band);border-top:1px solid var(--line)}
.practice:nth-of-type(even){background:var(--mist)}
.practice .wrap{
  display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);
  gap:clamp(32px,5vw,80px);align-items:start;
}
.practice-aside{position:sticky;top:calc(var(--mast) + 20px)}
/* Identical to .block h3, which is the eyebrow in the column beside it. The two
   sit on the same grid row and do the same job, but were 17.1px/.18em/1.62 and
   15.8px/.16em/1.08 — so their boxes aligned at the top while their baselines
   did not, and the two columns read as starting at different heights. */
.practice-num{
  font-family:var(--label);font-weight:600;font-size:var(--text-2xs);
  letter-spacing:var(--track-label);text-transform:uppercase;color:var(--steel);
  line-height:var(--lh-tight);
}
.practice-aside h2{margin:10px 0 14px}
/* No ch cap: the standfirst runs to the width of the photograph below it. At
   40ch it set to 440px against an 820px image at 2560px — 54% of its own
   column — which read as a short ragged block floating over a full-width
   photo rather than as a caption to it. The aside column is bounded by --wrap,
   so the widest this can ever get is ~74ch at 2560px: the top of the
   comfortable measure, not past it. */
.practice-who{color:var(--slate)}
.practice-aside .shot{margin-top:28px}
.practice-body{display:flex;flex-direction:column;gap:36px}
.block h3{
  font-family:var(--label);font-weight:600;font-size:var(--text-2xs);letter-spacing:.16em;
  text-transform:uppercase;color:var(--steel);margin:0 0 10px;
}
.block p{font-size:var(--text-base);max-width:62ch}
.do-list{border-top:1px solid var(--line)}
.do-list li{
  padding:11px 0;border-bottom:1px solid var(--line);
  display:flex;align-items:baseline;gap:12px;
}
.do-list li::before{content:"";width:6px;height:6px;flex:none;background:var(--ink);transform:translateY(-2px)}

/* ---- signature: a commitment set as a written clause -------------------- */
.clause{border-left:2px solid var(--ink);padding-left:24px;max-width:60ch}
.clause h3{
  font-family:var(--label);font-weight:600;font-size:var(--text-2xs);letter-spacing:.16em;
  text-transform:uppercase;color:var(--steel);margin:0 0 12px;
}
.clause p{font-family:var(--serif);font-size:var(--text-md);line-height:1.55;margin:0 0 18px}

/* -------------------------------------------------------------- sectors -- */
/* Typography, not a frame. Six bordered cells in one row read as a spreadsheet
   header, and the border promised a click the element did not accept — it was
   the only bordered, non-interactive thing on the site. These are links now,
   and the frame is gone: a hairline above each item puts them in the same
   device family as .section-head and .rule-item (a rule marks a position; a
   box encloses one) instead of inventing a fifth container idiom. */
.sectors{
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  gap:clamp(22px,2.2vw,38px) clamp(30px,3.2vw,60px);
}
.sectors a{
  display:block;text-decoration:none;color:inherit;
  padding-top:16px;border-top:1px solid var(--line);
  transition:border-top-color .2s ease,color .2s ease;
}
.sectors a:hover{border-top-color:var(--ink)}
.sectors a:hover h4{color:var(--steel)}
.sectors h4{font-size:var(--text-xl);margin:0 0 6px;transition:color .2s ease}
.sectors p{font-size:var(--text-sm);color:var(--slate)}

/* ----------------------------------------------------------- dark bands -- */
.band-dark{background:var(--deep);color:#fff}
.band-dark .label{color:var(--steel-3)}
.band-dark p{color:var(--on-dark)}
/* Every navy band runs the same grid: 1.1fr / 1fr, gap clamp(32px,5vw,80px).
   The three of them (.hero:not(.hero-split), .band-split, .band-cta) were on
   1.1/1, 1/1 and 1.15/1 with three different gaps and three different
   alignments, so the dark surfaces did not read as one device.

   Alignment is the one thing that legitimately varies, and only by content:
   text beside text aligns to `start`, because a shared top edge is the only
   relationship that stays constant as the copy changes length. A photograph
   has no baseline to share, so it centres. */
.band-split .wrap{
  display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);
  gap:clamp(32px,5vw,80px);align-items:center;
  padding-block:var(--band);
}
.band-split .shot{border-color:rgba(255,255,255,.25);background:transparent}
/* Two columns, like every other band on the site. As a flex row with
   space-between, the heading sat left and the button was thrown to the far
   right edge — on a 2560 screen that put ~1000px of nothing between the offer
   and the action, the same defect .index-row had. The copy and the button now
   share a right column, so the reader's eye lands on the sentence and the
   button immediately below it. */
.band-cta .wrap{
  display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);
  gap:clamp(32px,5vw,80px);align-items:start;
  padding-block:clamp(44px,6vw,72px);
}
/* A form, like a photograph, has no baseline to share with the heading beside
   it — and unlike a one-line button, its height varies a lot with field count,
   so start-aligning it left a growing block of empty band beneath a short
   heading. Full centring would fix that but reads as floating, disconnected
   from the top rule the rest of the band respects — so the lead block sits a
   fixed proportion into the leftover space instead of at 0% (start) or 50%
   (center). The 1:4 ratio between the fr tracks below/above puts it at 20% of
   whatever gap the form's height leaves; the row stretches with the column
   for this to have a height to divide, so it overrides the .wrap default.
   :not(:has(.subscribe)) excludes the Insights band: .subscribe is one input
   and a button in a row, not a multi-field form, so its height never varies
   and start-aligning it needs none of this — applying it anyway stretched the
   band to the height a tall form would need, leaving the short one floating
   in a mostly-empty band. */
.band-cta:has(.form):not(:has(.subscribe)) .wrap{align-items:stretch}
.band-cta:has(.form):not(:has(.subscribe)) .wrap > div:first-child{display:grid;grid-template-rows:1fr auto 4fr}
.band-cta-lead{grid-row:2}
/* No ch cap: the column already provides the measure, and 20ch was breaking
   "Begin with the Truvenn Diagnostic" over two lines inside a column wide
   enough to hold it (707px needed, 1057px available at 2560). balance keeps the
   wrap tidy at the widths where it genuinely has to wrap. */
.band-cta h2{max-width:none;text-wrap:balance}
.band-cta h2 + p{margin-top:16px;max-width:46ch}
.band-cta-side{display:flex;flex-direction:column;align-items:flex-start;gap:22px}
/* Direct child only — a descendant selector here also caught the char-count
   paragraph nested inside the form's .field, capping it at 46ch so it
   right-aligned inside a box narrower than the textarea instead of flush
   under its right edge. */
.band-cta-side > p{margin:0;max-width:46ch}
.band-cta-side .subscribe{margin:0}
.band-cta-side .form{align-self:stretch;margin:0}

/* A .form dropped into a dark band — the careers page's "Introduce yourself".
   .subscribe's inputs above already carry their own dark styling; this
   generalises the same treatment to any .form so it isn't rebuilt per
   feature. */
.band-dark .form{border-color:rgba(255,255,255,.24)}
.band-dark .field label{color:var(--steel-3)}
.band-dark .input{background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.32);color:#fff}
.band-dark .input:hover{border-color:rgba(255,255,255,.5)}
.band-dark .input::placeholder{color:var(--on-dark)}
.band-dark .form-note{color:var(--on-dark)}
.band-dark .form-status{color:var(--steel-3)}

/* ----------------------------------------------------------- diagnostic -- */
/* A surface, not a frame. As a hairline-bordered box 2048px wide, 571px of its
   796px right column stood empty — 72% of it — and a border around emptiness
   reads as a layout that failed to fill, where the same space unframed reads
   as air. The right column now carries the deliverables instead of stretched
   whitespace: they were already buried in the paragraph, and this is the
   page's conversion moment, so they should be scannable.

   The mist lives on the section (.section-mist), not on this element, so the
   band runs edge to edge and its text sits on the --wrap grid line — level
   with "Where we work" above it. As a padded block inside .wrap its copy
   started 64px further in than every other section on the page, which is the
   misalignment you notice before you can name it. */
.diagnostic-panel{
  display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);
  gap:clamp(30px,4vw,72px);
}
.diagnostic-panel p{max-width:52ch;color:var(--slate)}
/* Facts on record, set as an aligned row of term/value pairs. Started life as
   the diagnostic panel's two bordered chips of 198px and 224px stacked left —
   a ragged edge that read as form controls rather than as terms.

   .facts is the register itself and stands alone (the statutory particulars on
   the about page); .panel-facts is the variant that spans .diagnostic-panel's
   two columns. Term above value, on a hairline: the same "a rule marks a
   position" logic as .section-head and .sectors, never a box. */
.facts{
  display:grid;grid-template-columns:repeat(4,minmax(0,1fr));
  gap:clamp(18px,2vw,32px);
  border-top:1px solid var(--line);padding-top:clamp(22px,2.6vw,34px);
}
.facts dt{
  font-family:var(--label);font-size:var(--text-2xs);letter-spacing:var(--track-label);
  text-transform:uppercase;color:var(--steel);
}
.facts dd{margin:6px 0 0;font-size:var(--text-sm);font-weight:500;color:var(--ink)}
/* Titled register: the label sits above the hairline so the rule reads as the
   register's own top edge rather than as a divider between two things. */
.h-label + .facts{margin-top:clamp(14px,1.4vw,20px)}
.panel-facts{grid-column:1/-1}
.panel-side{display:flex;flex-direction:column;align-items:flex-start;gap:clamp(16px,1.8vw,24px)}
/* The left rule again, deliberately: DESIGN.md reserves it for a stated
   position, and a fixed-scope deliverable is exactly that. Reusing the spine
   device here is what keeps this panel from becoming another idiom. */
.panel-list{list-style:none;margin:0;padding:0;display:grid;gap:12px;width:100%}
.panel-list li{
  border-left:2px solid var(--ink);padding-left:16px;
  font-size:var(--text-sm);color:var(--ink);
}
/* ----------------------------------------------------------------- form -- */
.contact-grid{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.2fr);
  gap:clamp(36px,5vw,72px);align-items:start;
}
.form{border:1px solid var(--line);padding:clamp(24px,3vw,34px);display:flex;flex-direction:column;gap:18px}
/* Spam honeypot on .form — not subject to "never hide content in CSS unless
   JS can guarantee revealing it" (see the .js-reveal gate below). It isn't
   content; it's a decoy field that must never be revealed, to anyone, ever.
   aria-hidden removes it from the accessibility tree rather than merely
   painting it off-screen, so it can't confuse a screen-reader visitor. */
.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.form-row{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.field label{display:block;font-size:var(--text-xs);color:var(--slate);margin-bottom:6px}
.input{
  width:100%;min-height:44px;padding:10px 12px;font:inherit;font-size:var(--text-sm);
  color:var(--ink);background:var(--paper);border:1px solid var(--line);border-radius:0;
}
.input:hover{border-color:var(--slate)}
.input:focus-visible{border-color:var(--steel-2);outline-offset:0}
textarea.input{min-height:130px;resize:vertical}
.seg{display:flex;flex-wrap:wrap;gap:8px}
.seg-opt{
  display:inline-flex;align-items:center;padding:8px 14px;font-size:var(--text-xs);cursor:pointer;
  border:1px solid var(--line);transition:background .15s ease,border-color .15s ease;
}
.seg-opt input{position:absolute;opacity:0;width:0;height:0}
.seg-opt:hover{border-color:var(--slate)}
.seg-opt:has(input:checked){background:var(--ink);border-color:var(--ink);color:#fff;font-weight:600}
.seg-opt:has(input:focus-visible){outline:2px solid var(--steel-2);outline-offset:2px}
.check{display:inline-flex;align-items:center;gap:10px;font-size:var(--text-xs);cursor:pointer}
.check input{width:18px;height:18px;accent-color:var(--ink)}
.form-foot{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap}
.form-note{font-size:var(--text-2xs);color:var(--slate)}
.form-status{font-size:var(--text-xs);color:var(--steel);font-weight:500}
.char-count{margin-top:6px;text-align:right;font-variant-numeric:tabular-nums}

/* ---------------------------------------------------------- newsletter --- */
.subscribe{display:flex;gap:10px;flex-wrap:wrap}
/* .subscribe is also a .form now (site.js targets form.form), but .form's
   bordered, padded, column-stacked box is built for the multi-field forms —
   wrong shape for one input and a button in a row. Cancel the parts of .form
   this row doesn't want rather than fork the JS selector. */
.subscribe.form{border:0;padding:0;flex-direction:row}
.subscribe .input{
  width:280px;background:rgba(255,255,255,.08);
  border-color:rgba(255,255,255,.32);color:#fff;
}
.subscribe .input::placeholder{color:var(--on-dark)}

/* --------------------------------------------------------------- footer -- */
.sitefoot{border-top:2px solid var(--line-strong)}
.foot-main{
  display:grid;grid-template-columns:1.5fr 1fr 1fr 1fr;
  gap:clamp(24px,3.4vw,40px);padding-block:clamp(28px,3.6vw,44px);
}
/* The stamp lockup: the mark comes down onto the name and gets smaller, so it
   reads as a seal on the name rather than a co-equal element floating above
   it. The mark is 70% of the wordmark's cap height — the dial was tested at
   58% and 70% and 58% read as a small thing resting above the name. */
.stamp{
  --stamp:56px;
  display:inline-flex;flex-direction:column;align-items:center;
  text-decoration:none;color:var(--ink);
}
/* Centred, not left-aligned: the mark has to sit over the middle of the name.
   Left-aligned it crowned the "TR", which reads as the mark having slipped off
   its axis. TRUVENN is seven letters and V is the fourth, so the centre line
   and the V are the same place — the venn crowns the venn. Everything in the
   lockup shares that one axis, which is the same rule the plate follows. */
/* Every part of the stamp derives from --stamp so the lockup scales as one
   object. The first .70 is Barlow's cap height, read off the font itself; it
   was 0.72 by estimate until the plate was cut and the real metric turned up.
   The second is the mark's share of that cap height. The gap was briefly a hardcoded 8px, which at a 26px wordmark was
   proportionally twice what it had been in the study — enough to make the mark
   float above the name instead of sitting on it, which is the exact failure
   the frameless lockup exists to avoid. */
.stamp-mark{
  height:calc(var(--stamp) * .70 * .70);width:auto;
  margin-bottom:calc(var(--stamp) * .16);
}
.stamp .brand-word{font-size:var(--stamp)}
.stamp .brand-sub{
  font-size:calc(var(--stamp) * .36);letter-spacing:.36em;
  margin-top:calc(var(--stamp) * .17);
}
.foot-about{font-size:var(--text-2xs);color:var(--slate);margin-top:12px;max-width:34ch}
.foot-col h4{
  font-family:var(--label);font-weight:600;font-size:var(--text-2xs);letter-spacing:.16em;
  text-transform:uppercase;color:var(--ink);margin:0 0 12px;
}
.foot-col a,.foot-col span{display:block;font-size:var(--text-2xs);text-decoration:none;color:var(--slate);padding-block:4px}
.foot-col a:hover{color:var(--steel)}
.foot-base{border-top:1px solid var(--line)}
.foot-base .wrap{
  display:flex;justify-content:space-between;gap:20px;flex-wrap:wrap;
  padding-block:16px;font-size:var(--text-2xs);color:var(--slate);
}
.foot-base a{color:var(--slate);text-decoration:none}
.foot-base a:hover{color:var(--steel)}

/* --------------------------------------------------------------- motion --
   Content is only hidden once the script has confirmed it can reveal it again:
   the .js-reveal gate is set by site.js. Without it — JS disabled, blocked or
   broken — every .reveal element renders normally. */
.js-reveal .reveal{opacity:0;transform:translateY(14px)}
.js-reveal .reveal.is-in{opacity:1;transform:none;transition:opacity .7s ease,transform .7s cubic-bezier(.2,.7,.3,1)}

/* ---------------------------------------------------------- composition --
   Page-level spacing and inversion live here rather than in style attributes,
   so a heading looks the same on every page that uses it. */
.band-dark h1,.band-dark h2,.band-dark h3,.band-dark h4{color:#fff;font-weight:500}
.band-cta h2{margin-top:var(--space-3)}
.band-split h2{margin:var(--space-3) 0 var(--space-4);max-width:20ch}
.band-split .band-actions{margin-top:var(--space-5)}

.section--top{padding-top:clamp(52px,7vw,92px)}

.label + .rule-list{margin-top:var(--space-5)}
.rule-stack{margin-top:var(--space-6);gap:22px}
.rule-item .label + h4{margin-top:var(--space-2)}

.diagnostic-panel h2{margin:var(--space-3) 0}
.panel-side .btn{margin-top:var(--space-2)}

.contact-grid h1{margin-top:var(--space-4);max-width:12ch}
.contact-grid .lede{margin-top:var(--space-5);max-width:44ch}

fieldset.field{border:0;padding:0;margin:0}
.field legend{font-size:var(--text-xs);color:var(--slate);margin-bottom:var(--space-2);padding:0}

/* --------------------------------------------------------- long-form doc -- */
/* A single-column .measure block left the rest of a wide .wrap empty on a
   legal/policy page with nothing to put beside it — privacy.html is the one
   page on the site that's pure prose. Full width, edge to edge, like every
   other two-column section on the site (.hero, .band-cta, .practice), not a
   content-sized left column with the remainder left idle: prose has no ch
   cap and fills its 7fr track, growing with the viewport the way the rest
   of the page does. .doc-toc is the right-hand cluster, the same role the
   nav + CTA play in the masthead above it — sticky like .practice-aside so
   it tracks the reader down a page this long, and hugging the end of its
   track so it lines up with the masthead's own right edge rather than
   sitting wherever its (short) content happens to end. */
.doc{display:grid;grid-template-columns:minmax(0,7fr) minmax(0,3fr);gap:clamp(32px,5vw,80px);align-items:start}
.doc .measure{max-width:none}
/* Base h2 is --text-2xl, sized for one heading opening a whole section. Seven
   of them back to back at that size read as seven section openers rather
   than one policy answering seven questions — --text-xl (the h3/card-title
   step) still reads clearly as a heading at a glance down the page. Margins
   carry the rhythm instead of font metrics doing it implicitly, since
   shrinking the heading also shrinks the line-height whitespace that used to
   separate one answer from the next question. */
.doc .measure h2{font-size:var(--text-xl);margin-top:var(--space-6)}
.doc .measure h2:first-child{margin-top:0}
.doc .measure h2 + p{margin-top:var(--space-3)}
/* The lede everywhere else sits beside a hero photo or a short standfirst,
   so 60ch keeps it from running under something. Here it's alone above a
   full-width page, so it gets the same treatment as the prose below it. */
.lede-doc{max-width:none}
/* The standard section rhythm (half --band each, a full --band between two
   sections) is tuned for sections that open with a fresh heading and photo —
   here it's the gap between the lede and the first question in the same
   policy, which reads as a pause mid-thought at that size. Tightened to a
   fixed --space-5 on both sides of the seam rather than the fluid band. */
.section:has(+ .section .doc){padding-bottom:var(--space-5)}
.section:has(> .wrap.doc){padding-top:var(--space-5)}
/* Not sticky. .practice-aside can be: its row is only as tall as a short
   title block, so the sticky box never travels far. .doc-toc's row is the
   full length of a seven-question page — a sticky box there tracks the
   scroll for most of it, then hits the bottom of its own grid row and has to
   un-stick and snap back into flow, which reads as the list jumping up the
   screen right as the reader reaches the footer. Static avoids the jump. */
.doc-toc{justify-self:end;width:fit-content;display:flex;flex-direction:column;gap:12px}
.doc-toc .label{margin-bottom:2px}
.doc-toc a{font-size:var(--text-sm);color:var(--slate);text-decoration:none}
.doc-toc a:hover{color:var(--steel)}

/* ------------------------------------------------------------- 404 page -- */
.notfound{padding-block:clamp(72px,12vw,140px);text-align:left}
.notfound h1{max-width:16ch;margin-top:var(--space-4)}
.notfound .lede{margin-top:var(--space-5)}

/* ----------------------------------------------------------- responsive -- */
/* Above this width a two-up card grid gives each card over 900px, which reads
   as a banner rather than a card. Three-up keeps the six-item grids to two
   tidy rows and holds the card near a comfortable width. */
@media (min-width:1800px){
  .cardgrid-2{grid-template-columns:repeat(3,minmax(0,1fr))}
}

@media (max-width:1180px){
  .sectors{grid-template-columns:repeat(2,minmax(0,1fr))}
  .facts{grid-template-columns:repeat(2,minmax(0,1fr))}
}
@media (max-width:1080px){
  .practice .wrap{grid-template-columns:1fr;gap:36px}
  .practice-aside{position:static}
  .doc{grid-template-columns:1fr;gap:36px}
  .doc-toc{justify-self:stretch;width:auto;flex-direction:row;flex-wrap:wrap;gap:6px 20px}
  .doc-toc .label{flex-basis:100%}
  .index-row{grid-template-columns:52px minmax(0,1fr) 28px}
  .index-for{display:none}
  .cardgrid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .rule-cols-4{grid-template-columns:repeat(2,minmax(0,1fr))}
  /* Stacked: the photo goes back to a declared ratio, so the crop is
     predictable again on a phone rather than tracking the text height.
     min-height:0 is required — the desktop rule asks for a full screen, which
     stacked would strand the photo below the fold behind dead space. The
     .shot margin is dropped too: the grid gap already separates the rows, and
     the two were stacking to ~64px. */
  .hero-split{padding-block:clamp(40px,7vw,64px)}
  .hero-split .wrap{grid-template-columns:1fr;min-height:0}
  .hero-split .wrap > :first-child{padding-block:0}
  .hero-split h1{max-width:17ch}
  /* The dark band goes back to a single stack; two columns need the width. */
  .hero:not(.hero-split):not(.hero-bleed) .wrap{grid-template-columns:1fr;row-gap:0}
  .hero:not(.hero-split):not(.hero-bleed) h1{grid-column:1;grid-row:auto}
  .hero:not(.hero-split):not(.hero-bleed) .label{grid-row:auto}
  .hero:not(.hero-split):not(.hero-bleed) .lede{grid-column:1;grid-row:auto;margin-top:24px;max-width:60ch}
  .hero-split .shot{position:static;height:auto;min-height:0}
  .hero-split .shot img{position:static;height:auto;aspect-ratio:16/10}
  /* The full-bleed hero sizes to its copy on a phone. Holding 100svh here
     would leave the headline floating in a tall dark rectangle, and the copy
     already fills a narrow screen without help. */
  .hero-bleed{min-height:0}
  .band-split .wrap{grid-template-columns:1fr}
  .contact-grid{grid-template-columns:1fr}
  .diagnostic-panel{grid-template-columns:1fr}
  .foot-main{grid-template-columns:1fr 1fr}
}
/* The masthead CTA goes before the nav does. At 72px the logo lockup is ~218px
   wide, and between 800px and ~900px the brand, the five nav links and the
   button no longer fit the gutter — it overran by 9px at 834px (iPad portrait).
   Dropping the button frees 156px; the nav itself still fits down to 800px,
   where the burger takes over. */
@media (max-width:900px){
  .masthead .btn{display:none}
}
@media (max-width:800px){
  :root{--mast:86px}
  .brand{--brand:23px}
  .brand-sub{letter-spacing:.32em}
  .burger{
    display:flex;flex-direction:column;justify-content:center;gap:5px;
    width:42px;height:42px;cursor:pointer;margin-left:auto;
  }
  .burger span{display:block;height:2px;background:var(--ink)}
  .mainnav{
    display:none;position:absolute;left:0;right:0;top:100%;
    flex-direction:column;align-items:stretch;gap:0;background:var(--paper);
    border-bottom:1px solid var(--line);padding:8px var(--gutter) 20px;
  }
  .navtoggle:checked ~ .mainnav{display:flex}
  .mainnav a{padding:13px 0;border-bottom:1px solid var(--line)}
  .mainnav a::after{display:none}
  .masthead .btn{display:none}

  .cardgrid,.cardgrid-2{grid-template-columns:1fr}
  .rule-cols-3,.rule-cols-4{grid-template-columns:1fr}
  .sectors{grid-template-columns:1fr}
  .sectors div{border-right:0;border-bottom:1px solid var(--line)}
  .sectors div:last-child{border-bottom:0}
  .band-cta .wrap{grid-template-columns:1fr;align-items:start;gap:26px}
  .form-row{grid-template-columns:1fr}
  .foot-main{grid-template-columns:1fr;gap:30px}
  .shot img{aspect-ratio:16/9}
}
/* ---------------------------------------------------------- scroll snap --
   Each section lands as its own screen, so a scroll gives a whole new piece of
   information rather than a fragment of two.

   `proximity`, never `mandatory`. Proximity only snaps when the scroll already
   ends near a boundary, so a long section is scrolled the ordinary way. Two
   things keep it safe: that, and the spec rule that a snap area taller than
   the scrollport makes every position covering it valid — so an oversized
   section can never trap the reader partway through it.

   The gate comes from measurement, not taste. Section heights as a share of
   usable height (viewport minus masthead), 2026-08-16: at 1180x800 the
   practices grid is 103%, at 834x1112 the hero is 116%, at 390x844 the
   practices grid is 225% — over two screens. Below 1400x850 nothing is
   reliably shorter than the screen, so snapping is not offered at all and the
   page behaves normally. Above it every section fits with room to spare
   (worst case 95% at 1512x860, 64% at 2560x1440).

   Do not raise this to `mandatory` and do not widen the gate without
   re-running the measurement — section heights move whenever --band, the type
   scale or the card copy changes. scroll-padding-top keeps a snapped section
   clear of the sticky masthead. */
@media (min-width:1400px) and (min-height:850px){
  html{scroll-snap-type:y proximity}
  main > section{scroll-snap-align:start;scroll-snap-stop:normal}
}
html{scroll-padding-top:var(--mast)}

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto;scroll-snap-type:none}
  *,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important}
  .reveal{opacity:1;transform:none}
}
