
/* ═══════════════════════════════════════════════════════════════════════════
   The Verge structure, Niekie's colours.
   See DESIGN-theverge.md. Near-black editorial canvas, a display face that only
   ever shouts, mono uppercase for every label, pill corners on everything, and
   1px hairlines doing the work shadows would do elsewhere. No gradients.

   WHAT CHANGED FROM THE REFERENCE, AND WHY
   Acid mint (#3cffd0) and Verge ultraviolet (#5200ff) are that publication's
   brand, not ours. The hazard-accent ROLE is kept exactly, but filled with
   Niekie indigo. Two practical consequences:

   - Raw #533afd on #131313 measures about 3.5:1, which fails at label sizes.
     So dark polarity uses a lifted indigo (#8f7dff) for text and hairlines and
     keeps the saturated one for fills, where the text sits on top of it.
   - Ruby (#ea2261) exists but is NOT a decorative tile colour. In this system a
     saturated block means "read this first", and a red block on a casino would
     read as a verdict we have not made. Ruby is reserved for genuine warnings.

   The reference says there is no light mode. This site ships a theme switch, so
   there is one: the same structure with the canvas inverted, indigo darkened to
   hold contrast on white, and the accent-on-accent text flipped.

   TYPE SUBSTITUTES
   Manuka -> Anton, with display line-height loosened 0.80 -> 0.95 per the
   substitution note. PolySans -> Space Grotesk. PolySans Mono -> Space Mono.
   ═══════════════════════════════════════════════════════════════════════════ */
:root{
  /* ONE TEXT MEASURE, AND WHY IT IS NOT IN ch.
     Every prose block used to say max-width:var(--measure), which reads as consistent
     and is not: ch is relative to font-size, so the same declaration came out
     663px on .hero at 17px and 507px on .fineprint at 13px, with .paidnote at
     62ch landing on 403px. Stacked in one column that is seven different right
     edges and it looks like a mistake, because it is one.
     41rem is the hero's old width, so the main copy is unchanged and everything
     else lines up with it. */
  --measure:41rem;
  /* --frame was #313131: 1.43:1 against the canvas, a card edge you cannot
     actually see. Every box on the page is drawn with it, so nothing had a
     defined shape and the layout leaned entirely on the accent to hold
     together — which is why the seasonal skin, whose only change is a brighter
     accent, read as sharper. #4d4d4d is 2.20:1: still quiet, but present. */
  --canvas:#131313; --slate:#2d2d2d; --frame:#4d4d4d; --inset:#1b1b1b;
  --text:#ffffff; --text-2:#e9e9e9; --mute:#949494; --dim:#8c8c8c;
  --accent:#533afd;        /* fills, blocks, the loud thing on the page */
  --accent-ink:#b3a4ff;    /* as TEXT on dark: 8.56:1. Was #8f7dff at 5.79. */
  --accent-edge:#8878ff;   /* hairlines: 5.47:1. Was #6b57ff at 3.92. */
  --on-accent:#ffffff;     /* text sitting on an accent fill */
  --accent-2:#2a1a8c;      /* the deeper block, the reference's ultraviolet role */
  --rule:#3a2ba8;          /* the timeline spine */
  --ruby:#ea2261;          /* warnings only, never decoration */
  --link-hover:#8f7dff;
  --focus:#1eaedb;
  --display:"Anton",Impact,Haettenschweiler,sans-serif;
  --sans:"Space Grotesk",Helvetica,Arial,sans-serif;
  --mono:"Space Mono","Courier New",Courier,monospace;
  /* legacy aliases, so nothing that still references the old names breaks */
  --bg:var(--canvas); --surf:var(--canvas); --surf-2:var(--slate); --surf-3:var(--inset);
  --line:var(--frame); --line-strong:var(--mute); --ash:var(--mute);
  --brand:var(--accent); --brand-lift:var(--accent-ink); --brand-soft:var(--accent-ink);
  --on-brand:var(--on-accent); --ink-deep:var(--accent-2); --strip:var(--canvas);
  --moon:var(--mute); --radius:20px;
}
/* ── Halloween ───────────────────────────────────────────── */
/* Only the accent family moves. Canvas, frame and text stay where they are,
   because this is an index people check before spending money and it should
   still look like itself in October.

   WHITE STOPS BEING THE COLOUR ON THE FILL.
   The purple fill carries white at 6.19:1. Orange cannot: white on a pumpkin
   #ff8c1a is 3.35:1, which fails for button text. Near-black on the same fill
   is 8.27:1 — better than the purple it replaces — so --on-accent flips, which
   is the token's whole purpose and means no rule that uses it has to change.

   --ruby does not move. It is the warning colour and nothing else, and a
   seasonal skin is not a reason to repaint the one token that carries meaning.
   Worth knowing: orange and ruby sit closer in hue than purple and ruby did, so
   a reader who cannot separate those hues leans harder on the wording. Every
   warning is already written out, which is why that is a note and not a bug. */
/* ── Christmas ───────────────────────────────────────── */
/* Evergreen, not red. Red is --ruby's job here and --ruby is the warning
   colour, so painting the accent red would put the loudest decoration on the
   page in the same hue as 'this operator has a problem'. Green accent leaves
   ruby where it is, which incidentally gives the pairing anyway. */
:root[data-season="christmas"]{
  --canvas:#0f1411; --inset:#161d19;
  --accent:#4fd98a;        /* carries near-black at 10.43:1 */
  --on-accent:#07140d;
  --accent-ink:#5fe098;    /* as text on canvas, 11.17:1 */
  --accent-edge:#3cb36d;   /* 6.97:1 */
  --accent-2:#0b5a33;
  --rule:#1f7a4a;
  --link-hover:#8df0b6;
}
:root[data-season="christmas"][data-theme="light"]{
  --canvas:#f7fbf8; --inset:#eaf5ee;
  --accent:#4fd98a; --on-accent:#07140d;
  --accent-ink:#0f5c32;    /* 7.74:1 */
  --accent-edge:#157a42;   /* 5.16:1 */
  --accent-2:#0b5a33;
  --rule:#9bd9b5;
  --link-hover:#0f5c32;
}
:root[data-season="christmas"] .t3-peek{
  filter:drop-shadow(0 0 10px rgba(79,217,138,.26))}
:root[data-season="christmas"][data-theme="light"] .t3-peek{
  filter:invert(1) drop-shadow(0 0 10px rgba(21,122,66,.35))}
/* Snow. Nine flakes at three speeds, spread across the width by hand so they
   do not clump; a random offset would re-roll on every render and make the
   page look different to the same visitor twice. */
.nk-fall{position:absolute;top:-6%;color:var(--accent-ink);opacity:.45}
.nk-fall svg{display:block;width:100%;height:auto}
.nk-fall-1{left:4%;width:18px}  .nk-fall-2{left:14%;width:12px}
.nk-fall-3{left:27%;width:22px} .nk-fall-4{left:38%;width:14px}
.nk-fall-5{left:51%;width:18px} .nk-fall-6{left:63%;width:11px}
.nk-fall-7{left:74%;width:20px} .nk-fall-8{left:85%;width:13px}
.nk-fall-9{left:94%;width:16px}
@media (prefers-reduced-motion:no-preference){
  .nk-fall{animation:nk-drift linear infinite}
  .nk-fall-1{animation-duration:19s} .nk-fall-2{animation-duration:26s;animation-delay:3s}
  .nk-fall-3{animation-duration:16s;animation-delay:7s} .nk-fall-4{animation-duration:23s;animation-delay:1s}
  .nk-fall-5{animation-duration:21s;animation-delay:11s} .nk-fall-6{animation-duration:28s;animation-delay:5s}
  .nk-fall-7{animation-duration:17s;animation-delay:9s} .nk-fall-8{animation-duration:24s;animation-delay:13s}
  .nk-fall-9{animation-duration:20s;animation-delay:2s}
}
/* vh, so the fall covers the screen whatever the flake's own size. */
@keyframes nk-drift{
  0%{transform:translate(0,0) rotate(0deg);opacity:0}
  10%{opacity:.45}
  90%{opacity:.45}
  100%{transform:translate(-26px,112vh) rotate(220deg);opacity:0}
}
@media (max-width:720px){
  .nk-fall-2,.nk-fall-4,.nk-fall-6,.nk-fall-8{display:none}
}

/* Decoration sits under the page, never over it, and never on the pointer.
   overflow:hidden on the layer matters: a bat drifting past the right edge is
   the classic way a decorative element invents a horizontal scrollbar. */
.nk-decor{position:fixed;inset:0;z-index:0;pointer-events:none;overflow:hidden;
  contain:strict}
.main,.topbar,.strip{position:relative;z-index:1}
.nk-web{position:absolute;width:clamp(90px,11vw,170px);height:auto;
  color:var(--accent-ink);opacity:.17}
.nk-web-tl{top:0;left:0}
.nk-web-tr{top:0;right:0;transform:scaleX(-1)}
/* Two transforms, two elements. The wrapper carries the journey across the
   screen and the svg inside carries the flap, because one element cannot run
   two transform animations and compositing them by hand means rewriting the
   path on every keyframe.

   A bat squashed vertically reads as wings coming down: the silhouette is wide
   and flat, so scaleY is the cheapest honest flap there is. Each bat flaps on
   its own period, none of them a factor of another, so the three never fall
   into step. The drift is no longer linear either — a constant speed in a
   straight line was the thing that read as a sticker on a rail. */
.nk-flit{position:absolute;color:var(--accent-ink);opacity:.5}
.nk-flit .nk-bat{display:block;width:100%;height:auto;transform-origin:50% 40%}
.nk-flit-1{width:40px;top:14%;left:-8%}
.nk-flit-2{width:26px;top:33%;left:-14%}
.nk-flit-3{width:33px;top:63%;left:-10%}
@media (prefers-reduced-motion:no-preference){
  .nk-flit-1{animation:nk-cross 29s cubic-bezier(.45,.05,.55,.95) infinite}
  .nk-flit-2{animation:nk-cross 44s cubic-bezier(.45,.05,.55,.95) infinite 5s}
  .nk-flit-3{animation:nk-cross 36s cubic-bezier(.45,.05,.55,.95) infinite 13s}
  .nk-flit-1 .nk-bat{animation:nk-flap .9s ease-in-out infinite}
  .nk-flit-2 .nk-bat{animation:nk-flap .74s ease-in-out infinite}
  .nk-flit-3 .nk-bat{animation:nk-flap 1.05s ease-in-out infinite}
}
/* vw so the crossing covers the screen whatever the bat's own size. The height
   wanders by more than it used to and the roll follows the climb, the way a
   thing with wings banks into a turn. */
@keyframes nk-cross{
  0%{transform:translate(0,0) rotate(-6deg)}
  18%{transform:translate(24vw,-52px) rotate(7deg)}
  34%{transform:translate(44vw,18px) rotate(-9deg)}
  52%{transform:translate(66vw,-34px) rotate(6deg)}
  71%{transform:translate(90vw,26px) rotate(-7deg)}
  88%{transform:translate(112vw,-16px) rotate(5deg)}
  100%{transform:translate(130vw,4px) rotate(-6deg)}
}
/* Down fast, up slower, which is roughly what a wingbeat does.

   The amplitude was .42 and it squashed the whole animal — head and ears
   crushed with the wings, which reads as the bat being compressed rather than
   flying. Rendered as a filmstrip at .42, .58, .78 and rest, .78 is the point
   where the wings clearly move and the silhouette still reads as a bat. The
   honest fix would be two wing poses swapped frame by frame; scaling one path
   can only ever approximate it, so it is kept quiet enough not to claim more
   than it is. */
@keyframes nk-flap{
  0%{transform:scaleY(1)}
  40%{transform:scaleY(.78)}
  100%{transform:scaleY(1)}
}
/* On a phone the webs eat the corners the nav and the mark live in. */
@media (max-width:720px){
  .nk-web{width:84px;opacity:.13}
  .nk-flit-2,.nk-flit-3{display:none}
}

:root[data-season="halloween"]{
  --canvas:#12100f; --inset:#1a1613;
  --accent:#ff8c1a;        /* pumpkin, carries near-black at 8.27:1 */
  --on-accent:#140d06;     /* was white; see above */
  --accent-ink:#ff9f45;    /* as text on canvas, 9.30:1 */
  --accent-edge:#c9701a;   /* hairlines, 5.26:1 */
  --accent-2:#7a3508;
  --rule:#8a4a12;
  --link-hover:#ffb366;
}
:root[data-season="halloween"][data-theme="light"]{
  --canvas:#fffaf5; --inset:#fdf1e6;
  --accent:#ff8c1a; --on-accent:#2a1708;   /* 7.36:1 */
  --accent-ink:#a83f06;    /* as text on the warm white, 5.99:1 */
  --accent-edge:#d2560a;
  --accent-2:#7a3508;
  --rule:#e0a86a;
  --link-hover:#a83f06;
}
/* The mark is black line art on a black shield and reads as a silhouette. A
   warm rim in season, nothing more; the artwork itself is not reskinned. */
:root[data-season="halloween"] .t3-peek{
  filter:drop-shadow(0 0 10px rgba(255,140,26,.28))}
:root[data-season="halloween"][data-theme="light"] .t3-peek{
  filter:invert(1) drop-shadow(0 0 10px rgba(255,140,26,.45))}
@media (prefers-color-scheme:light){
  :root[data-season="halloween"]:not([data-theme="dark"]) .t3-peek{
    filter:invert(1) drop-shadow(0 0 10px rgba(255,140,26,.45))}
}

:root[data-theme="light"]{
  --canvas:#ffffff; --slate:#f2f1ef; --frame:#b8b3aa; --inset:#f7f6f4;
  --text:#131313; --text-2:#2d2d2d; --mute:#6b6b6b; --dim:#8c8c8c;
  --accent:#4527e0; --accent-ink:#4527e0; --accent-edge:#4527e0;
  --on-accent:#ffffff; --accent-2:#2a1a8c; --rule:#b9aefb;
  --link-hover:#4527e0;
}
@media (prefers-color-scheme:light){
  :root:not([data-theme="dark"]){
    --canvas:#ffffff; --slate:#f2f1ef; --frame:#b8b3aa; --inset:#f7f6f4;
    --text:#131313; --text-2:#2d2d2d; --mute:#6b6b6b; --dim:#8c8c8c;
    --accent:#4527e0; --accent-ink:#4527e0; --accent-edge:#4527e0;
    --on-accent:#ffffff; --accent-2:#2a1a8c; --rule:#b9aefb;
    --link-hover:#4527e0;
  }
}
*{box-sizing:border-box}
html,body{height:100%}
html{background:var(--canvas)}
img,svg,video{max-width:100%;height:auto}
body{
  margin:0;background:var(--canvas);color:var(--text);
  font-family:var(--sans);font-size:16px;font-weight:400;line-height:1.6;
  -webkit-font-smoothing:antialiased;
  display:flex;flex-direction:column;min-height:100%;
}
/* Colour-only hover, no underline. The reference is emphatic about this. */
a{color:inherit;text-decoration:none;transition:color 150ms ease}
a:hover{color:var(--link-hover)}
:focus-visible{outline:2px solid var(--focus);outline-offset:3px}
h1,h2,h3{margin:0}
.num{font-variant-numeric:tabular-nums}

/* Display shouts and nothing else. Anything under 34px is not this face. */
.verdict,.display{font-family:var(--display);font-weight:400;text-transform:uppercase;
  line-height:.95;letter-spacing:1.07px;font-size:clamp(38px,7vw,90px);margin:0}

/* ── Nav ─────────────────────────────────────────────────────────────── */
.topbar{display:flex;align-items:center;gap:24px;padding:14px 48px;
  border-bottom:1px solid var(--frame);position:sticky;top:0;z-index:30;
  background:var(--canvas)}
.topbar .spacer{flex:1}
/* The reading links. Same type as the tools so the two groups read as one bar,
   separated by position rather than by styling. */
.topbar .reads{display:flex;align-items:center;gap:2px;margin-left:10px}
.dd{position:relative}
.dd-btn{appearance:none;-webkit-appearance:none;background:none;border:0;cursor:pointer;
  display:inline-flex;align-items:center;gap:6px;font-family:var(--mono);font-size:11.5px;
  font-weight:700;text-transform:uppercase;letter-spacing:1.2px;color:var(--text-2);
  padding:7px 11px;border-radius:9px;white-space:nowrap;
  transition:color 120ms ease,background-color 120ms ease}
.dd-btn svg{transition:transform .15s ease}
.dd-btn:focus-visible{outline:2px solid var(--accent-edge);outline-offset:2px}
.dd.open>.dd-btn{background:var(--surf-2);color:var(--accent-ink)}
.dd.open>.dd-btn svg{transform:rotate(180deg)}
/* A panel, not a slab of accent. Four of these opened full-bleed purple with
   white text, which at six top-level controls read as six competing adverts
   rather than a navigation bar. It is now the same surface as a card, with the
   site's own frame and text colours, so the accent is free to mean "this is
   the thing you want" on the one control that should carry it. */
.dd-menu{display:none;position:absolute;top:calc(100% + 10px);left:50%;transform:translateX(-50%);
  min-width:232px;background:var(--canvas);color:var(--text);border:1px solid var(--frame);
  border-radius:16px;padding:16px 18px 14px;box-shadow:0 24px 48px -18px rgba(0,0,0,.55);z-index:40}
/* Explore carries two headed columns. The display rule has to beat the
   .dd.open>.dd-menu block rule and the hover rule beside it, so it is written at
   the same specificity rather than on .dd-wide alone. Anchored to the button's
   left edge instead of centred, because a two-column panel centred under a
   short label hangs off the start of the bar.
   (No backticks in this file's CSS: it is inside a JS template literal.) */
.dd-menu.dd-wide{left:0;transform:none;min-width:420px}
.dd.open>.dd-menu.dd-wide{display:grid}
.dd-menu.dd-wide{grid-template-columns:repeat(2,minmax(150px,1fr));gap:4px 28px}
/* A bridge across the gap, so the pointer can travel from label to panel. */
.dd-menu::before{content:"";position:absolute;left:0;right:0;top:-14px;height:14px}
.dd.open>.dd-menu{display:block}
.dd-label{font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:1.3px;
  text-transform:uppercase;color:var(--mute);margin:0 0 9px}
.topbar .reads .dd-menu a,.topbar .tools .dd-menu a{display:block;font-family:var(--sans);
  font-size:14.5px;font-weight:500;text-transform:none;letter-spacing:0;color:var(--text-2);
  padding:6px 0;white-space:nowrap;border-radius:6px}
.topbar .tools .dd-menu a:hover{color:var(--accent-ink);text-decoration:none}
.dd-tools>.dd-menu{left:auto;right:0;transform:none}
.topbar .reads .dd-menu a:hover{color:var(--accent-ink);text-decoration:none}
.dd-all,.topbar .reads .app-direct{display:none}
@media (hover:hover) and (min-width:641px){
  .dd:hover>.dd-menu,.dd:focus-within>.dd-menu{display:block}
  .dd:hover>.dd-menu.dd-wide,.dd:focus-within>.dd-menu.dd-wide{display:grid}
  .dd:hover>.dd-btn,.dd:focus-within>.dd-btn{background:var(--surf-2);color:var(--accent-ink)}
  .dd:hover>.dd-btn svg{transform:rotate(180deg)}
}
.topbar .reads a{font-family:var(--mono);font-size:11.5px;font-weight:700;text-transform:uppercase;
  letter-spacing:1.2px;color:var(--text-2);white-space:nowrap;padding:7px 11px;border-radius:9px;
  transition:color 120ms ease,background-color 120ms ease}
.topbar .reads a:hover{background:var(--surf-2);color:var(--accent-ink)}
.topbar .reads a:hover{color:var(--link-hover)}
.topbar a{font-family:var(--mono);font-size:12px;font-weight:700;text-transform:uppercase;
  letter-spacing:1.5px;color:var(--text);white-space:nowrap;padding:6px 0}
.topbar a:hover{color:var(--link-hover)}
/* The method page is the one reading link that is not in a panel, because the
   site's whole claim is the method. accent-ink rather than accent: the fill
   colour on this canvas is 3.6:1 as text, which is the same mistake the score
   meter made once already. */
.topbar .reads a.hws{color:var(--accent-ink)}
.topbar a.mark{display:inline-flex;align-items:center;gap:10px;padding:0}
.topbar a.mark-sm{display:none}
/* The mark is the artwork, not a mask.
   newlogo.png is a black shield with white line art inside it. Masking it
   fills the whole silhouette with one colour, so the shield and the drawing
   collapse into a single flat shape: on dark that reads as a plain white
   blob. It is drawn as an image everywhere, and because it carries its own
   black ground it needs no help from the theme. */
.mark img{width:28px;height:28px;display:block}
.glyph{display:block;background:var(--text);
  -webkit-mask:url(/newlogo.png) no-repeat center/contain;
  mask:url(/newlogo.png) no-repeat center/contain}
.mark::after{content:"NIEKIE";font-family:var(--display);font-size:20px;letter-spacing:.5px;
  line-height:1;color:var(--text)}
.tools{display:flex;align-items:center;gap:20px}
/* The theme switch is a mono uppercase label, like every other control here. */
.toggle{appearance:none;-webkit-appearance:none;outline:none;cursor:pointer;flex:none;
  margin:0;padding:0;border:0;background:none;width:auto;height:auto;
  font-family:var(--mono);font-size:12px;font-weight:700;text-transform:uppercase;
  letter-spacing:1.5px;color:var(--mute)}
.toggle::after{content:"[ LIGHT ]"}
.toggle:checked::after{content:"[ DARK ]"}
.toggle:hover{color:var(--link-hover)}
.topbar a{white-space:nowrap}
[data-auth-when]{display:inline-flex;align-items:center;gap:16px}
[data-auth-when][hidden]{display:none}
.topbar a.signin{color:var(--mute)}
.topbar a.acct,.cta{display:inline-block;background:var(--accent);color:var(--on-accent);
  border:0;border-radius:24px;padding:10px 24px;font-family:var(--mono);font-size:12px;
  font-weight:700;text-transform:uppercase;letter-spacing:1.5px;line-height:1.5;
  cursor:pointer;transition:background 180ms ease,box-shadow 180ms ease,color 180ms ease}
.topbar a.acct:hover,.cta:hover{background:rgba(255,255,255,.2);color:var(--text);
  box-shadow:0 0 0 1px #c2c2c2}
:root[data-theme="light"] .topbar a.acct:hover,
:root[data-theme="light"] .cta:hover{background:var(--accent-2);color:#fff;box-shadow:none}
/* One CTA per device, chosen by width rather than by user agent. A phone cannot
   install a browser extension and a desktop cannot install an Android app, so
   each was a dead end for half the visitors. The server cannot decide this: the
   page is cached at the edge, so whichever device warmed the cache would have
   picked the button for everyone behind it. */
.cta-mobile{display:none}
.cta-mobile .brand-ico{vertical-align:-3px;margin-right:6px}
@media (max-width:767px){
  .cta-desktop{display:none}
  .cta-mobile{display:inline-block}
}

.main{flex:1}
.has-below .main{min-height:auto}
/* ONE MEASURE FOR THE WHOLE PAGE.
   .wrap was full-bleed while the footer capped itself at 1240, so on a 1440
   screen the index ran to 1344 and the footer to 1240: a hundred-pixel step
   that nothing on the page lines up across. The 48px padding here and the 24px
   inside .strip .in both resolve to a 1192px content column, so the index, the
   two product sections, the proof strip and the footer now share an edge.
   Narrower screens are unaffected, since they never reach the cap. Articles and
   casino pages set their own reading measure well inside this and do not move. */
/* THE CONTENT COLUMN, DECLARED ONCE.
   --measure-w is the content width every full-width block resolves to. .wrap
   adds its 48px padding to it, .strip .in adds 24px, so the index, the product
   sections, the proof strip and the footer all share an edge.
   1288 (1192 of content) was tried and was too tight: the cards fell to 281px,
   which pushed the evidence line onto two lines, and because the three cards
   share subgrid rows the tallest one set the height for all of them. Narrower
   cards are also taller cards. 1344 puts them back to 331px and one line. */
:root{--measure-w:1344px}
.wrap{max-width:calc(var(--measure-w) + 96px);margin:0 auto;padding:24px 48px 64px;width:100%}
.wrap.wide{max-width:none}

/* ── Home masthead ───────────────────────────────────────────────────── */
/* THE FOLD BELONGS TO THE CASINOS, NOT THE INTRODUCTION.
   Measured on the live page at 1440x900: the first operator card began at
   673px, and the stacked hero owned 426px of that on its own — mark, headline,
   subhead, button and link each on their own line down the middle of the
   screen. Shrinking the type only bought back tens of pixels, because the cost
   was the stack itself, not the sizes.

   So the introduction stops being a screen and becomes a band. Mark, claim and
   subhead read left to right in one row; the two actions sit at the right end
   of the same row. Same elements, same words, one line instead of five, and
   the rule underneath separates it from the index rather than a screenful of
   empty space doing the job.

   The claim keeps display scale and stays the h1. It is the reason the product
   exists and the first thing a visitor should read — it just no longer needs a
   viewport to itself to say so.

   Everything here is scoped to .home, which only the home page carries, so
   casino and blog pages keep the display scale they were set at. */
.home{display:flex;align-items:center;justify-content:space-between;
  gap:18px 32px;flex-wrap:wrap;text-align:left;padding:14px 24px 16px;
  border-bottom:1px solid var(--frame);margin-bottom:12px}
/* flex-basis rather than a width: the claim is the thing allowed to take the
   room, and the pair wraps to two rows before the claim is squeezed. */
.mh-id{display:flex;align-items:center;gap:16px;min-width:0;flex:1 1 440px}
/* The mark is the artwork, never a themed glyph: it carries its own black
   shield and must look the same in both polarities. */
.mh-mark{width:58px;height:auto;flex:none}
.mh-words{min-width:0}
.home .verdict.mh-claim{font-size:clamp(22px,2.6vw,38px);line-height:1;
  letter-spacing:.5px;margin:0}
.mh-sub{font-family:var(--sans);font-weight:300;font-size:12px;letter-spacing:1.5px;
  line-height:1.3;text-transform:uppercase;color:var(--mute);margin:7px 0 0}
.mh-act{display:flex;flex-direction:column;align-items:flex-end;gap:8px;flex:none}
.mh-data{font-family:var(--mono);font-size:11px;letter-spacing:1.4px;
  text-transform:uppercase;color:var(--mute);text-decoration:none}
.mh-data:hover{color:var(--text-2)}
/* Below this the row is narrower than its own contents, so it stacks rather
   than wraps into a ragged two-and-a-half rows, and the button goes full
   width because a right-aligned one on a phone is a thumb-reach problem. */
@media (max-width:720px){
  .home{display:block;padding:12px 20px 16px}
  /* Beside the mark the claim had 250px and needed 277, so it broke to
     "...CASINO CAN / BUY" — the worst of both, a wrapped headline AND a
     narrow column. The mark goes above instead: the claim gets the full
     measure and can go up in size rather than down. */
  .mh-id{display:block}
  .mh-mark{width:44px;margin-bottom:10px}
  .home .verdict.mh-claim{font-size:26px}
  .mh-act{align-items:stretch;margin-top:14px}
  .mh-act .cta{text-align:center}
  .mh-data{text-align:center}
}
.sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0}
/* Unused while there is no search field on any page. Kept so the style does
   not have to be reconstructed if one returns. 2px is the typewriter-tag
   radius the reference gives inputs. */
.searchbar{display:flex;align-items:center;gap:12px;width:100%;background:var(--canvas);
  border:1px solid var(--text);border-radius:2px;padding:0 16px;height:48px;
  transition:border-color 150ms ease}
.searchbar:focus-within{border-color:var(--accent-ink)}
.searchbar svg{width:16px;height:16px;color:var(--mute);flex:none}
.searchbar input{flex:1;min-width:0;background:none;border:0;outline:none;color:var(--text);
  font-family:var(--sans);font-size:15px}
.searchbar input::placeholder{color:var(--mute)}

/* ── Tiles ───────────────────────────────────────────────────────────── */
/* TEXT LINES UP WITH TEXT, NOT WITH THE CARD'S BORDER.

   A card sat flush with the page gutter at 48px and then pushed its own
   contents in by a 1px border plus 32px of padding, so every paragraph inside
   a card started at 81px while the h1 and the intro above it started at 48px.
   Down a long page that is two left edges 33px apart, and it reads as a
   mistake rather than as an inset.

   The card is pulled out by exactly its own inset instead, so its border sits
   outside the gutter and the text inside lands on the same 48px as everything
   else. Only from 1024px up: below that the gutter is 24px and a 33px pull
   would hang the card off the side of the screen, where a 33px offset matters
   far less anyway. */
.card{background:var(--canvas);border:1px solid var(--frame);border-radius:20px;
  padding:28px 32px;margin:0 0 28px}
@media (min-width:1024px){
  .card{margin-inline:-33px}
  /* A table already bleeds by the gutter. Inside a pulled card it would now
     bleed by the gutter plus the pull and overhang the border. */
  .card .tp-scroll{margin:0 -32px;padding:0 32px}
}
.card.tight{padding:24px 28px}
.cardhead{display:flex;align-items:baseline;gap:16px;flex-wrap:wrap;margin-bottom:8px}
.cardhead .chips{display:flex;gap:8px;margin-left:auto}
.live-tick{color:var(--accent-ink)}
/* Every label in this system is mono, uppercase and tracked out. */
.label{font-family:var(--mono);font-size:11px;font-weight:700;text-transform:uppercase;
  letter-spacing:1.8px;line-height:1.3;color:var(--accent-ink);margin:0 0 16px;
  display:flex;align-items:center;gap:8px}
.eyebrow{display:block;font-family:var(--mono);font-size:11px;font-weight:700;
  text-transform:uppercase;letter-spacing:1.8px;color:var(--accent-ink);margin-bottom:6px}
/* The fashion-whisper: thin, capitalised, widely tracked, against the shout. */
/* The thin capitalised whisper is the reference's signature, and it is for a
   short eyebrow, not for prose. A verdict sentence at 1.9px tracking sprawls
   across the full-bleed width and reads as a headline it is not. Prose sits at
   a normal measure. The home page's own whisper is .mh-sub in the masthead. */
.hero{font-family:var(--sans);font-weight:400;font-size:17px;letter-spacing:0;
  line-height:1.6;color:var(--text-2);margin:12px 0 0;max-width:var(--measure)}
/* Sentence case. This carries real sentences, not labels: "30 of the 100
   points are not on offer for this operator..." set in tracked-out capitals
   was three lines of shouting and materially harder to read. Mono uppercase
   stays for things that are actually labels. */
.fineprint{font-family:var(--sans);font-size:13px;text-transform:none;
  letter-spacing:0;color:var(--mute);margin:12px 0 0;line-height:1.6;max-width:var(--measure)}
.foot{border-top:1px solid var(--frame);margin-top:40px;padding-top:24px;
  font-family:var(--sans);font-size:13px;text-transform:none;letter-spacing:0;
  color:var(--mute);line-height:1.6;max-width:var(--measure)}

/* ── Score ───────────────────────────────────────────────────────────── */
/* Drawn as type at display scale, with the true denominator beneath it. There is
   no meter and no bar: a length implies a precision the evidence rarely has, and
   this system encodes emphasis with colour blocks rather than with gauges. */
.score{font-family:var(--display);font-size:clamp(48px,8vw,72px);line-height:.95;
  letter-spacing:1.07px;color:var(--text)}
.score .of{display:block;font-family:var(--mono);font-size:11px;font-weight:400;
  letter-spacing:1.1px;text-transform:uppercase;color:var(--mute);margin-top:6px}
.score.unrated{font-family:var(--mono);font-size:12px;font-weight:700;letter-spacing:1.5px;
  text-transform:uppercase;color:var(--mute)}
/* EVERY SCORE IS THE SAME COLOUR.
   A score with no payout evidence behind it used to be drawn in muted ink, so
   a list showed some numbers white and some grey and the difference read as a
   rendering fault rather than as a claim. What is and is not backed by payout
   evidence is already said in words beside the number, in the unmeasured
   count, and in the payout row of the breakdown. It does not also need to be
   said in a colour nobody can decode. */
.band{display:none}          /* the ASCII meter has no place in this system */
.meter{display:none}
.scale{display:flex;justify-content:space-between;margin-top:10px;font-family:var(--mono);
  font-size:11px;text-transform:uppercase;letter-spacing:1.1px;color:var(--mute);
  max-width:var(--measure)}
.basis{grid-column:1/-1;margin:12px 0 0;font-family:var(--sans);font-size:14px;
  letter-spacing:0;color:var(--mute);line-height:1.6;max-width:var(--measure)}

/* ── Rows and severity ───────────────────────────────────────────────── */
/* Severity is a mono uppercase tag, not a coloured dot. Colour is reserved for
   editorial emphasis, so a failing check must still read without it. */
.row{display:flex;align-items:baseline;gap:14px;padding:14px 0;font-size:16px;
  color:var(--text-2);border-top:1px solid var(--frame)}
.row:first-child{border-top:0;padding-top:0}
.dot{flex:none;font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:1.8px;
  text-transform:uppercase;border-radius:20px;padding:4px 10px;line-height:1.4;
  width:auto;height:auto;background:var(--slate);color:var(--text-2)}
/* Fallback only. sevMark always attaches a modifier below, which overrides
   this, so it shows just when a caller passes a severity nobody defined.
   It said UNKNOWN while the screen reader said "Not measured" for the same
   state, so the two channels disagreed in the one case this can appear. */
.dot::before{content:"NOT MEASURED"}
.nk-good::before{content:"PASS"}
.nk-warn::before{content:"CAUTION"}
.nk-bad::before{content:"FAIL"}
.nk-unknown::before{content:"NOT MEASURED"}
.nk-good{background:var(--accent);color:var(--on-accent)}
.nk-warn{background:transparent;color:var(--accent-ink);box-shadow:0 0 0 1px var(--accent-edge) inset}
.nk-bad{background:var(--ruby);color:#fff}
.nk-unknown{background:var(--slate);color:var(--mute)}
.verdict-line{display:flex;align-items:center;gap:14px;margin:0 0 16px;font-size:20px;
  font-weight:700;line-height:1.2;color:var(--text)}
.detail{margin-left:auto;color:var(--mute);font-family:var(--sans);font-size:14px;
  text-transform:none;letter-spacing:0;text-align:right;padding-left:24px}
.log div{padding:14px 0;border-top:1px solid var(--frame);font-size:15px;color:var(--text-2)}
.log div:first-child{border-top:none;padding-top:0}
.log b{font-family:var(--mono);font-size:11px;font-weight:400;letter-spacing:1.1px;
  text-transform:uppercase;color:var(--mute)}

/* ── StoryStream ranking ─────────────────────────────────────────────── */
/* No top padding: the panel beside this column is aligned to the cards using
   --peek-clear alone, and a second number here would have to be added to it in
   two places and kept in step. */
.stream-sec{padding:0 0 24px}

/* ── Editor's picks ───────────────────────────────────────────────────────
   Curated, and framed so it cannot be mistaken for the index below it. The
   ranking is a spine of dashed rule with no border; this is a bordered card on
   inset ground. A reader who scrolls past has to notice they left one and
   entered the other, because the whole defence of this block is that it is
   visibly not the ranking. See api/lib/editorsPicks.js. */
.picks-sec{padding:24px;margin:28px 0 8px;border:1px solid var(--frame);
  border-radius:20px;background:var(--inset)}
/* The picks section stopped using .sechead when it got its own card layout. */
/* No spine and no numbers: a pick is not a rank and must not read as one. */
.stream.picks{padding-left:0}
.stream.picks::before{display:none}
.stream.picks .item::before{display:none}

/* ── Live observation strip ───────────────────────────────────────────────
   Under the header, above everything. It exists because the homepage read as
   static, and the one genuinely live thing here is the bet-feed collection:
   game_observations is written continuously and its newest row is seconds old.

   It scrolls, but it is not a casino win feed. Nothing here is one player's
   bet or one player's win: every item is a per-game observation count and its
   mean payout multiplier, which is a fact about the monitoring rather than
   about anybody's gambling. That distinction is what keeps a moving strip
   compatible with the sobriety the ratings depend on, so keep it. */
/* The paid-link disclosure. Deliberately not styled down into fine print:
   the FTC standard is clear and conspicuous, and a disclosure a reader skips
   is one that did not happen. Same size as body copy, directly under the
   button, no colour trick to make it recede. */
.paidnote{margin:12px 0 0;max-width:var(--measure);font-family:var(--sans);font-size:13px;
  line-height:1.6;color:var(--text-2)}
.paidnote a{color:var(--accent-ink);text-decoration:underline}

/* THE FOLDED REST OF THE INDEX.
   Styled as a control rather than a heading, because that is what it is. The
   marker is removed and replaced with a count, so the affordance is the row
   itself and there is no browser-default triangle fighting the type. */
.moreidx{margin-top:14px}
.moreidx>summary{list-style:none;cursor:pointer;display:flex;align-items:center;
  justify-content:space-between;gap:16px;padding:16px 24px;
  border:1px solid var(--frame);border-radius:14px;background:var(--inset);
  font-family:var(--mono);font-size:12px;font-weight:700;letter-spacing:1.6px;
  text-transform:uppercase;color:var(--text-2)}
.moreidx>summary::-webkit-details-marker{display:none}
.moreidx>summary:hover{border-color:var(--accent-edge);color:var(--text)}
.moreidx>summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.moreidx[open]>summary{margin-bottom:14px}
/* The count stays upright. An earlier version rotated it 45 degrees on open,
   which is a fine gesture for a plus sign and illegible on "+22". */
.moreidx-n{font-family:var(--display);font-size:16px;letter-spacing:1px;color:var(--accent-ink)}
.moreidx[open]>summary .moreidx-n{opacity:.45}

/* THE PROOF STRIP. Figures, not badges: each one resolves to something on the
   site a reader can open. Sized so the numbers read first and the labels
   second, which is the order they matter in. */
.proof-sec{padding:28px 0 8px}
/* SEPARATELY BORDERED CELLS, NOT A SEAMLESS 1px-GAP GRID.
   The strip was a four-column grid whose column count proofStrip wrote inline.
   An inline declaration beats a media query, so on a phone it stayed four
   columns of about 90px and overflow:hidden quietly cut the fourth cell off at
   the screen edge.
   Moving the count to a custom property did not fix it: an inline custom
   property wins too. And auto-fit alone cannot fix it either, because the
   seamless look comes from a 1px gap over a frame-coloured background, so any
   row that does not fill every track shows a block of bare frame beside the
   last cell — three cells in a two-up row being exactly that case.
   Each cell carries its own border instead. Now the count is nobody's business
   but the available width: it reflows at any number of cells, and an incomplete
   row is simply a shorter row. */
.proofstrip{display:grid;grid-template-columns:repeat(auto-fit,minmax(168px,1fr));gap:10px}
.pf-cell{background:var(--canvas);border:1px solid var(--frame);border-radius:14px;
  padding:20px 18px;display:flex;flex-direction:column;gap:6px}
.pf-n{font-family:var(--display);font-size:32px;line-height:1;letter-spacing:1px;color:var(--text);
  font-variant-numeric:tabular-nums}
.pf-l{font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:1.4px;
  text-transform:uppercase;color:var(--mute);line-height:1.5}
/* THE FIGURES TURNING OVER.
   Split into one cell per character by script, never by the server, so the
   served markup stays a plain number and a reader without JavaScript sees the
   same thing they always saw.
   Only characters that actually changed move. Rolling all six digits because
   the last one ticked is a slot machine, not a counter. */
.pf-d{position:relative;display:inline-block;overflow:hidden;vertical-align:top}
.pf-d>i{display:block;font-style:normal}
.pf-d>i.pf-out{position:absolute;left:0;top:0;
  animation:pf-out 300ms cubic-bezier(.32,.72,0,1) both}
.pf-d>i.pf-in{animation:pf-in 300ms cubic-bezier(.32,.72,0,1) both}
@keyframes pf-out{to{transform:translateY(-100%);opacity:0}}
@keyframes pf-in{from{transform:translateY(100%);opacity:0}}
@media (max-width:420px){ .pf-cell{padding:16px 14px} .pf-n{font-size:26px} }

/* WHAT MOVED. A ledger extract, so it is set as one: monospace figures,
   tabular, aligned on the delta rather than centred. */
.moves-sec{padding:28px 0 8px}
.moves{list-style:none;margin:0;padding:0;border:1px solid var(--frame);border-radius:14px;
  overflow:hidden}
.mv{display:flex;align-items:baseline;gap:16px;padding:14px 22px;background:var(--canvas)}
.mv+.mv{border-top:1px solid var(--frame)}
.mv-name{flex:1;min-width:0;font-family:var(--sans);font-size:15px;color:var(--text);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mv-delta{font-family:var(--display);font-size:19px;letter-spacing:.5px;font-variant-numeric:tabular-nums}
.mv.up .mv-delta{color:var(--accent-ink)}
.mv.down .mv-delta{color:var(--ruby)}
.mv-span{font-family:var(--mono);font-size:11px;color:var(--mute);white-space:nowrap}
@media (max-width:560px){ .mv-span{display:none} }

/* Article figures, built in HTML rather than shipped as a picture.
   The one this replaced was an AI generated diagram: garbled labels, invented
   words, and the old product name burned into the pixels where no rename could
   reach it. Marked up instead, it is searchable, translatable, readable to a
   screen reader, follows the theme, and weighs nothing. */
.fig-flow{display:grid;grid-template-columns:1fr auto 1fr auto 1fr;gap:14px;align-items:stretch;
  margin:28px 0;padding:0}
.fig-col{border:1px solid var(--frame);border-radius:12px;padding:16px 18px;background:var(--inset)}
.fig-problem{border-left:2px solid var(--ruby)}
.fig-split{border-left:2px solid var(--mute)}
.fig-solution{border-left:2px solid var(--accent-edge)}
.fig-tag{display:block;font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:1.4px;
  text-transform:uppercase;color:var(--mute)}
.fig-col h4{margin:8px 0 10px;font-family:var(--sans);font-size:15px;font-weight:700;color:var(--text)}
.fig-col ul{margin:0;padding-left:18px}
.fig-col li,.fig-col p{font-family:var(--sans);font-size:13.5px;line-height:1.55;color:var(--text-2);
  margin:0 0 7px}
.fig-arrow{align-self:center;font-size:20px;color:var(--mute)}
@media (max-width:760px){
  .fig-flow{grid-template-columns:1fr}
  /* The arrows point across a row that is now a column, so they would point
     the wrong way. Dropped rather than rotated: the order already reads. */
  .fig-arrow{display:none}
}

/* Article images. Constrained to the column and never wider, because the
   source is whatever resolution the author's screenshot happened to be: the
   first one in the bonus post is 734KB at full size. A rounded frame matches
   the cards around it, and the hairline keeps a screenshot with a white
   background from bleeding into the page in light mode. */
.prose img{display:block;max-width:100%;height:auto;margin:26px 0;
  border:1px solid var(--frame);border-radius:12px}

/* The evidence under the top pick. Quieter than the verdict above it, because
   it qualifies that verdict rather than competing with it. */
.chainline{display:block;margin-top:10px;font-family:var(--mono);font-size:12px;
  line-height:1.6;color:var(--text-2);max-width:var(--measure)}
/* The top pick sits on a filled accent panel, where a muted grey drops to
   roughly 2:1 against the background. White at 88% clears AA and still reads
   as secondary to the verdict above it. */
.top .chainline{color:rgba(255,255,255,.88)}

.livebar{border-bottom:1px solid var(--frame);background:var(--inset);
  padding:10px 48px;display:flex;align-items:center;gap:18px;flex-wrap:wrap;
  font-family:var(--mono);font-size:11px;letter-spacing:.4px;color:var(--mute)}
/* A frozen number that still looks live is worse than an obvious pause: the
   reader cannot tell a quiet minute from a dead collector. That used to be a
   pulsing dot beside the word OBSERVING, which is the stock live-data badge
   and said nothing the rail did not already say twice. The figures carry it
   instead. They go grey when collection stops, and the right-hand end of the
   strip gives the reader the thing a light cannot: how long ago. */
.livebar.is-stale .lb-n{color:var(--mute)}
.livebar .lb-n{font-family:var(--display);font-size:15px;letter-spacing:1px;
  color:var(--text);font-variant-numeric:tabular-nums}
.livebar .lb-sep{opacity:.35}
.livebar .lb-rail{display:flex;align-items:center;gap:10px;flex:none}
.livebar .lb-right{display:flex;align-items:center;gap:14px;flex:none}

/* The scrolling half. The viewport clips, the track moves, and the track holds
   two identical copies of the item list so translating it exactly -50% lands
   on the copy in the same visual position the original started in. That is
   what makes the loop seamless; any other distance shows a jump. */
.livebar .lb-marquee{flex:1 1 240px;min-width:0;overflow:hidden;
  /* Feathered instead of hard-cut, so items enter and leave rather than
     appearing at a border. */
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 32px,#000 calc(100% - 32px),transparent);
  mask-image:linear-gradient(90deg,transparent,#000 32px,#000 calc(100% - 32px),transparent)}
.livebar .lb-track{display:flex;width:max-content;gap:0;
  /* Duration is set per render from the item count, so adding items makes the
     strip longer rather than faster. A fixed duration would speed the text up
     every time a new feed came online. */
  animation:lb-scroll var(--lb-dur,72s) linear infinite}
.livebar .lb-marquee:hover .lb-track{animation-play-state:paused}
.livebar .lb-item{display:inline-flex;align-items:center;gap:8px;white-space:nowrap;
  padding:0 20px;border-right:1px solid var(--frame)}
/* The anchor has to carry the flex and the gap too. The gap on .lb-item only
   reaches .lb-item's own children, and when the row is a link its only child
   is the anchor, which would otherwise run every word together. */
.livebar .lb-item a{display:inline-flex;align-items:center;gap:8px;
  color:inherit;text-decoration:none}
.livebar .lb-item a:hover{color:var(--text)}
.livebar .lb-who{color:var(--text-2);font-weight:700;letter-spacing:.8px;text-transform:uppercase}
.livebar .lb-what{color:var(--mute)}
.livebar .lb-num{color:var(--text);font-variant-numeric:tabular-nums}
.livebar .lb-empty{padding:0 20px;color:var(--mute)}
@keyframes lb-scroll{from{transform:translateX(0)}to{transform:translateX(-50%)}}
/* Motion is the whole point of this element, so honouring the preference means
   giving the reader the same content another way rather than freezing a strip
   they can only see a third of. It becomes a scrollable row. */
@media (prefers-reduced-motion:reduce){
  .livebar .lb-track{animation:none}
  .livebar .lb-marquee{overflow-x:auto;-webkit-mask-image:none;mask-image:none}
}
@media (max-width:720px){
  .livebar{padding:9px 0 9px 20px;gap:12px;font-size:10px}
  .livebar .lb-games,.livebar .lb-right{display:none}
  .livebar .lb-item{padding:0 14px}
}
.sechead{display:flex;align-items:baseline;gap:16px;flex-wrap:wrap;margin-bottom:24px}
.sechead h2{margin:0;font-family:var(--sans);font-size:34px;font-weight:700;line-height:1}
.kicker{font-family:var(--mono);font-size:11px;font-weight:700;text-transform:uppercase;
  letter-spacing:1.8px;color:var(--accent-ink)}
/* The spine. A dashed rule the entries hang from, so the ranking reads as a
   sequence rather than as a table with the borders taken off. */
.stream{list-style:none;margin:0;padding:0 0 0 40px;position:relative}
.stream::before{content:"";position:absolute;left:16px;top:10px;bottom:10px;width:1px;
  background:repeating-linear-gradient(to bottom,var(--rule) 0 6px,transparent 6px 12px)}
.stream .item{position:relative;list-style:none}
.stream .item::before{content:"";position:absolute;left:-29px;top:28px;width:9px;height:9px;
  border-radius:50%;background:var(--canvas);border:1px solid var(--accent-edge)}
@media (max-width:640px){
  .stream{padding-left:0}
  .stream::before,.stream .item::before{display:none}
}
.top{position:relative;background:var(--accent);color:var(--on-accent);border-radius:24px;
  padding:32px 36px;margin:0 0 16px;display:grid;grid-template-columns:1fr auto;
  gap:6px 28px;align-items:center}
.top .eyebrow{color:var(--on-accent);opacity:.82}
.top .basis{color:var(--on-accent);opacity:.82}
/* The tile sits beside the name rather than above it: the top pick is one
   operator, and stacking the mark over a 32px name reads as two headings. */
.topname-row{display:flex;align-items:center;gap:14px;min-width:0}
/* On the accent block the list tile's --inset fill and --frame hairline both
   disappear into the purple, so it gets a white plate and a translucent edge. */
.top .clogo{background:#fff;border-color:rgba(255,255,255,.4)}
.topname{display:block;font-family:var(--sans);font-weight:700;font-size:32px;line-height:1;
  color:var(--on-accent)}
.topname:hover{color:var(--canvas)}
.topwhy{font-family:var(--sans);font-weight:300;font-size:15px;letter-spacing:1.2px;
  color:var(--on-accent);opacity:.86}
.topscore{grid-row:2/4;grid-column:2;text-align:right;font-family:var(--display);
  font-size:clamp(48px,7vw,72px);line-height:.95;letter-spacing:1.07px;color:var(--on-accent)}
.topscore .of{display:block;font-family:var(--mono);font-size:11px;letter-spacing:1.1px;
  text-transform:uppercase;opacity:.75;margin-top:4px}
.topscore.none{font-family:var(--mono);font-size:12px;letter-spacing:1.5px}
/* Inverted, not the standard .cta fill: the top pick sits on --accent
   already, so the normal purple-on-purple button would be invisible. */
.topscore .cta{display:inline-block;margin-top:14px;background:var(--on-accent);
  color:var(--accent-2)}
.topscore .cta:hover{background:rgba(255,255,255,.85)}

.pick{position:relative;display:grid;grid-template-columns:auto auto 1fr auto;gap:0 16px;
  align-items:center;border:1px solid var(--frame);border-radius:20px;padding:20px 24px;
  margin:0 0 28px;transition:border-color 150ms ease}
.pick:hover{border-color:var(--accent-edge)}
.pick::before{content:none}
.rank{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:1.8px;
  color:var(--accent-ink);width:3ch}

/* One square per operator, whatever rung of the ladder filled it. Fixed box and
   a shared corner are what stop a column of 64px favicons from reading as
   breakage; see casinoLogo() for why the fallback is the common case. */
.clogo{position:relative;flex:none;width:var(--clogo,46px);height:var(--clogo,46px);
  border-radius:8px;display:grid;place-items:center;overflow:hidden;
  background:var(--inset);border:1px solid var(--frame);
  transition:border-color 150ms ease}
.clogo-mono{font-family:var(--display);font-size:calc(var(--clogo,46px) * .46);
  line-height:1;color:var(--accent-ink)}
/* The stored logos are transparent PNGs drawn for a light page, so they get the
   same white plate the app gives them. Without it roughly half of them vanish
   into the dark surface. */
.clogo-img{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;
  padding:5px;background:#fff}
.pick:hover .clogo{border-color:var(--accent-edge)}
/* The detail page leads with the operator, so the tile sits on the headline
   rather than in a list. Baseline-ish alignment via centre, because the
   display face has enough leading that aligning the boxes reads crooked. */
.namerow{display:flex;align-items:center;gap:16px;margin-bottom:4px}
.namerow .verdict{margin:0}
/* Left, against the page's centred default. The row now opens with the
   operator's tile, and a centred name drifts into the middle of the column
   away from the logo that belongs to it, which reads as two unrelated things
   rather than one operator. */
.who{display:flex;flex-direction:column;gap:4px;min-width:0;text-align:left}

/* THE FEED STANDS BESIDE THE INDEX, NOT ABOVE THE PAGE.
   As a header strip it was decoration scrolling past a reader with no reason
   to tie it to the scores below. Beside the index it is an argument, so it is
   placed where the thing it argues about is and points at it. Sticky, because
   the claim should hold while the reader works down the list. */
/* ONE NUMBER FOR THE SPACE THE DOG OCCUPIES.
   The card row is pushed down to clear the mark perched on the leading card.
   The feed panel sits in the next column and was not pushed down at all, so it
   started 104px above the cards it is making a claim about. Both now read the
   same custom property, which is the only way they stay aligned the next time
   the artwork moves. The value is the air above the dog plus nothing else: the
   mark stands 74px proud of the card, so 76px leaves roughly 14px between the
   masthead rule and its ears. Below about 62 they touch.
   the artwork moves. */
.indexwrap{--peek-clear:70px;display:grid;grid-template-columns:1fr;gap:28px;align-items:start}
@media (min-width:1180px){
  .indexwrap{grid-template-columns:minmax(0,1fr) 290px}
  /* Starts level with the first card, then rides down with the scroll, which is
     the whole point of putting the live feed next to the index it feeds. */
  .feedside{position:sticky;top:96px;margin-top:var(--peek-clear)}
}
.feedside{border:1px solid var(--frame);border-radius:20px;padding:18px 18px 16px;
  background:var(--canvas);text-align:left;min-width:0}
.feedside.is-stale{opacity:.72}
.fp-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:14px}
.fp-title{font-family:var(--sans);font-size:14px;font-weight:700;line-height:1.3;color:var(--text);max-width:16ch}
/* Points back at the index it is making a claim about. Mirrored on narrow
   screens, where the panel sits under the list rather than beside it. */
.fp-arrow{width:52px;height:28px;flex:none;color:var(--accent-ink);transform:scaleX(-1)}
@media (min-width:1180px){ .fp-arrow{transform:none} }
.fp-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:7px;
  /* The rows are the moving part, so the box they move in has to stop moving.
     Without this the panel grows and shrinks a row's worth every poll and the
     whole index beside it shifts with it. */
  min-height:300px}
/* Mark, then the game, then what it paid at its best today. The game leads
   because that is what a round happened in; the operator is the attribution
   underneath it.

   THE GRID GOES ON WHATEVER DRAWS THE BOX, AND ONLY THAT.
   Both the li and its anchor carried it, so the li became a three-column grid
   whose only child was the anchor, which landed in the first auto column and
   shrank to its own content. The rows came out at four different widths down
   the panel. A row is a link when the game has a page and a plain li when it
   does not, so the box is described once and :has picks which element is it. */
.fp-row,.fp-row > a{display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:1px 10px;
  align-items:center;border:1px solid var(--frame);border-radius:12px;padding:8px 11px;
  text-decoration:none;transition:border-color 150ms ease,background-color 150ms ease}
.fp-row:has(> a){display:block;border:0;padding:0;border-radius:0}
.fp-row > a:hover{border-color:var(--accent-edge);background:var(--surf-2)}
.fp-row .clogo{grid-row:1/3;grid-column:1;align-self:center}
.fp-game{grid-row:1;grid-column:2;font-family:var(--sans);font-size:12px;font-weight:700;
  color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fp-who{grid-row:2;grid-column:2;font-family:var(--sans);font-size:10.5px;color:var(--mute);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fp-n{grid-row:1/3;grid-column:3;align-self:center;text-align:right;font-family:var(--mono);
  font-size:13px;font-weight:700;color:var(--accent-ink);font-variant-numeric:tabular-nums;
  white-space:nowrap}
.fp-n small{display:block;font-size:8.5px;font-weight:400;letter-spacing:.9px;
  text-transform:uppercase;color:var(--mute)}
/* A row arriving is the one thing on this page that is allowed to animate on
   its own, because it is reporting that something happened. It fires only for
   a casino+game key that was not in the previous paint — a count ticking up on
   a game already listed redraws in place, or every poll would look like a
   flood of new play when most of it is the same games being re-counted. */
@keyframes fp-arrive{
  from{opacity:0;transform:translateY(-10px)}
  to{opacity:1;transform:none}
}
@keyframes fp-settle{
  from{border-color:var(--accent-edge);background:var(--surf-2)}
  to{border-color:var(--frame);background:transparent}
}
.fp-row.is-new{animation:fp-arrive 380ms cubic-bezier(.2,.7,.3,1) both}
.fp-row.is-new a{animation:fp-settle 1400ms ease-out both}
@media (prefers-reduced-motion:reduce){
  .fp-row.is-new,.fp-row.is-new a{animation:none}
}
.fp-empty{font-family:var(--sans);font-size:12px;color:var(--mute);padding:8px 0}

/* -- Editor's picks ---------------------------------------------------- */
/* Deliberately not the index's row. These are one person's judgement, and when
   they wore casinoRow they read as a second ranking that happened to disagree
   with the first: same layout, same big right-aligned score, 763px of page for
   four operators, and a name a thousand pixels from the number belonging to it.
   A card puts the name, the reason and the score within one eye movement, and
   the score is set small because answering with a number is the index's job. */
/* Do not reset the padding. .picks-sec already draws a bordered, filled box
   with 24px of it; this rule shipped as 6px 0 26px and zeroed the
   horizontal side, so every line inside sat against the border. Only the
   vertical rhythm needed changing. */
.picks-sec{padding:22px 24px 24px}
.picks-h{margin:0;font-family:var(--sans);font-size:26px;font-weight:700;line-height:1}
.picks-note{font-family:var(--sans);font-size:12.5px;line-height:1.55;color:var(--mute);
  margin:8px 0 0;max-width:72ch}
/* Capped, not stretched. Four cards across 1344px came out 325 wide and 153
   tall, a 2.1 ratio holding three short lines, which reads as a squat band
   rather than a set of cards. A max on the track keeps the proportion while
   the row stays centred on the measure. */
.picks-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(224px,284px));gap:14px;
  justify-content:start;margin-top:14px}
/* start, not center: the heading, the note and the first card share a left
   edge, which is the same discipline the index and the footer follow. */
.pk{display:flex;flex-direction:column;border:1px solid var(--frame);border-radius:16px;
  padding:16px 16px 14px;transition:border-color 150ms ease}
.pk:hover{border-color:var(--accent-edge)}
/* Name and score on one row, button on its own.
   They were a flex pair at the foot of the card: the button took every spare
   pixel and the score sat beside it as a two-line stack with nothing to align
   to, so a 65 that matters read as a label stuck to the left of a button. The
   score moves up into the empty half of the name row, where there was already
   room for it, and the button gets the full width it was taking anyway. */
.pk-top{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;
  gap:10px;text-decoration:none;min-width:0}
.pk-name{font-family:var(--sans);font-weight:700;font-size:15.5px;line-height:1.15;
  color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.pk:hover .pk-name{color:var(--accent-ink)}
/* The one reason, not the index's full evidence sentence. */
.pk-why{font-family:var(--sans);font-size:12.5px;line-height:1.5;color:var(--text-2);
  margin:12px 0 0;flex:1}
/* Small on purpose. The index answers with a number; this section answers with a
   name, and the number is here only so the two cannot contradict each other. */
.pk-score{font-family:var(--mono);font-size:15px;font-weight:700;color:var(--text);
  font-variant-numeric:tabular-nums;white-space:nowrap;text-align:right;line-height:1}
.pk-score small{display:block;font-size:8.5px;font-weight:400;letter-spacing:1px;
  text-transform:uppercase;color:var(--mute);margin-top:3px}
/* Visit is the action; Review is the second, quieter one. Outlined rather than
   filled so the two do not compete, and only drawn where a review exists. */
.pk-act{display:flex;gap:8px;align-items:stretch;margin-top:14px}
.pk .rowvisit{margin:0;flex:1;text-align:center}
.pk-review{display:inline-flex;align-items:center;justify-content:center;flex:none;
  padding:0 14px;border:1px solid var(--frame);border-radius:999px;text-decoration:none;
  font-family:var(--sans);font-size:13px;font-weight:700;color:var(--text-2);
  white-space:nowrap;transition:border-color 150ms ease,color 150ms ease}
.pk-review:hover{border-color:var(--accent-edge);color:var(--accent-ink)}
@media (max-width:520px){ .picks-grid{grid-template-columns:1fr} }

/* ── Get it: the extension and the app ───────────────────────────────── */
/* Two bands at the foot of the home page, copy against a real screenshot of
   the thing being offered rather than store art. The second mirrors the first
   so the pair reads as a pair and not as the same band twice.

   The image column is capped rather than fluid: these are tall portrait shots
   of a popup and a phone, and letting them take half a wide viewport makes a
   product promo taller than the index it sits under. */
.getit{display:grid;grid-template-columns:minmax(0,1fr);gap:26px;align-items:center;
  border:1px solid var(--frame);border-radius:24px;padding:30px 26px;margin:34px 0 0}
@media (min-width:860px){
  .getit{grid-template-columns:minmax(0,1.25fr) minmax(0,.75fr);gap:40px;padding:38px 38px}
  .getit-flip .gt-copy{order:2}
  .getit-flip .gt-shot{order:1}
}
.gt-copy{min-width:0}
.getit h2{font-family:var(--display);font-size:clamp(26px,3vw,38px);line-height:1.02;
  letter-spacing:.6px;margin:10px 0 0}
.gt-lede{font-family:var(--sans);font-size:15px;line-height:1.6;color:var(--text-2);
  margin:14px 0 0;max-width:52ch}
.gt-pts{list-style:none;margin:18px 0 0;padding:0;display:flex;flex-direction:column;gap:9px}
/* The rule, not a bullet glyph: the index above sets evidence off the same way. */
.gt-pts li{font-family:var(--sans);font-size:13.5px;line-height:1.5;color:var(--text-2);
  padding-left:16px;border-left:2px solid var(--accent-edge)}
.gt-cta{display:flex;flex-wrap:wrap;gap:10px;margin-top:22px}
.getit .cta{display:inline-flex;align-items:center;gap:9px}
/* The second store is the same offer on another browser, not a lesser one, so
   it is outlined rather than demoted to a text link. */
.cta-ghost{background:transparent;color:var(--text);border:1px solid var(--frame)}
.cta-ghost:hover{border-color:var(--accent-edge);color:var(--accent-ink)}
.gt-fine{font-family:var(--sans);font-size:11.5px;line-height:1.55;color:var(--mute);
  margin:16px 0 0;max-width:56ch}
.gt-shot{min-width:0;display:flex;justify-content:center}
.gt-shot img{width:100%;max-width:252px;height:auto;border-radius:16px;
  border:1px solid var(--frame);display:block}
/* The popup is a floating panel, not a device, so it gets the frame without
   the device's rounded slab. */
.gt-shot-ext img{max-width:268px;border-radius:12px}
@media (max-width:620px){
  .getit{padding:24px 20px}
  .gt-cta .cta{width:100%;justify-content:center}
}
.fp-foot{margin-top:14px;padding-top:12px;border-top:1px solid var(--frame);
  font-family:var(--sans);font-size:11px;line-height:1.5;color:var(--mute);
  display:flex;flex-direction:column;gap:3px}
.fp-foot b{color:var(--text-2);font-family:var(--mono);font-variant-numeric:tabular-nums}
.fp-big{font-size:14px}
.fp-meta{font-family:var(--mono);font-size:10px;letter-spacing:.6px}

/* THE DOG HANGS OVER THE EDGE.
   Pulled up by most of its own height so the paws break the card's top border
   and the head clears it, which is the whole point of the artwork. The lead
   card gains top padding to match, or the mark lands on the operator logo.
   Decorative and non-interactive: the card underneath owns every link. */
.t3-peek{position:absolute;left:50%;top:0;transform:translate(-50%,-88%);
  width:128px;height:auto;pointer-events:none;z-index:2}
:root[data-theme="light"] .t3-peek{filter:invert(1)}
@media (prefers-color-scheme:light){
  :root:not([data-theme="dark"]) .t3-peek{filter:invert(1)}
}

/* Three abreast, stacking on a phone. minmax rather than a fixed count so the
   middle breakpoint gets two and a half cards' worth of room instead of three
   cramped ones. */
/* .home is a centred flex column, so a grid child shrink-wraps to its content
   and the three cards stack in a narrow stripe. The stretch is what makes it
   a row. */
/* The top margin is the dog's room, not the layout's. The mark stands 75px
   clear of the card, and with the masthead compressed the section heading
   now sits directly above it — at 72px the head landed across "Ranked
   for...". 98px puts the ears below the heading's descenders. */
.t3{display:grid;grid-template-columns:repeat(auto-fit,minmax(248px,1fr));
  grid-template-rows:repeat(7,auto);gap:16px;margin:var(--peek-clear,98px) 0 30px;
  width:100%;align-self:stretch}
/* ROWS LINE UP ACROSS THE THREE CARDS, OR THEY ARE NOT COMPARABLE.
   As a flex column each card packed its own content independently, so the
   evidence line sat at a different height in each one: "1,286 withdrawals
   counted on-chain" against "No on-chain payout evidence yet" against
   "5,410 withdrawals...", none of them on the same baseline. A reader
   comparing three operators is scanning across, and nothing lined up to scan.

   Subgrid hands every card the same six rows — identity, score, evidence,
   verdict, button, proof link — each sized to the tallest card's content.
   The rows are assigned explicitly rather than by order, so a card with no
   proof link leaves row six empty instead of pulling its button up a row. */
.t3-c{display:grid;grid-template-rows:subgrid;grid-row:span 7;
  justify-items:start;text-align:left;
  border:1px solid var(--frame);border-radius:20px;padding:30px 22px 18px;
  transition:border-color 150ms ease}
.t3-top{grid-row:1}
.t3-score{grid-row:2}
.t3-ev{grid-row:3}
.t3-why{grid-row:4}
.t3-c .cov{grid-row:5}
/* The same four marks the ranked rows carry. The cards showed only the points
   figure, so the three operators a visitor actually compares were the three
   whose evidence coverage was hardest to see. */
.t3-c .cov{margin-top:8px;gap:5px}
/* 231px of marks and label against 237px of card: one pixel of tracking and
   half a point of type is the whole difference between one line and two. */
.t3-c .cov-t{font-size:9.5px;letter-spacing:.8px}
.t3-c .rowvisit{grid-row:6}
.t3-c .rowproof{grid-row:7;margin-top:10px}
/* Subgrid needs the parent's rows to exist. Where it is unsupported the cards
   fall back to the column they were, which packs rather than aligns but is
   never broken. */
@supports not (grid-template-rows: subgrid){
  .t3-c{display:flex;flex-direction:column;align-items:flex-start}
}
.t3-c:hover{border-color:var(--accent-edge)}
/* First place is marked by its border, not by being bigger. Three cards that
   compare must stay comparable, and a scaled-up winner tells the reader the
   answer before they have read the evidence. */
/* The dog's clearance is now padding on all three cards, not extra padding on
   this one. When only the lead carried it, its whole column sat 8px lower than
   its neighbours — the misalignment was the marker, not the dog. */
.t3-c.lead{border-color:var(--accent-edge);position:relative;overflow:visible}
/* The artwork is open line-art, so the card's own top border was reading
   straight through the dog's chest: one continuous rule crossing the body
   between the paws, which is the opposite of resting on the edge. This paints
   the page canvas over the border for the span the paws cover, so the edge
   disappears behind the dog and resumes either side of it. Gradient rather
   than a hard strip because the paws are not a clean rectangle: the fade
   hands the line back under them instead of cutting it off in open space. */
.t3-c.lead::before{content:"";position:absolute;left:50%;top:-1px;
  transform:translateX(-50%);width:118px;height:3px;z-index:1;pointer-events:none;
  background:linear-gradient(90deg,transparent,var(--canvas) 16%,var(--canvas) 84%,transparent)}
.t3-top{display:flex;align-items:center;gap:12px;margin-bottom:16px}
.t3-id{display:flex;flex-direction:column;gap:3px;min-width:0}
.t3-name{font-family:var(--sans);font-weight:700;font-size:19px;line-height:1.1;color:var(--text)}
.t3-c:hover .t3-name{color:var(--accent-ink)}
.t3-rank{font-family:var(--mono);font-size:10px;letter-spacing:1.4px;text-transform:uppercase;color:var(--mute)}
.t3-score{font-family:var(--display);font-size:44px;line-height:.95;letter-spacing:1.07px;color:var(--text)}
.t3-of{display:block;font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:1.3px;
  text-transform:uppercase;color:var(--mute);margin-top:6px}
.t3-ev{font-family:var(--sans);font-size:13px;line-height:1.5;color:var(--text-2);margin:10px 0 0}
.t3-ev b{font-family:var(--mono);font-size:15px;font-weight:700;color:var(--text);
  font-variant-numeric:tabular-nums}
.t3-ev.t3-none{color:var(--mute)}
/* A hyphenated compound broken across lines reads as two words. The cards got
   narrower when the page took a measure, and 'on-chain' was the first casualty. */
.nb{white-space:nowrap}
.t3-why{font-family:var(--sans);font-size:12px;line-height:1.5;color:var(--mute);margin:8px 0 0}
/* justify-self, not align-self: in the grid the inline axis is the one that
   makes the button span the card. */
.t3-c .rowvisit{justify-self:stretch;align-self:end;text-align:center;margin-top:12px}

/* THE COUNT CARRIES THE ROW.
   Set above .why on purpose: the verdict is our sentence, this is the
   operator's own ledger. Mono for the figure because it is a measurement,
   which is the one thing that face is for here. */
.ev{font-family:var(--sans);font-size:14px;line-height:1.45;color:var(--text-2)}
.ev-n{font-family:var(--mono);font-size:16px;font-weight:700;letter-spacing:-.02em;
  color:var(--text);font-variant-numeric:tabular-nums}
.ev-src{color:var(--mute)}
/* Where the count exists it is the louder line, so the verdict steps back to
   a caption rather than competing with it. */
.ev + .why{font-size:13px;color:var(--mute)}

/* What we measured, and what we did not. Four marks, one per pillar. */
.cov{display:flex;align-items:center;gap:7px;margin-top:2px}
.cov-m{width:14px;height:3px;border-radius:2px;background:var(--frame);flex:none}
.cov-m.on{background:var(--accent-ink)}
.cov-t{font-family:var(--mono);font-size:10px;letter-spacing:1px;text-transform:uppercase;
  color:var(--mute)}
.name{font-family:var(--sans);font-weight:700;font-size:22px;line-height:1;color:var(--text)}
/* Promoted from --mute. This line used to be a caption; it now carries the
   on-chain evidence behind the score, which is the reason a reader is on
   the row at all, so it is read text rather than a whisper. */
.why{font-family:var(--sans);font-size:14px;letter-spacing:0;color:var(--text-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* One line with an ellipsis is fine while the line fits. On a phone it does
   not, and the half that gets clipped is the evidence, because the license
   text comes first. Two wrapped lines there rather than a truncation that
   removes the most useful thing on the row. */
@media (max-width:720px){
  .why{white-space:normal;display:-webkit-box;-webkit-line-clamp:3;
    -webkit-box-orient:vertical;line-height:1.45}
}
.pts{font-family:var(--display);font-size:38px;line-height:.95;letter-spacing:1.07px;
  color:var(--text);text-align:right}
.pts.none{font-family:var(--mono);font-size:12px;letter-spacing:1.5px;color:var(--mute)}
.ofsmall{display:block;font-family:var(--mono);font-size:10px;font-weight:400;
  letter-spacing:1.1px;text-transform:uppercase;color:var(--mute)}
/* THE ACTION HAS TO OUTWEIGH THE NUMBER.
   This was a 10px ghost outline on a row whose score is 38px display: the
   loudest thing on the row was a figure you cannot act on, and the thing you
   can act on was the quietest. Filled, and sized to be hit on a phone. */
.rowvisit{display:inline-block;margin-top:10px;font-family:var(--sans);font-size:14px;
  font-weight:700;letter-spacing:.01em;color:var(--on-accent);background:var(--accent);
  border:1px solid var(--accent);border-radius:999px;padding:9px 20px;white-space:nowrap;
  transition:transform 150ms cubic-bezier(.22,1,.36,1),box-shadow 150ms ease,background 150ms ease}
.rowvisit:hover{background:var(--accent-2);transform:translateY(-1px);
  box-shadow:0 6px 18px -6px rgba(83,58,253,.65)}
.rowvisit:active{transform:none;box-shadow:none}
/* The reader who is not ready to leave. Sending them to the evidence keeps
   them on the page that earns the next click, rather than losing them to a
   back button, and it is the one route a competitor cannot copy. */
/* The disclosure belongs to the button above it, so it sits tight against it
   and the secondary link is pushed clear. Evenly spaced, the three read as a
   list of three unrelated things and the label looks like it describes the
   proof link, which it does not. */
.rowproof{display:block;margin-top:14px;font-family:var(--sans);font-size:12px;
  color:var(--mute);text-decoration:underline;text-underline-offset:3px;
  text-decoration-color:var(--frame);transition:color 150ms ease,text-decoration-color 150ms ease}
.rowproof:hover{color:var(--text-2);text-decoration-color:var(--accent-edge)}
/* The whole row reads as reachable, not just the two links inside it. */
.pick:hover .name{color:var(--accent-ink)}
@media (max-width:720px){.rowvisit{margin-top:6px}}

.chip{display:inline-block;font-family:var(--mono);font-size:11px;font-weight:700;
  text-transform:uppercase;letter-spacing:1.5px;padding:6px 14px;border-radius:20px;
  border:1px solid var(--frame);color:var(--mute)}
.chip:hover{border-color:var(--accent-edge);color:var(--accent-ink)}
.chip.on{background:var(--accent);border-color:var(--accent);color:var(--on-accent)}

/* ── Diagram ─────────────────────────────────────────────────────────── */
.diagram{margin:0;padding:0}
.diagram svg{width:100%;height:auto;display:block;overflow:visible}
.d-box{fill:none;stroke:var(--frame);stroke-width:1}
.d-label{fill:var(--text-2);font-family:var(--mono);font-size:11px;letter-spacing:1.1px;
  text-transform:uppercase}
.d-wire{fill:none;stroke:var(--frame);stroke-width:1}
.d-wire-out{stroke:var(--accent-edge);stroke-width:1}
.d-core{fill:none;stroke:var(--accent-edge);stroke-width:1}
.d-core-t{fill:var(--text);font-family:var(--sans);font-size:18px;font-weight:700}
.d-out{fill:none;stroke:var(--accent-edge);stroke-width:1}
.d-score{fill:var(--text);font-family:var(--display);font-size:30px}
.d-sub{fill:var(--mute);font-family:var(--mono);font-size:10px;letter-spacing:1.1px;
  text-transform:uppercase}
.diagram figcaption{margin-top:16px;font-family:var(--sans);font-size:13px;
  color:var(--mute);line-height:1.6;max-width:var(--measure)}
.d-split{stroke:var(--frame);stroke-width:1;stroke-dasharray:3 5}
.d-box-feed{stroke-dasharray:4 4}
.d-wire-feed{stroke-dasharray:4 4}
.d-out-feed{fill:none;stroke-dasharray:4 4}
.d-score-feed{font-size:20px}

/* ── Below the fold ──────────────────────────────────────────────────── */
.below{border-top:1px solid var(--frame);background:var(--canvas)}
.below .in{max-width:none;margin:0;padding:64px 48px}
.below h2{font-family:var(--sans);font-size:34px;font-weight:700;line-height:1;margin:0 0 10px}
.below .lede{font-size:13px;line-height:1.6;color:var(--mute);margin:0 0 32px;max-width:620px}
.below-writing,.below-tracked,.below-games,.below-wallets{margin-top:64px;padding-top:40px;
  border-top:1px solid var(--frame)}
.below-tracked,.below-games,.below-wallets{width:auto;margin-left:0;transform:none}
.below-tracked h2,.below-tracked .lede,.tp-note{max-width:620px}
.postlist{margin-top:8px}
.below-more{margin:32px 0 0}

/* ── Tables ──────────────────────────────────────────────────────────── */
/* The bar is hidden, the scrolling is not. overflow-x stays auto so a
   trackpad swipe, a touch drag and the keyboard all still work; only the
   painted scrollbar goes. A wheel over the table scrolls it sideways,
   wired up in TABLE_SCROLL_SCRIPT.

   Hiding the bar removes the only hint that a table has more to the right,
   so .tp-scroll gets a fade on whichever edge has content beyond it. That
   is the affordance doing the scrollbar's old job. */
.tp-scroll{overflow-x:auto;margin:0 -48px;padding:0 48px;
  -webkit-overflow-scrolling:touch;scrollbar-width:none;-ms-overflow-style:none;
  position:relative}
.tp-scroll::-webkit-scrollbar{display:none}
.tp-scroll[data-more~="right"]{
  -webkit-mask-image:linear-gradient(to right,#000 calc(100% - 56px),transparent);
  mask-image:linear-gradient(to right,#000 calc(100% - 56px),transparent)}
.tp-scroll[data-more~="left"]{
  -webkit-mask-image:linear-gradient(to left,#000 calc(100% - 56px),transparent);
  mask-image:linear-gradient(to left,#000 calc(100% - 56px),transparent)}
.tp-scroll[data-more~="left"][data-more~="right"]{
  -webkit-mask-image:linear-gradient(to right,transparent,#000 56px,
    #000 calc(100% - 56px),transparent);
  mask-image:linear-gradient(to right,transparent,#000 56px,
    #000 calc(100% - 56px),transparent)}
@media (prefers-reduced-motion:reduce){.tp-scroll{scroll-behavior:auto}}
.tp{width:100%;min-width:660px;border-collapse:collapse;font-size:15px}
.tp th{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:1.8px;
  text-transform:uppercase;color:var(--accent-ink);text-align:left;padding:0 28px 14px 0;
  border-bottom:1px solid var(--frame);white-space:nowrap}
/* Columns need a gutter between them. Without one, cells sit flush and read as
   one word: the pillars table said "WEIGHTWHAT IT MEASURES" and "30Moderated
   reports...". It looked fine on most tables only because their numeric cells
   carry .tp-num, which was supplying the only horizontal padding on the page.
   The last column drops it so the table still ends flush with the text above. */
.tp td{padding:16px 28px 16px 0;border-bottom:1px solid var(--frame);vertical-align:middle}
.tp th:last-child,.tp td:last-child{padding-right:0}
.tp tbody tr:last-child td{border-bottom:0}
.tp th.tp-num,.tp td.tp-num{text-align:right;padding-left:24px;white-space:nowrap;
  font-variant-numeric:tabular-nums}
.tp-name{font-weight:700}
.tp-strong{font-weight:700;font-variant-numeric:tabular-nums}

/* THE MAGNITUDE RULE.
   A number in a table tells you its value and nothing about its size relative
   to the column. A rule under the figure, scaled to that column's largest
   value, gives you the shape of the column in one glance without a heatmap.
   No heatmap on purpose: colouring cells implies a precision this scoring
   model refuses, and it invites comparison across rows that is often invalid.
   One hue, one dimension, only ever within a column. */
.tp-mag{display:block;height:2px;margin-top:6px;background:var(--accent);
  opacity:.7;border-radius:1px;min-width:2px}
.tp-num .tp-mag{margin-left:auto}   /* grows from the right, like the digits */

/* THE VOID.
   Not measured is not zero, and it must never look like a small value. The
   same hatch the store artwork uses for unearned points: visibly a hole
   rather than a quantity. A dash would read as "nothing happened"; this
   reads as "we did not look, or could not". */
.tp-void{display:inline-block;min-width:44px;height:12px;vertical-align:middle;
  border:1px solid var(--frame);border-radius:2px;
  background:repeating-linear-gradient(135deg,transparent 0 4px,#1e1e1e 4px 8px)}
.tp-void-label{font-family:var(--mono);font-size:10px;letter-spacing:1.2px;
  text-transform:uppercase;color:var(--mute);margin-left:8px}

/* A header that survives a long table, and a row you can follow with your eye
   across eight columns. */
.tp thead th{position:sticky;top:0;z-index:2;background:var(--canvas)}
.tp tbody tr:hover td{background:var(--inset)}
.tp tbody tr:focus-within td{background:var(--inset);outline:1px solid var(--accent-edge)}

/* Rank, only where the order actually means something. */
.tp-rank{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:1.5px;
  color:var(--mute);width:3ch;text-align:right;padding-right:18px}
.tp tbody tr:first-child .tp-rank{color:var(--accent-ink)}
.tp-pm,.tp-na,.tp-mutecell{font-family:var(--mono);font-size:11px;letter-spacing:1.1px;
  text-transform:uppercase;color:var(--mute)}
.tp-flag{display:block;font-family:var(--mono);font-size:10px;letter-spacing:1.5px;
  text-transform:uppercase;color:var(--accent-ink);margin-top:4px}
.tp-gap.down,.tp-gap.up{font-family:var(--mono);font-size:11px;color:var(--mute)}
.tp-empty{font-family:var(--mono);font-size:11px;text-transform:uppercase;
  letter-spacing:1.1px;color:var(--mute);padding:24px 0}
.tp.wl td{white-space:normal}
.tp.wl td code{font-family:var(--mono);font-size:12px;word-break:break-all}
.wl-m{width:120px;vertical-align:top}
.wl-d{font-family:var(--sans);font-size:13px;color:var(--mute);line-height:1.6;
  margin-top:6px;max-width:var(--measure)}
.wl-group{margin-top:48px}
.wl-group h3{font-family:var(--sans);font-weight:700;font-size:24px;margin:0 0 12px}
.wl-count,.wl-corr,.wl-hops,.tg-meta,.tg-prov,.tg-house{font-family:var(--mono);font-size:11px;
  letter-spacing:1.1px;text-transform:uppercase;color:var(--mute)}
.wl-hops,.tg-prov{display:block;margin-top:4px}
.tg-house{margin-left:8px}
.inf{margin-left:8px;font-family:var(--mono);font-size:10px;letter-spacing:1.5px;
  text-transform:uppercase;color:var(--accent-ink);border:1px solid var(--accent-edge);
  padding:2px 8px;border-radius:20px}
.tp.data td{white-space:nowrap;padding:16px 28px 16px 0}
.tp.data td.tp-title{max-width:320px;overflow:hidden;text-overflow:ellipsis}
.tp.data td.tp-studio{max-width:180px;overflow:hidden;text-overflow:ellipsis;
  font-family:var(--mono);font-size:11px;letter-spacing:1.1px;text-transform:uppercase;
  color:var(--mute)}
.tp.data tbody tr{border-left:0}
.tp.data td:first-child{padding-left:0}
.st-figure{font-family:var(--sans);font-size:17px;font-weight:700;font-variant-numeric:tabular-nums}
.st,.st-curated,.st-collecting,.st-note{font-family:var(--mono);font-size:10px;
  letter-spacing:1.5px;text-transform:uppercase;color:var(--mute)}
.st-note{display:block;margin-top:4px}
.st-bar{display:block;width:96px;height:2px;background:var(--frame);margin:8px 0 0 auto}
.st-bar span{display:block;height:100%;background:var(--accent-ink)}
.ib{position:relative;display:block;width:150px;height:14px;margin:8px 0 0 auto}
.ib-track{position:absolute;top:6px;left:0;right:0;height:1px;background:var(--frame)}
.ib-even{position:absolute;top:2px;width:1px;height:10px;background:var(--mute)}
.ib-range{position:absolute;top:5px;height:3px;background:var(--accent-ink);opacity:.5}
.ib-mean{position:absolute;top:3px;width:1px;height:8px;background:var(--accent-ink)}
.ib-declared{position:absolute;top:0;width:1px;height:14px;background:var(--text)}
@media (max-width:640px){ .ib{width:110px} }
.tp-empty-note{max-width:620px}
.tp-note{font-family:var(--sans);font-size:13px;color:var(--mute);margin:20px 0 0;
  line-height:1.6;max-width:var(--measure)}
/* .label is the small mono kicker and is often carried BY an h2, so the
   section-headline rule must not fight it: without :not(.label) the kicker
   renders at 34px while still uppercase and tracked out, which is neither
   thing the system defines. */
.card h2:not(.label){font-family:var(--sans);font-size:34px;font-weight:700;line-height:1;
  margin-top:48px}
.card > h2:first-child{margin-top:0}
.card h2.label{margin-top:0}
.tally{display:flex;gap:36px;flex-wrap:wrap;margin:24px 0 20px;padding-bottom:20px;
  border-bottom:1px solid var(--frame)}
.tally-i{font-family:var(--mono);font-size:10px;letter-spacing:1.5px;text-transform:uppercase;
  color:var(--mute)}
.tally-i b{display:block;font-family:var(--display);font-size:44px;line-height:.95;
  letter-spacing:1.07px;color:var(--text);font-variant-numeric:tabular-nums;margin-bottom:4px}
.t-measured,.t-curated,.t-collecting{color:var(--text)}
.mth-item{margin:28px 0;padding:20px 24px;border:1px solid var(--frame);border-radius:20px;
  max-width:720px}
.mth-item h3{font-family:var(--sans);font-size:20px;font-weight:700;margin:0 0 6px}
.mth-item p{margin:0;font-size:15px;line-height:1.6;color:var(--text-2)}

/* ── Consent ─────────────────────────────────────────────────────────── */
#nk-consent{position:fixed;left:0;right:0;bottom:0;z-index:60;background:var(--slate);
  border-top:1px solid var(--frame)}
#nk-consent[hidden]{display:none}
.nk-consent-in{max-width:none;margin:0;padding:20px 48px;display:flex;gap:24px;
  align-items:center;flex-wrap:wrap}
.nk-consent-in p{margin:0;font-size:13px;line-height:1.6;color:var(--text-2);flex:1 1 320px}
.nk-consent-btns{display:flex;gap:10px}
#nk-consent button{font-family:var(--mono);font-size:12px;font-weight:700;
  text-transform:uppercase;letter-spacing:1.5px;padding:10px 24px;cursor:pointer;
  border:0;border-radius:24px;background:var(--accent);color:var(--on-accent)}
#nk-consent button.nk-ghost{background:transparent;border:1px solid var(--frame);
  color:var(--text)}

/* ── Auth ────────────────────────────────────────────────────────────── */
.authbg{position:fixed;inset:0;background:rgba(0,0,0,.72);display:none;align-items:center;
  justify-content:center;padding:20px;z-index:40}
.authbg[open]{display:flex}
.authcard{position:relative;width:100%;max-width:420px;background:var(--canvas);
  border:1px solid var(--text);border-radius:24px;padding:32px 28px}
.authtabs{display:flex;gap:0;margin-bottom:24px;border-bottom:1px solid var(--frame)}
.authtabs button{flex:1;padding:10px 0;background:none;border:0;cursor:pointer;
  font-family:var(--mono);font-size:12px;font-weight:700;text-transform:uppercase;
  letter-spacing:1.5px;color:var(--mute);margin-bottom:-1px}
.authtabs button[aria-selected="true"]{color:var(--text);
  box-shadow:0 -1px 0 0 inset var(--accent-edge)}
.authcard p{margin:0 0 16px;font-size:15px;line-height:1.6;color:var(--text-2)}
.authcard input{width:100%;padding:12px 14px;background:var(--canvas);color:var(--text);
  border:1px solid var(--frame);border-radius:2px;font-family:var(--sans);font-size:15px;
  outline:none}
.authcard input:focus{border-color:var(--accent-edge)}
.authgo{width:100%;margin-top:16px;padding:12px 24px;border:0;border-radius:24px;
  cursor:pointer;background:var(--accent);color:var(--on-accent);font-family:var(--mono);
  font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:1.5px}
.authgo:disabled{background:var(--slate);color:var(--mute)}
.authalt{width:100%;margin-top:10px;padding:8px;background:none;border:0;cursor:pointer;
  color:var(--mute);font-family:var(--mono);font-size:11px;text-transform:uppercase;
  letter-spacing:1.1px}
.autherr{margin:16px 0 0;font-family:var(--mono);font-size:11px;text-transform:uppercase;
  letter-spacing:1.1px;color:var(--ruby);line-height:1.6}
.authnote{margin:16px 0 0;font-family:var(--mono);font-size:10px;text-transform:uppercase;
  letter-spacing:1.1px;color:var(--mute);line-height:1.8}
.authclose{position:absolute;top:14px;right:16px;background:none;border:0;cursor:pointer;
  color:var(--mute);font-size:20px;line-height:1;padding:4px}

/* ── Blog ────────────────────────────────────────────────────────────── */
.post{padding:20px 0;border-top:1px solid var(--frame)}
.post:first-child{border-top:none;padding-top:0}
.postlink{font-family:var(--sans);font-weight:700;font-size:24px;line-height:1;color:var(--text)}
.postdate{font-family:var(--mono);font-size:10px;text-transform:uppercase;letter-spacing:1.5px;
  color:var(--mute);margin-top:8px}
/* Category chips on the blog index, and the one on a post's own byline. */
/* Read-next and operators-in-this-article, appended below a post. Both exist
   so the blog stops being seventeen dead ends; neither touches the prose. */
.relbox{margin-top:18px}
.relgrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;margin-top:14px}
.relcard{display:flex;flex-direction:column;gap:7px;border:1px solid var(--frame);
  border-radius:14px;padding:15px 16px;text-decoration:none;
  transition:border-color 150ms ease}
.relcard:hover{border-color:var(--accent-edge)}
.relcat{font-family:var(--mono);font-size:9.5px;font-weight:700;letter-spacing:1.2px;
  text-transform:uppercase;color:var(--accent-ink)}
.relname{font-family:var(--sans);font-size:14.5px;font-weight:700;line-height:1.3;color:var(--text)}
.relcard:hover .relname{color:var(--accent-ink)}
.relmeta{font-family:var(--mono);font-size:10px;letter-spacing:1px;text-transform:uppercase;color:var(--mute)}
.opsrow{display:flex;flex-wrap:wrap;gap:9px;margin-top:14px}
.opschip{display:inline-flex;align-items:center;gap:8px;border:1px solid var(--frame);
  border-radius:999px;padding:8px 14px;text-decoration:none;font-family:var(--sans);
  font-size:13.5px;font-weight:700;color:var(--text);transition:border-color 150ms ease}
.opschip:hover{border-color:var(--accent-edge);color:var(--accent-ink)}
.opschip b{font-family:var(--mono);font-size:12px;color:var(--accent-ink);
  font-variant-numeric:tabular-nums}

/* The in-article review CTA. Sits in the article's own .card, not a card of
   its own, so a border inside a border was the wrong call — a quieter inset
   plate instead, the same move .ledger-pre already makes for the same reason. */
.revcta{display:flex;flex-direction:column;gap:14px;background:var(--inset);
  border:1px solid var(--frame);border-radius:16px;padding:20px 24px;margin:20px 0 0}
.revcta-top{display:flex;align-items:center;gap:14px}
.revcta-info{display:flex;flex-direction:column;gap:2px;min-width:0}
.revcta-name{font-family:var(--sans);font-weight:700;font-size:16px;color:var(--text);
  text-decoration:none}
.revcta-name:hover{color:var(--accent-ink)}
.revcta-score{font-family:var(--mono);font-size:13px;color:var(--text-2);
  font-variant-numeric:tabular-nums}
.revcta-score small{font-weight:400}
.revcta .cta{align-self:flex-start}

.blogjump{display:flex;flex-wrap:wrap;gap:8px;margin:16px 0 0}
.blogjump a{font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:1.2px;
  text-transform:uppercase;color:var(--text-2);border:1px solid var(--frame);
  border-radius:999px;padding:6px 12px;text-decoration:none;
  transition:color 120ms ease,border-color 120ms ease}
.blogjump a:hover{color:var(--accent-ink);border-color:var(--accent-edge)}
.postcat{font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:1.2px;
  text-transform:uppercase;color:var(--accent-ink);text-decoration:none}
.postcat:hover{text-decoration:underline;text-underline-offset:3px}

.postdesc{font-size:13px;line-height:1.6;color:var(--mute);margin:8px 0 0;max-width:640px}
.prose{margin-top:28px;color:var(--text-2);font-size:16px;line-height:1.6;max-width:var(--measure)}
/* A deliberate exception to the one-measure rule above, not an oversight: a
   long-form article reads better wide, and a blog post shares no right edge
   with a score card or a verdict block on the same visit, so there is nothing
   for it to stay aligned with. Scoped to .prose.wide (blog posts only) so
   every other page keeps the shared 41rem edge. */
.prose.wide{max-width:60rem}
.prose h2{font-family:var(--sans);font-size:34px;font-weight:700;line-height:1;
  color:var(--text);margin:44px 0 14px}
.prose h3{font-family:var(--sans);font-size:20px;font-weight:700;color:var(--text);
  margin:32px 0 8px}
.prose p{margin:0 0 18px}
.prose ul,.prose ol{margin:0 0 18px;padding-left:22px}
.prose li{margin:0 0 8px}
.prose a{color:var(--accent-ink);box-shadow:0 -1px 0 0 inset var(--accent-edge)}
.prose a:hover{color:var(--link-hover)}
.prose blockquote{margin:0 0 18px;padding:16px 20px;border:1px solid var(--frame);
  border-radius:20px;color:var(--text-2)}
.prose code{background:var(--inset);padding:2px 6px;border-radius:4px;font-family:var(--mono);
  font-size:13px}
.prose pre{background:var(--inset);padding:20px 24px;border-radius:20px;overflow-x:auto;
  margin:0 0 18px;font-family:var(--mono);font-size:13px;line-height:1.6}
.prose pre code{background:none;padding:0}
.prose hr{border:0;border-top:1px solid var(--frame);margin:40px 0}
.prose table{width:100%;border-collapse:collapse;margin:0 0 18px;font-size:14px}
.prose th{font-family:var(--mono);font-size:10px;font-weight:700;letter-spacing:1.5px;
  text-transform:uppercase;color:var(--accent-ink);text-align:left;padding:0 12px 12px 0;
  border-bottom:1px solid var(--frame)}
.prose td{padding:12px 12px 12px 0;border-bottom:1px solid var(--frame);vertical-align:top}

/* ── About, forms ────────────────────────────────────────────────────── */
.person{padding:20px 0;border-top:1px solid var(--frame)}
.person:first-of-type{border-top:none;padding-top:0}
.person-name{font-family:var(--sans);font-weight:700;font-size:22px;color:var(--text)}
.person-role{font-family:var(--mono);font-size:10px;text-transform:uppercase;
  letter-spacing:1.5px;color:var(--accent-ink);margin-top:6px}
.person .hero{margin-top:10px;font-size:15px;letter-spacing:0;font-weight:400}
.cform{display:flex;flex-direction:column;gap:6px;margin-top:28px;max-width:520px}
.cform label{font-family:var(--mono);font-size:10px;text-transform:uppercase;
  letter-spacing:1.5px;color:var(--mute);margin-top:16px}
.cform input,.cform select,.cform textarea{width:100%;padding:12px 14px;border-radius:2px;
  background:var(--canvas);color:var(--text);border:1px solid var(--frame);
  font-family:var(--sans);font-size:15px;outline:none}
.cform input:focus,.cform select:focus,.cform textarea:focus{border-color:var(--accent-edge)}
.cform textarea{resize:vertical;line-height:1.6}
.cform .cta{margin-top:24px;align-self:flex-start;border:0;cursor:pointer}
.cform .trap{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.formerr{margin-top:18px;padding:14px 18px;border-radius:20px;font-family:var(--mono);
  font-size:11px;text-transform:uppercase;letter-spacing:1.1px;line-height:1.7;
  border:1px solid var(--ruby);color:var(--ruby)}

/* ── Ledger ──────────────────────────────────────────────────────────── */
.mono{font-family:var(--mono)}
.ledger{margin-top:20px}
.ledger summary{cursor:pointer;font-family:var(--mono);font-size:11px;font-weight:700;
  text-transform:uppercase;letter-spacing:1.5px;color:var(--accent-ink);padding:6px 0}
.ledger summary:hover{color:var(--link-hover)}
.ledger-pre{font-family:var(--mono);font-size:12px;line-height:1.7;color:var(--text-2);
  background:var(--inset);border:1px solid var(--frame);border-radius:20px;padding:20px 24px;
  margin:0;overflow-x:auto;white-space:pre}

/* ── Bottom strip ────────────────────────────────────────────────────── */
.strip{background:var(--canvas);border-top:1px solid var(--frame);color:var(--mute);
  font-family:var(--mono);font-size:10px;text-transform:uppercase;letter-spacing:1.1px}
/* Held to the content measure, not the window. Eighteen links spread across a
   1440px viewport read as a sitemap nobody laid out; the same links centred on
   the measure the index uses read as a footer. */
.strip .in{max-width:calc(var(--measure-w) + 48px);margin:0 auto;display:flex;flex-wrap:wrap;align-items:center}
.strip .in.links{gap:7px 16px;justify-content:center;padding:16px 24px 12px}
.strip .in.meta{gap:10px 14px;padding:0 24px 16px;justify-content:center}
.strip .in.meta .note,.strip .checked{font-family:var(--sans);font-size:11.5px;
  text-transform:none;letter-spacing:0;color:var(--mute)}
.strip a{color:var(--mute)}
.strip a:hover{color:var(--link-hover)}
.strip .spacer{flex:1;min-width:0}
.strip form{display:flex;align-items:center;gap:8px;margin:0}
.strip select{font-family:var(--mono);font-size:10px;text-transform:uppercase;
  letter-spacing:1.1px;padding:4px 8px;border:1px solid var(--frame);border-radius:6px;
  background:var(--canvas);color:var(--text-2)}
/* The links wrap rather than scrolling behind a hidden scrollbar. That put
   "Independent, not owned by any operator  ABOUT  FORUM  D" against the right
   edge and stopped, with nothing to say the rest was reachable by dragging. */
@media (max-width:900px){
  .strip .in.links{justify-content:flex-start;gap:9px 14px;padding:16px 24px 10px}
  .strip .in.meta{justify-content:flex-start;padding:0 24px 16px}
  .strip .spacer{display:none}
  .strip .in.meta .note{flex:1 0 100%}
  .strip .checked{display:none}
}

}

@media (max-width:1023px){
  .topbar{padding:12px 24px;gap:16px}
  .wrap{padding:28px 24px 48px}
  .below .in{padding:48px 24px}
  .tp-scroll{margin:0 -24px;padding:0 24px}
  .nk-consent-in,.strip .in{padding-left:24px;padding-right:24px}
}
/* THE RAIL NEEDS 786px AND KEPT ITS SLOT DOWN TO 640.
   Between those two the bar overflowed its own width: at 768px the Sign up
   pill sat 18px past the right edge. Two steps rather than one, so a tablet is
   not sent straight to the phone menu:
     under 900  -> "How we score" folds into the panels, saving about 130px
     under 700  -> the rail folds into Menu, which is the block below. */
@media (max-width:899px){
  .topbar .reads a.hws{display:none}
}
@media (max-width:699px){
  /* The 1023 block leaves the bar at 24px padding and a 16px gap. With the
     mark, the theme label, Sign in and the Sign up pill all still present,
     that put the pill 12px past the viewport on a 390px screen. */
  /* Vertical padding was 0, so the Sign up pill was the tallest thing in the
     bar and therefore set its height: a 33px pill in a 34px bar, touching the
     rule at the bottom and the screen edge at the top. The bar now reserves its
     own vertical space and the pill sits inside it. */
  .topbar{padding:7px 14px;gap:10px}
  .topbar>a:not(.mark){display:none}
  /* The mark stays, the wordmark beside it does not: "NIEKIE" at 20px was the
     whole overflow, 70px of it at 320px, and it pushed the Sign up pill off
     the right edge of every inner page. flex:none because the shield was
     being squeezed to 6px once the bar ran out of room.
     The theme switch goes too, and is not replaced by a glyph: a phone has its
     own light/dark setting and the stylesheet already follows it. */
  .topbar a.mark-sm{display:inline-flex}
  .topbar a.mark{flex:none}
  .topbar a.mark::after{display:none}
  .topbar a.mark img{width:26px;height:26px}
  .topbar .tools .toggle,.topbar .tools label[for="nk-theme"]{display:none}
  .topbar .tools a:not(.acct):not(.signin){display:none}
  /* A phone fits two words beside the mark. The groups fold into one Menu, and
     App stays a plain link: a phone is where the Android download is wanted.
     "How we score" goes with them — it earns a slot on a wide bar, but at 390px
     it pushed Sign up off the right edge. It is the first row of the Menu
     panel's own "The score" column, so nothing is unreachable. */
  .topbar .reads{gap:6px;margin-left:0}
  .topbar .reads a.hws{display:none}
  .dd:not(.dd-all){display:none}
  .dd-all{display:block;position:static}
  .topbar .reads .app-direct{display:inline}
  .dd-all>.dd-btn{padding:8px 10px}
  .dd-all>.dd-menu{position:fixed;left:12px;right:12px;top:62px;transform:none;min-width:0;
    max-height:calc(100vh - 80px);overflow:auto;display:none;grid-template-columns:1fr 1fr;
    gap:18px 20px;padding:22px}
  .dd-all.open>.dd-menu{display:grid}
  .dd-all .dd-menu::before{display:none}
  .topbar .reads .dd-all .dd-menu a{font-size:15px;white-space:normal}
  /* The brackets cost the width App needed on a 360px phone. */
  .toggle::after{content:"LIGHT"}
  .toggle:checked::after{content:"DARK"}
  .tools{gap:14px}
  .topbar a.acct{padding:6px 15px;font-size:11px}
  .home{padding:32px 16px}
  .card{padding:20px;border-radius:20px}
  .top{padding:24px;border-radius:20px}
  .top{grid-template-columns:1fr}
  .topscore,.pts{grid-row:auto;grid-column:1;text-align:left}
  /* Rank and tile share the top line; the name and the score each take the
     full width under them. Collapsing to one column instead stranded a 46px
     square on a line of its own above the name. */
  .pick{grid-template-columns:auto 1fr;gap:8px 12px}
  .pick .who,.pick .pts{grid-column:1/-1}
  /* One property, and the box, the monogram and the image all follow. */
  .pick .clogo{--clogo:38px}
  .namerow{--clogo:48px;gap:12px}
  .namerow .verdict{font-size:30px}
  .detail{margin-left:0;padding-left:0;width:100%;text-align:left}
  .row{flex-wrap:wrap;gap:8px}
  .card h2,.below h2,.prose h2{font-size:24px}
  .tally-i b{font-size:32px}
}
@media (max-width:359px){
  /* The smallest phones fit one control, and Menu reaches everything, App
     included. */
  .topbar .reads .app-direct{display:none}
  .topbar a.signin{display:none}
  .dd-all>.dd-menu{grid-template-columns:1fr}
}
/* ── Motion ──────────────────────────────────────────────────────────── */
/* The rules this section keeps to, so it stays a layer and does not become a
   pile of effects:
     - Transform and opacity only. Nothing here can trigger layout.
     - Anything the reader operates settles inside 200ms. Anything they meet
       once per visit may take longer, and only the page heading does.
     - Every loop stops when it is off screen.
     - Reduced motion means gentler, not absent.
   Nothing below changes a resting state. Every rule describes how an element
   gets to a position it already had. */

/* FEEDBACK. These controls all changed state instantly, which reads as a
   redraw rather than a response. */
.chip{transition:border-color 150ms ease,color 150ms ease,background 150ms ease}
.moreidx>summary{transition:border-color 150ms ease,color 150ms ease}
.tp tbody tr td{transition:background 120ms ease}
.topname{transition:color 150ms ease}
.mv{transition:background 150ms ease}
.authtabs button{transition:color 150ms ease,box-shadow 150ms ease}
.authcard input,.searchbar{transition:border-color 150ms ease}
.authclose{transition:color 150ms ease}
.authclose:hover{color:var(--text)}

/* A press should acknowledge itself before the navigation resolves. One pixel
   is enough to read as a press and small enough to leave the row alone. */
.cta,.topbar a.acct,.authgo,#nk-consent button{
  transition:background 180ms ease,box-shadow 180ms ease,color 180ms ease,transform 90ms ease}
.cta:active,.topbar a.acct:active,.chip:active,
.authgo:active:not(:disabled),#nk-consent button:active{transform:translateY(1px)}

/* ENTRANCE. Blocks arrive as the reader reaches them, once, and never move
   again afterwards.
   The gate is a class on <html> set by the boot script, and the script only
   sets it when the browser can observe intersections and the reader has not
   asked for less motion. So the hiding and the revealing are decided by the
   same condition: with JavaScript off, or on a browser that cannot do this,
   no rule below ever matches and the page is simply the page. */
@keyframes nk-rise{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
.nk-anim .top,.nk-anim .card,.nk-anim .pick,.nk-anim .pf-cell,
.nk-anim .mv,.nk-anim .post,.nk-anim .mth-item{opacity:0}
.nk-anim .nk-in{animation:nk-rise 420ms cubic-bezier(.23,1,.32,1) both;
  animation-delay:var(--nk-d,0ms)}

/* ONE FOCAL ENTRANCE PER PAGE, and it is the heading. It wipes up out of its
   own box rather than fading, so it reads as the heading arriving rather than
   as the page still loading. 620ms is longer than anything else here and it
   is the only place that budget is spent. */
@keyframes nk-focal{
  from{opacity:0;transform:translateY(16px);clip-path:inset(0 0 100% 0)}
  to{opacity:1;transform:none;clip-path:inset(-20% -20% -20% -20%)}
}
.nk-anim .nk-focal{animation:nk-focal 620ms cubic-bezier(.16,1,.3,1) both}

/* THE FOLD. Where the browser can interpolate to a keyword height the rest of
   the index opens instead of appearing; where it cannot, this whole block is
   skipped and the behaviour is exactly what it was. */
@supports (interpolate-size:allow-keywords){
  :root{interpolate-size:allow-keywords}
  .nk-anim .moreidx::details-content{
    block-size:0;overflow:hidden;content-visibility:hidden;
    transition:block-size 340ms cubic-bezier(.32,.72,0,1),
               content-visibility 340ms allow-discrete}
  .nk-anim .moreidx[open]::details-content{block-size:auto;content-visibility:visible}
}

/* OVERLAYS. Scale from .97, never from zero: nothing in the world appears out
   of nothing. The scrim carries most of the work. */
@keyframes nk-scrim{from{opacity:0}to{opacity:1}}
@keyframes nk-pop{from{opacity:0;transform:scale(.97)}to{opacity:1;transform:none}}
.nk-anim .authbg[open]{animation:nk-scrim 180ms ease both}
.nk-anim .authbg[open] .authcard{animation:nk-pop 200ms cubic-bezier(.23,1,.32,1) both}
@keyframes nk-slideup{from{transform:translateY(100%)}to{transform:none}}
.nk-anim #nk-consent:not([hidden]){animation:nk-slideup 320ms cubic-bezier(.32,.72,0,1) both}

/* Reduced motion. The blanket rule that used to live here switched every
   transition and animation off, including the colour changes that tell a
   reader a control answered them, and including the deliberate substitution
   the live bar makes just above. Travel and looping go; response stays. */
@media (prefers-reduced-motion:reduce){
  /* Only animation is neutralised. The authored transitions are all colour,
     border and background: they answer the reader without moving anything, so
     they stay. Setting transition-duration here instead would have been a
     bug, because transition-property still defaults to all and every
     element on the page would have started transitioning every property. */
  *{animation-duration:.001ms !important;animation-iteration-count:1 !important;
    animation-delay:0ms !important;scroll-behavior:auto !important}
  .cta:active,.topbar a.acct:active,.chip:active,
  .authgo:active:not(:disabled),#nk-consent button:active{transform:none}
}

/* ═══════════════════════════════════════════════════════════════════════════
   REDESIGN: sidebar shell, Geist/Geist Mono, one orange accent. Ported 1:1
   from the approved Design-canvas reference (Styleguide + Sidebar + Main).
   Scoped entirely under .nk2 so it cannot collide with the legacy theme
   above; pages opt in one at a time via Page.astro's redesign prop. Dark is
   the default polarity, same convention the legacy tokens above use.
   ═══════════════════════════════════════════════════════════════════════════ */
.nk2{
  --bg:#09090B;--side:#0B0B0E;--surf:#0F0F12;--surf2:#17171B;--hover:#141418;
  --bd:#1F1F25;--bd2:#2E2E36;--tx:#F4F4F5;--tx2:#A1A1AA;--tx3:#8B8B94;
  --ac:#FF8A1F;--actx:#FFA24D;--onac:#0A0A0B;--acsoft:rgba(255,138,31,.12);--acbd:rgba(255,138,31,.35);
  --postx:#86EFAC;--posdot:#4ADE80;--sevtx:#FCA5A5;--sev:#F87171;--warn:#FBBF24;--warntx:#FCD34D;
  --line:#1E1E24;--line2:#2C2C34;--track:#1C1C21;--bar:#3A3A44;--qrbd:#FFFFFF;--phone:#050506;--sh:none;
  min-height:100vh;display:flex;flex-wrap:wrap;align-items:stretch;
  background:var(--bg);color:var(--tx);font-family:Geist,system-ui,sans-serif;
  font-size:14px;line-height:1.5;font-variant-numeric:tabular-nums;-webkit-font-smoothing:antialiased;
}
html[data-theme="light"] .nk2{
  --bg:#F6F6F7;--side:#FFFFFF;--surf:#FFFFFF;--surf2:#F1F1F3;--hover:#F3F3F5;
  --bd:#E6E6EA;--bd2:#D2D2D9;--tx:#0B0B0E;--tx2:#4B4B55;--tx3:#666670;
  --ac:#FF8A1F;--actx:#C2410C;--onac:#0A0A0B;--acsoft:#FFF2E6;--acbd:#FBD3AE;
  --postx:#15803D;--posdot:#16A34A;--sevtx:#B91C1C;--sev:#DC2626;--warn:#D97706;--warntx:#A15C07;
  --line:#E4E4E9;--line2:#D0D0D8;--track:#ECECF0;--bar:#C4C4CE;--qrbd:#E6E6EA;--phone:#EDEDF0;
  --sh:0 1px 2px rgba(16,16,24,.04),0 4px 16px rgba(16,16,24,.04);
}
@media (prefers-color-scheme:light){
  html:not([data-theme="dark"]) .nk2{
    --bg:#F6F6F7;--side:#FFFFFF;--surf:#FFFFFF;--surf2:#F1F1F3;--hover:#F3F3F5;
    --bd:#E6E6EA;--bd2:#D2D2D9;--tx:#0B0B0E;--tx2:#4B4B55;--tx3:#666670;
    --ac:#FF8A1F;--actx:#C2410C;--onac:#0A0A0B;--acsoft:#FFF2E6;--acbd:#FBD3AE;
    --postx:#15803D;--posdot:#16A34A;--sevtx:#B91C1C;--sev:#DC2626;--warn:#D97706;--warntx:#A15C07;
    --line:#E4E4E9;--line2:#D0D0D8;--track:#ECECF0;--bar:#C4C4CE;--qrbd:#E6E6EA;--phone:#EDEDF0;
    --sh:0 1px 2px rgba(16,16,24,.04),0 4px 16px rgba(16,16,24,.04);
  }
}
.nk2 a{color:inherit;text-decoration:none}
.nk2 a:hover{color:var(--actx)}
.nk2 button{font-family:inherit}
.nk2 :focus-visible{outline:2px solid var(--ac);outline-offset:2px;border-radius:6px}
.nk2 ::selection{background:var(--acsoft)}

/* Legacy bridge: topThree(), casinoRow(), trustFooter(), extensionPromo(),
   appPromo(), proofStrip(), editorsPicks() and friends are still built with
   the pre-redesign classes (.card, .cta, .hero, .stream, .item...), which
   read their colours and type from these variable NAMES (see the alias block
   at the top of this file). Overriding the names here, rather than rewriting
   every one of those functions' markup, repaints them in the new palette and
   type for free: same component, same data, new look, one place to keep it
   in sync. New redesign-only markup never reads these names -- it reads
   --bg/--tx/--ac directly -- so this bridge is pure addition.*/
.nk2{
  --canvas:var(--bg);--inset:var(--surf2);--slate:var(--surf);--frame:var(--bd2);
  --text:var(--tx);--text-2:var(--tx2);--mute:var(--tx3);--dim:var(--tx3);
  --accent:var(--ac);--accent-ink:var(--actx);--accent-edge:var(--actx);
  --on-accent:var(--onac);--accent-2:var(--surf2);--rule:var(--ac);
  --ruby:var(--sev);--link-hover:var(--actx);
  --display:Geist,system-ui,sans-serif;--sans:Geist,system-ui,sans-serif;--mono:'Geist Mono',monospace;
  --radius:16px;
}

/* Sidebar */
.sb2{
  flex:1 1 272px;border-right:1px solid var(--bd);background:var(--side);
  box-sizing:border-box;width:100%;min-height:100%;padding:20px 16px;color:var(--tx);
  display:flex;flex-direction:column;gap:22px;
}
.sb-mono{font-family:'Geist Mono',monospace}
.sb-search{display:flex;align-items:center;gap:10px;width:100%;height:40px;padding:0 10px 0 12px;
  border:1px solid var(--bd);border-radius:10px;background:var(--surf2);color:var(--tx3);
  font-family:inherit;font-size:13px;cursor:pointer}
.sb-kbd{font-size:11px;padding:2px 6px;border:1px solid var(--bd2);border-radius:6px;color:var(--tx2)}
.sb-group{font-family:'Geist Mono',monospace;font-size:11px;font-weight:500;letter-spacing:.08em;
  text-transform:uppercase;color:var(--tx3);padding:0 10px 6px}
.sb-item{display:flex;align-items:center;gap:10px;height:36px;padding:0 10px;border-radius:8px;
  font-size:13.5px;text-decoration:none;color:var(--tx2)}
.sb-item:hover{background:var(--hover);color:var(--tx)}
.sb-item.on{background:var(--surf2);color:var(--tx);font-weight:500;box-shadow:inset 0 0 0 1px var(--bd)}
.sb-badge{font-family:'Geist Mono',monospace;font-size:10px;font-weight:600;padding:1px 6px;
  border-radius:999px;background:var(--acsoft);color:var(--actx)}
.sb-cta{margin-top:auto;border:1px solid var(--bd);border-radius:16px;background:var(--surf);
  box-shadow:var(--sh);padding:16px;display:flex;flex-direction:column;gap:14px}
.sb-qr{position:relative;width:168px;height:168px;box-sizing:border-box;padding:8px;border-radius:14px;
  background:#FFFFFF;border:1px solid var(--qrbd)}
.sb-qr img:first-child{display:block;width:150px;height:150px}
.sb-qr-logo{position:absolute;left:63px;top:63px;width:40px;height:40px}
.sb-play{display:flex;align-items:center;justify-content:center;gap:10px;height:48px;border-radius:10px;
  border:1px solid #A6A6A6;background:#000000;color:#FFFFFF}
.sb-themeseg{display:flex;padding:3px;border:1px solid var(--bd);border-radius:10px;background:var(--surf2)}
.sb-themeseg button{flex:1;display:inline-flex;align-items:center;justify-content:center;gap:6px;height:30px;
  border:0;border-radius:7px;font-family:inherit;font-size:12.5px;cursor:pointer;background:transparent;
  color:var(--tx3)}
.sb-themeseg button:hover{background:var(--hover);color:var(--tx)}
html[data-theme="dark"] .sb-themeseg button[data-theme-set="dark"],
html[data-theme="light"] .sb-themeseg button[data-theme-set="light"]{
  background:var(--surf);color:var(--tx);font-weight:500;box-shadow:0 0 0 1px var(--bd)}
.sb-btn{flex:1;display:flex;align-items:center;justify-content:center;height:40px;border-radius:10px;
  font-size:13px;text-decoration:none}
.sb-btn-ghost{border:1px solid var(--bd);color:var(--tx);font-weight:500}
.sb-btn-ghost:hover{background:var(--hover);border-color:var(--bd2);color:var(--tx)}
.sb-btn-fill{background:var(--ac);color:var(--onac);font-weight:600}
.sb-btn-fill:hover{filter:brightness(1.07);color:var(--onac)}
@media (max-width:1023px){.sb2{position:static}}

/* Content column: header, body, footer */
.nk2-main{flex:999 1 560px;min-width:0;display:flex;flex-direction:column}
.hd2{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:12px;
  min-height:64px;box-sizing:border-box;padding:12px 40px;border-bottom:1px solid var(--bd);background:var(--side)}
.hd2-region form{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--tx2)}
.hd2-region select{height:32px;padding:0 10px;border:1px solid var(--bd);border-radius:8px;
  background:transparent;color:var(--tx2);font-size:12.5px;font-family:inherit}
.nk2-body{width:100%;max-width:1160px;box-sizing:border-box;margin:0 auto;padding:56px 40px 40px;
  display:flex;flex-direction:column;gap:96px}
@media (max-width:640px){.nk2-body{padding:32px 16px}}
.ft2{margin-top:auto;border-top:1px solid var(--bd);background:var(--side);padding:32px 40px;
  display:flex;flex-direction:column;gap:20px}
.ft2-meta{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:12px;
  padding-top:20px;border-top:1px solid var(--bd);font-size:12.5px;color:var(--tx3)}
.ft2-age{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;
  border-radius:50%;border:1.5px solid var(--tx3);font-size:10.5px;font-weight:700;color:var(--tx2)}

/* Shared components: buttons, cards, pills */
.nk2 .bp{display:inline-flex;align-items:center;justify-content:center;gap:8px;height:44px;padding:0 18px;
  border:0;border-radius:10px;background:var(--ac);color:var(--onac);font-weight:600;cursor:pointer}
.nk2 .bp:hover{filter:brightness(1.07);color:var(--onac)}
.nk2 .bg{display:inline-flex;align-items:center;justify-content:center;height:44px;padding:0 18px;
  border-radius:10px;border:1px solid var(--bd2);color:var(--tx);font-weight:500}
.nk2 .bg:hover{background:var(--hover);border-color:var(--bd2)}
.nk2 .card2{border:1px solid var(--bd);border-radius:18px;background:var(--surf);box-shadow:var(--sh)}
.nk2 .chip2{display:inline-flex;align-items:center;min-height:40px;padding:8px 14px;border:1px solid var(--bd);
  border-radius:999px;background:transparent;color:var(--tx2);font-size:13px}
.nk2 .chip2:hover{border-color:var(--bd2);background:var(--hover)}

/* Long-form article prose, ported from Methodology.dc.html's .prose rules;
   shared by Methodology and (once migrated) the blog post template. */
.nk2 .prose2 p{margin:0 0 14px;font-size:16px;line-height:1.75;color:var(--tx2)}
.nk2 .prose2 h2{margin:0 0 14px;font-size:24px;font-weight:600;letter-spacing:-0.02em;color:var(--tx);scroll-margin-top:24px}
.nk2 .prose2 li{font-size:16px;line-height:1.7;color:var(--tx2);margin-bottom:6px}
.nk2 .prose2 strong{color:var(--tx);font-weight:600}
.nk2 .prose2 a{color:var(--actx);text-decoration:underline}
.nk2 .toc2{display:block;padding:6px 12px;font-size:13.5px;color:var(--tx3);border-left:2px solid var(--bd)}
.nk2 .toc2:hover{color:var(--tx)}

/* The legacy body is a 100%-tall flex column; without flex:none the shell is
   shrunk to one viewport and long pages overflow past its background. */
.nk2{flex:none}
/* Sidebar: 272px, sticky, full height (Styleguide). Below 1024px it stacks. */
@media (min-width:1024px){
  .sb2{position:sticky;top:0;align-self:flex-start;height:100vh;overflow-y:auto;flex:0 0 272px}
}
.nk2 .tp tbody th{font-family:Geist,system-ui,sans-serif;font-size:14px;font-weight:500;letter-spacing:0;
  text-transform:none;color:var(--tx);white-space:normal;padding:16px 24px 16px 0;border-bottom:1px solid var(--bd)}
.nk2 .tp td{white-space:normal}
.nk2 .tp td.tp-num{white-space:nowrap}
.nk2 .tp.data td:not(:last-child){white-space:nowrap}
.nk2 .tp tbody th{min-width:180px}
.nk2 .tp-scroll{overflow-x:auto}

/* Section rhythm: the reference spaces stacked panels 32-40px apart and
   home-page sections 88-96px apart. */
.nk2-body{gap:40px}
.nk2-body.gap-lg{gap:72px}
.nk2-body.gap-xl{gap:96px}

/* Status-pill tokens from the reference's Styleguide (pass / caution / fail). */
.nk2{--posbg:#0E1511;--posbd:#1F2A22;--warnbg:#18140A;--warnbd:#3A3015;--sevbg:#1A0E0E;--sevbd:#3A1C1C}
html[data-theme="light"] .nk2{--posbg:#EEFBF2;--posbd:#C6EDD3;--warnbg:#FFFBEB;--warnbd:#FDE68A;--sevbg:#FEF2F2;--sevbd:#FECACA}
@media (prefers-color-scheme:light){html:not([data-theme="dark"]) .nk2{--posbg:#EEFBF2;--posbd:#C6EDD3;--warnbg:#FFFBEB;--warnbd:#FDE68A;--sevbg:#FEF2F2;--sevbd:#FECACA}}

/* ═══ Legacy component skin ═══
   The shared server-rendered components (cards, buttons, labels, severity
   tags, the score, tables, forms, FAQs, the ranked stream, the diagram) were
   drawn in the old editorial language: Anton capitals, tracked mono labels,
   pill buttons, cards pulled 33px past the column. Inside the new shell they
   take the reference's language instead, so every page that still uses them
   matches the pages rebuilt from scratch. */
.nk2 .wrap{max-width:none;padding:0}
.nk2 .card{background:var(--surf);border:1px solid var(--bd);border-radius:18px;box-shadow:var(--sh);
  padding:24px;margin:0}
.nk2 .card.tight{padding:20px 22px}
.nk2 .card + .card{margin-top:0}
.nk2 .verdict,.nk2 .display{font-family:Geist,system-ui,sans-serif;font-weight:600;text-transform:none;
  letter-spacing:-0.03em;line-height:1.1;font-size:clamp(28px,3.4vw,40px);color:var(--tx)}
.nk2 .label{font-family:Geist,system-ui,sans-serif;font-size:18px;font-weight:600;text-transform:none;
  letter-spacing:-0.01em;line-height:1.3;color:var(--tx);margin:0 0 12px}
.nk2 .eyebrow,.nk2 .kicker{font-family:'Geist Mono',monospace;font-size:11.5px;font-weight:500;
  letter-spacing:0.06em;text-transform:uppercase;color:var(--actx)}
.nk2 .sechead h2{font-family:Geist,system-ui,sans-serif;font-size:34px;font-weight:600;letter-spacing:-0.03em;line-height:1.1}
.nk2 .hero{font-family:Geist,system-ui,sans-serif;font-size:15px;line-height:1.6;color:var(--tx2);
  letter-spacing:0;max-width:72ch;margin:8px 0 0}
.nk2 .fineprint,.nk2 .foot,.nk2 .basis,.nk2 .paidnote{font-family:Geist,system-ui,sans-serif;font-size:12.5px;
  line-height:1.6;color:var(--tx3);letter-spacing:0;text-transform:none}
.nk2 .foot{border-top:1px solid var(--bd);margin-top:8px;padding-top:20px}
.nk2 .fineprint a,.nk2 .foot a,.nk2 .paidnote a{color:var(--tx2);text-decoration:underline}
.nk2 .postdate,.nk2 .relmeta,.nk2 .person-role,.nk2 .scale,.nk2 .cov-t,.nk2 .tally-i,.nk2 .t3-rank,
.nk2 .t3-of,.nk2 .relcat{font-family:Geist,system-ui,sans-serif;font-size:12.5px;font-weight:400;
  letter-spacing:0;text-transform:none;color:var(--tx3)}

.nk2 .cta,.nk2 .authgo{display:inline-flex;align-items:center;justify-content:center;gap:8px;height:44px;
  padding:0 18px;border:0;border-radius:10px;background:var(--ac);color:var(--onac);
  font-family:Geist,system-ui,sans-serif;font-size:14px;font-weight:600;letter-spacing:0;text-transform:none;
  line-height:1;box-shadow:none;cursor:pointer}
.nk2 .cta:hover,.nk2 .authgo:hover{background:var(--ac);color:var(--onac);filter:brightness(1.07);box-shadow:none}
.nk2 .rowvisit{display:inline-flex;align-items:center;height:36px;padding:0 14px;border-radius:9px;
  border:1px solid var(--bd2);background:transparent;color:var(--tx);font-family:Geist,system-ui,sans-serif;
  font-size:13px;font-weight:500;letter-spacing:0;text-transform:none;box-shadow:none}
.nk2 .rowvisit:hover{background:var(--hover);transform:none;box-shadow:none;color:var(--tx)}
.nk2 .chip{font-family:Geist,system-ui,sans-serif;font-size:13px;font-weight:400;letter-spacing:0;
  text-transform:none;padding:8px 14px;border-radius:999px;border:1px solid var(--bd);color:var(--tx2)}
.nk2 .chip:hover{border-color:var(--bd2);background:var(--hover);color:var(--tx)}
.nk2 .chip.on{background:var(--surf2);border-color:var(--bd2);color:var(--tx)}

/* Severity: the reference's status pill, a dot and a sentence-case word. */
.nk2 .row{border-top:1px solid var(--bd);color:var(--tx2);font-size:14.5px;align-items:center}
.nk2 .row:first-child{border-top:0}
.nk2 .dot{display:inline-flex;flex-direction:row-reverse;align-items:center;gap:6px;
  font-family:Geist,system-ui,sans-serif;font-size:12px;font-weight:500;letter-spacing:0;text-transform:none;
  border-radius:999px;padding:3px 10px 3px 8px;border:1px solid var(--bd);background:var(--surf2);color:var(--tx3);box-shadow:none}
.nk2 .dot::after{content:"";width:6px;height:6px;border-radius:50%;background:currentColor;flex:none}
.nk2 .nk-good::before{content:"Pass"}
.nk2 .nk-warn::before{content:"Caution"}
.nk2 .nk-bad::before{content:"Fail"}
.nk2 .nk-unknown::before{content:"Not measured"}
.nk2 .nk-good{background:var(--posbg);border-color:var(--posbd);color:var(--postx)}
.nk2 .nk-good::after{background:var(--posdot)}
.nk2 .nk-warn{background:var(--warnbg);border-color:var(--warnbd);color:var(--warntx);box-shadow:none}
.nk2 .nk-warn::after{background:var(--warn)}
.nk2 .nk-bad{background:var(--sevbg);border-color:var(--sevbd);color:var(--sevtx)}
.nk2 .nk-bad::after{background:var(--sev)}

/* The score: Geist Mono numerals, as in the reference's cards. */
.nk2 .score,.nk2 .t3-score{font-family:'Geist Mono',monospace;font-weight:500;font-size:52px;
  letter-spacing:-0.04em;line-height:1;color:var(--tx)}
.nk2 .score .of{display:block;margin-top:10px;font-family:Geist,system-ui,sans-serif;font-size:12.5px;
  font-weight:400;letter-spacing:0;text-transform:none;color:var(--tx3)}
.nk2 .score.unrated{font-family:Geist,system-ui,sans-serif;font-size:15px;font-weight:500;letter-spacing:0;text-transform:none}

/* Ranked stream: the dashed spine and its beads are the old system's. */
.nk2 .stream{padding-left:0}
.nk2 .stream::before,.nk2 .stream .item::before{display:none}
.nk2 .moreidx>summary{justify-content:center;height:52px;padding:0 20px;border:1px solid var(--bd);
  border-radius:14px;background:var(--surf);font-family:Geist,system-ui,sans-serif;font-size:13.5px;
  font-weight:500;letter-spacing:0;text-transform:none;color:var(--tx2)}
.nk2 .moreidx>summary:hover{border-color:var(--bd2);background:var(--hover);color:var(--tx)}
.nk2 .moreidx-n{font-family:'Geist Mono',monospace;font-size:13px;letter-spacing:0;color:var(--actx)}

/* Tables: mono column heads in muted ink, hairline rows. */
.nk2 .tp{font-size:14px}
.nk2 .tp th{font-family:'Geist Mono',monospace;font-size:11px;font-weight:500;letter-spacing:0.06em;
  color:var(--tx3);border-bottom:1px solid var(--bd)}
.nk2 .tp td{border-bottom:1px solid var(--bd);color:var(--tx2)}
.nk2 .tp thead th{background:var(--surf)}
.nk2 .tp-strong,.nk2 .tp-name{color:var(--tx)}

/* Forms. */
.nk2 .cform{gap:6px;margin-top:16px}
.nk2 .cform label{font-family:Geist,system-ui,sans-serif;font-size:12.5px;letter-spacing:0;text-transform:none;
  color:var(--tx2);margin-top:12px}
.nk2 .cform input,.nk2 .cform select,.nk2 .cform textarea{padding:0 12px;height:44px;border-radius:10px;
  background:var(--bg);color:var(--tx);border:1px solid var(--bd2);font-family:Geist,system-ui,sans-serif;font-size:14px}
.nk2 .cform textarea{height:auto;padding:12px}
.nk2 .cform input:focus,.nk2 .cform select:focus,.nk2 .cform textarea:focus{border-color:var(--ac);box-shadow:0 0 0 3px var(--acsoft)}
.nk2 .cform .cta{margin-top:16px}
.nk2 .formerr{padding:12px 14px;border:1px solid var(--sevbd);border-radius:10px;background:var(--sevbg);
  color:var(--sevtx);font-family:Geist,system-ui,sans-serif;font-size:13.5px;letter-spacing:0;text-transform:none}

.nk2 .tp-pm,.nk2 .tp-na,.nk2 .tp-mutecell{font-family:Geist,system-ui,sans-serif;font-size:12.5px;
  letter-spacing:0;text-transform:none;color:var(--tx3)}
.nk2 .lede{font-family:Geist,system-ui,sans-serif;font-size:15px;line-height:1.6;letter-spacing:0;
  text-transform:none;color:var(--tx2)}
.nk2 .ledger summary{font-family:Geist,system-ui,sans-serif;font-size:13.5px;font-weight:500;
  letter-spacing:0;text-transform:none;color:var(--actx)}
.nk2 .ledger-pre{background:var(--bg);border:1px solid var(--bd);border-radius:10px;padding:12px 14px;color:var(--tx)}

/* The system diagram. */
.nk2 .d-box,.nk2 .d-wire{stroke:var(--bd2)}
.nk2 .d-label,.nk2 .d-sub{font-family:'Geist Mono',monospace;letter-spacing:0.04em}
.nk2 .d-score{font-family:'Geist Mono',monospace}

/* Content is never held back for a scroll-in animation inside the new shell:
   a page that renders blank until scrolled reads as broken. */
.nk2 .mv,.nk2 .post,.nk2 .mth-item,.nk2 .card,.nk2 [class*="nk-in"]{opacity:1 !important;transform:none !important}
