/* =============================================================================
   AssayBase — the shared component layer.
   Implements design-language.md v1.0 sections 4-8. Depends on tokens.css and
   contains no colour of its own.

   Plain-English: these are the reusable pieces every page is built from —
   buttons, inputs, chips, the line grid of data cells, stat tiles, charts,
   tables, bars, the authored-text block and the cite block. A new page writes
   plain HTML with these class names and inherits the whole design language
   without deciding anything.

   Ruling 3 matters most and is easy to get wrong: a "card" is NOT a floating
   box. It is a cell in a line grid, and neighbouring cells SHARE one 1px rule.
   That is what .cell-grid does — it paints itself the line colour and lets a
   1px gap show through, so no two borders can ever double up.
   ========================================================================== */

/* --------------------------------------------------- the two-tier site header
   Every page carries the same header, and it has two tiers.

   Tier 1 (.site-nav) is the site: the dot-grid mark and the AssayBase wordmark,
   the page links, and the "Request a list" ghost button. Everything in it sits
   on ONE text baseline — the row is a baseline-aligned flex row and every piece
   of type is set with line-height 1. Nothing is vertically centred.

   Tier 2 (.page-nav) is the page you are on: "On this page", the links to that
   page's own sections, and, on the weekly report, the issue and week on the
   right. It sticks to the top of the window while you scroll, and script.js
   marks the section you are looking at.

   Both tiers live here rather than in a page stylesheet because every page has
   them, including a brand-new page copied from _starter.template.html, which
   links only tokens.css and this file. */
/* The two tiers are wrapped in one <header> so BOTH stay pinned together as
   you scroll: the header itself is the sticky element (position:sticky on
   .sitehead), not Tier 2 alone. It needs its own opaque background so page
   content scrolling underneath does not show through the gap between the two
   tiers (Tier 1 and Tier 2 each still paint their own background too, for the
   colour split between them, but a transparent header would let a fast flick
   show one frame of daylight at the seam). z-index lifts it above sticky
   in-page elements (the highest of which, .page-nav on its own, no longer
   exists as a second sticky layer). */
.sitehead{position:sticky;top:0;z-index:60;background:var(--black)}
.sitehead a{text-decoration:none;color:inherit}

/* ---- Tier 1 ---- */
.site-nav{display:flex;align-items:baseline;gap:var(--sp-40);background:var(--black);
          padding:21px var(--head-pad) 19px;border-bottom:var(--bw) solid var(--line)}
/* The logo is one entity: mark and wordmark are centred on each other. But
   the row around them (.site-nav) is baseline-aligned, and the page links and
   the CTA must still sit on the wordmark's BOTTOM line, not on some centred
   mid-point of the logo. Flexbox lets one item in a baseline-aligned row opt
   out: .brand keeps align-items:baseline (so the wordmark, the one item here
   with a real text baseline, IS .brand's exported baseline — the thing
   .site-nav lines everything else up against), and only the dot mark
   overrides its own align-self to center, so it centres on the line's height
   without moving that exported baseline. */
.brand{display:flex;align-items:baseline;gap:var(--sp-12)}
/* The mark is nine dots, the middle one lit. No image, no font. align-self
   pulls it out of the baseline group above and centres it on the wordmark
   instead — see the comment on .brand. */
.dotmark{display:grid;grid-template-columns:repeat(3,5px);gap:3px;align-self:center}
.dotmark i{width:5px;height:5px;border-radius:50%;background:var(--muted-2);display:block}
.dotmark i.is-lit{background:var(--mint)}
.wordmark{font:700 19px/1 var(--display);letter-spacing:-.03em;color:var(--text)}
.wordmark em{font-style:normal;color:var(--mint)}

/* The page links. A flex row with a 28px gap, so six or seven of them fit the
   same way two do — no width is written down anywhere. */
.nav-pages{display:flex;align-items:baseline;gap:var(--sp-28);margin-right:auto;
           min-width:0;overflow-x:auto;scrollbar-width:none}
.nav-pages::-webkit-scrollbar{display:none}
.nav-page{font:500 14px/1 var(--mono);color:var(--muted);white-space:nowrap;
          transition:color var(--dur)}
.nav-page:hover{color:var(--mint)}
/* The page you are on. script.js puts aria-current on the matching link from
   the address bar, so the marker can never disagree with the page. */
.nav-page[aria-current="page"]{font-weight:600;color:var(--mint)}

.nav-cta{font:600 12px/1 var(--mono);letter-spacing:.04em;color:var(--mint);
         border:var(--bw) solid var(--mint);padding:10px 16px;white-space:nowrap;
         transition:var(--dur)}
.nav-cta:hover{background:var(--mint);color:var(--black)}

/* ---- Tier 2 ---- */
/* The row itself is centred rather than baseline-aligned for ONE reason: the
   links sit in a sideways-scrolling box, and a box that scrolls has no baseline
   to share. Inside that box the links are baseline-aligned as the design asks,
   and every piece of type here is 11-12px with line-height 1, so centred and
   baseline read the same to the eye. */
/* The row wraps only when it has to. The design drew the section links and the
   issue line on one line, and they fit — but the live issue line is longer than
   the one in the drawing, and a link disappearing off the right edge would be a
   bug, not a design. So when the two cannot share a line, the issue line drops
   underneath and every link stays readable. */
/* Tier 2 no longer sticks on its own — it is pinned as part of .sitehead
   above, which keeps both tiers stacked and visible together. It keeps its
   own background so the two tiers still read as two bands of colour. */
.page-nav{display:flex;align-items:center;
          justify-content:space-between;gap:var(--sp-24);flex-wrap:wrap;row-gap:var(--sp-8);
          padding:15px var(--head-pad);background:var(--panel);
          border-bottom:var(--bw) solid var(--line)}
.page-nav-scroll{display:flex;align-items:baseline;gap:var(--sp-24);min-width:0;
                 flex:1 1 auto;overflow-x:auto;scrollbar-width:none}
.page-nav-scroll::-webkit-scrollbar{display:none}
.page-nav-label{font:500 var(--fs-mono-label)/1 var(--mono);color:var(--muted-2);
                letter-spacing:.06em;white-space:nowrap}
.page-nav-link{font:500 var(--fs-mono-label)/1 var(--mono);color:var(--muted);
               white-space:nowrap;transition:color var(--dur)}
.page-nav-link:hover{color:var(--mint)}
.page-nav-link.is-current{color:var(--text)}
.page-nav-meta{font:500 var(--fs-mono-eyebrow)/1 var(--mono);color:var(--muted-2);
               white-space:nowrap;margin-left:auto}

/* A jump from Tier 2 must not land under the pinned header. Both tiers now
   stick together, and the header's real height moves around — Tier 1 wraps
   to two lines on a phone, and Tier 2's issue line can drop to a second line
   on the hiring page at some widths. A fixed pixel number could not track
   either, so script.js measures the live header and writes it into
   --header-h; the value below (roughly two lines of Tier 1 plus one line of
   Tier 2 at the desktop padding used above) is only the fallback for the
   instant before that first measurement runs, and for a page with no JS. */
:root{--header-h:110px}
[id]{scroll-margin-top:calc(var(--header-h) + var(--sp-12))}
html{scroll-behavior:smooth}

/* On a phone Tier 1 keeps the brand and the button on the first line and drops
   the page links onto their own line beneath; Tier 2 scrolls sideways, and its
   issue line steps aside so the pinned bar stays one thin line. Neither tier
   ever makes the page itself scroll sideways. */
@media(max-width:760px){
  .site-nav{flex-wrap:wrap;gap:var(--sp-16);padding:16px var(--head-pad) 14px}
  .nav-pages{order:1;flex-basis:100%;margin-right:0;gap:var(--sp-20)}
  .nav-cta{margin-left:auto;padding:9px 12px}
  .page-nav{padding:13px var(--head-pad)}
  .page-nav-meta{display:none}
}

/* ------------------------------------------------------------------ buttons */
.btn{display:inline-block;border:var(--bw) solid transparent;padding:14px 18px;
     font:600 var(--fs-mono-cta)/1 var(--mono);letter-spacing:.04em;
     text-align:center;cursor:pointer;transition:var(--dur)}
.btn--compact{padding:11px 14px}
.btn--primary{background:var(--mint);color:var(--black);border-color:var(--mint)}
.btn--primary:hover{background:var(--mint-pale);color:var(--black)}
.btn--ghost{background:transparent;border-color:var(--line-strong);color:var(--text)}
.btn--ghost:hover{border-color:var(--mint);color:var(--mint)}
.btn--on-mint{background:var(--black);color:var(--mint);border-color:var(--black)}
.btn--on-mint:hover{background:var(--ink)}
/* Disabled keeps opacity 1 on purpose: a faded button reads as "broken", a
   quiet one reads as "not yet". */
.btn:disabled,.btn[aria-disabled="true"]{opacity:1;border-color:var(--line);
     color:var(--muted-2);background:transparent;cursor:not-allowed}

/* ------------------------------------------------------------ inputs, forms */
.input{background:var(--ink);border:var(--bw) solid var(--line-strong);color:var(--text);
       padding:12px 14px;font:400 var(--fs-body-sm)/1.5 var(--sans);width:100%}
.input::placeholder{color:var(--muted-2)}
.input--hero{padding:17px 18px}
.on-mint .input{background:var(--input-on-mint);border-color:var(--black);color:var(--black)}
.on-mint .input::placeholder{color:var(--placeholder-on-mint)}

/* The one line a form writes after a submit. Its height is reserved so the
   layout never jumps when the answer arrives. */
.form-status{font:500 var(--fs-mono-label)/1.5 var(--mono);color:var(--mint);
             min-height:1px;margin:14px 0 0}
.form-status.is-error{color:var(--error)}
.on-light .form-status,.on-mint .form-status{color:var(--status-on-mint)}
.on-light .form-status.is-error,.on-mint .form-status.is-error{color:var(--error-on-light)}

.checkbox{display:inline-flex;align-items:center;gap:var(--sp-8);cursor:pointer;
          font:500 var(--fs-mono-label)/1.5 var(--mono);color:var(--text)}
.checkbox input[type=checkbox]{width:15px;height:15px;margin:0;flex:0 0 15px;
          accent-color:var(--mint);cursor:pointer}

/* ------------------------------------------------------------------- chips */
/* A chip's colour IS its series colour, set inline by hiring.js as --chip so the
   chip and the line it switches on can never disagree. */
.chip{background:transparent;border:var(--bw) solid var(--line);color:var(--muted);
      padding:7px 10px;font:500 var(--fs-mono-cta)/1 var(--mono);letter-spacing:.04em;
      cursor:pointer;transition:var(--dur)}
.chip:hover{border-color:var(--line-strong);color:var(--text)}
.chip.is-active{border-color:var(--chip,var(--mint));color:var(--chip,var(--mint));
      background:color-mix(in srgb,var(--chip,var(--mint)) 10%,transparent)}
.chip:disabled{color:var(--muted-2);border-color:var(--line);background:transparent;
      cursor:not-allowed}

/* ------------------------------------------------------- the cell line grid */
/* The grid paints itself --line and shows 1px of itself through every gap, so
   adjacent cells share exactly one rule and nothing ever doubles up. */
.cell-grid{display:grid;gap:var(--bw);background:var(--line);
           border:var(--bw) solid var(--line)}
.cell{background:var(--panel);padding:var(--cell-pad);display:flex;flex-direction:column}
.cell--compact{padding:var(--cell-pad-compact)}
.cell-grid--2{grid-template-columns:2fr 1fr}
.cell-grid--halves{grid-template-columns:1fr 1fr}
.cell-grid--3{grid-template-columns:repeat(3,1fr)}

/* §6.3 — the ONE floating object a page may have. The lint counts them. */
.cell--highlight{border:var(--bw) solid var(--mint);box-shadow:var(--shadow-pop)}

/* The pieces every cell is made of, top to bottom (§6.1, §6.2). */
.cell-eyebrow{font:600 var(--fs-mono-eyebrow)/1 var(--mono);letter-spacing:.12em;
              text-transform:uppercase;color:var(--mint);margin:0 0 10px}
.on-light .cell-eyebrow{color:var(--accent-on-light)}
.cell-stamp{font:500 var(--fs-mono-label)/1.5 var(--mono);color:var(--muted);margin:0 0 var(--sp-12)}
.cell-note{font:400 var(--fs-body-sm)/1.6 var(--sans);color:var(--muted);margin:10px 0 0}
.cell h2,.cell h3{margin:0}

/* §6.1 stat tile: the number and its delta share one baseline. */
.stat-row{display:flex;align-items:baseline;gap:var(--sp-12);flex-wrap:wrap}
.stat-xl{font:700 var(--fs-stat-xl)/1 var(--mono);letter-spacing:-.02em;color:var(--text)}
.stat-md{font:700 var(--fs-stat-md)/1 var(--mono);letter-spacing:-.02em;color:var(--text)}
.stat-xl.is-hero,.stat-md.is-hero{color:var(--mint)}   /* at most one per page */
.stat-basis{font:500 var(--fs-mono-label)/1.5 var(--mono);color:var(--muted)}

/* §8.1 deltas. Down is amber, never red. The class comes from Python
   (display_format.delta) so the page never picks a colour. */
.delta{font:500 var(--fs-mono-label)/1.5 var(--mono);white-space:nowrap}
.delta.is-up{color:var(--mint)}
.delta.is-down{color:var(--chart-2)}
.delta.is-flat{color:var(--muted)}
.on-light .delta.is-up{color:var(--accent-on-light)}
.on-light .delta.is-down{color:var(--down-on-light)}
.on-light .delta.is-flat{color:var(--body-on-light)}

/* §8.3 honesty conventions — never a blank, never a bare zero. */
.insufficient{font:500 14px/1.5 var(--mono);color:var(--muted)}
.coverage{font:600 var(--fs-mono-micro)/1 var(--mono);letter-spacing:.08em;color:var(--muted-2)}

/* ------------------------------------------------------------- data table */
.data-table{width:100%;border-collapse:collapse}
.data-table th{font:500 var(--fs-mono-eyebrow)/1.5 var(--mono);color:var(--muted);
     text-align:right;padding:6px 0;border-bottom:var(--bw) solid var(--line)}
.data-table th:first-child,.data-table td:first-child{text-align:left}
.data-table td{padding:10px 0;border-bottom:var(--bw) solid var(--line-soft);
     text-align:right;font:500 var(--fs-body-sm)/1.5 var(--mono);color:var(--text)}
.data-table td:first-child{font:400 14px/1.5 var(--sans)}
.data-table td.is-label{font:400 var(--fs-body-sm)/1.5 var(--sans);color:var(--muted)}
.data-table tbody tr:hover{background:var(--panel-2)}
.table-scroll{overflow-x:auto}

/* ------------------------------------------------------------- share bars */
.share-bars{display:grid;gap:var(--sp-8)}
.share-row{display:grid;grid-template-columns:150px 1fr 96px;align-items:center;gap:10px}
.share-row>span:first-child{font:400 var(--fs-body-sm)/1.5 var(--sans);color:var(--text)}
.share-track{height:10px;background:var(--panel-3)}
.share-fill{height:100%;background:var(--chart-1)}
.share-value{font:500 var(--fs-mono-label)/1.5 var(--mono);color:var(--muted);text-align:right}

/* ------------------------------------------- §6.6 authored text, §6.7 cite */
/* The 6px mint left border is the single documented exception to "borders are
   always 1px". It marks the one place a human voice appears. */
.authored{border-left:6px solid var(--mint)}
.authored p{font:400 var(--fs-body)/1.65 var(--sans);color:var(--text);max-width:70ch;margin:0}

/* Dashed mint reads as "free to take". Used only here. */
.cite-block{border:var(--bw) dashed var(--mint);background:transparent;padding:24px 28px;
      display:flex;gap:var(--sp-16);align-items:center;flex-wrap:wrap}
.cite-copy{flex:1;min-width:240px}
.cite-copy strong{display:block;font-weight:700;margin-bottom:var(--sp-4)}
.cite-copy p{font:400 var(--fs-body-sm)/1.6 var(--sans);color:var(--muted);margin:0}
.cite-form{display:flex;flex-direction:column;gap:var(--sp-8);align-items:flex-start}
.cite-form .cite-line{display:flex;gap:var(--sp-8);align-items:center;flex-wrap:wrap}
.cite-form .input{min-width:190px;width:auto}

/* ------------------------------------------------------------------ charts */
/* §7.1 anatomy. The SVG carries no colour of its own — every stroke and fill is
   one of these tokens, so the charts obey the design language for free and the
   lint can police them like any other element. Charts never animate. */
.chart{width:100%;height:auto;display:block;background:transparent}
.chart .grid-line{stroke:var(--line-soft);stroke-width:1}
.chart .axis-label{font:500 var(--fs-axis)/1 var(--mono);fill:var(--muted)}
.chart .series-line{fill:none;stroke-width:2;stroke-linecap:butt}
.chart .s1{stroke:var(--chart-1)}.chart .s2{stroke:var(--chart-2)}
.chart .s3{stroke:var(--chart-3)}.chart .s4{stroke:var(--chart-4)}
.chart .bar.s1{fill:var(--chart-1);stroke:none}
.chart .bar.s2{fill:var(--chart-2);stroke:none}
.chart .bar.s3{fill:var(--chart-3);stroke:none}
.chart .bar.s4{fill:var(--chart-4);stroke:none}
.chart .ref-line{stroke:var(--muted-2);stroke-width:1;stroke-dasharray:4 4}
.chart .ref-label{font:600 var(--fs-mono-micro)/1 var(--mono);letter-spacing:.08em;fill:var(--muted-2)}
.chart-empty{display:flex;flex-direction:column;justify-content:center;gap:var(--sp-8)}

.chart-legend{display:flex;flex-wrap:wrap;gap:18px;margin-top:var(--sp-12)}
.chart-legend span{display:inline-flex;align-items:center;gap:var(--sp-8);
      font:500 var(--fs-mono-label)/1.5 var(--mono);color:var(--muted)}
.chart-legend i{width:10px;height:10px;display:inline-block}
.chart-legend i.s1{background:var(--chart-1)}.chart-legend i.s2{background:var(--chart-2)}
.chart-legend i.s3{background:var(--chart-3)}.chart-legend i.s4{background:var(--chart-4)}

.chart-tip{position:absolute;pointer-events:none;background:var(--ink);
      border:var(--bw) solid var(--line);padding:10px 12px;z-index:5;display:none}
.chart-tip .tip-label{font:500 var(--fs-mono-label)/1.5 var(--mono);color:var(--muted)}
.chart-tip .tip-row{display:flex;align-items:center;gap:var(--sp-8);
      font:500 var(--fs-mono-label)/1.5 var(--mono);color:var(--text)}
.chart-tip .tip-row i{width:8px;height:8px;display:inline-block}
.chart-tip .tip-row i.s1{background:var(--chart-1)}.chart-tip .tip-row i.s2{background:var(--chart-2)}
.chart-tip .tip-row i.s3{background:var(--chart-3)}.chart-tip .tip-row i.s4{background:var(--chart-4)}
.chart-wrap{position:relative}

@media(prefers-reduced-motion:reduce){
  .btn,.chip{transition:none}
  /* A visitor who asked for less motion gets an instant jump, not a glide. */
  html{scroll-behavior:auto}
  .nav-page,.nav-cta,.page-nav-link{transition:none}
}
