/* ═══════════════════════════════════════════════════════════════════════════
   YARVIXO DESIGN SYSTEM — "technical passport"
   ═══════════════════════════════════════════════════════════════════════════

   One file for every page on the site. It replaced services/services.css and
   the 345-line inline <style> in index.html, which had drifted into two
   different systems: 29 distinct font sizes between 0.67rem and 2.5rem, twelve
   border radii, sixteen gradient-clipped headlines and eight neon glows.

   The rules that produced this file, in order of how often they get broken:

     1. No gradients on text. No glow shadows. No backdrop-filter. A number is
        set in the mono face, in ink, with its measurement conditions under it.
     2. Six colours. Anything not in the token block below does not appear.
     3. Seven type steps. Anything between them does not appear.
     4. Borders are 1px hairlines at radius 0–2px. Cards with soft shadows and
        14px corners are what the old design used instead of hierarchy.
     5. Motion is hover and focus only, 120ms. Nothing animates on load or on
        scroll — the scan line, the floating chips, the pulsing rings and the
        particle canvas are gone and are not coming back.

   Light only, deliberately. The dark slate + cyan palette was half of why the
   old design read as generated, so the tokens below are structured for a dark
   theme to be added later without touching a single component rule — but no
   dark palette ships until somebody decides it should.
   ═══════════════════════════════════════════════════════════════════════════ */

:root{
  /* ── Colour: six values, and that is the whole palette ────────────────── */
  --paper:#F4F5F7;        /* page ground — cool, blue-biased, not cream      */
  --paper-2:#EAECEF;      /* inset surfaces: table heads, code, quiet bands  */
  --ink:#141920;          /* text, structural rules — near-black, blue bias  */
  --ink-2:#4E5765;        /* secondary text — 6.6:1 on paper, 6.1 on paper-2 */
  --ink-3:#575F6C;        /* mono labels — 5.9:1 on paper, and still 4.8:1
                             where a background bloom is at its darkest   */
  --rule:#C7CBD3;         /* decorative hairlines                            */
  --rule-2:#AEB4BE;       /* hairlines that carry structure                  */
  --field:#7B838F;        /* borders of things you can operate — 3.2:1       */
  --navy:#243A74;         /* the brand. Links, one primary button per view.  */
  --navy-dark:#1A2B57;    /* its hover                                       */
  --navy-soft:rgba(36,58,116,.07);
  --white:#FFFFFF;

  /* Motion. Two durations, one easing, and every use of them sits behind the
     prefers-reduced-motion switch at the bottom of this file. */
  --dur-1:120ms;          /* hover, focus, state flips                       */
  --dur-2:460ms;          /* entrances and the chart draw                    */
  --ease:cubic-bezier(.22,.68,.24,1);

  /* Legacy aliases. The old palette's names are still referenced by a handful
     of inline style attributes in page markup; mapping them here means a
     missed one renders in the new palette instead of rendering invisible.
     Nothing in this file uses them — do not reach for them in new work. */
  --brand:var(--navy); --brand-deep:var(--navy-dark);
  --accent:var(--navy); --cyan:var(--navy);
  --bg:var(--paper); --bg2:var(--paper-2); --bg3:var(--paper-2);
  --text:var(--ink); --text2:var(--ink-2); --text3:var(--ink-2);
  --border:var(--rule); --border2:var(--rule-2);

  /* ── Type: two families, one of them flexed across two widths ─────────── */
  --f-display:'Archivo','Helvetica Neue',Arial,sans-serif;
  --f-body:'Archivo','Helvetica Neue',Arial,sans-serif;
  --f-mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;

  /* Seven steps. Not eight. The old stylesheet had twenty-nine. */
  --t-xs:.6875rem;        /* 11px — mono labels, eyebrows                    */
  --t-sm:.8125rem;        /* 13px — captions, measurement conditions         */
  --t-md:.9375rem;        /* 15px — dense UI, table cells, nav               */
  --t-base:1.0625rem;     /* 17px — body                                     */
  --t-lg:1.375rem;        /* 22px — h3                                       */
  --t-xl:clamp(1.6rem,3.2vw,2.125rem);   /* h2                              */
  --t-2xl:clamp(2.15rem,5vw,3.35rem);    /* h1                              */

  /* ── Geometry ─────────────────────────────────────────────────────────── */
  --r:2px;                /* the only radius. Pills and circles are gone.    */
  --page-x:clamp(20px,6vw,72px);
  --maxw:1200px;
  --measure:66ch;
  /* The page gutter: a var(--page-x) margin that grows to centre the content
     once the viewport is wider than --maxw. Used by every block that sits at
     the top level of the document rather than inside .wrap. */
  --gutter:max(var(--page-x),calc((100% - var(--maxw)) / 2));
  --rail:96px;            /* the section-number column                       */
  --gap:36px;
}

/* ── Base ──────────────────────────────────────────────────────────────── */
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{
  background-color:var(--paper);
  color:var(--ink);
  font-family:var(--f-body);
  font-size:var(--t-base);
  line-height:1.62;
  font-variant-numeric:tabular-nums;
  -webkit-font-smoothing:antialiased;
}
/* The light blooms are PALE BLUE, not navy, and that is the whole trick.

   A dark bloom buys its visibility by darkening the ground, and every level it
   darkens comes out of the text's contrast budget: navy at 20% composites to
   rgb(202,208,221) and drops the mono label colour to 3.53:1, below AA, wherever
   it drifts under a label. Backing it off to a safe alpha leaves something you
   cannot see.

   A pale blue inverts that. #B9CCEC at 38% composites to rgb(215,225,242) —
   obviously blue to the eye, and LIGHTER than the paper's own luminance, so
   contrast with dark text goes up rather than down. Two overlapping still
   measure 4.6:1 against the mono label. Three times the visible tint of the navy
   version, and it hands contrast back instead of spending it.

   The dark bands are not bound by this at all: paper-coloured text on ink has
   far more headroom, which is why the blooms there stay saturated blue.

   Note this is invisible to a contrast checker that compares colour against
   background-color, because the bloom is a background-image on a fixed layer
   two stacking levels below the text. It has to be worked out, not observed.

   ═══ ATMOSPHERE ═══════════════════════════════════════════════════════════
   The page's living ground: two fixed layers behind every section, so the
   movement is there wherever you have scrolled to rather than only while one
   particular band happens to be on screen.

   Three soft navy blooms and the ruled pattern, split across the two layers and
   given periods that do not divide into each other — 19s against 26s comes back
   into phase about every eight minutes, so there is no loop to catch. Sections
   that paint their own surface cover this and bring their own ambience; plain
   sections let it through.

   Both layers are position:fixed and animate only translate and scale, so they
   live on the compositor and neither scroll nor animation touches layout. */
body::before,body::after{
  content:'';position:fixed;inset:-25vh -25vw;z-index:-1;pointer-events:none;
  will-change:translate,scale;
}
body::before{
  background-image:
    radial-gradient(42rem 34rem at 16% 20%,rgba(185,204,236,.38),transparent 62%),
    radial-gradient(34rem 30rem at 84% 44%,rgba(163,190,234,.34),transparent 66%);
  animation:atmos-a 8s ease-in-out infinite alternate;
}
body::after{
  background-image:
    radial-gradient(38rem 32rem at 52% 88%,rgba(178,199,236,.36),transparent 66%),
    radial-gradient(rgba(36,58,116,.16) 1px,transparent 1px),
    linear-gradient(to bottom,rgba(36,58,116,.05) 1px,transparent 1px),
    linear-gradient(to right,rgba(36,58,116,.05) 1px,transparent 1px);
  background-size:auto,2.25rem 2.25rem,100% 4.5rem,4.5rem 100%;
  animation:atmos-b 11s ease-in-out infinite alternate;
}
@keyframes atmos-a{
  from{translate:0 0;scale:1}
  to{translate:-17rem 11rem;scale:1.38}
}
@keyframes atmos-b{
  from{translate:0 0;scale:1.3}
  to{translate:13rem -9rem;scale:1}
}

a{color:inherit;text-decoration:none}
img,svg{max-width:100%}
button{font-family:inherit}
:focus-visible{outline:2px solid var(--navy);outline-offset:3px}
::selection{background:var(--navy);color:var(--white)}

/* Skip-link target and anything else anchored under the sticky header. */
[id]{scroll-margin-top:80px}

/* ── Typographic primitives ────────────────────────────────────────────── */
h1,h2,h3,h4{font-family:var(--f-display);letter-spacing:-.018em;text-wrap:balance}
h1{font-stretch:82%;font-weight:800;font-size:var(--t-2xl);line-height:1.02;letter-spacing:-.028em}
h2{font-stretch:84%;font-weight:700;font-size:var(--t-xl);line-height:1.06}
h3{font-stretch:88%;font-weight:700;font-size:var(--t-lg);line-height:1.2}
h4{font-stretch:90%;font-weight:700;font-size:var(--t-base);line-height:1.3;letter-spacing:-.008em}
strong,b{font-weight:700;color:var(--ink)}

/* The mono label. Replaces the centred, letter-spaced, accent-coloured
   "eyebrow" that opened ten identical sections on the old homepage. */
.lbl,.kicker{
  font-family:var(--f-mono);font-size:var(--t-xs);font-weight:700;
  letter-spacing:.11em;text-transform:uppercase;color:var(--ink-3);
  display:block;
}

/* Numbers. The single most load-bearing rule in the file. */
.num{font-family:var(--f-mono);font-weight:700;letter-spacing:-.02em;font-variant-numeric:tabular-nums;color:var(--ink)}
.num-unit{font-size:.55em;font-weight:400;letter-spacing:0}

.measure{max-width:var(--measure)}

/* ── Layout shell ──────────────────────────────────────────────────────── */
.wrap{max-width:var(--maxw);margin:0 auto;padding-inline:var(--page-x)}

/* The gutter lives on .sec itself, because the 35 inner pages put their content
   straight into <section class="sec"> with no wrapper element. max() gives that
   content the same centred 1200px column the homepage gets from .wrap, without
   needing the extra div in the markup. The homepage nests .wrap inside .sec, so
   .wrap drops its own gutter there rather than paying it twice. */
.sec,.sec-alt{padding-block:clamp(52px,7vw,84px);padding-inline:var(--gutter)}
.sec{border-top:1px solid var(--rule)}
.sec:first-of-type{border-top:0}
.sec>.wrap,.sec-alt>.wrap{padding-inline:0;max-width:none}
/* .sec-alt / .alt: a quiet inset band rather than a second background colour
   alternating down the page. Inner pages still use both spellings. */
.sec-alt,.sec.alt{background:var(--paper-2)}
/* The grid is a property of the page ground, so any band that paints its own
   surface covers it. That is the intent: the ruled ground reads through the
   open sections and stops at the solid ones, which is what gives the page its
   rhythm without a second accent colour. */

/* Section header: number, title, rule to the right edge. The number is only
   there where the sections genuinely are a sequence; .sec-head with no
   .sec-head-n renders identically without it. */
.sec-head{display:flex;align-items:baseline;gap:18px;margin-bottom:14px}
.sec-head-n{font-family:var(--f-mono);font-size:var(--t-sm);font-weight:700;color:var(--ink-3);flex:none}
.sec-head h2,.sec-head h1{margin:0;white-space:nowrap}
.sec-head .rule{flex:1;height:1px;background:var(--rule-2);transform:translateY(-.35em)}
.sec-dek,.sub{color:var(--ink-2);max-width:64ch;font-size:var(--t-base);margin-bottom:34px}
.sub a{color:var(--navy);text-decoration:underline;text-underline-offset:3px}

/* ── Navigation ────────────────────────────────────────────────────────── */
nav.main-nav{
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:14px var(--page-x);
  background:var(--paper);
  border-bottom:1px solid var(--rule);
  position:sticky;top:0;z-index:200;
}
.logo{display:flex;align-items:center;flex-shrink:0}
.logo a{display:flex;align-items:center}
.nl{display:flex;gap:26px;font-size:var(--t-md);color:var(--ink-2);font-weight:500}
.nl a{padding:2px 0;border-bottom:1px solid transparent}
.nl a:hover{color:var(--ink);border-bottom-color:var(--ink)}
.nr{display:flex;align-items:center;gap:12px;flex-shrink:0}
.btn-nav{
  background:var(--navy);color:var(--white);border:0;border-radius:var(--r);
  padding:9px 18px;font-size:var(--t-md);font-weight:700;cursor:pointer;
  transition:background .12s linear;
}
.btn-nav:hover{background:var(--navy-dark)}
.burger{display:none;flex-direction:column;gap:5px;cursor:pointer;padding:4px;background:none;border:0}
.burger span{display:block;width:22px;height:2px;background:var(--ink);transition:transform .15s linear,opacity .15s linear}
.burger.open span:nth-child(1){transform:translateY(7px) rotate(45deg)}
.burger.open span:nth-child(2){opacity:0}
.burger.open span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
.mob{display:none;flex-direction:column;background:var(--paper);border-bottom:1px solid var(--rule)}
.mob.open{display:flex}
.mob a{padding:13px var(--page-x);font-size:var(--t-md);color:var(--ink-2);border-bottom:1px solid var(--rule)}
.mob a:last-child{border-bottom:0}
.mob a:hover{color:var(--ink);background:var(--paper-2)}

/* ── Breadcrumb ────────────────────────────────────────────────────────── */
.breadcrumb{
  padding:10px var(--page-x);font-family:var(--f-mono);font-size:var(--t-xs);
  letter-spacing:.04em;color:var(--ink-3);border-bottom:1px solid var(--rule);
}
.breadcrumb a{color:var(--ink-2)}
.breadcrumb a:hover{color:var(--ink);text-decoration:underline;text-underline-offset:3px}
/* The separator is text, not a border, and takes a text colour: in --rule-2 it
   measured 1.91:1 against paper. */
.breadcrumb span{margin:0 8px;color:var(--ink-3)}

/* ── Buttons ───────────────────────────────────────────────────────────── */
/* One filled button per view. Everything else is an underlined text button:
   the old design gave a primary CTA and a "Book a call" identical weight and
   then lit both of them, which told the visitor nothing about which to press. */
.btn,.btn-primary,.bp,.btn-cyan,.fbtn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  background:var(--navy);color:var(--white);
  border:0;border-radius:var(--r);
  padding:13px 26px;font-family:var(--f-body);font-size:var(--t-base);
  font-weight:700;letter-spacing:.005em;cursor:pointer;
  transition:background .12s linear;
}
.btn:hover,.btn-primary:hover,.bp:hover,.btn-cyan:hover,.fbtn:hover{background:var(--navy-dark)}
.btn-link,.btn-secondary,.bs,.btn-out{
  display:inline-flex;align-items:center;gap:8px;
  background:none;border:0;border-bottom:2px solid var(--navy);border-radius:0;
  padding:11px 0;font-family:var(--f-body);font-size:var(--t-base);
  font-weight:700;color:var(--navy);cursor:pointer;
  transition:color .12s linear,border-color .12s linear;
}
.btn-link:hover,.btn-secondary:hover,.bs:hover,.btn-out:hover{color:var(--navy-dark);border-bottom-color:var(--navy-dark)}
.cta-row,.hero-btns,.cta-btns{display:flex;gap:28px;flex-wrap:wrap;align-items:center}

/* ── Prose links ───────────────────────────────────────────────────────── */
.prose a,.sec-dek a,.answer-box a,.faq-a-inner a,.case-note a{
  color:var(--navy);text-decoration:underline;
  text-underline-offset:3px;text-decoration-thickness:1px;
}
.prose a:hover,.sec-dek a:hover,.answer-box a:hover,.faq-a-inner a:hover{text-decoration-thickness:2px}

/* ═══ HOME HERO ════════════════════════════════════════════════════════════
   A specification, not a slogan. The headline is plain ink; the load-bearing
   element is the row of measured numbers under it, each with the conditions it
   was measured under. Conditions are what a number cannot be faked with, and
   they are the only reason a stranger should believe the number above them. */
/* Ink, and it runs straight into the logo band below it — one block, not two.
   A photograph of an engineering bench sits behind it, anchored right, where the
   frame is lit; the left of that frame is almost black by design and the scrim
   below carries it the rest of the way down so the headline never competes with
   the picture. The ruled ground is redrawn over the top in reverse polarity so
   the texture carries across the seam into the logo band. */
.hero{
  position:relative;isolation:isolate;
  background-color:var(--ink);
  color:var(--paper);
  padding:clamp(52px,7vw,104px) var(--gutter) clamp(40px,5vw,60px);
  overflow:hidden;
}
.hero::before{
  content:'';position:absolute;inset:0;z-index:-2;
  background:
    url('/generated/hero-workbench.webp') right center / cover no-repeat;
  opacity:.62;
}
/* The scrim. Ink at full strength on the left, gone by the right edge, so the
   text column keeps its contrast and the lit half of the photograph survives. */
/* Scrim only — the grid moved to .hero-grid so it can be animated. */
.hero::after{
  content:'';position:absolute;inset:0;z-index:-1;
  background:
    linear-gradient(100deg,var(--ink) 0%,var(--ink) 26%,rgba(20,25,32,.86) 48%,rgba(20,25,32,.55) 72%,rgba(20,25,32,.42) 100%),
    linear-gradient(to bottom,rgba(20,25,32,.30),transparent 30%,rgba(20,25,32,.55));
}
/* The hero's ground: a masked dot-and-line pattern drifting over the scrim, and
   a bloom of its own behind it on the dark left side where the photograph has
   nothing to show. */
/* z-index 0, not -1. At -1 this tied with .hero::after, and a tie is broken by
   tree order — the pseudo-element wins, so the scrim painted over the pattern
   and the bloom. 0 puts it above the scrim and still below .wrap, which is 1. */
.hero-grid{
  position:absolute;inset:-20rem;z-index:0;pointer-events:none;
  background-image:
    radial-gradient(rgba(205,220,250,.20) 1px,transparent 1px),
    linear-gradient(to bottom,rgba(205,220,250,.07) 1px,transparent 1px),
    linear-gradient(to right,rgba(205,220,250,.07) 1px,transparent 1px);
  background-size:2.5rem 2.5rem,100% 3.5rem,3.5rem 100%;
  -webkit-mask-image:radial-gradient(70% 80% at 30% 45%,#000 0%,transparent 76%);
  mask-image:radial-gradient(70% 80% at 30% 45%,#000 0%,transparent 76%);
  animation:drift-far 13s linear infinite;
  will-change:translate;
}
.hero-grid::after{
  content:'';position:absolute;inset:8rem;
  background-image:
    radial-gradient(28rem 28rem at 22% 30%,rgba(104,142,236,.34),transparent 68%),
    radial-gradient(22rem 22rem at 8% 82%,rgba(64,100,200,.24),transparent 72%);
  animation:aurora 7s ease-in-out infinite alternate;
  will-change:translate,scale;
}
.hero>.wrap{padding-inline:0;max-width:var(--maxw);position:relative;z-index:1}

/* Below the point where the two columns stop fitting side by side, the picture
   would sit behind the whole text block rather than beside it. Drop it back and
   let the ink and the grid carry the section on their own. */
@media(max-width:820px){
  .hero::before{opacity:.30;background-position:70% center}
  .hero::after{background-image:linear-gradient(to bottom,rgba(20,25,32,.78),rgba(20,25,32,.90))}
}
.hero-kicker{margin-bottom:22px;color:#9AA3B1}
.hero h1{max-width:17ch;margin-bottom:20px;color:var(--paper)}
.hero-lede{font-size:clamp(1.0625rem,1.7vw,1.25rem);line-height:1.5;color:#AEB6C2;max-width:56ch;margin-bottom:34px}
.hero-lede strong{color:var(--paper);font-weight:700}

/* The spec panels invert with the block. Same geometry, same two-pixel rule on
   top; only the surface and the ink swap. */
.hero .spec-cell{
  background:rgba(24,30,40,.92);border-color:#39424F;border-top-color:var(--paper);
  box-shadow:none;
}
.hero .spec-n{color:var(--paper)}
.hero .spec-l{color:#98A0AD}
.hero .spec-c{color:#AEB6C2}
.hero .spec-c a{color:#A9C0F0}
.hero .btn{background:var(--paper);color:var(--ink)}
.hero .btn:hover{background:var(--white)}
.hero .btn-link{color:var(--paper);border-bottom-color:var(--paper)}
.hero .btn-link:hover{color:var(--white);border-bottom-color:var(--white)}
.hero :focus-visible{outline-color:var(--paper)}

/* ── Panels ────────────────────────────────────────────────────────────────
   The one surface treatment, shared by every grid cell on the site. White
   against the ruled paper ground, a hairline, two-pixel corners and a shadow
   just deep enough to lift the panel off the grid behind it. No glow, no blur,
   no gradient — the lift comes from one pixel of shadow at 5% black.

   Crucially the border belongs to the cell, not to a container showing through
   the gaps. That was the previous approach and it painted empty grid cells as
   solid grey blocks whenever the last row came up short. */
.spec-cell,.about-stat,.pstep,.card,.step,.rel-card{
  background:var(--white);
  border:1px solid var(--rule);
  border-radius:var(--r);
  box-shadow:0 1px 2px rgba(20,25,32,.05);
}
/* The cells that carry a measured number get the spec-sheet rule on top. */
.spec-cell,.about-stat,.pstep,.step{border-top:2px solid var(--ink)}
a.card:hover,.card-link:hover,.rel-card:hover{
  border-color:var(--navy);
  box-shadow:0 2px 8px rgba(20,25,32,.09);
}

.spec{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(196px,1fr));
  gap:14px;margin-bottom:34px;
}
.spec-cell{padding:20px 20px 22px;min-width:0}
.spec-n{font-family:var(--f-mono);font-weight:700;font-size:clamp(1.75rem,3.4vw,2.3rem);line-height:1;letter-spacing:-.03em}
.spec-n .u{font-size:.5em;font-weight:400;letter-spacing:0}
.spec-l{font-family:var(--f-mono);font-size:var(--t-xs);font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3);margin-top:11px}
.spec-c{font-size:var(--t-sm);line-height:1.45;color:var(--ink-2);margin-top:7px}
.spec-c a{color:var(--navy);text-decoration:underline;text-underline-offset:2px}

/* /about carries the same idea in its own markup. Same rendering, so the two
   pages do not disagree about what a number looks like. */
.about-stats{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(176px,1fr));
  gap:14px;margin-top:12px;
}
.about-stat{padding:18px 18px 20px}
.about-stat strong{
  display:block;font-family:var(--f-mono);font-weight:700;
  font-size:clamp(1.6rem,3vw,2.05rem);line-height:1;letter-spacing:-.03em;color:var(--ink);
}
.about-stat span{
  display:block;margin-top:10px;font-family:var(--f-mono);font-size:var(--t-xs);
  font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3);
}

/* ── Inner-page hero (services, hire, insights, case studies) ──────────── */
.hero-sp-wrap{background:var(--paper);border-bottom:1px solid var(--rule)}
.hero-sp{max-width:var(--maxw);margin:0 auto;padding:clamp(44px,6vw,76px) var(--page-x) clamp(36px,4.5vw,56px);text-align:left}
.hero-sp h1{margin-bottom:18px;max-width:20ch}
.hero-sp p{font-size:clamp(1rem,1.6vw,1.1875rem);color:var(--ink-2);max-width:62ch;line-height:1.55;margin-bottom:30px}
.hero-sp .lbl{margin-bottom:18px}
.hero-sp .cta-row{justify-content:flex-start}

/* ── Named case study header ───────────────────────────────────────────────
   The three named cases get an ink header carrying the same picture their card
   on the homepage carries, so clicking a card lands somewhere recognisable. The
   anonymised three deliberately do not: the furniture should say which kind of
   evidence this is before the first sentence does.

   The image is passed in as --case-img on the element, which keeps this rule
   generic and puts the choice of picture next to the page it belongs to. */
.case-hero{
  position:relative;isolation:isolate;overflow:hidden;
  background:var(--ink);border-bottom:0;
}
.case-hero::before{
  content:'';position:absolute;inset:-8% -3%;z-index:-2;
  background:var(--case-img) center 42% / cover no-repeat;
  opacity:.5;
  animation:breathe 13s ease-in-out infinite;
  will-change:scale;
}
/* Ink at full strength under the text, thinning toward the right where the
   picture has something worth seeing. */
.case-hero::after{
  content:'';position:absolute;inset:0;z-index:-1;
  background:
    linear-gradient(96deg,var(--ink) 0%,var(--ink) 22%,rgba(20,25,32,.90) 46%,rgba(20,25,32,.62) 76%,rgba(20,25,32,.48) 100%),
    linear-gradient(to bottom,rgba(20,25,32,.35),transparent 34%,rgba(20,25,32,.45));
}
.case-hero .hero-sp{position:relative;z-index:1}
.case-hero .lbl{color:#98A0AD}
.case-hero h1{color:var(--paper)}
.case-hero h1 span{color:#A9C0F0}
.case-hero p{color:#AEB6C2}
.case-hero p strong{color:var(--paper)}
/* The buy-or-build fork, used once, on the ArendaOS hero. It cannot reuse
   .answer-box: that box is dark ink on a paper panel, and this hero is light
   type over a photograph, so the two together render light grey on light grey -
   which is exactly what shipped for about ten minutes on 2026-08-29. A left
   rule and the hero's own paper colour need no background at all. */
.case-hero .fork{border-left:3px solid #A9C0F0;padding-left:18px;max-width:64ch;color:var(--paper)}
.case-hero .fork a{color:#A9C0F0}
.case-hero .btn-primary{background:var(--paper);color:var(--ink)}
.case-hero .btn-primary:hover{background:var(--white)}
.case-hero .btn-secondary{color:var(--paper);border-bottom-color:var(--paper)}
.case-hero .btn-secondary:hover{color:var(--white);border-bottom-color:var(--white)}
.case-hero :focus-visible{outline-color:var(--paper)}
@media(max-width:820px){
  .case-hero::before{opacity:.28;background-position:64% 42%}
}

/* ── Trust strip ───────────────────────────────────────────────────────── */
.trust{background:var(--paper-2);border-top:1px solid var(--rule);border-bottom:1px solid var(--rule)}
.trust-inner{
  max-width:var(--maxw);margin:0 auto;padding:13px var(--page-x);
  display:flex;flex-wrap:wrap;align-items:center;gap:10px 26px;
  font-family:var(--f-mono);font-size:var(--t-xs);letter-spacing:.05em;
  text-transform:uppercase;color:var(--ink-2);
}
.trust-item{display:flex;align-items:center;gap:8px;white-space:nowrap}
.trust-item svg{width:12px;height:12px;stroke:var(--navy);stroke-width:2.5;fill:none;flex:none}
.trust-sep{display:none}

/* ── Client logo band ──────────────────────────────────────────────────────
   Ink ground, because the artwork demands it: every one of the six marks is
   white or near-white, drawn for the old dark site. On paper they measured
   246-255 average luminance and were, quite literally, not visible. No filter
   and no greyscale — 1CGS is gold and InGame is yellow, and a client's mark is
   not ours to recolour to suit our background. */
.logo-band{
  background:var(--ink);
  padding-block:0 clamp(34px,4vw,48px);
  padding-inline:var(--gutter);
}
/* A hairline between the hero and the logos, drawn inside the same ink block so
   the two read as one mass with a rule across it rather than as two bands. */
.logo-band .wrap{border-top:1px solid #2E3744;padding-top:clamp(26px,3vw,36px)}
.logo-band .lbl{color:#98A0AD;margin-bottom:24px}
.clients-row{display:flex;align-items:center;gap:22px 52px;flex-wrap:wrap}
.cl-logo{display:flex;align-items:center;height:34px;opacity:.92;transition:opacity var(--dur-1) linear}
.cl-logo:hover{opacity:1}
.cl-logo img{height:32px;width:auto;display:block}
.cl-logo img.cl-logo-sm{height:26px}

/* ═══ RECORDS ══════════════════════════════════════════════════════════════
   What replaced the card grid. A rule, an index, a heading, a line of prose —
   no icon, no box, no shadow. The fact goes in the sentence. */
.records{display:grid;grid-template-columns:repeat(auto-fit,minmax(330px,1fr));gap:16px}
.rec{
  display:grid;grid-template-columns:auto minmax(0,1fr);column-gap:18px;
  align-content:start;padding:24px;color:inherit;
  background:var(--white);border:1px solid var(--rule);
  border-top:2px solid var(--ink);border-radius:var(--r);
  box-shadow:0 1px 2px rgba(20,25,32,.05);
  transition:border-color var(--dur-1) linear,box-shadow var(--dur-1) linear;
}
a.rec:hover{border-color:var(--navy);box-shadow:0 2px 8px rgba(20,25,32,.09)}
.rec-i{font-family:var(--f-mono);font-size:var(--t-xs);font-weight:700;color:var(--ink-3);padding-top:.42em}
.rec-b{min-width:0;display:flex;flex-direction:column}
.rec h3{font-size:var(--t-lg);margin-bottom:8px}
.rec p{color:var(--ink-2);font-size:var(--t-md);line-height:1.55}
/* The stack, as chips rather than a run-on line: they are the first thing a
   technical reader scans for, and the panel gives them room to be scannable. */
.rec-meta{
  display:flex;flex-wrap:wrap;gap:6px;margin-top:14px;
}
.rec-meta span{
  background:var(--paper-2);border:1px solid var(--rule);border-radius:var(--r);
  padding:3px 9px;font-family:var(--f-mono);font-size:var(--t-xs);
  letter-spacing:.02em;color:var(--ink-2);
}
a.rec .rec-go{
  display:inline-block;align-self:flex-start;margin-top:auto;padding-top:16px;
  font-size:var(--t-md);font-weight:700;color:var(--navy);
}
a.rec .rec-go::after{bottom:-1px}

/* ═══ CASES ════════════════════════════════════════════════════════════════
   The spine of the site, not its sixth section. Named cases get a full record
   with a result row; anonymised ones get a single compact line, because that is
   honestly all they are worth as evidence. */
.cases{display:grid;gap:0;margin-bottom:26px}
.case{
  display:grid;grid-template-columns:minmax(0,320px) minmax(0,1fr);
  gap:14px 34px;align-items:start;color:inherit;
  border-top:1px solid var(--rule);padding:28px 0;
  transition:background var(--dur-1) linear;
}
.case:first-child{border-top:2px solid var(--ink)}
.case:hover{background:var(--navy-soft)}
.case-body{min-width:0}
/* Evidence, not atmosphere — from 2026-08-30 the two game covers are the real
   product, taken from the storefront each card tells the reader to go and check:
   King's Bounty II from its Nintendo eShop listing, Metarun from its Google Play
   listing. That is the whole point of them, so anything that replaces one has to
   be a picture of the thing itself, not a stand-in for it. The frame is ink so a
   slow connection shows a dark panel of the right shape rather than a white
   hole that shifts the text when the picture lands. */
.case-cover{
  display:block;overflow:hidden;background:var(--ink);
  border:1px solid var(--rule);border-radius:var(--r);
}
.case-cover img{
  display:block;width:100%;height:auto;aspect-ratio:3/2;object-fit:cover;
  transition:transform 700ms var(--ease);
}
.case:hover .case-cover img{transform:scale(1.04)}
/* The picture column: the cover, and under it — where the artwork is somebody
   else's — the line saying whose. They are wrapped rather than left as loose
   children of .case: row 1 is as tall as the case body, so an auto-placed third
   child lands at the foot of the whole card instead of under the picture it
   credits. All three cards carry the wrapper even though ArendaOS needs no
   credit, so the grid has the same shape on every row. */
.case-media{min-width:0}
.case-credit{
  margin-top:9px;
  font-family:var(--f-mono);font-size:var(--t-xs);line-height:1.5;
  color:var(--ink-3);
}
@media(max-width:860px){
  .case{grid-template-columns:minmax(0,1fr)}
  .case-media{max-width:420px}
}
.case-tag{font-family:var(--f-mono);font-size:var(--t-xs);font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3);margin-bottom:9px;display:block}
.case h3{font-size:clamp(1.25rem,2.4vw,1.6rem);margin-bottom:16px;max-width:26ch}
.case-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(268px,1fr));gap:20px 36px;margin-bottom:18px}
.case-item label{
  display:block;font-family:var(--f-mono);font-size:var(--t-xs);font-weight:700;
  letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3);margin-bottom:6px;
}
.case-item p{font-size:var(--t-md);color:var(--ink-2);line-height:1.6}
.case-result{
  background:var(--paper-2);border-left:3px solid var(--navy);
  padding:13px 17px;font-size:var(--t-md);line-height:1.6;color:var(--ink-2);
}
.case-result strong{color:var(--ink)}
.case-arr{display:inline-block;margin-top:16px;font-size:var(--t-md);font-weight:700;color:var(--navy);border-bottom:2px solid var(--navy);padding-bottom:1px}
/* ── Score chart ───────────────────────────────────────────────────────────
   Four published Metacritic platform scores, drawn to scale on a 0-100 track so
   the comparison is the point rather than the individual number. The bars grow
   from zero when the figure is first scrolled into view; with JS off or motion
   reduced they are simply already at width, because the data is the content and
   must never depend on an animation having run. */
.scores{margin-top:20px;border-top:1px solid var(--rule);padding-top:16px}
.scores-cap{
  font-family:var(--f-mono);font-size:var(--t-xs);font-weight:700;
  letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3);margin-bottom:14px;
}
.scores-rows{display:flex;flex-direction:column;gap:7px;max-width:520px}
.score{display:grid;grid-template-columns:78px minmax(0,1fr) 34px;align-items:center;gap:12px}
.score-k{font-family:var(--f-mono);font-size:var(--t-xs);color:var(--ink-2);letter-spacing:.03em}
.score-t{display:block;height:10px;background:var(--paper-2);border:1px solid var(--rule);position:relative;overflow:hidden}
/* The width is the datum and is always correct — with JS off, with motion
   reduced, in print. Only the scale is animated, and it is a transform, so the
   growth costs no layout. */
.score-f{
  position:absolute;inset:0 auto 0 0;
  width:calc(var(--v) * 1%);
  background:var(--rule-2);
  transform-origin:left center;
}
.score.is-ours .score-f{background:var(--navy)}
.score-n{
  font-family:var(--f-mono);font-size:var(--t-sm);font-weight:700;
  color:var(--ink-2);text-align:right;font-variant-numeric:tabular-nums;
}
.score.is-ours .score-k,.score.is-ours .score-n{color:var(--ink)}
.scores-note{font-size:var(--t-sm);color:var(--ink-2);line-height:1.55;margin-top:13px;max-width:62ch}

.cases-more{margin-top:22px}
.cases-more a{font-size:var(--t-md);font-weight:700;color:var(--navy);border-bottom:2px solid var(--navy);padding-bottom:1px}

/* Compact row for the anonymised cases. */
.case-slim{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:8px 22px;align-items:baseline;border-top:1px solid var(--rule);padding:15px 0;color:inherit;transition:background .12s linear}
.case-slim:hover{background:var(--navy-soft)}
.case-slim .t{font-family:var(--f-mono);font-size:var(--t-xs);font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3);white-space:nowrap}
.case-slim h4{font-size:var(--t-base);margin:0}
.case-slim p{font-size:var(--t-sm);color:var(--ink-2);margin-top:3px}
.case-slim .go{font-family:var(--f-mono);font-size:var(--t-xs);color:var(--navy);white-space:nowrap}

/* ── Process ───────────────────────────────────────────────────────────── */
.proc{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:14px}
.pstep{padding:20px}
.pstep-n{font-family:var(--f-mono);font-size:var(--t-xs);font-weight:700;color:var(--ink-3);margin-bottom:10px}
.pstep h4{margin-bottom:6px}
.pstep p{font-size:var(--t-sm);color:var(--ink-2);line-height:1.55}

/* ═══ CARDS (inner pages) ══════════════════════════════════════════════════
   35 pages use .card / .grid-3 / .steps / .related markup. The surface comes
   from the panel rule above; only layout and type live here. */
.grid-3{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:16px}
.card{padding:24px;transition:border-color var(--dur-1) linear,box-shadow var(--dur-1) linear}
.card-link{display:block;color:inherit}
.card h3{font-size:var(--t-base);font-weight:700;margin-bottom:8px;color:var(--ink)}
.card h4{margin-bottom:7px}
.card p{font-size:var(--t-md);color:var(--ink-2);line-height:1.6}
.card ul{list-style:none;display:flex;flex-direction:column;gap:6px;margin-top:10px}
.card ul li{font-size:var(--t-md);color:var(--ink-2);padding-left:16px;position:relative}
.card ul li::before{content:'—';position:absolute;left:0;color:var(--ink-3);font-family:var(--f-mono);font-size:.8em}
.card .arr,.card-link .arr{display:inline-block;margin-top:14px;font-size:var(--t-md);font-weight:700;color:var(--navy);border-bottom:2px solid var(--navy);padding-bottom:1px}
.card p.price-band{
  font-family:var(--f-mono);font-size:1.5rem;font-weight:700;letter-spacing:-.03em;
  line-height:1.15;margin:2px 0 12px;color:var(--ink);background:none;
  -webkit-text-fill-color:currentColor;
}
.tags{display:flex;flex-wrap:wrap;gap:6px;margin-top:14px}
.tag{
  background:var(--paper-2);border:1px solid var(--rule);border-radius:var(--r);
  padding:3px 9px;font-family:var(--f-mono);font-size:var(--t-xs);
  color:var(--ink-2);font-weight:500;letter-spacing:.02em;
}

/* Service cards on the homepage kept their own class name. */
.sc{display:block;color:inherit}
.sc-ico{display:none}

.steps{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:14px;counter-reset:step}
.step{padding:20px;counter-increment:step}
.step::before{content:counter(step,decimal-leading-zero);display:block;font-family:var(--f-mono);font-size:var(--t-xs);font-weight:700;color:var(--ink-3);margin-bottom:10px}
.step h4{margin-bottom:6px}
.step p{font-size:var(--t-sm);color:var(--ink-2);line-height:1.55}

.related{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:16px}
.rel-card{padding:20px;transition:border-color var(--dur-1) linear,box-shadow var(--dur-1) linear}
.rel-card h4{margin-bottom:6px}
.rel-card p{font-size:var(--t-sm);color:var(--ink-2);line-height:1.55}
.rel-card .arr{display:inline-block;margin-top:12px;font-size:var(--t-md);font-weight:700;color:var(--navy);border-bottom:2px solid var(--navy);padding-bottom:1px}


/* ═══ ARCHITECTURE DRAWING ═════════════════════════════════════════════════
   Hand-authored SVG. Everything except the three measurement callouts is drawn
   in ink so the callouts are the only thing the eye is pulled to — they are the
   point of the figure. It scrolls inside its own frame below 900px rather than
   scaling down: at phone width a scaled 1120-unit viewBox renders 13px labels
   at about 4px, which is not a diagram any more. */
.diagram-arch{
  background:var(--white);border:1px solid var(--rule);border-top:2px solid var(--ink);
  border-radius:var(--r);padding:clamp(16px,2.4vw,28px);
  box-shadow:0 1px 2px rgba(20,25,32,.05);
  overflow-x:auto;-webkit-overflow-scrolling:touch;
}
/* Doubled selector to beat .diagram svg, which has the same specificity and
   is declared later in this file. Its 600px floor rendered these labels at
   about 7px on a phone. */
.diagram-arch.diagram-arch svg{display:block;width:100%;min-width:880px;height:auto;color:var(--ink)}
.diagram-arch figcaption{
  margin-top:16px;padding-top:14px;border-top:1px solid var(--rule);border-left:0;padding-left:0;
  font-family:var(--f-body);font-size:var(--t-md);line-height:1.55;color:var(--ink-2);
}
.diagram-arch figcaption a{color:var(--navy);text-decoration:underline;text-underline-offset:3px}
/* Below the drawing's own width it scrolls inside its frame. Unannounced, a
   figure with its right edge cut off reads as a rendering fault rather than as
   something you can drag. */
.diagram-hint{display:none}
@media(max-width:940px){
  .diagram-hint{
    display:block;margin-bottom:12px;
    font-family:var(--f-mono);font-size:var(--t-xs);font-weight:700;
    letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3);
  }
}

.d-col text{font-family:var(--f-mono);font-size:12px;font-weight:700;letter-spacing:.14em;fill:var(--ink-3)}
.d-rule{stroke:var(--rule);stroke-width:1;fill:none}
.d-box{fill:var(--paper);stroke:var(--ink);stroke-width:1.5}
.d-fleet{fill:var(--paper-2);stroke:var(--ink);stroke-width:1.5;stroke-dasharray:6 5}
.d-node rect{fill:var(--white);stroke:var(--rule-2);stroke-width:1}
.d-lab{font-family:var(--f-display);font-stretch:88%;font-weight:700;font-size:19px;fill:var(--ink)}
.d-sub{font-family:var(--f-body);font-size:13px;fill:var(--ink-2)}
.d-arrow{fill:none;stroke:var(--ink);stroke-width:1.5}
.d-note text{font-family:var(--f-mono);font-size:12px;font-weight:700;letter-spacing:.07em;fill:var(--ink)}
.d-note .d-note-sub{font-weight:400;letter-spacing:.01em;font-size:11px;fill:var(--ink-3)}
.d-note-accent text{fill:var(--navy)}
.d-note-accent .d-note-sub{fill:var(--ink-2)}
.d-tick{stroke:var(--navy);stroke-width:1.5;stroke-dasharray:3 4;fill:none}
.d-brace{fill:none;stroke:var(--navy);stroke-width:1.5}
.d-foot{font-family:var(--f-mono);font-size:11px;fill:var(--ink-3)}

/* ═══ STACK ════════════════════════════════════════════════════════════════ */
.stack{display:grid;grid-template-columns:repeat(auto-fit,minmax(195px,1fr));gap:16px}
.stack-col{
  background:var(--white);border:1px solid var(--rule);border-top:2px solid var(--ink);
  border-radius:var(--r);padding:20px;box-shadow:0 1px 2px rgba(20,25,32,.05);
}
.stack-h{
  font-family:var(--f-mono);font-size:var(--t-xs);font-weight:700;letter-spacing:.11em;
  text-transform:uppercase;color:var(--ink-3);
  margin-bottom:13px;padding-bottom:11px;border-bottom:1px solid var(--rule);
}
.stack-list{list-style:none;display:flex;flex-direction:column;gap:7px}
.stack-list li{font-size:var(--t-md);color:var(--ink-2);padding-left:15px;position:relative;line-height:1.4}
.stack-list li::before{content:'';position:absolute;left:0;top:.66em;width:6px;height:1px;background:var(--rule-2)}

/* ═══ WRITING ══════════════════════════════════════════════════════════════ */
.writing{display:grid;grid-template-columns:repeat(auto-fit,minmax(302px,1fr));gap:16px}
.wr{
  display:block;color:inherit;padding:22px;
  background:var(--white);border:1px solid var(--rule);border-top:2px solid var(--ink);
  border-radius:var(--r);box-shadow:0 1px 2px rgba(20,25,32,.05);
  transition:border-color var(--dur-1) linear,box-shadow var(--dur-1) linear;
}
.wr:hover{border-color:var(--navy);box-shadow:0 2px 8px rgba(20,25,32,.09)}
.wr-k{
  display:block;margin-bottom:11px;font-family:var(--f-mono);font-size:var(--t-xs);
  font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3);
}
.wr h3{font-size:var(--t-base);line-height:1.28;margin-bottom:9px}
.wr:hover h3{color:var(--navy)}
.wr p{font-size:var(--t-md);color:var(--ink-2);line-height:1.55}

/* ── Answer box ────────────────────────────────────────────────────────── */
.answer-box{max-width:72ch;background:var(--paper-2);border-left:3px solid var(--navy);padding:20px 24px;border-radius:0}
.answer-box p{color:var(--ink-2);font-size:var(--t-base);line-height:1.65;margin-bottom:14px}
.answer-box p:last-child{margin-bottom:0}
.answer-box strong{color:var(--ink)}
.answer-links{font-size:var(--t-md)!important}
.answer-links a{color:var(--navy);font-weight:700}

/* ── Named quote ───────────────────────────────────────────────────────── */
/* No stars, no gradient avatar disc, no italics. A quote is worth exactly as
   much as the name attached to it, so the name is what gets the weight. */
.svc-quote{max-width:72ch;border-left:3px solid var(--rule-2);padding:4px 0 4px 24px;margin-top:26px}
.svc-quote blockquote{color:var(--ink);font-size:var(--t-base);line-height:1.6;font-style:normal}
.svc-quote blockquote::before{content:'\201C'}
.svc-quote blockquote::after{content:'\201D'}
.svc-quote figcaption{margin-top:14px;font-family:var(--f-mono);font-size:var(--t-xs);letter-spacing:.05em;text-transform:uppercase;color:var(--ink-3);display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 10px}
.svc-quote-av{display:none}
.svc-quote figcaption strong{color:var(--ink-2);font-weight:700}

/* ═══ ARTICLE (/insights/*) ════════════════════════════════════════════════ */
.post{max-width:70ch;margin:0 auto;padding:clamp(36px,5vw,60px) var(--page-x) 24px}
.post-meta{font-family:var(--f-mono);font-size:var(--t-xs);letter-spacing:.05em;text-transform:uppercase;color:var(--ink-3);margin-bottom:34px;padding-bottom:16px;border-bottom:1px solid var(--rule)}
/* The separators are text and take a text colour. In --rule-2, a border
   colour, they measured 1.91:1 against paper on ten article pages — the same
   mistake as the breadcrumb chevron, made twice. */
.post-meta span{margin:0 8px;color:var(--ink-3)}
.post-answer{background:var(--paper-2);border-left:3px solid var(--navy);padding:18px 22px;margin-bottom:36px;font-size:var(--t-base);line-height:1.6}
.post h2{font-size:clamp(1.4rem,2.8vw,1.8rem);margin:44px 0 14px;text-align:left}
.post h3{font-size:var(--t-lg);margin:30px 0 10px;color:var(--ink)}
.post p{color:var(--ink-2);margin-bottom:18px}
.post ul,.post ol{color:var(--ink-2);margin:0 0 20px 22px}
.post li{margin-bottom:8px}
.post strong{color:var(--ink);font-weight:700}
.post code{font-family:var(--f-mono);font-size:.88em;color:var(--ink);background:var(--paper-2);border:1px solid var(--rule);border-radius:var(--r);padding:.1em .36em}
.post pre{overflow-x:auto;-webkit-overflow-scrolling:touch;background:var(--paper-2);border:1px solid var(--rule);border-left:3px solid var(--navy);border-radius:0;padding:16px 20px;margin:0 0 22px;font-size:var(--t-sm);line-height:1.65}
.post pre code{background:none;border:0;padding:0;font-size:inherit;color:var(--ink-2);white-space:pre}
.post pre .kw{color:var(--navy);font-weight:700}
.post pre .cm{color:var(--ink-3);font-style:italic}
.post figcaption{font-family:var(--f-mono);font-size:var(--t-xs);line-height:1.6;color:var(--ink-3);margin:-12px 0 24px;padding-left:16px;border-left:2px solid var(--rule)}
.post a{color:var(--navy);text-decoration:underline;text-underline-offset:3px;text-decoration-thickness:1px}
.post a:hover{text-decoration-thickness:2px}
.post-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch}
.post-note{border-left:3px solid var(--rule-2);padding-left:18px;margin:0 0 22px;font-size:var(--t-md);color:var(--ink-2)}

/* ── Tables ────────────────────────────────────────────────────────────── */
.post table,.tbl{width:100%;border-collapse:collapse;margin:0 0 24px;font-size:var(--t-md)}
.post th,.post td,.tbl th,.tbl td{border:1px solid var(--rule);padding:10px 13px;text-align:left;vertical-align:top;color:var(--ink-2)}
.post th,.tbl th{
  background:var(--paper-2);color:var(--ink-3);font-family:var(--f-mono);
  font-size:var(--t-xs);font-weight:700;letter-spacing:.09em;text-transform:uppercase;
}
.post td strong,.tbl td strong{color:var(--ink)}

/* ── Diagrams ──────────────────────────────────────────────────────────── */
.diagram{margin:0 0 26px;color:var(--ink);overflow-x:auto;-webkit-overflow-scrolling:touch}
.diagram svg{display:block;width:100%;min-width:600px;height:auto}
.diagram figcaption{margin-top:12px;font-family:var(--f-mono);font-size:var(--t-xs);line-height:1.6;color:var(--ink-3);border-left:2px solid var(--rule);padding-left:16px}

/* ═══ FAQ ══════════════════════════════════════════════════════════════════
   The disclosure mechanics are load-bearing and unchanged: main.js toggles
   `.open` on .faq-item and keeps aria-expanded in step, and the panel animates
   on grid-template-rows rather than max-height so a longer answer cannot be
   clipped mid-sentence the way the project-cost answer was. Only the skin here
   is new. */
.faq-wrap{max-width:76ch}
.faq-item{border-bottom:1px solid var(--rule)}
.faq-item:first-child{border-top:1px solid var(--rule)}
.faq-q{
  display:flex;justify-content:space-between;align-items:center;gap:18px;
  width:100%;padding:16px 0;margin:0;background:none;border:0;
  color:inherit;font-family:inherit;font-size:var(--t-base);font-weight:600;
  line-height:1.45;text-align:left;cursor:pointer;
}
.faq-q span{font-weight:600}
.faq-q:focus-visible{outline:2px solid var(--navy);outline-offset:2px}
.faq-q:hover{color:var(--navy)}
.faq-icon{
  flex:none;width:22px;height:22px;border:1px solid var(--field);border-radius:0;
  display:flex;align-items:center;justify-content:center;
  font-family:var(--f-mono);font-size:var(--t-sm);color:var(--ink-3);
  transition:transform .18s linear;
}
.faq-item.open .faq-icon{transform:rotate(45deg);color:var(--navy);border-color:var(--navy)}
.faq-a{display:grid;grid-template-rows:0fr;transition:grid-template-rows .25s ease}
.faq-a-inner{overflow:hidden;font-size:var(--t-md);color:var(--ink-2);line-height:1.65}
.faq-item.open .faq-a{grid-template-rows:1fr}
.faq-item.open .faq-a-inner{padding-bottom:18px}

/* ── Final CTA ─────────────────────────────────────────────────────────── */
.cta-sec{background:var(--ink);color:var(--paper);padding:clamp(52px,7vw,84px) 0;border:0}
.cta-sec .wrap{max-width:var(--maxw)}
.cta-sec h2{color:var(--paper);max-width:20ch;margin-bottom:14px}
.cta-sec p{color:#A8AFBB;font-size:var(--t-base);max-width:56ch;margin-bottom:30px}
.cta-sec .btn,.cta-sec .btn-cyan{background:var(--paper);color:var(--ink)}
.cta-sec .btn:hover,.cta-sec .btn-cyan:hover{background:var(--white)}
.cta-sec .btn-link,.cta-sec .btn-out{color:var(--paper);border-bottom-color:var(--paper)}
.cta-sec .btn-link:hover,.cta-sec .btn-out:hover{color:var(--white);border-bottom-color:var(--white)}
.cta-band{
  background:var(--paper-2);border:1px solid var(--rule);border-radius:0;
  padding:34px;margin-block:0 44px;margin-inline:var(--gutter);
}
.cta-band h2{margin-bottom:12px}
.cta-band p{color:var(--ink-2);margin-bottom:26px;max-width:56ch}

/* The band's own form. Every field style already exists under `.form-wrap`,
   so the band opts into that class and only overrides what it genuinely does
   differently: a shorter textarea, because this sits at the end of a long read
   rather than in a section built for it, and a row that puts the call beside
   the send instead of under it. The success box drops its border and padding —
   the band is already a bordered box, and one inside another reads as a bug. */
.cta-band .band-form{margin-bottom:0}
.cta-band .band-form .fg textarea{min-height:88px}
.cta-band .band-actions{display:flex;gap:24px;flex-wrap:wrap;align-items:center}
.cta-band .fsuccess{padding:0;border:0;background:none}
@media(max-width:560px){
  .cta-band .band-actions{gap:18px}
  .cta-band .band-actions .btn-primary{width:100%}
}

/* ── Contact form ──────────────────────────────────────────────────────── */
.contact-grid{display:grid;grid-template-columns:1fr 1.4fr;gap:52px;align-items:start}
.contact-info h3{margin-bottom:10px}
.contact-info p{font-size:var(--t-md);color:var(--ink-2);line-height:1.6;margin-bottom:26px}
.cinfo-list{display:flex;flex-direction:column;border-top:1px solid var(--rule)}
.cinfo-row{display:flex;align-items:center;gap:12px;padding:11px 0;border-bottom:1px solid var(--rule);font-size:var(--t-md);color:var(--ink-2)}
.cinfo-row svg{width:15px;height:15px;stroke:var(--navy);fill:none;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;flex:none}
.form-wrap .fg{display:flex;flex-direction:column;gap:6px;margin-bottom:16px}
.form-wrap .fg label{font-family:var(--f-mono);font-size:var(--t-xs);font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--ink-3)}
.form-wrap .fg input,.form-wrap .fg textarea,.form-wrap .fg select{
  background:var(--white);border:1px solid var(--field);border-radius:var(--r);
  padding:11px 13px;font-size:var(--t-md);color:var(--ink);
  font-family:var(--f-body);outline:none;width:100%;-webkit-appearance:none;appearance:none;
}
.form-wrap .fg select{
  background-image:linear-gradient(45deg,transparent 50%,var(--ink-3) 50%),linear-gradient(135deg,var(--ink-3) 50%,transparent 50%);
  background-position:calc(100% - 17px) 50%,calc(100% - 12px) 50%;
  background-size:5px 5px,5px 5px;background-repeat:no-repeat;padding-right:34px;
}
.form-wrap .fg input:focus,.form-wrap .fg textarea:focus,.form-wrap .fg select:focus{border-color:var(--navy);box-shadow:inset 0 0 0 1px var(--navy)}
.form-wrap .fg textarea{resize:vertical;min-height:118px;line-height:1.55}
.frow{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.chk{display:flex;align-items:flex-start;gap:10px;margin-bottom:20px}
.chk input{margin-top:3px;accent-color:var(--navy);width:16px;height:16px;flex:none;cursor:pointer}
.chk label{font-size:var(--t-sm);color:var(--ink-2);line-height:1.5}
.chk label a{color:var(--navy);text-decoration:underline;text-underline-offset:2px}
#ferr{color:#A8281C;font-size:var(--t-sm);margin-bottom:12px;display:none}
.fbtn{width:100%}
.fsuccess{display:none;border:1px solid var(--rule);border-top:3px solid var(--navy);padding:34px;background:var(--paper-2)}
.fsuccess h3{margin-bottom:8px}
.fsuccess p{color:var(--ink-2);font-size:var(--t-md)}

/* ═══ CROSS-LINK BLOCK ═════════════════════════════════════════════════════
   Above the footer on every page: one click from anywhere to anywhere. The
   articles used to have 3–6 inbound internal links each and were reachable
   mainly through the sitemap, which is a far weaker crawl signal than a link
   from an indexed page. Labels are <p> rather than headings on purpose — four
   more H2s per page would dilute the outline of the content being ranked. */
.xlinks{background:var(--paper-2);border-top:1px solid var(--rule);padding:clamp(34px,4.5vw,50px) var(--page-x) 32px}
.xlinks-inner{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:26px 36px;max-width:var(--maxw);margin:0 auto}
.xlinks-h{font-family:var(--f-mono);font-size:var(--t-xs);font-weight:700;letter-spacing:.11em;text-transform:uppercase;color:var(--ink-3);margin-bottom:11px;padding-bottom:8px;border-bottom:1px solid var(--rule)}
.xlinks-col a{display:block;font-size:var(--t-md);line-height:1.45;color:var(--ink-2);padding:4px 0}
.xlinks-col a:hover{color:var(--ink);text-decoration:underline;text-underline-offset:3px}

/* ── Footer ──────────────────────────────────────────────────────────────
   One shape on all 36 pages: the row sits directly in <footer>, and the legal
   paragraph claims a row of its own through flex-basis. The homepage used to
   wrap its row in .foot-inner and draw the wordmark as an inline SVG, which
   meant two layouts to keep in step and a :has() rule to stop the wordmark
   being drawn twice. */
footer{
  background:var(--ink);color:#A8AFBB;border:0;
  display:flex;flex-wrap:wrap;align-items:center;gap:14px 26px;
  padding:26px var(--page-x);
}
footer>*{min-width:0}
/* The wordmark, as text rather than as the logo SVG — the same mark on every
   page without three <path> strings per page to keep in sync. */
footer::before{
  content:'Yarvixo';font-family:var(--f-display);font-stretch:80%;font-weight:800;
  font-size:1.05rem;letter-spacing:-.012em;color:var(--paper);margin-right:4px;
}
.foot-links{display:flex;flex-wrap:wrap;align-items:center;gap:8px 22px;font-size:var(--t-sm)}
.foot-links a{color:#A8AFBB;overflow-wrap:anywhere}
.foot-links a:hover{color:var(--white);text-decoration:underline;text-underline-offset:3px}
.foot-links address{font-style:normal}
.foot-copy{font-family:var(--f-mono);font-size:var(--t-xs);letter-spacing:.04em;color:#8A919D}
/* Written by scripts/generate-seo.js — a crawler and an assistant both read the
   rendered text, not the JSON-LD, so the date has to be visible to count. */
.foot-updated{font-family:var(--f-mono);font-size:var(--t-xs);letter-spacing:.04em;color:#8A919D}
.foot-legal{flex-basis:100%;font-size:var(--t-sm);line-height:1.6;color:#7A8290;margin-top:2px}
.foot-legal a{color:#A8AFBB;text-decoration:underline;text-underline-offset:3px}

/* ── Policy modal ──────────────────────────────────────────────────────── */
.policy-overlay{display:none;position:fixed;inset:0;background:rgba(20,25,32,.55);z-index:10000;align-items:flex-start;justify-content:center;padding:32px 16px;overflow-y:auto}
.policy-overlay.open{display:flex}
.policy-modal{background:var(--paper);border:1px solid var(--rule-2);border-top:3px solid var(--ink);max-width:760px;width:100%;margin:auto}
.policy-modal-head{padding:24px 28px 16px;border-bottom:1px solid var(--rule);display:flex;align-items:flex-start;justify-content:space-between;gap:16px;position:sticky;top:0;background:var(--paper);z-index:1}
.policy-modal-head h2{font-size:var(--t-lg)}
.policy-meta{font-family:var(--f-mono);font-size:var(--t-xs);letter-spacing:.05em;text-transform:uppercase;color:var(--ink-3);margin-top:6px}
.policy-close{background:none;border:1px solid var(--field);color:var(--ink-2);width:32px;height:32px;border-radius:var(--r);cursor:pointer;font-size:var(--t-md);display:flex;align-items:center;justify-content:center;flex:none;transition:background .12s linear}
.policy-close:hover{background:var(--paper-2);color:var(--ink)}
.policy-body{padding:22px 28px 34px;overflow-y:auto;max-height:70vh}
.policy-body h3{font-size:var(--t-base);margin:26px 0 8px;padding-bottom:6px;border-bottom:1px solid var(--rule)}
.policy-body h3:first-child{margin-top:0}
.policy-body p{color:var(--ink-2);font-size:var(--t-md);margin-bottom:13px;line-height:1.65}
.policy-body ul{color:var(--ink-2);font-size:var(--t-md);padding-left:20px;margin-bottom:13px;line-height:1.65}
.policy-body li{margin-bottom:5px}
.policy-body strong{color:var(--ink)}
.policy-body a{color:var(--navy);text-decoration:underline;text-underline-offset:3px}
.policy-info{background:var(--paper-2);border-left:3px solid var(--navy);padding:14px 18px;margin-bottom:20px}
.policy-info p{margin-bottom:0;font-size:var(--t-sm)}

/* ── Consent banner ────────────────────────────────────────────────────────
   Built by analytics.js and appended to <body> after load, so it belongs to no
   page's markup and costs no layout shift. Fixed to the bottom rather than
   overlaid across the page: this asks a question, it does not need to block
   the answer to another one. z-index sits under .policy-overlay (10000) on
   purpose — the Cookie Policy the banner links to has to open ON TOP of it. */
.consent{
  position:fixed;left:0;right:0;bottom:0;z-index:9000;
  background:var(--paper);border-top:3px solid var(--ink);
  box-shadow:0 -6px 24px rgba(20,25,32,.10);
}
.consent-in{
  max-width:1180px;margin:0 auto;padding:16px var(--gutter);
  display:flex;align-items:center;gap:22px;
}
.consent-t{font-size:var(--t-sm);line-height:1.6;color:var(--ink-2);margin:0}
.consent-t a{color:var(--navy);text-decoration:underline;text-underline-offset:3px}
.consent-b{display:flex;gap:10px;flex:none}
.consent-b button{
  font-family:inherit;font-size:var(--t-md);font-weight:700;
  padding:9px 20px;border-radius:var(--r);cursor:pointer;
  transition:background var(--dur-1) linear,color var(--dur-1) linear;
}
.consent-no{background:none;border:1px solid var(--field);color:var(--ink-2)}
.consent-no:hover{background:var(--paper-2);color:var(--ink)}
.consent-yes{background:var(--navy);border:1px solid var(--navy);color:var(--white)}
.consent-yes:hover{background:var(--navy-dark);border-color:var(--navy-dark)}
@media(max-width:700px){
  .consent-in{flex-direction:column;align-items:stretch;gap:14px}
  .consent-b button{flex:1}
}
@media print{.consent{display:none!important}}

/* ── Responsive ────────────────────────────────────────────────────────── */
@media(max-width:900px){
  .nl{display:none}
  /* 44px, because from here down this button is the only navigation there is.
     Sized where it is turned on, not in a narrower query — see the note on
     scrollbars in the phone block below. */
  .burger{
    display:flex;width:44px;height:44px;
    align-items:center;justify-content:center;margin-right:-10px;
  }
}
@media(max-width:760px){
  .contact-grid{grid-template-columns:1fr;gap:36px}
  .frow{grid-template-columns:1fr}
  .sec-head{flex-wrap:wrap;gap:10px 18px}
  .sec-head h2,.sec-head h1{white-space:normal}
  .sec-head .rule{display:none}
  .case-slim{grid-template-columns:minmax(0,1fr);gap:4px}
  .case-slim .go{display:none}
  .cta-row,.hero-btns,.cta-btns{gap:18px 26px}
}
@media(max-width:560px){
  /* The hero carries the same call to action twenty pixels lower; two of them
     competing for a 375px header just squeezes the wordmark. */
  .btn-nav{display:none}
}

/* ═══ PHONE AND TABLET ═════════════════════════════════════════════════════
   Every grid collapsing to a single column is what turned a 10,840px page into
   an 18,665px one: correct, and an eighteen-metre scroll. Two columns wherever
   the content is short enough to take them. */
@media(max-width:760px){
  /* Short lists and short paragraphs pair up happily at 360px. */
  .stack{grid-template-columns:repeat(2,1fr);gap:10px}
  .stack-col{padding:16px 14px}
  .proc{grid-template-columns:repeat(2,1fr);gap:10px}
  .pstep{padding:16px 14px}
  .about-stats{grid-template-columns:repeat(2,1fr);gap:10px}
  .about-stat{padding:15px 14px 16px}
  .writing,.records,.grid-3,.related{gap:12px}
  /* Thirty-three links in one column is nine hundred pixels of footer. */
  .xlinks-inner{grid-template-columns:repeat(2,1fr);gap:22px 18px}
  .xlinks-col a{font-size:.875rem;padding:5px 0}
  .sec,.sec-alt{padding-block:clamp(38px,9vw,54px)}
  .sec-dek{margin-bottom:24px}

  /* The spec cells stay one per row and go compact instead. Two columns would
     put a 57-character measurement condition into a 125px well — seven lines a
     cell, taller than what it replaced. Figure and label share a baseline, the
     condition sits underneath, and the cell drops from 147px to about 95. */
  .spec{gap:10px}
  .spec-cell{
    padding:14px 16px 16px;
    display:grid;grid-template-columns:auto minmax(0,1fr);
    column-gap:12px;align-items:baseline;
  }
  .spec-n{font-size:1.75rem}
  .spec-l{margin-top:0;letter-spacing:.06em;text-align:right}
  .spec-c{grid-column:1 / -1;margin-top:8px;font-size:.8125rem}
}

/* Genuinely narrow screens only. 360px is the commonest Android width, not an
   edge case, and it belongs in the two-column branch above. */
@media(max-width:330px){
  .stack,.proc,.about-stats{grid-template-columns:1fr}
}
@media(max-width:480px){
  .rec{grid-template-columns:minmax(0,1fr);row-gap:8px}
  .rec-i{padding-top:0}
  .btn,.btn-primary,.bp,.btn-cyan{width:100%}
  .policy-body{max-height:64vh;padding-left:18px;padding-right:18px}
  .policy-modal-head{padding-left:18px;padding-right:18px}
}
/* ═══ MOTION ═══════════════════════════════════════════════════════════════
   Three rules govern everything below.

   1. Nothing is hidden unless JavaScript is running. main.js stamps .js-motion
      on <html>; every start state is scoped to it. If the script fails, 404s or
      is blocked, the page renders complete and static — the old design hid the
      hero headline behind a CSS animation-delay, which meant a stalled
      stylesheet showed a visitor an empty page.
   2. Nothing moves far. 10px and one opacity step. Entrances announce the
      structure of the page; they are not the point of it.
   3. Everything here is off under prefers-reduced-motion, including the start
      states, so a reduced-motion visitor sees the finished page immediately.  */

/* The transition is declared here, on the start state, and not on .in — see the
   note on the score bars. A shorthand on the end state would both fail to
   animate and silently reset the delay below to zero.

   The stagger: --i is set per element by main.js, counting its position among
   its revealing siblings, so a group of four arrives as a wave rather than as a
   single flash. Capped at 5 so a long list never leaves the reader waiting. */
.js-motion .reveal{
  opacity:0;transform:translateY(10px);
  transition:opacity var(--dur-2) var(--ease),transform var(--dur-2) var(--ease);
  transition-delay:calc(var(--i,0) * 60ms);
}
.js-motion .reveal.in{opacity:1;transform:none}

/* The drawing assembles itself once: the boxes fade up, then the arrows draw
   along their own length. pathLength="1" on each arrow normalises every path to
   a single unit, so one dash offset animates a 70px connector and a 200px
   dogleg at the same rate — without it the dash pattern shows gaps on the long
   ones. As everywhere else, the finished state is the default and the start
   state only exists while js-motion is on. */
.js-motion .diagram-arch .d-arrow{
  stroke-dasharray:1;stroke-dashoffset:0;
  transition:stroke-dashoffset 900ms var(--ease) 260ms;
}
.js-motion .diagram-arch:not(.in) .d-arrow{stroke-dashoffset:1}
.js-motion .diagram-arch .d-box,.js-motion .diagram-arch .d-fleet,
.js-motion .diagram-arch .d-node rect,.js-motion .diagram-arch .d-note{
  transition:opacity 520ms var(--ease);
}
.js-motion .diagram-arch:not(.in) .d-box,.js-motion .diagram-arch:not(.in) .d-fleet,
.js-motion .diagram-arch:not(.in) .d-node rect,.js-motion .diagram-arch:not(.in) .d-note{opacity:0}

/* Two motions on one element, kept apart by using the independent transform
   properties rather than the transform shorthand: `translate` carries the
   scroll parallax that main.js writes into --par, `scale` carries the 48-second
   breath from the keyframes. Written as one `transform` they would overwrite
   each other. The element is inset past the frame on all sides so neither ever
   exposes an edge. */
.hero::before{
  inset:-7% -3%;
  translate:0 var(--par,0px);
  animation:breathe 13s ease-in-out infinite;
}
@keyframes breathe{0%,100%{scale:1}50%{scale:1.06}}

/* ═══ LIVING BACKGROUNDS ═══════════════════════════════════════════════════
   Two blooms of light drifting and breathing behind the dark bands, and a
   quieter version of the same idea behind the tinted light ones. The blooms are
   what make a background feel alive — a drifting grid on its own is wallpaper.

   Everything is translate and scale on a layer behind the content, so none of
   it costs layout or repaints the text above it. All of it is navy and its
   blues: a living background is not an excuse for a second accent colour. */

@keyframes aurora{
  from{translate:0 0;scale:1}
  to{translate:-16rem 9rem;scale:1.42}
}
@keyframes aurora-b{
  from{translate:0 0;scale:1.22}
  to{translate:7rem -4.5rem;scale:1}
}

/* ── Dark bands ────────────────────────────────────────────────────────── */
.cta-sec,.logo-band{position:relative;isolation:isolate;overflow:hidden}
.cta-sec::before,.logo-band::before{
  content:'';position:absolute;inset:-16rem;z-index:-1;pointer-events:none;
  background-image:
    radial-gradient(32rem 32rem at 78% 14%,rgba(104,142,236,.40),transparent 66%),
    radial-gradient(26rem 26rem at 12% 88%,rgba(64,100,200,.30),transparent 70%);
  animation:aurora 7s ease-in-out infinite alternate;
  will-change:translate,scale;
}
/* The pattern, masked so it fades out rather than tiling to the corners. */
.cta-sec::after,.logo-band::after{
  content:'';position:absolute;inset:-20rem;z-index:-1;pointer-events:none;
  background-image:
    radial-gradient(rgba(205,220,250,.22) 1px,transparent 1px),
    linear-gradient(to bottom,rgba(205,220,250,.075) 1px,transparent 1px),
    linear-gradient(to right,rgba(205,220,250,.075) 1px,transparent 1px);
  background-size:2.5rem 2.5rem,100% 3.5rem,3.5rem 100%;
  -webkit-mask-image:radial-gradient(80% 70% at 50% 40%,#000 0%,transparent 78%);
  mask-image:radial-gradient(80% 70% at 50% 40%,#000 0%,transparent 78%);
  animation:drift-far 13s linear infinite;
  will-change:translate;
}
/* Two full pattern steps, so the loop lands back on itself with no seam. */
@keyframes drift-far{from{translate:0 0}to{translate:-17.5rem -17.5rem}}
.logo-band::before{animation-delay:-5s}
.logo-band::after{animation-delay:-8s}

/* ── Tinted light bands ────────────────────────────────────────────────────
   The same idea at a quarter of the strength. These were completely static,
   which is most of why the page still read as flat below the hero. */
.sec-alt{position:relative;isolation:isolate;overflow:hidden}
.sec-alt>*{position:relative;z-index:1}
.sec-alt::before{
  content:'';position:absolute;inset:-6rem;z-index:0;pointer-events:none;
  background-image:radial-gradient(rgba(36,58,116,.20) 1px,transparent 1px);
  background-size:2.25rem 2.25rem;
  -webkit-mask-image:radial-gradient(78% 68% at 50% 45%,#000 0%,transparent 80%);
  mask-image:radial-gradient(78% 68% at 50% 45%,#000 0%,transparent 80%);
  animation:dots-light 7s linear infinite;
  will-change:translate;
}
@keyframes dots-light{from{translate:0 0}to{translate:-4.5rem -4.5rem}}
.sec-alt::after{
  content:'';position:absolute;top:-18rem;right:-12rem;
  width:40rem;height:40rem;border-radius:999px;z-index:0;pointer-events:none;
  background:radial-gradient(circle at center,rgba(168,193,235,.46),rgba(185,204,236,.20) 46%,transparent 72%);
  animation:aurora-b 6s ease-in-out infinite alternate;
  will-change:translate,scale;
}
@media(max-width:640px){
  .sec-alt::after{top:-12rem;right:-9rem;width:28rem;height:28rem}
}

/* The score bars grow from nothing the first time the figure is seen. The
   transition sits on the base selector, not on .in — a property that only
   becomes transitionable in the same style recalc that changes its value does
   not animate at all, which is how the first version of this ended up with four
   bars stuck at zero. */
.js-motion .score-f{
  transition:transform 700ms var(--ease);
  transition-delay:calc(var(--i,0) * 80ms);
}
.js-motion .scores:not(.in) .score-f{transform:scaleX(0)}

/* The header gains a hairline shadow once the page has left the top, so it
   separates from content scrolling under it. */
nav.main-nav{transition:box-shadow var(--dur-1) linear,border-color var(--dur-1) linear}
nav.main-nav.scrolled{box-shadow:0 1px 0 rgba(20,25,32,.10),0 6px 18px rgba(20,25,32,.05)}

/* Hover: the arrow on a record or a case steps forward. Cheap, and it tells you
   the whole row is the link rather than just the words. */
.rec-go,.case-arr,.cases-more a,.card .arr,.rel-card .arr{transition:color var(--dur-1) linear}
a.rec:hover .rec-go,.case:hover .case-arr{color:var(--navy-dark)}
.case-arr,.rec-go{position:relative}
a.rec .rec-go::after,.case .case-arr::after{
  content:'';position:absolute;left:0;right:0;bottom:-2px;height:2px;
  background:var(--navy);transform:scaleX(0);transform-origin:left;
  transition:transform var(--dur-2) var(--ease);
}
a.rec .rec-go::after{background:var(--navy-dark)}
a.rec:hover .rec-go::after,.case:hover .case-arr::after{transform:scaleX(1)}

/* ── Skip link ─────────────────────────────────────────────────────────────
   Off-screen until focused, then a real target in the top-left. Every page has
   a <main id="main-content"> for it to reach. */
.skip{
  position:absolute;left:-9999px;top:0;z-index:9999;
  background:var(--ink);color:var(--paper);
  padding:12px 20px;border-radius:0;
  font-family:var(--f-mono);font-size:var(--t-sm);font-weight:700;
  letter-spacing:.05em;text-transform:uppercase;
}
.skip:focus{left:0}

/* ═══ PRINT ════════════════════════════════════════════════════════════════
   The entrance start state must not survive into print. An observer never runs
   in a print context, so every un-revealed block would come out as blank paper
   — the case studies and the pricing bands among them. */
@media print{
  .js-motion .reveal{opacity:1!important;transform:none!important}
  .js-motion .scores:not(.in) .score-f{transform:none!important}
  /* Ink-on-paper for real. The ruled ground, the ink bands and the reversed
     type all cost toner and read worse than plain black on white. */
  body{background:#fff!important;background-image:none!important;color:#000}
  body::before,body::after,.hero-grid,.case-hero::before,.case-hero::after,
  .cta-sec::before,.cta-sec::after,.logo-band::before,.logo-band::after,
  .sec-alt::before,.sec-alt::after{display:none!important}
  .logo-band,.cta-sec,footer{background:#fff!important;color:#000!important}
  .logo-band .lbl,.cta-sec h2,.cta-sec p,.foot-links a,.foot-copy,.foot-updated,.foot-legal{color:#000!important}
  nav.main-nav{position:static;box-shadow:none}
  .skip,.burger,.mob,.btn,.btn-primary,.bp,.btn-cyan,.fbtn,.btn-link,.btn-secondary,.bs,.btn-out,.policy-overlay{display:none!important}
  /* Answers are the content on the FAQ; printing eighteen collapsed rows prints
     eighteen questions and no answers. */
  .faq-a{grid-template-rows:1fr!important}
  .faq-a-inner{overflow:visible!important}
  .sec,.sec-alt{padding-block:18px;break-inside:auto}
  .case,.rec,.card{break-inside:avoid}
  a[href^="http"]::after{content:" (" attr(href) ")";font-size:.85em;color:#444}
}

@media(prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *{animation:none!important;transition:none!important}
  /* The start states go too, or a reduced-motion visitor is left with a page
     of invisible blocks waiting for a transition that will never run. */
  .js-motion .reveal{opacity:1;transform:none}
  .js-motion .scores:not(.in) .score-f{transform:none}
  .js-motion .diagram-arch:not(.in) .d-arrow{stroke-dashoffset:0}
  .js-motion .diagram-arch:not(.in) .d-box,.js-motion .diagram-arch:not(.in) .d-fleet,
  .js-motion .diagram-arch:not(.in) .d-node rect,.js-motion .diagram-arch:not(.in) .d-note{opacity:1}
  .hero::before{translate:none;scale:none}
  body::before,body::after,
  .case-hero::before,
  .hero-grid,.hero-grid::after,
  .cta-sec::before,.cta-sec::after,.logo-band::before,.logo-band::after,
  .sec-alt::before,.sec-alt::after{animation:none;translate:none;scale:none}
}
