/* Radar portal, white-label. The structure is the source portal's. This
   SOURCE is neutral: every colour, both typefaces and the wordmark are
   tokens (BRAND names between double underscores) that export_radar.py fills from the profile's
   `brand` key (radar_brand.py: four from the brand, the rest derived from
   them by rule; theme.md says which). No customer's name, colour or font
   is written here. Gradient lives on the dark ground; on white, permanent
   elements take the flat base colour and only the hover sweep and the
   notch cut may carry the gradient. Weight ceiling 600.

   Item 25c (the source portal's item 24, in this portal's tokens): the
   control tiers, the contrast tokens, the phone bar and filter sheet,
   the column sets, the tiles as tabs, the chip rows, the collapsed
   prose, the drawer's header, jump row and width. Every tone a control
   needs is DERIVED from the brand's tokens below, never chosen, and the
   harness measures every text and edge pair from the compiled values. */
@font-face{font-family:'Manrope';font-style:normal;font-weight:600;font-display:swap;src:url('brand/Manrope-latin-9b2a88eeab4d.woff2') format('woff2')}
@font-face{font-family:'Inter';font-style:normal;font-weight:400 600;font-display:swap;src:url('brand/Inter-latin-76816c2c06a8.woff2') format('woff2')}
:root{
  --base:#1E1E24;
  --base-rgb:30,30,36;
  --paper:#FFFFFF;
  --accent-a:#8380D1;
  --accent-b:#A7A5DF;

  --ink:var(--base);
  --ink-2:#2C2C31;
  --hair:#3E3E43;
  --hair-soft:#2D2D32;
  --bone:var(--paper);
  --bone-dim:rgba(255,255,255,.78);
  --mute:#8F8F92;
  --mute-on-paper:#8F8F92;
  /* the mute colour for text on the raised surface (--ink-2: a table
     heading, a chip's count, a hovered row), where --mute may fall under
     4.5:1: --mute moved 15% of the way to the paper. And the mute colour
     for text on the paper (the drawer, the light sheet), where the
     brand's own muted may fall under 4.5:1 on its surface: that muted
     moved 45% of the way to the base. The harness measures both. */
  --mute-2:color-mix(in srgb,var(--mute) 85%,var(--paper));
  --mute-paper:color-mix(in srgb,var(--mute-on-paper) 55%,var(--base));
  /* the controls' fill and edge: the hairline tone as the fill (white
     text on it at its measured ratio), the mute colour as the edge (at
     least 3:1 against the base, by the rule that derives --mute); the
     one control height and the chip height */
  --ctrl-fill:var(--hair);
  --ctrl-edge:var(--mute);
  --ctrl-h:40px;
  --chip-h:32px;
  --aqua:#9592D8;
  --aqua-from:var(--accent-a);
  --aqua-to:var(--accent-b);
  --aqua-mid:#9592D8;
  --grad:linear-gradient(90deg,var(--accent-a),var(--accent-b));
  --grad-v:linear-gradient(180deg,var(--accent-a),var(--accent-b));
  --grad-text:var(--grad);
  --light-grey:#F4F4F4;
  --yes:#00A67E;
  --no:#D9534F;

  --display:'Manrope',system-ui,sans-serif;
  --body:'Inter',system-ui,sans-serif;

  --pad:clamp(20px,4vw,56px);
  --max:1440px;
  --ease:cubic-bezier(.2,.7,.2,1);
}
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;scroll-padding-top:92px}
body{
  background:var(--ink);color:var(--bone);
  font-family:var(--body);font-weight:400;line-height:1.5;
  letter-spacing:-.005em;overflow-x:hidden;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
}
::selection{background:var(--aqua);color:var(--base)}
a{color:inherit;text-decoration:none}
svg{display:block}
/* a visible 2px ring on every control, square as the controls are */
:focus-visible{outline:2px solid var(--aqua);outline-offset:3px;border-radius:0}

.grid-bg{
  position:fixed;inset:0;z-index:0;pointer-events:none;
  background-image:
    linear-gradient(to right,rgba(255,255,255,.025) 1px,transparent 1px),
    linear-gradient(to bottom,rgba(255,255,255,.025) 1px,transparent 1px);
  background-size:80px 80px;
  -webkit-mask-image:radial-gradient(ellipse at top,rgba(var(--base-rgb),.85),rgba(var(--base-rgb),.2) 70%);
  mask-image:radial-gradient(ellipse at top,rgba(var(--base-rgb),.85),rgba(var(--base-rgb),.2) 70%);
}
main,header{position:relative;z-index:1}
/* The wave: the homepage hero's construction, whole. On the site .hero
   is min-height:100vh; .hero-canvas fills it (inset:0, 100% x 100%);
   .hero::after bleeds the box's bottom 24% into the ink. The wrap here
   IS the hero box for this page, so all three port verbatim, and the
   engine's own geometry sets the vertical drop: the mesh paints from
   ~15% (far edge, fog-dimmed) to ~69% (nearest grid row) of the box,
   swinging with wave amplitude. Page furniture occludes it exactly as
   hero copy does. On every page since item 25c. */
.wave-wrap{position:absolute;top:0;left:0;right:0;height:100vh;z-index:0;
  pointer-events:none;overflow:hidden}
.wave-wrap::after{content:'';position:absolute;left:0;right:0;bottom:0;
  height:24%;background:linear-gradient(to bottom,transparent,var(--ink) 90%);
  pointer-events:none;z-index:0}
.hero-canvas{position:absolute;inset:0;width:100%;height:100%;z-index:0;
  pointer-events:none;opacity:0;transition:opacity 1.4s ease-out}
.hero-canvas.ready{opacity:1}
.wrap{max-width:var(--max);margin:0 auto;padding-left:var(--pad);padding-right:var(--pad)}
/* The data grid is not editorial content, so it is not held to the 1280px
   reading measure. Sized so the widest realistic row holds one line. */
.wrap-wide{max-width:1800px;margin:0 auto;padding-left:var(--pad);padding-right:var(--pad)}

/* ---------- section shell ---------- */
.sheet{position:relative;padding:clamp(80px,12vh,160px) 0}
/* The first sheet of every page starts under the fixed bar (item 25b,
   fault 1). Until then each page guessed the bar's height inline
   (padding-top:clamp(106px,14vh,132px)), and under 800px, where the bar
   wrapped to about 180px, it covered the search box and the range
   inputs. shared.js measures the bar into --bar-h on the root (and
   again when it wraps or unwraps); the offset is that height plus the
   gap, 85px (the one-row bar) until the measure lands. */
.sheet.first{padding-top:calc(var(--bar-h,85px) + clamp(21px,3vh,47px))}
.sheet.light{
  background:var(--paper);color:var(--base);
  --ink:var(--paper);--ink-2:var(--light-grey);
  --hair:rgba(var(--base-rgb),.14);--hair-soft:rgba(var(--base-rgb),.07);
  --bone:var(--base);--bone-dim:rgba(0,0,0,.82);
  --grad-text:linear-gradient(90deg,var(--base),var(--base));
  --aqua:var(--base);
  --mute:var(--mute-paper);--mute-2:var(--mute-paper);
}
.sheet.light .eyebrow{color:var(--mute)}

/* ---------- the notch ---------- */
.notch{position:absolute;top:0;left:0;right:0;height:0;z-index:5;pointer-events:none}
.notch::before,.notch::after{content:'';position:absolute;width:50%;height:15px}
.notch::before{left:-3px;top:-1px;transform:skewX(-12deg);border-bottom-right-radius:50px;background:var(--notch-light,var(--paper))}
.notch::after{right:-3px;top:-14px;transform:skewX(-12deg);border-top-left-radius:50px;background:linear-gradient(90deg,var(--accent-a),var(--accent-b))}
@media(min-width:768px){
  .notch::before,.notch::after{height:30px}
  .notch::after{top:-29px}
}
/* dark-over-light joints: the approved top joint rotated 180deg */
.notch--below{transform:scale(-1,-1)}

/* ---------- headings ---------- */
.eyebrow{font-family:var(--display);font-weight:500;font-size:11px;letter-spacing:.2em;text-transform:uppercase;color:var(--mute)}
.num{font-family:var(--display);font-weight:500;font-size:11px;letter-spacing:.2em;
  background-image:var(--grad-text);-webkit-background-clip:text;background-clip:text;color:transparent;
  display:block;margin-bottom:8px}
h2.sec{font-family:var(--display);font-weight:600;font-size:clamp(40px,6.5vw,96px);line-height:1.02;letter-spacing:-.015em;margin-bottom:8px}
h2.sec em{font-style:italic;font-weight:500;text-transform:lowercase;
  background-image:var(--grad-text);-webkit-background-clip:text;background-clip:text;color:transparent}
.sec-head{display:grid;grid-template-columns:1fr 2fr;gap:60px;margin-bottom:clamp(40px,6vh,80px);align-items:start}
.sec-head>div:first-child{position:sticky;top:100px;align-self:start}
.sec-head .lead{font-size:17px;line-height:1.6;color:var(--bone-dim);max-width:62ch;margin-top:14px}
@media(max-width:800px){.sec-head{grid-template-columns:1fr;gap:16px}.sec-head>div:first-child{position:static;top:auto}}
h3{font-family:var(--display);font-weight:600;font-size:22px;letter-spacing:-.005em;margin-bottom:14px}
h4{font-family:var(--display);font-weight:600;font-size:15px;letter-spacing:-.005em;margin-bottom:10px}
p{font-size:15.5px;line-height:1.6;color:var(--bone-dim);max-width:74ch}
.cap{font-family:var(--display);font-weight:500;font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--mute)}
code{font-family:var(--display);font-weight:500;font-size:.95em;letter-spacing:.01em;color:var(--aqua)}

/* The ink-bar device. One bar per authored line; every bar sits behind ALL
   the text (stacking context on the title, never the line), lines overlap by
   0.44em to hold the rhythm. */
.ink-line{display:block;width:fit-content;position:relative;color:var(--paper);padding:.22em;margin-top:-.44em}
.ink-line:first-child{margin-top:0}
.ink-line::before{content:'';position:absolute;inset:0;background:var(--base);z-index:-1}
.sheet.light h2.sec{position:relative;z-index:0}
.sheet.light h2.sec em{background-image:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent}

/* ---------- hairline grid device ---------- */
.cols{display:grid;gap:1px;background:var(--hair);border:1px solid var(--hair)}
.cols>*{background:var(--ink);padding:22px 24px}

/* ---------- controls (item 25c, the source portal's item 24) ---------- */
/* Three tiers. .btn, the primary: the accent sweep as the fill (both
   ends lifted to read on the base by radar_brand.py's rule, so the base
   as text on the darker end is 4.5:1 by construction), at most one per
   view besides the bar's (the bar's Downloads on every page since
   2026-11-06, a button on the ranked list and an a.btn to ./#downloads
   on the other two; "Show N companies" in the phone filter sheet; Save
   profile on the builder); on hover the base with the sweep on the text
   and a 2px lift. Every rule of the tier is by class and none names
   button.btn alone, and the anchor rule above drops the underline, so
   an a.btn renders as the button does (the height, the padding, the
   sweep, the focus ring, the span). .btn-2, the secondary: everything
   else clicked or typed in on the dark ground, a solid fill of the
   hairline tone, the paper as text, a 1px edge in the mute colour (at
   least 3:1 against the base), the sweep on the edge on hover with the
   text staying put. .btn-base: the paper surface's buttons (the drawer),
   the base as the fill, the paper as text, the sweep on hover. The
   ghost tier is retired: a hairline edge against the base reads under
   3:1, so those buttons read as text floating on the wave. No control
   is ever transparent: the harness greps this stylesheet. One height
   (40px; 44px under 800px; chips 32px), square corners throughout, the
   display face at 500 14px .02em, padding 0 18px (inputs and selects
   0 12px, the select with room for its arrow). */
.btn,.btn-2,.btn-base,.chip,.filters input,.filters select,.pb-add input,.pb-wide,#incbefore,.rel{
  border-radius:0;font-family:var(--display);font-weight:500;font-size:14px;letter-spacing:.02em;line-height:1}
.btn,.btn-2,.btn-base{display:inline-flex;align-items:center;justify-content:center;gap:10px;
  height:var(--ctrl-h);padding:0 18px;cursor:pointer;white-space:nowrap;
  transition:border-color .3s ease,background-color .3s ease,color .3s ease,transform .3s ease}
.btn{background:var(--grad);color:var(--base);border:1px solid transparent;border-image:var(--grad) 1}
.btn>span{display:inline-block}
.btn:hover{background:var(--base);transform:translateY(-2px)}
.btn:hover>span{background-image:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent}
.btn-2,.filters input,.filters select,.pb-add input,.pb-wide,#incbefore,.rel{
  background:var(--ctrl-fill);color:var(--paper);border:1px solid var(--ctrl-edge)}
.btn-2:hover,.rel:hover{border-image:var(--grad) 1;color:var(--paper)}
.btn-base{background:var(--base);color:var(--paper);border:1px solid var(--base)}
.btn-base:hover{background:var(--grad);color:var(--base);border-color:transparent}
.filters input,.filters select,.pb-add input,.pb-wide,#incbefore{height:var(--ctrl-h);padding:0 12px}
.filters input::placeholder,.pb-add input::placeholder,.pb-wide::placeholder{color:var(--bone-dim)}
/* the select's arrow, drawn on the control's own fill. It is a data URI,
   which cannot read a token, so it is white: the one place the paper is
   assumed white (a brand with a tinted surface gets a white arrow). */
.filters select{appearance:none;-webkit-appearance:none;padding-right:34px;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='white' stroke-width='1.5'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right 12px center;background-size:10px 6px}
.filters select option{background:var(--ctrl-fill);color:var(--paper)}

/* ---------- nav ---------- */
.nav{position:fixed;top:0;left:0;right:0;z-index:100;padding:22px var(--pad);
  display:flex;align-items:center;justify-content:space-between;gap:20px;
  background:linear-gradient(to bottom,rgba(var(--base-rgb),.85),rgba(var(--base-rgb),0));
  backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
  transition:padding .3s ease,background .3s ease}
.nav.scrolled{padding-top:14px;padding-bottom:14px;background:rgba(var(--base-rgb),.92);
  border-bottom:1px solid var(--hair-soft)}
.nav .lock{display:flex;align-items:center;gap:16px}
/* the wordmark slot: the customer's own mark, as they supply it, held to
   the bar's height; the product name sits beside it */
.brandmark{display:block;height:34px;width:auto;max-width:42vw}
.nav.scrolled .brandmark{height:28px}
footer .foot-logo .brandmark{height:44px;margin-bottom:10px}
footer .foot-claims{letter-spacing:.08em;font-size:12px;text-transform:uppercase}
@media(max-width:800px){.brandmark{height:26px}.nav.scrolled .brandmark{height:24px}}
/* The wordmark is the profile's title as text, beside the mark. It
   compacts with the bar as the source lockup did. */
.wordmark{font-family:var(--display);font-weight:600;font-size:24px;
  letter-spacing:-.01em;line-height:1;color:var(--bone);transition:font-size .3s ease}
.nav.scrolled .wordmark{font-size:20px}
@media(max-width:800px){
  .wordmark{font-size:21px}
  .nav.scrolled .wordmark{font-size:19px}
}
.nav .right{display:flex;align-items:center;gap:16px;flex-wrap:wrap}
/* The two dates: the dataset the page shows and the day it was
   published, side by side; from 1,200px down they stack so the bar
   stays one row. */
.nav .stamps{display:flex;align-items:center;gap:16px}
@media(max-width:1200px){
  .nav .stamps{flex-direction:column;align-items:flex-end;gap:4px}
}
/* The pages menu (item 25c, the source portal's item 16): the same three
   links on every page, uppercase display labels in the mute colour,
   white on hover; the page the reader is on white with a 2px rule of the
   accent sweep beneath it (the bar is the dark ground, so the sweep is
   permitted) and aria-current="page" on the link, so the state is never
   colour alone. */
.nav .pages{display:flex;align-items:center;flex-wrap:wrap;gap:6px 22px}
.nav .pages a{position:relative;display:inline-block;padding:6px 0;font-family:var(--display);font-weight:500;
  font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--mute);white-space:nowrap;transition:color .3s ease}
.nav .pages a:hover{color:var(--bone)}
.nav .pages a[aria-current=page]{color:var(--bone)}
.nav .pages a[aria-current=page]::after{content:'';position:absolute;left:0;right:0;bottom:0;height:2px;background:var(--grad)}
/* From 800px down the bar is one row: the lockup left and a Menu button
   right, which shared.js inserts on every page; the bar's right-hand
   block (the stamps, the menu, Guide and Downloads) becomes a
   sheet under the bar that the button opens. While open the sheet is
   the bar's next sibling (shared.js moves it out), so the bar's backdrop
   blur is not its containing block and top and bottom are the viewport's
   (the source portal's review fault 1, never ported). The sheet is out
   of the flow, so --bar-h is the one-row bar and nothing sits under it. */
.nav .menubtn{display:none}
@media(max-width:800px){
  .nav{padding-top:12px;padding-bottom:12px;flex-wrap:nowrap}
  .nav.scrolled{padding-top:8px;padding-bottom:8px}
  .nav .menubtn{display:inline-flex;margin-left:auto}
  .nav .right{display:none}
  .right.open{display:flex;position:fixed;top:var(--bar-h,68px);left:0;right:0;bottom:0;z-index:99;
    flex-direction:column;align-items:stretch;gap:0;padding:8px var(--pad) 24px;
    background:var(--ink);overflow-y:auto;border-top:1px solid var(--hair)}
  .right.open .pages{display:flex;flex-direction:column;align-items:stretch;gap:0;order:0}
  .right.open .pages a{position:relative;display:inline-block;padding:14px 0;font-family:var(--display);font-weight:500;font-size:13px;
    letter-spacing:.16em;text-transform:uppercase;color:var(--mute);white-space:nowrap;border-bottom:1px solid var(--hair-soft)}
  .right.open .pages a:hover,.right.open .pages a[aria-current=page]{color:var(--bone)}
  .right.open .pages a[aria-current=page]::after{content:'';position:absolute;left:0;bottom:0;height:2px;width:48px;background:var(--grad)}
  .right.open .btn,.right.open .btn-2{order:1;margin-top:12px;justify-content:center;width:100%}
  .right.open .stamps,.right.open>.live{order:2;margin-top:16px;padding-top:12px;border-top:1px solid var(--hair);
    display:flex;flex-direction:column;align-items:flex-start;gap:4px}
  body.sheet-open{overflow:hidden}
}
.live{color:var(--aqua);display:inline-flex;align-items:center;gap:8px;
  font-family:var(--display);font-weight:500;font-size:11px;letter-spacing:.16em;text-transform:uppercase}
.live::before{content:'';width:6px;height:6px;border-radius:50%;background:var(--aqua);animation:pulse 1.5s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:.4;transform:scale(.85)}50%{opacity:1;transform:scale(1.15)}}


/* ---------- filters ---------- */
/* One row (item 25c, the source portal's item 24): the search box,
   Role, Public buyer, Profile, Web check, Columns and "More filters",
   which opens the rest in place on a second row (gate, confidence,
   measures, relevance, the four ranges); the count right-aligned. Active
   filters show as removable chips under the row with Reset at their end.
   Until this the controls ran two rows of mixed widths in three
   hairline-split groups. The .fsheet holder is display:contents here, so
   its controls sit in the row; under 800px it is the Filters button's
   sheet (the phone block at the end of this file). */
.filters{display:grid;gap:10px;padding:14px 0;margin-bottom:2px}
.frow{display:flex;gap:10px;flex-wrap:wrap;align-items:center}
.frow.more{flex:1 0 100%;order:2}
.frow.more[hidden]{display:none}
.filters .fsheet{display:contents}
.filters .fbtn,.filters .showbtn{display:none}
.filters .sp,.filters .tally{order:1}
/* 264px holds the whole placeholder, "Search name, number, town", at 14px
   with the box's own padding; at 230px it was cut to "towr" (2026-10-08). */
.filters input[type=search]{min-width:264px}
.filters .rin{width:122px}
.filters .sp{flex:1}
.tally{font-family:var(--display);font-weight:500;font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--mute)}
.tally b{color:var(--bone);font-weight:600}
.fchips{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.fchips[hidden]{display:none}
.fchips .list{display:contents}
.fchips .chip .x{font-size:14px;line-height:1;color:var(--mute)}
.fchips .chip:hover .x{color:var(--bone)}
.fchips .cap{margin-right:4px}

/* ---------- the table, per the guide ---------- */
/* Its own scroll box in both axes: body sets overflow-x:hidden, and a sticky
   thead needs its scrolling ancestor to be this box so top:0 is exact. The
   tbody is WINDOWED - 20k real rows would be 20k*14 live cells - so two
   spacer rows carry the off-screen height and only the visible slice plus an
   overscan is ever in the DOM. Every row is the same fixed height by
   construction (the company cell is always two lines, nothing else wraps),
   which is what makes the spacer arithmetic exact. */
.tw{position:relative;overflow:auto;max-height:76vh;border:1px solid var(--hair);background:var(--ink)}
/* The guide gives cards AND tables the hover sweep. Construction from
   .legal-table-wrap::before; one departure: z-index 4 (site value 1) to
   clear this table's sticky th at z-index 2 and its pinned corner (# and
   Company, below) at 3. */
.tw::before{content:'';position:absolute;top:0;left:0;width:0;height:2px;
  background:var(--grad);transition:width .6s var(--ease);z-index:4}
.tw:hover::before{width:100%}
table{width:100%;border-collapse:collapse;font-size:14.5px}
/* Header cells at 14px 12px: the site's .legal-table th is 14px 18px (F4),
   diverged 2026-09-01 so all seventeen columns fit a ~1,700px window
   without a horizontal scroll. Tracking stays at the site's .13em. */
th{text-align:left;font-family:var(--display);font-weight:500;font-size:11px;
  letter-spacing:.13em;text-transform:uppercase;color:var(--mute-2);
  background:var(--ink-2);padding:14px 12px;border-bottom:1px solid var(--hair);
  white-space:nowrap;position:sticky;top:0;z-index:2}
th.sortable{cursor:pointer}
th.sortable:hover{color:var(--bone-dim)}
th[aria-sort]{color:var(--bone)}
/* Body cells at 9px 9px DELIBERATELY: a 20k-row working tool needs the
   density, and the horizontal side came down from 14px on 2026-09-01 for
   the same column-fit reason as th. Knowingly diverged from the site
   reference, .legal-table td at 15px 18px. */
td{padding:9px 9px;vertical-align:middle;color:var(--bone-dim);
  border-bottom:1px solid var(--hair-soft);border-right:1px solid var(--hair-soft);
  white-space:nowrap}
td:last-child{border-right:0}
td.co{color:var(--bone);font-weight:500;min-width:240px;max-width:300px;
  overflow:hidden;text-overflow:ellipsis}
/* the name and the number line are blocks inside the cell, so the cell's
   ellipsis never reached them and a long name was cut mid-word (the
   source portal's review fault 4, not ported) */
td.co>div{overflow:hidden;text-overflow:ellipsis}
/* Location is a post town now (clean_towns.py), so 170px holds all but
   the longest; those ellipsise and carry the full name as a title. */
td.loc{max-width:170px;overflow:hidden;text-overflow:ellipsis}
tbody tr.row{cursor:pointer}
tbody tr.row:hover td{background:var(--ink-2)}
/* on the raised hover surface the mute text takes its measured colour:
   the custom property is remapped for the row */
tbody tr:hover{--mute:var(--mute-2)}
tbody tr.spacer td{padding:0;border:0}
/* # and Company stay put while the other columns scroll under them
   (2026-10-08), as on the source portal. The row does not fit a 1,440px
   window legibly (this table has the role column too), so the two cells
   that name the row are pinned rather than every column squeezed. Pinned
   from 821px up; narrower screens scroll the whole row as before. # is a
   fixed 60px so Company knows where to stand. Separate borders, because a
   collapsed border does not travel with a sticky cell. A marked row fades
   its pinned cells by colour (the same 45% and 75%) rather than by
   opacity: a translucent pinned cell would show the columns passing
   beneath it. Every rule is scoped to .tw.pin, the ranked list's own box
   (item 25b, fault 2): the tender page shares .tw for its scroll box,
   and the unscoped th rules gave it a 60px first heading and a second
   heading pinned at 60px, out of line with their columns. */
@media(min-width:821px){
  .tw.pin table{border-collapse:separate;border-spacing:0}
  .tw.pin thead th:first-child,.tw.pin tr.row>td:first-child{width:60px;min-width:60px}
  .tw.pin thead th:first-child,.tw.pin thead th:nth-child(2){position:sticky;z-index:3}
  .tw.pin thead th:first-child,.tw.pin tr.row>td:first-child{left:0}
  .tw.pin thead th:nth-child(2),.tw.pin tr.row>td.co{left:60px}
  .tw.pin tr.row>td:first-child,.tw.pin tr.row>td.co{position:sticky;z-index:1;background:var(--ink)}
  .tw.pin thead th:nth-child(2),.tw.pin tr.row>td.co{border-right:1px solid var(--hair)}
  .tw.pin tbody tr.row:hover>td:first-child,.tw.pin tbody tr.row:hover>td.co{background:var(--ink-2)}
  .tw.pin tr.dim>td:first-child,.tw.pin tr.dim>td.co{opacity:1}
  .tw.pin tr.dim>td:first-child,.tw.pin tr.dim>td.co .sub{color:color-mix(in srgb,var(--mute) 45%,var(--ink))}
  .tw.pin tr.dim>td.co{color:color-mix(in srgb,var(--bone) 45%,var(--ink))}
  .tw.pin tr.dim:hover>td:first-child,.tw.pin tr.dim:hover>td.co .sub{color:color-mix(in srgb,var(--mute) 75%,var(--ink-2))}
  .tw.pin tr.dim:hover>td.co{color:color-mix(in srgb,var(--bone) 75%,var(--ink-2))}
}
.sub{font-size:12px;color:var(--mute);margin-top:1px;font-weight:400}
.rk{font-family:var(--display);font-weight:500;font-variant-numeric:tabular-nums lining-nums;font-size:13px;color:var(--mute)}
.pv{font-family:var(--display);font-weight:500;font-variant-numeric:tabular-nums lining-nums;font-size:13px;display:inline-block;min-width:44px}
.pv small{font-family:var(--display);font-size:10px;color:var(--mute);letter-spacing:.04em}
.compo{display:inline-block;min-width:56px}
/* The bars are empty spans, and an inline box takes no width or height,
   so until 2026-10-08 all three (this one and the panel's percentile and
   confidence bars, below) drew at zero size. display:block gives each the
   size and colour declared beside it: here 3px of the accent sweep under
   the figure, on the dark ground. */
.compo .bar{display:block;height:3px;background:var(--grad);margin-top:4px;min-width:2px}

/* measures device: three cells, filled = measure present. The count is
   printed beside it, so colour never carries the meaning alone. */
.gates{display:inline-flex;gap:2px;vertical-align:middle;margin-right:8px}
.gate{width:9px;height:16px;background:var(--hair-soft)}
.gate.on{background:var(--aqua-from)}

/* D1: the site's small-tag grammar (.case .tag): uppercase 11px .16em,
   gradient text, no border. Evidence chips take it straight; .grp takes
   the site's mute micro-label variant (.case-cap). Flat navy on light via
   the --grad-text remap, as everywhere. */
.tag{display:inline-block;font-family:var(--display);font-weight:500;font-size:11px;
  letter-spacing:.16em;text-transform:uppercase;white-space:nowrap;
  background-image:var(--grad-text);-webkit-background-clip:text;background-clip:text;color:transparent}
.tag.grp{background-image:none;color:var(--mute)}
.tag.fx{background-image:none;color:var(--aqua);border:1px solid var(--hair)}
.gkey{display:flex;gap:16px;flex-wrap:wrap;align-items:center;padding:14px 0 0}
.gkey .cap{font-family:var(--body);font-weight:400;font-size:13px;
  letter-spacing:0;text-transform:none;color:var(--mute)}

/* manual review: marked rows fade but stay readable on hover; the restore
   control stays at full strength so the way back is always visible. The
   mark is a control of the family at the cell's size: a 28px square with
   the fill and the edge, never transparent. */
tr.dim td{opacity:.45}
tr.dim:hover td{opacity:.75}
tr.dim td.relc{opacity:1}
.relc{width:1%;text-align:center}
.rel{width:28px;height:28px;padding:0;cursor:pointer;display:inline-flex;align-items:center;justify-content:center}
/* the review column's help mark: marks are per-browser, and the header
   is where a reviewer will look for that */
.relh{cursor:help}
.qm{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;
  border:1px solid var(--hair);color:var(--mute);font-family:var(--display);font-weight:500;
  font-size:11px;line-height:1;letter-spacing:0}
th.relh:hover .qm{border-color:var(--bone-dim);color:var(--bone-dim)}

.dls{display:flex;gap:12px;flex-wrap:wrap;align-items:center;margin-top:clamp(28px,4vh,48px)}

/* ---------- drawer ---------- */
/* min(720px, 100vw) on desktop, full width under 800px (item 25c); the
   mute colour on the paper is the AA-lifted token; the padding moved
   into the sticky header and the body. */
.drawer{position:fixed;inset:0 0 0 auto;width:min(720px,100vw);
  background:var(--paper);color:var(--base);
  --ink:var(--paper);--ink-2:var(--light-grey);
  --hair:rgba(var(--base-rgb),.14);--hair-soft:rgba(var(--base-rgb),.07);
  --bone:var(--base);--bone-dim:rgba(0,0,0,.82);--aqua:var(--base);
  --mute:var(--mute-paper);--mute-2:var(--mute-paper);
  --grad-text:linear-gradient(90deg,var(--base),var(--base));
  border-left:1px solid rgba(var(--base-rgb),.14);
  transform:translateX(100%);transition:transform .3s var(--ease);
  /* Above the fixed nav (100): the panel is modal chrome, and the bar's
     backdrop blur otherwise smears the panel header. The scrim dims the
     bar along with the page. */
  overflow-y:auto;z-index:120;padding:0 0 72px}
/* Per the guide: headings are sentence case, so panel section labels take
   the heading recipe (600, negative tracking) at panel scale. The
   .16-.2em positive tracking belongs to the uppercase technical label
   alone, so the demoted micro-labels carry none of it.
   One type scale for the panel (2026-09-01), five sizes with a job each:
   title 26; section label 15, the site's h4; body 14 for every sentence,
   key-value pair and score row; meta 12 for labels, notes and the series
   table; micro 10 for axis and unit marks. The markup carries no
   font-size at all. */
.drawer{--t-title:26px;--t-label:15px;--t-body:14px;--t-meta:12px;--t-micro:10px}
.drawer .eyebrow{text-transform:none;font-weight:600;font-size:var(--t-label);letter-spacing:-.005em;color:var(--base)}
.drawer p,.drawer dl,.drawer .dirs,.drawer .decomp,.drawer .confp,
.drawer .decomp .mono,.drawer .confp .mono{font-size:var(--t-body)}
/* score-row labels sit on their row; the legend row above them is meta */
.drawer .decomp .cap,.drawer .confp .cap{font-size:var(--t-body);color:var(--mute)}
.drawer .cap,.drawer .decomp .cap.hd,.drawer .confp .cap.hd,.drawer .note,.drawer .sub,
.drawer .ser,.drawer .ser th,.drawer .ser td,.drawer .spark .hd .mono{font-size:var(--t-meta)}
.drawer .cap{text-transform:none;font-weight:500;letter-spacing:0}
.drawer .kick{color:var(--base)}
.drawer .note{color:var(--mute);margin-top:10px}
.drawer .ser th{text-transform:none;letter-spacing:0}
.drawer .ser .u,.drawer .spark .yl,.drawer .spark .xl{font-size:var(--t-micro)}
.drawer .dirs{list-style:none;display:grid;gap:7px}
.drawer .dirs a,.drawer .brief .links a,.drawer .nws-list a,.drawer .trk a,.drawer dd a,.drawer .ser a{
  color:var(--base);text-decoration:underline;text-underline-offset:3px;text-decoration-color:rgba(var(--base-rgb),.35)}
.drawer .dirs a:hover,.drawer .brief .links a:hover,.drawer .nws-list a:hover,.drawer .trk a:hover,.drawer dd a:hover,.drawer .ser a:hover{text-decoration-color:var(--base)}
.drawer.open{transform:none}
.drawer h3{font-size:var(--t-title)}
/* The drawer's sticky header (item 25c): the kick, the name, the number
   line and Close stay at the top while the body scrolls, shrinking to
   the name on one line once scrolled. A grid, with Close in a column of
   its own that the text never enters and the jump row on a row below
   both, so the kick never runs under Close (item 25b, fault 4) and nor
   does the jump row once scrolled (the source portal's review fault 5,
   not ported). The jump row under them lists the sections this company
   has, the one in view marked while scrolling. The body sits below in
   its own box. */
.drawer .dhead{position:sticky;top:0;z-index:3;background:var(--paper);padding:22px var(--pad) 0;border-bottom:1px solid var(--hair);
  display:grid;grid-template-columns:minmax(0,1fr) auto;column-gap:16px;align-items:start}
.drawer .dhead .x{grid-column:2;grid-row:1 / span 3;align-self:start}
.drawer .dhead .kick{grid-column:1;grid-row:1;display:block}
.drawer .dhead h3{grid-column:1;grid-row:2;margin-top:8px}
.drawer .dhead .num{grid-column:1;grid-row:3;margin-bottom:10px}
.drawer .dhead .jump{grid-column:1 / -1;grid-row:4}
.drawer.scrolled .dhead{padding-top:10px}
.drawer.scrolled .dhead .kick,.drawer.scrolled .dhead .num{display:none}
.drawer.scrolled .dhead h3{grid-row:1;font-size:var(--t-label);line-height:var(--ctrl-h);margin:0 0 6px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.drawer.scrolled .dhead .x{grid-row:1}
.drawer.scrolled .dhead .jump{grid-row:2}
.drawer .jump{display:flex;gap:2px 16px;flex-wrap:wrap;padding:0 0 10px;list-style:none;margin:0}
.drawer .jump a{display:inline-block;font-family:var(--display);font-weight:500;font-size:var(--t-meta);letter-spacing:0;color:var(--mute);
  padding:4px 0;border-bottom:2px solid transparent;text-decoration:none}
.drawer .jump a:hover{color:var(--base)}
.drawer .jump a[aria-current=true]{color:var(--base);border-bottom-color:var(--base)}
.drawer .dbody{padding:0 var(--pad)}
/* no table inside the drawer is wider than it (item 25b, fault 5): the
   tender history, the public buyer's notices and the financial series
   each scroll inside their own box with the first column pinned; under
   800px the pinned column is capped so the other columns can be seen */
.drawer .serwrap{overflow-x:auto;max-width:100%}
.drawer .ser th:first-child,.drawer .ser td:first-child{position:sticky;left:0;top:auto;background:var(--paper);z-index:1}
@media(max-width:800px){
  .drawer .ser th:first-child,.drawer .ser td:first-child{max-width:40vw;overflow:hidden;text-overflow:ellipsis}
}
.drawer section{padding:20px 0;border-top:1px solid var(--hair);margin-top:18px}
.drawer section:first-of-type{border-top:0;margin-top:14px}
.drawer dl{display:grid;grid-template-columns:auto 1fr;gap:7px 18px}
.drawer dt{color:var(--mute)}
.drawer dd{color:var(--bone-dim)}
/* the drawer in reading order (item 25c, port 4): In brief opens with the
   two link lines (the website, the LinkedIn page, each with its search
   fallback), then the company overview, the summary and the Companies
   House figures under .cap subtitles; People holds the directors under
   a subtitle; the role chip links the list filtered to it */
.drawer .brief .links{margin-top:12px;display:grid;gap:4px}
.drawer .brief .ovw,.drawer .brief .sumry,.drawer .brief .chouse{margin-top:16px}
.drawer .brief .ovw p{margin-top:3px}
.drawer .brief .ovw .quotes{font-style:italic}
.drawer .brief .chouse dl{margin-top:8px}
.drawer .ppl .dirsec{margin-top:12px}
.drawer .dirsec .dirs{margin-top:8px}
.drawer .rolesec .chips{margin-top:10px}
.drawer .nws-list{margin:4px 0 0;padding-left:18px}
.drawer .nws-list li{margin:2px 0}
.drawer .tnh table{margin-top:0}
.drawer .tnh .serwrap{margin-top:10px}
.drawer .tnh .serwrap+.serwrap{margin-top:14px}
/* the tender tables (the tender history, the public buyer's notices) hold
   words, not figures: the series table's right-aligned, unwrapping cells
   pushed an incumbent's name and a notice's title out of the box, so a
   public body's drawer showed a Notice column with nothing in it until
   the table was scrolled sideways (2026-11-06). Text cells read left and
   wrap in the body face; the date and the platform keep one line; a
   Value cell keeps its inline right alignment and the figure face. */
.drawer .tnh .ser th{text-align:left}
.drawer .tnh .ser td{text-align:left;white-space:normal;font-family:var(--body);font-weight:400;vertical-align:top}
.drawer .tnh .ser td:first-child,.drawer .tnh .ser td.u{white-space:nowrap}
.drawer .tnh .ser td[style*="text-align:right"]{font-family:var(--display);font-weight:500;white-space:nowrap}
.sc{display:inline-block;padding:2px 8px;margin:1px 4px 1px 0;border:1px solid var(--hair);border-radius:0;white-space:nowrap}
.drawer .sc{border-color:rgba(var(--base-rgb),.35);color:var(--base)}
a.sc{cursor:pointer}
a.sc:hover{border-color:var(--bone)}
.drawer a.sc:hover{border-color:var(--base)}
.scrim{position:fixed;inset:0;background:rgba(var(--base-rgb),.72);z-index:110;display:none}
.scrim.on{display:block}

/* score decomposition rows */
.decomp{display:grid;grid-template-columns:110px 1fr 66px 66px;gap:8px 14px;
  font-size:13.5px;align-items:center}
.decomp .cap{font-size:10px}
/* block, as the table's bar above: 6px of the flat base colour on the
   white panel, a missing measure's bar at the hairline */
.decomp .db{display:block;height:6px;background:var(--base);min-width:2px}
.decomp .imputed .db{background:var(--hair)}
.decomp .mono{font-family:var(--display);font-weight:500;font-variant-numeric:tabular-nums lining-nums;font-size:12.5px;text-align:right}
.decomp .imputed{color:var(--mute)}

/* confidence parts */
.confp{display:grid;grid-template-columns:88px 1fr 52px;gap:7px 14px;font-size:13px;align-items:center}
.confp .db{display:block;height:5px;background:var(--aqua);min-width:2px}
.confp .mono{font-family:var(--display);font-weight:500;font-variant-numeric:tabular-nums lining-nums;font-size:12.5px;text-align:right}

/* series table in the drawer */
.ser{width:100%;border-collapse:collapse;font-size:12.5px}
.ser th{position:static;background:none;border-bottom:1px solid var(--hair);
  padding:6px 8px;font-size:9.5px;cursor:default}
.ser td{padding:6px 8px;border-right:0;font-family:var(--display);font-weight:500;
  font-variant-numeric:tabular-nums lining-nums;
  font-size:12px;text-align:right;white-space:nowrap}
.ser td:first-child{text-align:left;font-family:var(--body)}
.ser tr.grp td{color:var(--mute)}
.ser .u{font-family:var(--display);font-size:9px;color:var(--mute);letter-spacing:.06em}

/* ---------- method / honesty section (light) ---------- */
.method{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:1px;
  background:var(--hair);border:1px solid var(--hair)}
.method>div{background:var(--ink);padding:36px 28px;position:relative;overflow:hidden;
  transition:background .4s ease,transform .5s var(--ease),box-shadow .5s var(--ease)}
.method>div::before{content:'';position:absolute;top:0;left:0;width:0;height:2px;
  background:var(--grad);transition:width .6s var(--ease)}
.method>div:hover{background:var(--ink-2);transform:translateY(-4px);
  box-shadow:0 24px 60px -30px rgba(0,0,0,.6)}
.method>div:hover::before{width:100%}
/* Body copy starts level across a row: each card is a two-row subgrid
   (title, copy), so the title track is as tall as the tallest title in
   that row and a one-line title leaves the same space as a two-line one.
   Without subgrid support the title takes a two-line floor instead. */
.method>div{display:grid;grid-template-rows:subgrid;grid-row:span 2}
@supports not (grid-template-rows:subgrid){.method h4{min-height:2.3em}}
/* D2: titles and copy take the site .case recipe. */
.method h4{font-size:clamp(28px,3vw,42px);line-height:1.15;letter-spacing:-.005em;margin-bottom:16px}
.method p{font-size:15px;line-height:1.55;max-width:none}
/* A card grid whose rows come out full (2026-10-08). auto-fit at 280px
   gave four columns at 1,440, so the six Interpretation cards left two
   empty cells, a block of the grid's hairline colour. .k6 is three
   across, two from 1,000px down, one on a phone. */
.method.k6{grid-template-columns:repeat(3,1fr)}
@media(max-width:1000px){.method.k6{grid-template-columns:repeat(2,1fr)}}
@media(max-width:680px){.method.k6{grid-template-columns:1fr}}

.docfoot{padding:44px 0 0;display:grid;gap:14px}
.docfoot .sig{font-family:var(--display);font-weight:500;font-size:11px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--mute);display:flex;justify-content:space-between;gap:20px;
  flex-wrap:wrap;padding-top:22px;border-top:1px solid var(--hair-soft);margin-top:8px}

.err{border:1px solid rgba(217,83,79,.5);padding:18px 22px;max-width:70ch}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation:none!important;transition:none!important}
}
@media(max-width:820px){
  .tw{max-height:70vh}
}

/* ---------- methodology flow (light sheet) ----------
   The approved diagram, rebuilt live: navy for the value spine, light grey
   for structure, aqua border on confidence only, gradient withheld -
   permanent elements on white take flat navy. */
.flow{max-width:920px;margin:0 auto}
.fgrid2{display:grid;grid-template-columns:1fr 1fr;gap:14px}
.m3{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}
.fcard{border:1px solid var(--hair);background:var(--light-grey);padding:18px 22px;text-align:center}
.fcard h5{font-family:var(--display);font-weight:600;font-size:16px;color:var(--base);margin-bottom:5px}
.fcard p{font-size:13.5px;line-height:1.55;color:var(--bone-dim);max-width:none;margin:0}
.fcard .fcap{display:block;margin-top:10px;font-size:9.5px}
.fcard.dark{background:var(--base);border-color:var(--base)}
.fcard.dark h5{color:var(--paper)}
.fcard.dark p{color:rgba(255,255,255,.8)}
.fcard.measure{background:var(--paper);border:1px solid var(--base)}
.fcard.conf{background:var(--paper);border:1.5px solid var(--aqua)}
.farr{width:1px;height:26px;background:var(--base);margin:10px auto;position:relative}
.farr::after{content:'';position:absolute;left:-4.5px;bottom:-9px;
  border:5px solid transparent;border-top:9px solid var(--base);border-bottom:0}
.wbar{display:flex;height:36px;border:1px solid var(--base);margin-top:14px}
.wbar>span{display:block}
.wlabs{display:flex;margin-top:8px}
.wlabs .cap{text-align:center}
/* The note takes the flow's width (2026-10-08): under p's 74ch cap it sat
   in a box at the flow's left, centred off the flow's axis, and its new
   wording, the page's own "sits at the middle of the list", wrapped. */
.wnote{text-align:center;margin:16px 0 2px;max-width:none}
.sides{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:8px}
.sides .up{width:1px;height:24px;margin:0 auto 8px;position:relative;
  background:repeating-linear-gradient(180deg,var(--base) 0 4px,transparent 4px 8px)}
.sides .up::after{content:'';position:absolute;left:-4.5px;top:-1px;
  border:5px solid transparent;border-bottom:9px solid var(--base);border-top:0}
@media(max-width:680px){.fgrid2,.m3,.sides{grid-template-columns:1fr}}

/* validation callout on the light sheet: the accent number sits in a navy
   block with gradient glyphs - the approved case-fig construction. */
.vthesis{display:grid;grid-template-columns:auto 1fr;gap:30px;align-items:center;
  border:1px solid var(--hair);background:var(--paper);padding:26px 30px;margin-top:44px}
.vthesis .zero{font-family:var(--display);font-weight:600;line-height:.9;
  font-size:clamp(56px,7vw,104px);letter-spacing:-.02em;
  background:var(--base);padding:.15em}
.vthesis .zero span{background-image:var(--grad);-webkit-background-clip:text;
  background-clip:text;color:transparent}
@media(max-width:680px){.vthesis{grid-template-columns:1fr}}

/* ---------- drawer: summary + sparklines ---------- */
.sumry{display:grid;gap:12px}
.sumry p{margin-top:3px}
.spark{border:1px solid var(--hair);padding:10px 14px 8px;margin-bottom:10px}
.spark .hd{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:4px}
.spark .hd .mono{font-family:var(--display);font-weight:500;font-variant-numeric:tabular-nums lining-nums;font-size:12px;color:var(--bone-dim)}
.spark svg{width:100%;height:56px;display:block}
.spark path{fill:none;stroke:var(--base);stroke-width:1.5}
.spark circle{fill:var(--base)}
.spark circle.od{fill:var(--paper);stroke:var(--base);stroke-width:1.2}
.spark circle.hit{fill:transparent;stroke:none}
.spark .plot{position:relative}
.spark .yl{position:absolute;left:4px;font-family:var(--display);font-weight:500;
  font-size:9px;color:var(--mute);background:rgba(255,255,255,.85);padding:0 3px;
  font-variant-numeric:tabular-nums lining-nums;pointer-events:none}
.spark .yl.t{top:1px}
.spark .yl.b{bottom:1px}
.spark .xl{display:flex;justify-content:space-between;font-family:var(--display);
  font-weight:500;font-size:9px;color:var(--mute);margin-top:2px}
.tipbox{position:fixed;transform:translate(-50%,-100%);background:var(--paper);
  color:var(--base);border:1px solid var(--base);padding:4px 9px;
  font-family:var(--display);font-weight:500;font-size:12px;
  font-variant-numeric:tabular-nums lining-nums;white-space:nowrap;
  pointer-events:none;opacity:0;z-index:130;transition:opacity .12s ease}
.tipbox.on{opacity:1}

/* ---------- the main website's footer, from globals.css ---------- */
.footwrap{position:relative;background:var(--ink)}
footer{padding:60px var(--pad) 40px;display:grid;
  grid-template-columns:1fr auto 1fr;gap:32px clamp(48px,8vw,140px);
  align-items:end;font-family:var(--display);font-weight:500;font-size:15px;
  color:var(--mute);position:relative;z-index:1}
footer .foot-left{display:flex;flex-direction:column;gap:8px}
footer .foot-logo{margin-bottom:16px}
footer .foot-logo .wordmark{font-size:32px}
footer .foot-mid{text-align:center}
footer .foot-right{text-align:right;display:flex;flex-direction:column;gap:8px}
footer .foot-right a{transition:color .3s ease}
footer .foot-right a:hover{color:var(--bone)}
@media(max-width:700px){
  footer{grid-template-columns:1fr}
  footer .foot-mid{text-align:left}
  footer .foot-right{text-align:left}
  footer .foot-logo .wordmark{font-size:28px}
}
@media(max-width:500px){
  footer{padding-top:48px;padding-bottom:32px}
  footer .foot-logo .wordmark{font-size:24px}
}

/* ---------- profile builder (profile.html) ---------- */
.pb-head{display:flex;justify-content:space-between;align-items:flex-end;gap:24px;
  flex-wrap:wrap;margin-bottom:clamp(28px,4vh,44px)}
.pb-count{font-family:var(--display);font-weight:600;font-size:clamp(40px,6.5vw,96px);
  line-height:1.02;letter-spacing:-.015em;font-variant-numeric:tabular-nums lining-nums;
  background-image:var(--grad);-webkit-background-clip:text;background-clip:text;color:transparent}
.pb-actions{display:flex;gap:12px;flex-wrap:wrap;align-items:center}
.pb-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:1px;
  background:var(--hair);border:1px solid var(--hair)}
.pb-grid+.pb-grid{margin-top:24px}
.pb-block{background:var(--ink);padding:26px 24px}
.pb-block h3{margin-bottom:6px}
.pb-block h4{margin-bottom:12px}
.pb-block .cap{display:block;margin-bottom:14px;text-transform:none;letter-spacing:0;
  font-family:var(--body);font-weight:400;font-size:13px}
.chips{display:flex;flex-wrap:wrap;gap:8px}
/* chips: resting, the raised surface's fill with the mute edge (the fill
   alone barely reads against the base, so the edge carries it);
   selected, the accent sweep on the edge, the paper as text and
   aria-pressed, never colour alone; the count in the raised surface's
   mute. A chip is never wider than its row: its text ellipsises in its
   own span, the count keeps its place, the full text is the chip's
   title (the source portal's review fault 3, not ported). A row of more
   than eight shows the eight largest by count and a "+N more" chip. */
.chip{display:inline-flex;align-items:center;gap:8px;height:var(--chip-h);padding:0 12px;cursor:pointer;white-space:nowrap;
  border:1px solid var(--ctrl-edge);background:var(--ink-2);color:var(--bone-dim);--mute:var(--mute-2);max-width:100%}
.chip>.t{min-width:0;overflow:hidden;text-overflow:ellipsis}
.chip>.n{flex:0 0 auto}
.chip:hover{border-image:var(--grad) 1;color:var(--bone)}
.chip.on,.chip[aria-pressed=true]{border-image:var(--grad) 1;color:var(--bone)}
.chip .n{font-variant-numeric:tabular-nums lining-nums;color:var(--mute);font-size:12px;letter-spacing:0}
.chip.on .n{color:var(--aqua)}
.chip.more{color:var(--bone)}
.pb-add{display:flex;gap:8px;flex-wrap:wrap;margin-top:14px}
.pb-add input{width:120px}
.pb-add input#rlabel{width:180px}
.pb-wide{width:100%;max-width:420px}
.pb-list div{display:flex;justify-content:space-between;gap:16px;padding:7px 0;
  border-bottom:1px solid var(--hair-soft);font-size:14px;color:var(--bone-dim)}
.pb-list .mono{font-family:var(--display);font-weight:500;font-variant-numeric:tabular-nums lining-nums;
  color:var(--bone)}

/* ---------- the tender views (tenders.html) ---------- */
/* The count tiles are the page's view switch, a tablist: a grid of as
   many columns as tiles on show (--tiles, set by the page), each tile
   drawing its own hairline and the grid no background, so a short row
   leaves the page showing and never a grey slab (item 25b, fault 3);
   two of half from 1,200px down, two columns under 800px; the selected
   tab carries the menu's accent rule and aria-selected. */
.tn-counts{display:grid;grid-template-columns:repeat(var(--tiles,4),1fr);gap:1px;padding:1px;margin-top:14px}
.tn-count{display:flex;flex-direction:column;gap:6px;align-items:flex-start;min-width:0;
  padding:16px 14px;background:var(--ink);border:none;outline:1px solid var(--hair);color:var(--bone-dim);cursor:pointer;text-align:left;
  font:inherit;position:relative;transition:background .2s ease}
.tn-count[hidden]{display:none}
.tn-count b{font-family:var(--display);font-weight:600;font-size:clamp(26px,2.6vw,44px);line-height:1;
  color:var(--bone);font-variant-numeric:tabular-nums lining-nums}
.tn-count .cap{white-space:normal}
.tn-count:hover{background:var(--ink-2);--mute:var(--mute-2)}
.tn-count.on,.tn-count[aria-selected=true]{background:var(--ink-2)}
.tn-count[aria-selected=true]::after{content:'';position:absolute;left:0;right:0;bottom:0;height:2px;background:var(--grad)}
.tn-count.on .cap{color:var(--bone)}
.tn-count:focus-visible{outline:2px solid var(--aqua);outline-offset:-2px;z-index:1}
@media(max-width:1200px){.tn-counts{grid-template-columns:repeat(var(--tiles-half,2),1fr)}}
.tn-scope{max-width:110ch;margin:18px 0;font-size:14px;color:var(--mute)}
/* the page's prose, collapsed: the first sentence stays, the rest behind
   "How to read this page" */
.tn-scope .howto{display:inline}
.tn-scope .howto summary{display:inline-block;cursor:pointer;list-style:none;font-family:var(--display);font-weight:500;font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--bone);padding:2px 0;border-bottom:1px solid var(--ctrl-edge);margin-left:6px}
.tn-scope .howto summary::-webkit-details-marker{display:none}
.tn-scope .howto summary::after{content:' +'}
.tn-scope .howto[open] summary::after{content:' \2212'}
.tn-scope .howto p{font-size:inherit;color:inherit;max-width:none;margin-top:8px;line-height:1.6}
.tn-chiprow{display:flex;gap:14px;align-items:baseline;flex-wrap:wrap;margin:10px 0}
.tn-chiprow>.chips{flex:1 1 auto;min-width:0}
.tn-chiprow>.cap{min-width:84px}
.tn-cap{display:block;font-family:var(--body);font-size:11px;color:var(--mute);letter-spacing:0;white-space:nowrap}
.tn-empty{padding:26px 12px;color:var(--mute)}
.tn-link{border-bottom:1px solid var(--hair);transition:border-color .2s ease,color .2s ease}
.tn-link:hover{color:var(--bone);border-color:var(--aqua)}
.ct-co{color:var(--bone)}
.tn-role.tn-other,.tn-role.tn-unknown{background-image:none;color:var(--mute)}
.tn-role.tn-competitor{background-image:none;color:var(--no)}
.tag.tn-approved{background-image:none;color:var(--aqua);border:1px solid var(--hair);margin-left:6px}
tr.tn-exp{cursor:pointer}
tr.tn-exp:hover td{color:var(--bone)}
tr.tn-buyers td{background:var(--ink-2);color:var(--bone-dim);font-size:13.5px;white-space:normal;line-height:1.7}
tr.tn-buyers small{color:var(--mute)}

/* ---------- the phone (item 25c, the source portal's item 24) ---------- */
@media(max-width:800px){
  :root{--ctrl-h:44px}
  /* the ranked list's filters: the search box above the table, every
     other control behind the Filters button in a full-height sheet with
     the controls stacked full width and "Show N companies" at its foot;
     the second row is always open there and the Columns select is not
     offered (the phone table is rank, company and composite). Nothing in
     the sheet shrinks: the sheet scrolls (the source portal's review
     fault 2, not ported) */
  .filters input[type=search]{min-width:0;flex:1 0 100%}
  .filters .fbtn{display:inline-flex}
  .filters .fsheet{display:none;position:fixed;top:var(--bar-h,68px);left:0;right:0;bottom:0;z-index:98;
    flex-direction:column;gap:10px;padding:16px var(--pad) 24px;background:var(--ink);overflow-y:auto}
  .filters.open .fsheet{display:flex}
  .filters .fsheet>*{width:100%;min-width:0;flex-shrink:0}
  .filters .fsheet .frow{flex-direction:column;align-items:stretch;order:0}
  .filters .fsheet .frow>*{width:100%;min-width:0}
  .filters .fsheet .frow.more[hidden]{display:flex}
  .filters .fsheet #fmore,.filters .fsheet #fcols{display:none}
  .filters .fsheet .rin{width:100%}
  .filters .showbtn{display:inline-flex;margin-top:auto;width:100%}
  /* the ranked list's table scrolls with the page: no inner scroller,
     no sideways scroll; the header sticks under the bar; the rows are
     rank, company (the number and town on a second line) and composite,
     the rest in the drawer */
  .tw.pin{max-height:none;overflow:visible;border-left:0;border-right:0}
  .tw.pin th{top:var(--bar-h,68px)}
  .tw.pin td.co{min-width:0;max-width:calc(100vw - 170px)}
  /* the tender page's table: no inner vertical scroller; the box scrolls
     sideways inside itself with the first column pinned and capped, so
     the other columns can be seen beside it (the source portal's review
     fault 6, not ported) and the page never scrolls sideways */
  .tw:not(.pin){max-height:none;overflow:auto}
  .tw:not(.pin) th:first-child,.tw:not(.pin) td:first-child{position:sticky;left:0;z-index:1;background:var(--ink);max-width:60vw;overflow:hidden;text-overflow:ellipsis}
  .tw:not(.pin) td.co{min-width:0}
  .tw:not(.pin) th:first-child{z-index:3}
  .tn-counts{grid-template-columns:repeat(2,1fr)}
  .drawer{width:100vw;border-left:0}
  /* the jump row on one line that scrolls sideways, bled to the drawer's
     edges, so the unscrolled header gives the body most of the screen */
  .drawer .jump{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;margin:0 calc(-1*var(--pad));padding:0 var(--pad) 10px}
  .drawer .jump::-webkit-scrollbar{display:none}
  .drawer .jump a{flex:0 0 auto}
  .pb-head{flex-direction:column;align-items:stretch}
  .pb-actions{width:100%}
  .pb-actions .btn,.pb-actions .btn-2{flex:1 1 auto}
}
