/* Self-hosted. Generated by fonts.py; 5 faces, nothing external.
   5 font-display declarations rewritten swap -> optional. */
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-display: optional;
  font-weight: 300 700;
  src: url(/f/space-grotesk-latin-wght-normal.woff2) format('woff2-variations');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-display: optional;
  font-weight: 400;
  src: url(/f/ibm-plex-mono-latin-400-normal.woff2) format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-display: optional;
  font-weight: 500;
  src: url(/f/ibm-plex-mono-latin-500-normal.woff2) format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-display: optional;
  font-weight: 600;
  src: url(/f/ibm-plex-mono-latin-600-normal.woff2) format('woff2');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}
@font-face {
  font-family: 'Newsreader';
  font-style: italic;
  font-display: optional;
  font-weight: 200 800;
  src: url(/f/newsreader-latin-standard-italic.woff2) format('woff2-variations');
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}

/* No @import. This file used to open by fetching three typefaces from Google,
   the only external request the site made -- on a site whose argument is that it
   depends on nobody. build.py now injects @font-face rules for exactly the woff2
   files present in f/, so a missing download degrades to the fallback stack
   rather than to three 404s. Run `python3 fonts.py` once to populate f/. */
:root{
  /* GENERATED from cruxvine.py -- do not restate these values. */
  --ash:#9FB2AC; --ash-rgb:159,178,172;
  --chalk:#EDF3F1; --chalk-rgb:237,243,241;
  --claret:#F2557A; --claret-rgb:242,85,122;
  --faint:#7E938D; --faint-rgb:126,147,141;
  --garnet:#A31840; --garnet-rgb:163,24,64;
  --ice:#2EC4D6; --ice-rgb:46,196,214;
  --ice-dk:#0F4742; --ice-dk-rgb:15,71,66;
  --line:#283331; --line-rgb:40,51,49;
  --line2:#3B4A47; --line2-rgb:59,74,71;
  --oak:#96632F; --oak-rgb:150,99,47;
  --panel:#121716; --panel-rgb:18,23,22;
  --panel2:#1D2524; --panel2-rgb:29,37,36;
  --steel:#607672; --steel-rgb:96,118,114;
  --straw:#F5C84B; --straw-rgb:245,200,75;
  --void:#090C0B; --void-rgb:9,12,11;
  --liq-red:#F2557A; --liq-red-deep:#CA365D; --liq-red-top:#F2557A; --men-red:#FFD9E4;
  --liq-smoke:#96632F; --liq-smoke-deep:#96632F; --liq-smoke-top:#B18049; --men-smoke:#E8CFAE;
  --liq-white:#F5C84B; --liq-white-deep:#C59B30; --liq-white-top:#F5C84B; --men-white:#FFF0C4;
  --liq-red-rgb:242,85,122;
  --scale-lo:70; --scale-hi:100;
  /* ONE constant for one physical fact. The sticky header's height was written
     three times -- 58px implicitly by its own padding, 70px in .sechead's
     scroll-margin, 74px in the landmark rule -- and the last two already
     disagreed. Anything that has to clear the header reads this. */
  --navh:58px; --clearnav:calc(var(--navh) + 16px);
  /* EVERY colour on this site is generated into the block above from
     cruxvine.py. There are no hex literals and no rgb()/rgba() literals below
     this line -- a tint is `rgba(var(--chalk-rgb),.045)`, never the channels
     typed out again. t_dry.py fails the build on either.
     The tokens this file used to define here were a SECOND author for the three
     that cruxvine.py already emitted, which is the defect class this build spends
     most of its assertions on. */

  /* SELECTION IS NOT A DATA VALUE. Straw meant five things at once -- white-wine
     producer, active filter chip, above-median delta, rank gold, locked vintage --
     so on the mobile home the yellow ALL/ANY chips sat directly above yellow bars
     meaning "white-wine producer", and the legend became unlearnable. Data colours
     mean data ONLY. And under the organising rule a control is INSTRUMENT, so the
     pressed state is not merely off-series, it is cold: an ice-dark face with ice
     ink and edge, 4.99:1, on a face that is itself 1.49 against the card beneath.
     An earlier draft put a garnet face on a pressed chip -- warm, on a control,
     in violation of the one rule the palette exists to state. */
  --sel:var(--ice-dk); --sel-ink:var(--ice); --sel-edge:var(--ice);

  /* THE TYPE SCALE.
     MONO-CAPS IS FOR NUMBERS AND UNITS. It was for everything: 56 tracked
     uppercase labels at <=12.5px outside the SVG and 392 more inside it. Every
     kicker, section subtitle, meta line, column head, status and footer word was
     11px / .16em / uppercase / IBM Plex Mono, so the page was wallpapered in
     labels -- and that texture, not any missing decoration, is most of what read
     as "dashboard" and "plain". A sentence set in 11px tracked caps is also
     simply slower to read than the same sentence set in 13px sentence case.
     --t-lab  stays mono-caps: axis notes, column heads, unit suffixes, controls.
     --t-note is prose that happens to be small: section subtitles, wayfinding. */
  --t-lab:11px; --t-note:13px;
}
*{box-sizing:border-box;margin:0;padding:0}
body{background:var(--void);color:var(--chalk);
  font-family:'Space Grotesk',-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
  line-height:1.55;-webkit-font-smoothing:antialiased}
::selection{background:var(--ice-dk);color:var(--chalk)}
a{color:inherit}
.mono{font-family:'IBM Plex Mono',ui-monospace,monospace;font-variant-numeric:tabular-nums}
.verdict{font-family:'Newsreader',Georgia,serif;font-style:italic;font-weight:300;color:var(--chalk)}

/* ---------- chrome ---------- */
/* The nav is OPAQUE. It was rgba(var(--void-rgb),.92), which is a smear pane, not a
   surface: at 8% transparency over the flight the specimen slot's numerals
   printed straight through METHODOLOGY and WHO PAYS US, and a whole ghost link
   row bled through the header on the no-JS flight. Both were in the delivered
   screenshot set, uncaught, because every suite measured geometry and ink and
   none of them measured whether the ink was legible where it landed. A sticky
   bar's job is to be the thing in front. */
/* `body > nav`, not `nav`. The bare element selector claimed EVERY <nav> on the
   site, and the moment a second one appeared -- the sticky question index -- it
   inherited the site header's height, flex layout, background, border and
   `top:0`, and rendered as a 58px bar over the page chrome. A selector that says
   "the navigation" when it means "the site header" is a bug waiting for its
   second instance. */
body > nav{position:sticky;top:0;z-index:40;display:flex;align-items:center;justify-content:space-between;
  height:var(--navh);padding:0 30px;background:var(--void);border-bottom:1px solid var(--line)}
.brand{display:flex;align-items:center;gap:10px;font-weight:600;letter-spacing:.16em;font-size:13px;
  text-transform:uppercase;text-decoration:none}
.brand svg{width:19px;height:19px}
.navlinks{display:flex;gap:24px;align-items:center;font-size:13.5px}
.navlinks a{color:var(--ash);text-decoration:none}
.navlinks a:hover{color:var(--chalk)}
.navlinks a.here{color:var(--chalk)}
.wrap{max-width:1340px;margin:0 auto;padding:0 30px}
/* The token moves and the bar moves with it; nothing else needs to know. A
   second constant for the same physical fact is how 70px and 74px came to
   disagree about a 58px header. t_legible measures the rendered nav at both
   widths and fails if it is not exactly --navh. */
/* THE MOBILE NAV USED TO BE DELETED, NOT REPLACED. The rule was
   `.navlinks a:not(.cta){display:none}` and nav() never emits a `.cta`, so the
   exception matched nothing and all four links vanished below 760px -- leaving a
   phone with no navigation at all except the footer, 3,900px down. The money page
   was unreachable from the header on the majority device class.
   There are four links. They do not need a menu: they need a second row. No JS,
   no disclosure state, works with scripting off, and idle-identical. */
@media(max-width:760px){
  :root{--navh:88px}
  body > nav{padding:0;flex-direction:column;align-items:stretch;justify-content:flex-start}
  .brand{height:46px;padding:0 16px;flex:0 0 auto}
  .navlinks{gap:0;height:42px;border-top:1px solid var(--line);
    overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}
  .navlinks::-webkit-scrollbar{display:none}
  .navlinks a{flex:0 0 auto;display:flex;align-items:center;height:42px;padding:0 16px;
    font-size:11px;white-space:nowrap}
  .wrap{padding:0 16px}
}
@media(max-width:760px){ input[type=search]{min-width:0} }
/* Every control a thumb is asked to hit clears the platform floor. The selects
   were 33px and the reset link 14px -- a link styled as a control, sized as text. */
select,.cctl select{min-height:44px;padding-top:0;padding-bottom:0}
button,.lbt,[role=button]{min-height:44px}
#reset,.resetlink{display:inline-flex;align-items:center;min-height:44px;padding:0 6px}

h1{font-size:clamp(30px,5.4vw,72px);font-weight:600;letter-spacing:-.038em;line-height:1.02}
.kicker{font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.26em;text-transform:uppercase;color:var(--ash)}
.lede{color:var(--ash);font-size:15.5px;max-width:660px;margin-top:10px}
.lede b{color:var(--chalk);font-weight:500}
footer{border-top:1px solid var(--line);margin-top:70px;padding:26px 30px;max-width:1340px;
  margin-left:auto;margin-right:auto;display:flex;justify-content:space-between;flex-wrap:wrap;gap:14px;
  font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint)}

/* ---------- the console bezel ----------
   Controls and state are one piece of hardware, physically attached to the
   field below them. Count, median, range and ordering are stated ONCE, here. */
.console{margin-top:22px;border:1px solid var(--line);border-radius:12px;background:var(--panel);
  overflow:visible}
.cbez{padding:10px 14px}
.cctl{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
.cctl label{font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--faint);margin-right:2px}
select,input[type=search]{font-family:'Space Grotesk',sans-serif;font-size:13.5px;color:var(--chalk);
  background:var(--panel2);border:1px solid var(--line2);border-radius:7px;padding:7px 10px;outline:none}
select:focus,input[type=search]:focus{border-color:var(--ice)}
/* The search field grows into whatever the row has left. It was a fixed 190px,
   which was enough until the two chip groups became segmented switches and took
   their padding: the placeholder then clipped mid-word at 1440, which is exactly
   the failure the `data-short` mobile placeholder exists to avoid on phones. */
input[type=search]{min-width:190px;flex:1 1 220px;max-width:340px}
input[type=search]::placeholder{color:var(--faint)}
input[type=search]{min-height:44px}
.chips{display:flex;gap:5px}
.chip{font-family:'IBM Plex Mono',monospace;font-size:12px;letter-spacing:.1em;text-transform:uppercase;
  /* 29px tall at 7px/10.5px, against a 44px iOS and 48px Android floor. A control
     the platform considers too small to hit reliably is not a control. */
  min-height:44px;padding:12px 15px;display:inline-flex;align-items:center;
  border-radius:7px;border:1px solid var(--line2);color:var(--ash);
  background:var(--panel2);cursor:pointer}
/* Pressed uses --sel, which belongs to no data series. It was --straw, which on the
   mobile home put yellow ALL/ANY chips directly above yellow bars that mean
   "white-wine producer" -- a control state wearing a data value's clothes. */
.chip[aria-pressed=true]{color:var(--sel-ink);background:var(--sel);
  border-color:var(--sel-edge);font-weight:600}
.chip.red[aria-pressed=true]{background:var(--sel);border-color:var(--sel-edge);color:var(--sel-ink)}
/* THE SEGMENTED SWITCH (fx.js). `.seg` is added by script, so everything below
   applies only where the thumb actually exists; a no-JS reader keeps the plain
   pressed-chip face above and loses nothing. */
.chips.seg{position:relative;display:inline-flex;gap:0;padding:3px;
  border:1px solid var(--line2);border-radius:10px;background:var(--panel)}
.chips.seg .chip{position:relative;z-index:1;border-color:transparent;background:transparent;
  border-radius:7px}
/* `.seg` is SERVED, `.thumbed` is not.
   The track -- border, padding, zero gap -- must exist at first paint or the
   control row grows 8px the moment fx.js runs, which is a layout change after
   first paint on a site that refuses them. But the pressed chip may only give up
   its own face once something is there to replace it, so the transparent face is
   keyed to `.thumbed`, which the script adds when it inserts the thumb. A no-JS
   reader gets the machined track AND a visible pressed chip. */
.chips.seg.thumbed .chip[aria-pressed=true],
.chips.seg.thumbed .chip.red[aria-pressed=true]{background:transparent;border-color:transparent;
  color:var(--sel-ink)}
.chips.seg .chip:hover{color:var(--chalk)}
.segthumb{position:absolute;left:0;top:0;z-index:0;border-radius:7px;opacity:0;
  background:var(--sel);border:1px solid var(--sel-edge);pointer-events:none;
  transition:transform 140ms cubic-bezier(.2,.8,.2,1),
             width 140ms cubic-bezier(.2,.8,.2,1),
             height 140ms cubic-bezier(.2,.8,.2,1)}
.segthumb.noanim{transition:none}
@media(prefers-reduced-motion:reduce){ .segthumb{transition:none!important} }
/* the status strip: one authoritative sentence about what is on the bench */
.cstate{display:flex;gap:26px;align-items:flex-end;flex-wrap:wrap;justify-content:flex-end;
  padding:7px 14px 8px;border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  background:rgba(var(--void-rgb),.35);
  font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--faint)}
.cshint{margin-right:auto;align-self:center;color:var(--faint);font-size:var(--t-note);font-family:'Space Grotesk',-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;letter-spacing:0;text-transform:none}
@media(max-width:760px){.cshint{display:none}}
.cs{display:flex;flex-direction:column;align-items:flex-end;line-height:1.05}
.csv{display:flex;align-items:center;gap:6px}
/* THE STRIP RESERVES THE ODOMETER'S BOX BEFORE THE ODOMETER EXISTS.
   The served markup carries plain text; odo.js replaces it with a wheel whose
   window, digit and translate unit are all exactly 1.22em (see the ledger's
   registration invariant). Plain text in the same font-size sat at the default
   ~1.55 line box, so the status strip grew 3px the moment the script ran -- a
   layout change after first paint, below the fold, which is exactly the class of
   thing this build refuses everywhere else. Declaring the same 1.22em here means
   the box the odometer lands in is the box that was already there. */
.cs b{color:var(--chalk);font-weight:600;font-size:15px;letter-spacing:.01em;
  display:inline-block;line-height:1.22}
.cs > i{font-style:normal;margin-top:4px}
.cs.by b{color:var(--straw);font-size:11.5px;letter-spacing:.09em;text-transform:uppercase}
.cstate.left{justify-content:flex-start;border:0;background:none;padding:14px 0 0}
.cstate.left .cs{align-items:flex-start}
.tiehint{font-family:'Space Grotesk',-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
  letter-spacing:0;text-transform:none;font-size:var(--t-note);color:var(--faint);margin-top:9px}
@media(max-width:760px){.cstate{gap:14px;justify-content:flex-start}.cs b{font-size:13px}}
.reset{font-size:12.5px;color:var(--faint);background:none;border:none;cursor:pointer;text-decoration:underline}
.reset:hover{color:var(--chalk)}


/* ---------- specimen slot (reserved: never shifts) ---------- */
.spec{min-height:150px;padding:12px 14px 0;
  display:grid;grid-template-columns:1fr auto auto;gap:20px;align-items:start}
.spec .nm{font-size:19px;font-weight:600;letter-spacing:-.015em}
/* The meta line is reserved at TWO lines in both states. The idle appellation
   line ("Paso Robles AVA · est. 1983 · 11 districts · 40,000 acres planted ·
   614,000-acre appellation") wraps to two below ~900px while a producer's wraps to
   one, and that 18px difference was the whole of the shift that moved the field
   under the pointer -- not the verdict box I had been rewriting. Reserving both
   lines keeps every character and moves nothing; the clamp is a floor for widths
   narrower than anything measured here. */
.spec .meta{font-family:'IBM Plex Mono',monospace;font-size:11.5px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--faint);margin-top:4px;overflow:hidden}
/* RESERVED BY MEASUREMENT, NOT BY GUESS.
   Three rounds were spent moving this number by feel -- two lines, then two
   below 900, then below 980, then one everywhere, which held "nothing moves" by
   deleting the sentence "every character survives" was supposed to protect.
   These three regimes come from measuring, at every width, how many lines each
   state's longest string actually needs. t_seam asserts BOTH halves now: the
   field does not move when read, and neither state is ever ellipsised -- so a
   wrong boundary is a failing suite rather than a silently swallowed fact. */
.spec .meta{height:calc(11.5px * 1.55 * 2)}
@media(max-width:620px){ .spec .meta{height:calc(11.5px * 1.55 * 3)} }
@media(min-width:1100px){ .spec .meta{height:calc(11.5px * 1.55)} }
/* And again against the CONTAINER, which is what actually decides how many lines
   the sentence takes. These win because they are later and equally specific.
   The boundaries are re-measured for the new column, not carried over: t_seam
   walks the widths and fails on an ellipsis or a moved field either way. */
@container specbox (max-width: 1000px){ .spec .meta{height:calc(11.5px * 1.55 * 2)} }
@container specbox (max-width: 430px){ .spec .meta{height:calc(11.5px * 1.55 * 3)} }
@container specbox (min-width: 1001px){ .spec .meta{height:calc(11.5px * 1.55)} }

/* The verdict line is RESERVED, not fitted. Verdicts vary from one line to two
   at this width, and letting the box size to its content made the whole flight
   below it move 1.44px as the pointer swept -- the field is bedrock, and a
   reflow under the pointer is the rule this instrument was built around. Two
   lines are reserved because two is the measured maximum here (143 verdicts take
   one line, 44 take two); t_odo fails if the flightbox moves at all. */
/* At rest the height is released: no pointer is over the field, so nothing can be
   displaced by the slot growing. The moment a producer is read the reservation is
   back and the flightbox is bedrock again. */
/* THE READOUT IS A FIXED BOX IN BOTH STATES, AND IT NEVER AMPUTATES.
   Three attempts converged here. Reserving two lines cut the idle appellation
   blurb mid-word with no ellipsis. Releasing the height at rest fixed the cut and
   introduced something worse -- the first sweep swapped a three-line idle box for
   a two-line live one and moved the flightbox 22px UNDER THE POINTER, which is the
   one thing the reservation exists to prevent. Reserving three lines below 900px
   just moved the seam to 900.
   A reservation whose size depends on the content is a reservation that will
   always be wrong at some width. So the box is fixed, identically, in both states,
   and the text is clamped with a real ellipsis instead of being sliced: the field
   cannot move, and no sentence ends mid-word. The full blurb is one click away,
   where it has room -- the readout already links to it. */
.spec .vd{margin-top:8px;font-size:15.5px;max-width:760px;line-height:1.45;
  height:calc(15.5px * 1.45 * 2);overflow:hidden;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
.spec .num{font-family:'IBM Plex Mono',monospace;font-size:30px;font-weight:600;color:var(--straw);line-height:1}
.spec .numlab{font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.2em;
  text-transform:uppercase;color:var(--faint);margin-top:5px;text-align:right}
.spec .col{text-align:right}
.spec .col.price .num{color:var(--chalk)}
.spec.idle .nm{color:var(--ash);font-weight:500}
.spec.idle .num{color:var(--ash)}
.spec.idle .col.price .num{color:var(--ash)}
.spec .go{font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--ice);margin-top:9px;display:inline-block;text-decoration:none;border-bottom:1px solid transparent}
.spec .go:hover{border-bottom-color:var(--ice)}
@media(max-width:760px){.spec{grid-template-columns:1fr auto}.spec .col.price{display:none}}
/* IN THE HERO THE DISPLAY IS A COLUMN, NOT A BANNER.
   Three columns -- text, median, top -- works at 1240px and crowds at 500. The
   two numerals drop under the text instead of squeezing it, and the verdict gets
   a third line back, because at this width two lines clipped the appellation
   blurb mid-sentence. Both states reserve the same box at the same width, which
   is the invariant; how many lines that box is may follow the column. */
@container specbox (max-width:760px){
  .spec{grid-template-columns:auto auto;grid-template-areas:"t t" "a b";
    justify-content:start;gap:12px 34px}
  .spec .specL{grid-area:t;min-width:0}
  .spec .col{grid-area:a;text-align:left}
  .spec .col.price{grid-area:b;display:block;text-align:left}
  .spec .numlab{text-align:left}
  .spec .vd{font-size:14.5px;-webkit-line-clamp:3;height:calc(14.5px * 1.45 * 3)}
  .spec .nm{font-size:17px}
}
/* A phone's column is half again narrower than the hero's, and the appellation
   blurb takes four lines there. The box is still FIXED and still identical in
   both states at any given width -- which is the invariant -- it is just a taller
   fixed box where the sentence is longer. */
@container specbox (max-width:430px){
  /* line-height is stated here rather than inherited, so the reserved height and
     the clamp are computed from the SAME number. Inherited 1.45 against a serif
     italic's own metrics resolved to ~1.50, and four lines then needed 87px in an
     84px box -- three pixels of a sentence, cut. */
  .spec .vd{-webkit-line-clamp:4;line-height:1.45;height:calc(14.5px * 1.45 * 4 + 2px)}
}

/* ---------- the flight ---------- */
.flightbox{margin-top:2px;position:relative;padding:0 14px 12px}
.scroller{overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}
svg.flight{display:block}
.grid line{stroke:var(--line2);stroke-width:1}
.grid text{fill:var(--faint);font-family:'IBM Plex Mono',monospace;font-size:9.5px;letter-spacing:.14em}
.grid text.dlab{font-size:8.5px;letter-spacing:.1em;fill:var(--ash)}
.grid line.dtick{stroke:var(--line2)}
/* The median is a datum, not a gridline: it is the one line every vessel is read
   against, so it is drawn in the reference colour and labelled with its value. */
.grid line.med{stroke:var(--straw);stroke-dasharray:2 4;opacity:.55}
.grid text.medlab{fill:var(--straw);text-anchor:end;font-size:10px;letter-spacing:.16em;opacity:.9}
/* A 1px inset top highlight on every bounded surface. Static, costs nothing, and
   is the whole of what "machined" looks like without a single moving pixel. */
/* ---- ONE LIGHT SOURCE ---------------------------------------------------
   315 degrees: above and to the left, at 45. Every bounded surface on the site
   takes a 1px highlight on its top and left edges and a 1px shade on its bottom
   and right, from that one direction. Before this the panels had a top highlight
   and nothing else -- light from directly above, which is no direction at all --
   and the vessel caps were lit from a third place. Two pixels per surface, no
   blur, no gradient, nothing that moves: this is the whole of what "machined"
   costs when the ground is doing the work.
   `--bezel` is one declaration so the angle cannot drift between surfaces; a
   second author for a light source is how a page comes to have two suns. */
:root{
  --bezel: inset 1px 1px 0 rgba(var(--chalk-rgb),.055),
           inset -1px -1px 0 rgba(var(--void-rgb),.55);
}
.console,.instrument,.av,.way,.qi,.col-item,.ihfield,.ihR .spec,.cbez,.tbl,
.vtrack,.wbar,.bvglass,.minives,.skves{box-shadow:var(--bezel)}
/* The answers band has no panels to bezel -- it is columns divided by a rule --
   so it takes the etched groove instead. Same light, different object. */
.ways.band{box-shadow:none}
.ways.band .way{box-shadow:none}
/* AN ETCHED RULE, not a drawn one. A hairline on a dark ground reads as a line
   painted on; a hairline with a single lighter pixel under it reads as a groove
   cut into the surface, which is what a divider on an instrument is. Applied
   where a rule separates two things the reader is meant to compare. */
.tiles,.ways.band,.qnav li,.qbody .qi,.sechead{
  box-shadow:0 1px 0 rgba(var(--chalk-rgb),.030)}
.tiles,.ways.band{box-shadow:0 -1px 0 rgba(var(--chalk-rgb),.030)}
.emptystate{display:none;position:absolute;left:50%;top:38%;transform:translate(-50%,-50%);text-align:center}
.emptystate p{font-size:15px;color:var(--ash)}
.emptystate button{margin-top:12px;font-family:'IBM Plex Mono',monospace;font-size:12px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--sel-ink);background:var(--sel);
  border:1px solid var(--sel-edge);border-radius:6px;
  min-height:44px;padding:12px 18px;cursor:pointer}
.base{stroke:var(--line2);stroke-width:1}
.ves{cursor:pointer}
.ves .ghost{fill:rgba(var(--chalk-rgb),.045)}
.ves .men{fill:var(--men-red);opacity:.85}
.ves.on .ghost{fill:rgba(var(--chalk-rgb),.10)}
.ves.on .outline{stroke:var(--chalk);stroke-width:1;fill:none;vector-effect:non-scaling-stroke;opacity:.9}
.ves .outline{stroke:none;fill:none}
.ves.dim{opacity:.55}
.axisnote{font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--faint);margin-top:10px;display:flex;justify-content:space-between}
.flightbox:focus-visible{outline:1px solid var(--ice);outline-offset:6px}
/* .hint is the section subtitle and it is ALWAYS a sentence -- "187 producers,
   one vessel each, filled to its score", "Delta is against this district's median
   of 84", "Region-wide median by year". Twenty-one of them, every one set in
   11px tracked mono-caps. The markup still carries `mono` beside it; this rule is
   later and wins, so the case change needed no touch to twenty-one call sites. */
.hint{font-family:'Space Grotesk',-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
  font-size:var(--t-note);letter-spacing:0;text-transform:none;color:var(--faint);font-weight:400}

/* motion: the ONLY star. user-initiated, one-shot. */
.ves{transition:transform 480ms cubic-bezier(.2,.8,.2,1),opacity 240ms linear}
.liq{transition:transform 480ms cubic-bezier(.22,1.10,.36,1)}
/* The load is still. While #flight carries .seating -- from before the first
   layout until a frame after the last load-time measurement -- neither property
   may travel, so a deep-linked view seats at its restored geometry instead of
   sliding into it. Nothing removes .seating except that frame. */
#flight.seating .ves,#flight.seating .liq{transition:none}
/* A DEEP-LINKED FIELD IS NOT SHOWN UNTIL IT IS RIGHT.
   `restoring` is stamped by the four-line script in <head> when the URL carries a
   view parameter, and removed by flight.js the instant the field has been drawn
   in that view. visibility, not display: the box still reserves its space, so
   there is no shift when it appears, and no-JS readers never receive the class.
   See cruxvine.py head_js() for why: first paint measured on both sides of the
   correction, so half of deep-linked readers were shown the wrong field first. */
html.restoring #flight{visibility:hidden}
@media(prefers-reduced-motion:reduce){
  .ves,.liq{transition:none!important}
  *{animation:none!important}
}

/* ---------- flat sections ---------- */
section{padding:44px 0}
.sechead{display:flex;justify-content:space-between;align-items:baseline;gap:20px;flex-wrap:wrap;
  border-bottom:1px solid var(--line);padding-bottom:12px;margin-bottom:26px}
.sechead h2{font-size:22px;font-weight:600;letter-spacing:-.02em}
.cols{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(250px,100%),1fr));gap:26px}
.col-item .h{font-size:15px;font-weight:600;color:var(--straw);margin-bottom:6px}
.col-item p{font-size:14px;color:var(--ash);line-height:1.55}
.tbl{width:100%;border-collapse:collapse;font-size:14px}
.tbl th{text-align:left;font-family:'IBM Plex Mono',monospace;font-size:11px;letter-spacing:.16em;
  text-transform:uppercase;color:var(--faint);font-weight:500;padding:0 12px 10px 0;border-bottom:1px solid var(--line)}
.tbl td{padding:11px 12px 11px 0;border-bottom:1px solid var(--line);color:var(--ash);vertical-align:top}
.tbl tr:hover td{background:rgba(var(--chalk-rgb),.018)}
.tbl a{color:var(--chalk);text-decoration:none;font-weight:600}
.tbl a:hover{color:var(--ice)}
.num{font-family:'IBM Plex Mono',monospace;text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}
/* A NUMERIC HEADER SITS OVER ITS NUMBERS. `.tbl th{text-align:left}` is one class
   plus one type and beats a bare `.num`, so every numeric column on the site had
   its heading pinned to the left of a right-aligned column of figures -- readable
   on a two-column table and plainly wrong on a five-column one, where the eye
   reads each value under the heading of the column to its left. */
.tbl th.num{text-align:right}
.gold{color:var(--chalk)}

/* ---------- winery profile: the vessel system, everywhere a number lives ---------- */
.crumb{font-size:var(--t-note);font-family:'Space Grotesk',-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;letter-spacing:0;text-transform:none;color:var(--faint);padding:26px 0 18px}
.crumb a{color:var(--ice);text-decoration:none}
.whead{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:44px;align-items:center;
  border-bottom:1px solid var(--line);padding-bottom:26px}
.chips2{font-size:var(--t-note);font-family:'Space Grotesk',-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;letter-spacing:0;text-transform:none;color:var(--faint);margin-top:10px}
.verdict.big{font-size:20px;line-height:1.5;margin-top:18px;max-width:640px;
  border-left:2px solid var(--line2);padding-left:18px}
.mstrip{font-size:var(--t-note);font-family:'Space Grotesk',-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;letter-spacing:0;text-transform:none;color:var(--faint);margin-top:18px}
.mstrip b{color:var(--chalk);font-weight:600}
/* GLASS AND NUMERAL SIDE BY SIDE.
   Stacked, the right column was 268px of glass plus a 120px numeral plus its
   label -- taller than everything in the left column put together -- so at
   align-items:center the page opened a 120px hole under the stats and the two
   halves of the header stopped reading as one object. Side by side they are the
   same object they are on every card on the site: a level, and its number. */
.bigves{display:flex;align-items:center;justify-content:flex-end;gap:26px;text-align:left}
.bvread{display:flex;flex-direction:column;align-items:flex-start}
.bigves .bvnum{margin-top:0}
.bvglass{position:relative;width:74px;height:268px;margin:0 auto;border:1px solid var(--line2);
  border-radius:4px 4px 12px 12px;background:rgba(var(--chalk-rgb),.035);overflow:visible}
.bvliq{position:absolute;left:0;right:0;bottom:0;background:var(--liq-red);border-radius:0 0 11px 11px}
.bvliq.wht{background:var(--liq-white)}
.bvliq.wht .bvmen{background:var(--men-white)}
.skliq.wht{background:var(--liq-white);border-top-color:var(--men-white)}
.bvmen{position:absolute;left:0;right:0;top:0;height:2px;background:var(--men-red);opacity:.9}
.bvg80,.bvg90{position:absolute;left:0;right:0;height:0;border-top:1px dashed rgba(var(--chalk-rgb),.28)}
.bvg80 i,.bvg90 i{position:absolute;right:calc(100% + 7px);top:-7px;font-family:'IBM Plex Mono',monospace;
  font-size:9px;color:var(--faint);font-style:normal;letter-spacing:.1em}
.bvnum{font-size:clamp(56px,8vw,120px);font-weight:600;color:var(--straw);margin-top:10px;line-height:.92;letter-spacing:-.03em}
.bvlab{font-size:11px;letter-spacing:.2em;text-transform:uppercase;color:var(--faint);margin-top:5px}
@media(max-width:820px){.whead{grid-template-columns:1fr;gap:28px}.bigves{justify-content:flex-start}
  /* The glass keeps a left inset so its own axis numerals have somewhere to be.
     At margin:0 they hung at right:calc(100% + 7px) -- i.e. off the screen. */
  .bvglass{margin:0 0 0 30px}}

.vials{display:flex;flex-direction:column;gap:14px;max-width:760px}
.vrow{display:grid;grid-template-columns:150px 1fr 34px;gap:16px;align-items:center}
.vlab{font-size:14px;color:var(--ash)}
.vtrack{height:16px;border:1px solid var(--line2);border-radius:4px;background:rgba(var(--chalk-rgb),.03);overflow:hidden;display:block}
.vfill{display:block;height:100%;background:var(--liq-red);border-right:2px solid var(--men-red)}
.vfill.wht{background:var(--liq-white);border-right-color:var(--men-white)}
.vnum{font-size:13px;color:var(--chalk);text-align:right}
@media(max-width:600px){.vrow{grid-template-columns:110px 1fr 30px;gap:10px}}

.win{max-width:820px}
.wintrack{position:relative;height:30px;border:1px solid var(--line2);border-radius:5px;
  background:rgba(var(--chalk-rgb),.03)}
.windrink{position:absolute;top:0;bottom:0;background:color-mix(in srgb, var(--liq-red) 42%, transparent)}
.winpeak{position:absolute;top:0;bottom:0;background:var(--liq-white)}
.winnow{position:absolute;top:-6px;bottom:-6px;width:2px;background:var(--chalk)}
.winnow i{position:absolute;top:-15px;left:50%;transform:translateX(-50%);font-size:8.5px;
  letter-spacing:.16em;color:var(--chalk);font-style:normal}
.winscale{display:flex;justify-content:space-between;margin-top:7px;font-size:11px;color:var(--faint)}
.winkey{display:flex;gap:22px;margin-top:11px;font-size:11px;color:var(--ash);letter-spacing:.06em}
.winkey i{display:inline-block;width:11px;height:11px;border-radius:2px;margin-right:7px;vertical-align:-1px}
.winkey .kd{background:color-mix(in srgb, var(--liq-red) 42%, transparent)} .winkey .kp{background:var(--liq-white)}

.skyline{display:flex;gap:9px;align-items:flex-end}
.sk{flex:1;text-align:center}
.skves{height:152px;border:1px solid var(--line2);border-radius:3px 3px 5px 5px;
  background:rgba(var(--chalk-rgb),.03);position:relative;overflow:hidden}
.skliq{position:absolute;left:0;right:0;bottom:0;background:var(--liq-red);border-top:1.5px solid var(--men-red)}
.skliq.warn{background:var(--liq-smoke);border-top-color:var(--men-smoke)}
.sky{font-size:12.5px;color:var(--chalk);margin-top:7px;font-weight:600}
.skn{font-size:11px;color:var(--ash);margin-top:2px}
.foot{font-size:12px;color:var(--faint);margin-top:16px;letter-spacing:.04em}

.pager{display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;
  border-top:1px solid var(--line);padding-top:22px;margin-top:60px;
  font-family:'IBM Plex Mono',monospace;font-size:11.5px;letter-spacing:.06em}
.pager a{color:var(--ash);text-decoration:none}
.pager a:hover{color:var(--chalk)}

/* ---------- region ranking ---------- */
.tbl td.rk{color:var(--faint);width:44px;font-size:12.5px}
.tbl .sub{font-size:12.5px;font-family:'Space Grotesk',-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;letter-spacing:0;text-transform:none;color:var(--faint);margin-top:3px}
.tbl .vd{font-size:14px;color:var(--ash);margin-top:6px;max-width:640px;line-height:1.45}
.tbl .sc{color:var(--straw);font-weight:600;font-size:16px}
.cell-v{width:26px}
.minives{display:block;width:11px;height:44px;border:1px solid var(--line2);border-radius:2px 2px 3px 3px;
  background:rgba(var(--chalk-rgb),.03);position:relative;overflow:hidden}
.miniliq{position:absolute;left:0;right:0;bottom:0;background:var(--liq-red);border-top:1px solid var(--men-red)}
.miniliq.wht{background:var(--liq-white);border-top-color:var(--men-white)}
.tablewrap{max-height:none}
/* the region's facts are the same value-over-label column as every console
   strip, hairline-separated. One vocabulary, top to bottom. */
.cstate.facts{gap:0;margin-top:22px;border-top:1px solid var(--line);border-bottom:1px solid var(--line);
  padding:15px 0}
.cstate.facts .cs{padding:0 26px;border-left:1px solid var(--line)}
.cstate.facts .cs:first-of-type{padding-left:0;border-left:0}
@media(max-width:760px){.cstate.facts{flex-wrap:wrap;gap:14px 0}.cstate.facts .cs{padding:0 16px}}
.avas{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(196px,100%),1fr));gap:10px;margin-top:8px}
.av{display:block;border:1px solid var(--line);border-radius:8px;padding:13px 15px;background:var(--panel);
  text-decoration:none;color:inherit}
.av:hover{border-color:var(--ice)}
.av:focus-visible{outline:2px solid var(--ice);outline-offset:2px}
.avr{display:flex;flex-wrap:wrap;gap:5px 14px;align-items:center;margin-top:9px;padding-top:9px;
  border-top:1px solid var(--line);font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--faint)}
.avr span{display:flex;align-items:center;gap:5px}
.avr b{color:var(--chalk);font-size:12.5px;font-weight:600;letter-spacing:0}
.av .an{font-size:14px;font-weight:600}
/* A seven-column table cannot fit a 320px phone at a legible size, and scrolling
   it breaks the anchors. So the two columns a reader can most afford to lose --
   the value sub-score and the delta against the median, both of which are on the
   producer's own page and in the published dataset -- step out below 560px. The
   rank, the name, the vessel, the score and the price stay. */
@media(max-width:560px){
  .tbl .c-opt{display:none}
  /* ...and the cells give up their padding before the page gives up its width.
     Every remaining overflow was a table sitting at its own min-content width:
     long mono field names that would not break, and 14px of padding per cell
     across six columns. Both are cheaper to lose than a page that slides
     sideways under a thumb. */
  .tbl th,.tbl td{padding-left:6px;padding-right:6px}
  .tbl th:first-child,.tbl td:first-child{padding-left:0}
  .tbl th:last-child,.tbl td:last-child{padding-right:0}
}
.tbl th,.tbl td{overflow-wrap:anywhere}
.tbl .num{overflow-wrap:normal}
.av .ad{font-family:'IBM Plex Mono',monospace;font-size:11.5px;color:var(--faint);margin-top:3px}
/* every score gets a vessel — including aggregate readouts */
.inlv{display:inline-block;width:8px;height:15px;border:1px solid var(--line2);border-radius:2px;
  background:rgba(var(--chalk-rgb),.03);position:relative;overflow:hidden;vertical-align:-3px;margin-right:6px}
.inlv i{position:absolute;left:0;right:0;bottom:0;background:var(--liq-red);border-top:1px solid var(--men-red)}

/* On a phone the specimen docks BELOW the strip: the flight stays at a fixed
   place near the top of the viewport and the verdict can grow without moving it. */
@media(max-width:760px){
  .instrument{display:flex;flex-direction:column}
  .flightbox{order:1}
  .spec{order:2;min-height:0;border-top:none;border-bottom:1px solid var(--line);
        padding-top:0;padding-bottom:16px;margin-top:16px;grid-template-columns:1fr auto}
  .spec .vd{font-size:15px}
  .cctl{gap:8px}
  .cctl label{display:none}
  select,input[type=search]{font-size:12.5px;padding:6px 8px}
  input[type=search]{min-width:140px;flex:1}
  h1{font-size:30px}
  .lede{font-size:14.5px}

}

.hint.coarse,.cshint.coarse{display:none}
@media(hover:none),(pointer:coarse){ .hint.fine,.cshint.fine{display:none} .hint.coarse{display:inline} .cshint.coarse{display:inline} }

/* ---------- THE LENS: optics, not animation ---------- */
.lenslabel{fill:var(--faint);font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:.5em;
  transition:opacity .25s}
.lenswin{fill:none;stroke:var(--line2);stroke-width:1}
.xh{stroke:var(--men-red);stroke-width:1;opacity:.55}
/* THE CROSSHAIR'S HORIZONTAL ARM AND ITS READING.
   Cold, because it is the instrument reading the sample and not the sample. It
   is drawn only while a hand is on the field and erased when the hand leaves;
   nothing here transitions, so there is no state in which it is moving on its
   own. The value sits in the axis gutter in the same voice as the 80/90 labels,
   one weight up, so the eye finds it without being pulled to it. */
.xhh{stroke:var(--ice);stroke-width:1;opacity:.9;stroke-dasharray:2 3}
.xhv{fill:var(--ice);font-family:'IBM Plex Mono',ui-monospace,monospace;font-size:10px;
  font-weight:600;letter-spacing:.04em}

/* THE THRESHOLD.
   The grip is the axis gutter: invisible, 22px minimum, and a real slider. It
   shows its affordance in the cursor rather than in ink, because a permanent
   vertical bar down the left of the field would be one more thing on a chart
   whose whole argument is that it is uncluttered. */
.thgrip{fill:transparent;cursor:ns-resize;touch-action:none}
.thgrip:focus{outline:none}
.thgrip:focus-visible{outline:2px solid var(--ice);outline-offset:2px}
.thl{stroke:var(--straw);stroke-width:1.5;opacity:.95}
.thv{fill:var(--straw);font-family:'IBM Plex Mono',ui-monospace,monospace;font-size:11px;
  font-weight:600;letter-spacing:.02em}
/* Below the line a producer is still drawn, still hit-testable and still in the
   count -- it is out of the ANSWER, not out of the data. Dimming rather than
   hiding is the difference between a filter and a reading. */
.ves.sub{opacity:.22}
#axl.lit{color:var(--straw)}

/* THE CALIPERS. Two datum lines at two locked values, a dimension line across
   the gap, and the delta on it. Ice, because the instrument is measuring; the
   two vessels themselves keep the lock tint. Nothing here transitions: the lines
   are where the two locked producers are and stay there until a hand releases
   them, exactly as the threshold does. */
.cald{stroke:var(--ice);stroke-width:1;opacity:.75;stroke-dasharray:4 3}
.caldim{stroke:var(--ice);stroke-width:1.5;opacity:.9}
.calcap{stroke:var(--ice);stroke-width:1.5;opacity:.9}
.calv{fill:var(--ice);font-family:'IBM Plex Mono',ui-monospace,monospace;font-size:11.5px;
  font-weight:600;letter-spacing:.02em}
/* The full sentence -- both names, both values, the delta and the count between --
   goes in the display's reserved delta line, which is a fixed box, so printing a
   longer string there cannot move the field. */
.spec .delta.cal{color:var(--ice);letter-spacing:.03em}

/* ---- EDGE LIGHT (edge.js) ------------------------------------------------
   Light that tracks the hand is permitted. Light that breathes is not.
   The highlight is on the BORDER, not the face: a lit face washes out the text
   sitting on it, and the border is where a bounded surface's edge already is.
   The mask is the standard two-layer xor -- paint the whole box, then punch out
   everything inside the 1px ring -- so the gradient becomes a border and cannot
   tint the content. No transition on any property here, deliberately: the light
   is at the pointer's position and nowhere else, in the same frame. */
.av,.qi,.col-item,.way,.tile,.acb{position:relative}
/* A faint wash on the FACE as well as the ring. Four per cent is below the
   threshold at which it competes with body text -- measured against --chalk on
   --panel, the text loses 0.1:1 of contrast -- and it is what makes the ring read
   as light falling on a surface rather than as a border that changed colour. */
.elit::before{content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  background:radial-gradient(320px circle at var(--mx,50%) var(--my,50%),
    rgba(var(--ice-rgb),.055), transparent 68%)}
.elit::after{content:'';position:absolute;inset:-1px;border-radius:inherit;padding:1.6px;
  background:radial-gradient(240px circle at var(--mx,50%) var(--my,50%),
    rgba(var(--ice-rgb),1), rgba(var(--ice-rgb),.30) 38%, transparent 66%);
  /* currentColor, not #000: a mask reads ALPHA, so any fully opaque paint does
     the job, and this file is not allowed to author a colour literal -- the ban
     is what stops a tint being typed out and then not moving when its token
     does. currentColor is opaque here and carries no palette meaning. */
  -webkit-mask:linear-gradient(currentColor 0 0) content-box, linear-gradient(currentColor 0 0);
  mask:linear-gradient(currentColor 0 0) content-box, linear-gradient(currentColor 0 0);
  -webkit-mask-composite:xor; mask-composite:exclude;
  pointer-events:none}
/* A tile has no border of its own -- it is separated by a 1px rule between
   siblings -- so its light is drawn as a hairline under the numeral instead of
   a ring around nothing. */
.tile.elit::after{inset:auto -1px -1px -1px;height:2px;padding:0;border-radius:0;
  -webkit-mask:none;mask:none;
  background:linear-gradient(90deg,transparent,rgba(var(--ice-rgb),.9),transparent)}
.frus{stroke:var(--line2);stroke-width:1;opacity:.75;stroke-dasharray:2 3}
#lens{opacity:0;transition:opacity .18s}
#lens.up{opacity:1}
/* The seeded rest lens: visible until the live one rises, and back again at rest.
   Nine real producers at their true fill, in the served HTML -- so the frame is
   never empty, and nine more names are readable with scripting off. */
/* Horizontal drag belongs to the instrument; vertical scroll belongs to the page.
   Without this the browser claimed the gesture and a finger could never read the
   chart at all. */
.scroller{touch-action:pan-y}
/* The lens rides 58px clear of the contact point on a coarse pointer, so the
   thumb is never on top of the reading it just asked for. Static offset, applied
   on press and removed on release -- no travel, nothing to animate. */
.flightbox.liftlens .lensg,.flightbox.liftlens #lensfx{transform:translateY(-58px)}
@media(prefers-reduced-motion:reduce){
  .flightbox.liftlens .lensg,.flightbox.liftlens #lensfx{transform:translateY(-58px)}
}
.scroller.overflows{touch-action:auto}
#lensrest{opacity:1}
#lensrest.down{opacity:0}
@media(prefers-reduced-motion:reduce){ #lens{transition:none} }
#lenslabel.hid{opacity:0}
.lves .lghost{fill:rgba(var(--chalk-rgb),.05)}
.lves .lfill{fill:var(--liq-red)}
.lves .lfill.wht{fill:var(--liq-white)}
.lves .lmen{fill:var(--men-red);opacity:.9}
.lves .lmen.wht{fill:var(--men-white)}
.lves .lsc{fill:var(--ash);font-family:'IBM Plex Mono',monospace;font-size:10.5px;font-variant-numeric:tabular-nums}
.lves .lnm{fill:var(--faint);font-family:'IBM Plex Mono',monospace;font-size:7.5px;letter-spacing:.1em}
.lves.lon .lghost{fill:rgba(var(--chalk-rgb),.13)}
.lves.lon .lsc{fill:var(--chalk);font-weight:600}
.lves.lon .lnm{fill:rgba(var(--chalk-rgb),.92)}

/* the field is bedrock — light, not motion, carries attention */
.ves.on .ghost{fill:rgba(var(--chalk-rgb),.16)}
.ves.locked .ghost{fill:rgba(var(--ice-rgb),.20)}
.ves.locked .men{fill:var(--men-red)}

/* LOCK DELTA READOUT.
   The pair used to be straw ABOVE / claret BELOW. Claret is wine now and nothing
   that is not wine may be warm, so below-reference is --ice. It is not a
   downgrade of the signal: straw against ice is 1.33:1 in luminance AND 142
   degrees apart in hue, where straw against claret separated by hue alone -- and
   the sign glyph (+ / - / +/-) carries it a third time regardless of colour. */
.spec .delta{font-size:12px;letter-spacing:.06em;color:var(--ash);margin-top:9px}
.spec .delta b{font-weight:600;font-size:13px}
.spec .delta b.up{color:var(--straw)}
.spec .delta b.dn{color:var(--ice)}
.spec .delta .vs{color:var(--faint)}
.spec .delta.locked-tag{color:var(--ice);letter-spacing:.14em;text-transform:uppercase;font-size:10.5px}

/* odometer: values transit, they don't snap. 150ms, tabular so nothing reflows. */
.spec .num{display:inline-block}

/* commit physics: acknowledge on the control, consequence in the field */
@keyframes commitflash{0%{box-shadow:0 0 0 0 rgba(var(--ice-rgb),.55)}100%{box-shadow:0 0 0 6px rgba(var(--ice-rgb),0)}}
.commit{animation:commitflash .26s ease-out}
.lbb:active,.chip:active,.reset:active,.verb:active,button:active{transform:translateY(1px);filter:brightness(.92)}
.lbb,.chip,.reset,.verb,button{transition:filter .08s,transform .08s}
/* vernier: a rescaled glass must print its own scale or it is a lying chart */
.vn{fill:var(--straw);font-family:'IBM Plex Mono',monospace;font-size:9px;letter-spacing:.08em}
.vnl{stroke:var(--straw);stroke-width:1;opacity:.5}
.vnl.spine{opacity:.28}
.vnscrim{fill:var(--void);opacity:.82}
.vnm{fill:var(--faint);font-family:'IBM Plex Mono',monospace;font-size:8px;letter-spacing:.14em}
.lliq{transition:transform .14s cubic-bezier(.2,.8,.2,1)}
.frus.rest{opacity:.55}
.lves .lnm{font-size:8px;letter-spacing:.06em;fill:rgba(var(--chalk-rgb),.5)}
.lves .lsc{opacity:.5}
.lves.lon .lsc{opacity:1}
@media(prefers-reduced-motion:reduce){ .lliq{transition:none!important} }

/* odometer wheels: the numeral is a column of digits behind a one-line window.
   REGISTRATION IS THE INVARIANT. Window box, digit box and the translate unit are
   all exactly 1.22em, every child of the wheel is a block of that height, and the
   row registers at flex-start — so the wheel's vertical position can never be
   derived from the surrounding inline baseline. An overflow:hidden inline-block
   baselines at its bottom edge; letting that reach the wheel is what sheared the
   tops off the 15px status strip. */
.odo{display:inline-block;vertical-align:baseline;line-height:1.22;
  font-variant-numeric:tabular-nums}
.odo .odw{display:inline-flex;align-items:flex-start;
  -webkit-user-select:none;user-select:none}
.odo .odc{display:block;overflow:hidden;height:1.22em}
.odo .odr{display:block;will-change:transform}
.odo .odr i{display:block;height:1.22em;line-height:1.22;font-style:normal;text-align:center;
  margin:0;padding:0;border:0;letter-spacing:0}
.odo .odf{display:block;height:1.22em;line-height:1.22;white-space:pre}
@media(prefers-reduced-motion:reduce){ .odo .odr{transition:none!important} }
.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.spec .go[style*="hidden"]{pointer-events:none}

/* ---------- THE ASSAY (producer page) ---------- */
/* NO TRANSITIONS HERE. Ledger rows 6.1-6.4 are retired, and their declarations
   go with them: a `transition` that nothing ever triggers is not harmless, it is
   a loaded gun -- the next person to write `el.style.width = x` on a .vfill gets
   a 520ms animation they did not ask for and did not know they had armed. The
   reduced-motion override that used to sit here is gone for the same reason:
   there is nothing left for it to suppress.
   `.winclip.undrawn` stays as a class WITHOUT a transition, because the default
   state of the stylesheet is what a crawler and a no-JS reader get, and the
   default must be the drawn one. */
.winclip{position:absolute;inset:0;border-radius:4px;overflow:hidden;
  clip-path:inset(0 0 0 0)}
.winclip.undrawn{clip-path:inset(0 100% 0 0)}
/* The probe line is reserved space: it exists at rest, so nothing can shift. */
.probe{min-height:34px;display:flex;align-items:center;gap:0;font-size:12px;letter-spacing:.05em;
  color:var(--faint);border-left:2px solid var(--line);padding-left:11px;margin:-2px 0 14px}
.probe.live{color:var(--ash);border-left-color:var(--ice)}
.probe .pt{display:block}
.probe b{color:var(--straw);font-weight:600;font-size:14px;margin-right:8px}
.probe b.up{color:var(--straw)} .probe b.dn{color:var(--ice)}
.probe .pk{color:var(--chalk);letter-spacing:.14em;font-size:11.5px}
.probe .pk.st-peak{color:var(--straw)} .probe .pk.st-drink{color:var(--ash)}
.probe .pk.st-past{color:var(--men-smoke)} .probe .pk.st-young{color:var(--faint)}
.probe .pk.lockpk{color:var(--ice)}
/* the hairline: a 1px read-head, never a decoration */
.hair{position:absolute;top:-4px;bottom:-4px;width:1px;background:var(--ice);opacity:0;
  pointer-events:none;box-shadow:0 0 0 1px rgba(var(--void-rgb),.6)}
.skywrap{position:relative}
.rline{position:absolute;left:0;right:0;height:0;border-top:1px solid var(--straw);opacity:0;pointer-events:none}
.rline.hov{border-top-style:dashed;border-top-color:var(--chalk)}
.rline i{position:absolute;right:0;top:-15px;font-family:'IBM Plex Mono',monospace;font-size:9px;
  letter-spacing:.12em;font-style:normal;color:var(--straw);background:var(--void);padding:0 4px}
.rline.hov i{color:var(--chalk);top:2px}
.vrow.lit .vlab{color:var(--chalk)}
.vrow.lit .vtrack{border-color:var(--ice)}
.vrow:focus{outline:none} .vrow:focus-visible .vtrack{outline:2px solid var(--ice);outline-offset:2px}
.sk.lit .skves{border-color:var(--ice)} .sk.lit .sky{color:var(--ice)}
.sk.locked .skves{border-color:var(--straw);box-shadow:inset 0 0 0 1px var(--straw)}
.sk.locked .sky{color:var(--straw)}
.sk{cursor:pointer} .sk:focus{outline:none} .sk:focus-visible .skves{outline:2px solid var(--ice);outline-offset:2px}

/* ---------- the console listbox ---------- */
.lb{position:relative}
.lbb{font-family:'IBM Plex Mono',monospace;font-size:11.5px;letter-spacing:.06em;color:var(--chalk);
  background:rgba(var(--chalk-rgb),.04);border:1px solid var(--line2);border-radius:6px;
  padding:8px 26px 8px 12px;cursor:pointer;text-align:left;min-width:150px;position:relative}
.lbb:hover{border-color:var(--ice)}
.lbb:focus-visible{outline:2px solid var(--ice);outline-offset:2px}
.lbk{position:absolute;right:10px;top:50%;width:7px;height:7px;margin-top:-5px;
  border-right:1px solid var(--ash);border-bottom:1px solid var(--ash);transform:rotate(45deg)}
.lb.on .lbk{margin-top:-1px;transform:rotate(225deg)}
.lbp{display:none;position:absolute;z-index:40;top:calc(100% + 6px);left:0;min-width:100%;
  max-height:290px;overflow-y:auto;background:var(--panel);border:1px solid var(--line2);border-radius:8px;
  box-shadow:0 18px 44px rgba(var(--void-rgb),.62);padding:5px}
.lb.on .lbp{display:block}
.lbp:focus{outline:none}
.lbo{display:flex;align-items:center;gap:14px;justify-content:space-between;
  padding:7px 10px;border-radius:5px;cursor:pointer;white-space:nowrap}
.lbt{font-family:'IBM Plex Mono',monospace;font-size:11.5px;letter-spacing:.06em;color:var(--ash)}
.lbt[data-note]::after{content:attr(data-note);display:block;font-size:9px;letter-spacing:.14em;
  color:var(--faint);margin-top:2px}
.lbo.act{background:rgba(var(--ice-rgb),.14)}
.lbo.act .lbt{color:var(--chalk)}
.lbo[aria-selected="true"] .lbt{color:var(--chalk)}
.lbo[aria-selected="true"]::before{content:'';position:absolute;left:2px;width:2px;height:15px;background:var(--sel-edge)}
.lbo{position:relative}
.lbx{display:flex;align-items:center}
.lbs{width:78px;height:19px;opacity:.55}
.lbs rect{fill:var(--ice)}
.lbo.act .lbs{opacity:1}
.lbo[aria-selected="true"] .lbs rect{fill:var(--sel-edge)}

/* ---------- THE RANK RAIL (region page) ---------- */
.rrail{position:fixed;left:0;top:0;bottom:0;width:26px;z-index:30;opacity:0;pointer-events:none;
  background:linear-gradient(90deg,rgba(var(--void-rgb),.92),rgba(var(--void-rgb),0));cursor:pointer}
.rrail.on{opacity:1;pointer-events:auto}
.rrail svg{position:absolute;left:5px;top:0;width:18px;height:100%}
.rrail .rt{fill:var(--liq-red-deep)}
.rrail .rt.wht{fill:var(--liq-white-deep)}
.rrail .rt.lit{fill:var(--chalk)}
.rrbr{position:absolute;left:2px;right:2px;border:1px solid rgba(var(--chalk-rgb),.55);border-radius:2px;
  background:rgba(var(--chalk-rgb),.07);pointer-events:none}
.tbl .dh{width:64px}
.tbl .dlt{width:64px;color:rgba(var(--chalk-rgb),.24);font-variant-numeric:tabular-nums}
.tbl .dlt.on{font-weight:600}
.tbl .dlt.on.up{color:var(--straw)} .tbl .dlt.on.dn{color:var(--ice)}
.tbl tr.lit{background:rgba(var(--ice-rgb),.07)}
@media(max-width:900px){ .rrail{display:none} }
/* Row 9.1 retired; the declaration goes with it. See the assay block above. */
/* the rail's read line: the site reads into its own voice, never an OS tooltip */
.rrread{min-height:32px;display:flex;align-items:center;padding:0 14px;font-size:11.5px;
  letter-spacing:.05em;color:var(--faint)}
.rrread.dock{position:fixed;left:30px;bottom:16px;z-index:31;pointer-events:none;display:none;
  max-width:calc(100vw - 60px);padding:7px 13px;border:1px solid var(--line2);border-radius:8px;
  background:rgba(var(--panel-rgb),.94);box-shadow:0 12px 30px rgba(var(--void-rgb),.55)}
body.railon .rrread.dock.live{display:flex}
@media(max-width:900px){ .rrread.dock{display:none!important} }
.rrread.live{color:var(--ash)}
.rrread .pt{display:block}
.rrread b{color:var(--straw);font-weight:600;font-size:13px;margin-right:7px}
.rrread b.up{color:var(--straw)} .rrread b.dn{color:var(--ice)}
.rrread .pk{color:var(--chalk);letter-spacing:.12em;font-size:11.5px}
.tbl .dh b{color:var(--chalk);font-weight:600}
.lenswin.idle{stroke:var(--line);stroke-dasharray:3 4}

@media(max-width:760px){ .lenslabel{font-size:7.5px;letter-spacing:.24em} }

/* the hero's two verbs are the site's two mechanisms, not bold text */
.verb{font:inherit;color:var(--chalk);background:none;border:0;padding:0;cursor:pointer;
  border-bottom:1px solid var(--ice);text-decoration:none}
.verb:hover{color:var(--ice)}
.verb:focus-visible{outline:2px solid var(--ice);outline-offset:3px}
/* the nav learns the bezel voice: mono, engraved, active marked by a rule not by weight */
.navlinks a{font-family:'IBM Plex Mono',monospace;font-size:11.5px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--faint);text-decoration:none;padding:6px 0;
  border-bottom:1px solid transparent}
/* Restated INSIDE a media block that comes after the one it used to lose to, so
   the mobile row keeps its own metrics no matter where the base rule lands. */
@media(max-width:760px){
  .navlinks a{padding:0 15px;border-bottom:0;height:42px;display:flex;align-items:center}
  .navlinks a + a{border-left:1px solid var(--line)}
  .navlinks a.here{color:var(--chalk);box-shadow:inset 0 -2px 0 var(--straw)}
}
/* FOUR LINKS, FOUR VISIBLE LINKS.
   At 390 the row measured 500px and scrolled, with scrollbar-width:none -- so the
   fourth link, WHO PAYS US, was 20px of "W" with no affordance that it existed.
   Of the four that is the one the site's entire credibility argument rests on;
   burying it behind an undiscoverable swipe is the opposite of what the page says
   about itself. Below 430px the tracking and padding tighten until all four seat,
   and the row stops scrolling at all. */
@media(max-width:430px){
  /* Tighter, but never below the 11px floor -- shrinking the type to fit is how
     the wayfinding layer became fuzz in the first place. */
  .navlinks a{padding:0 8px;letter-spacing:.05em}
}
@media(max-width:385px){
  /* Below this the four will not seat in one row at a legible size, so they take
     two. Four visible links in a taller header beats three visible links and a
     sliver, on the one page whose argument is that it hides nothing. */
  /* 39px rows were under the 40px floor this project's own mobile suite enforces
     for header links -- unseen because that suite ran only at 390, where the row
     is 42. The floor and the viewport the floor is checked at have to overlap. */
  :root{--navh:130px}
  .navlinks{display:grid;grid-template-columns:1fr 1fr;height:84px;overflow:visible}
  .navlinks a{height:42px;padding:0 12px;border-left:0}
  .navlinks a:nth-child(2n){border-left:1px solid var(--line)}
  .navlinks a:nth-child(n+3){border-top:1px solid var(--line)}
}
.navlinks a:hover{color:var(--ash)}
.navlinks a.here{color:var(--chalk);border-bottom-color:var(--straw);font-weight:400}

/* ---------- the answer, as a list you can read without hovering anything ---------- */
.alist{list-style:none;counter-reset:none;margin-top:6px}
.ac{display:grid;grid-template-columns:58px 1fr;gap:16px;padding:15px 0;border-top:1px solid var(--line)}
.ac:first-child{border-top:0}
.acr{font-size:15px;font-weight:600;color:var(--ash);letter-spacing:.02em;padding-top:2px;
  font-variant-numeric:tabular-nums}
.acb h3{font-size:17px;font-weight:600;letter-spacing:-.01em;line-height:1.25}
.acb h3 a{text-decoration:none;border-bottom:1px solid transparent}
.acb h3 a:hover{color:var(--ice);border-bottom-color:var(--ice)}
.acm{font-size:var(--t-note);font-family:'Space Grotesk',-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;letter-spacing:0;text-transform:none;color:var(--faint);margin-top:4px}
.acb .verdict{font-size:14.5px;color:var(--chalk);margin-top:7px;max-width:70ch;line-height:1.45}
.acf{display:flex;flex-wrap:wrap;gap:6px 20px;margin-top:11px;font-size:var(--t-lab);letter-spacing:.14em;
  text-transform:uppercase;color:var(--faint)}
.acf b{color:var(--chalk);font-size:12.5px;font-weight:600;letter-spacing:0;margin-right:4px;
  font-variant-numeric:tabular-nums}
@media(max-width:600px){.ac{grid-template-columns:44px 1fr;gap:11px}}

/* the passage a person skims and a model lifts */
.summary{border-left:2px solid var(--line2);padding-left:16px}
.srh{font-size:var(--t-note)!important;letter-spacing:0;text-transform:none;color:var(--faint);
  font-weight:500!important;font-family:'Space Grotesk',-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif}
.sum{font-size:16px;line-height:1.62;max-width:74ch;margin-top:8px;color:var(--chalk)}
.sum b{color:var(--chalk);font-weight:600}
.sumkey{font-size:var(--t-note);font-family:'Space Grotesk',-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;letter-spacing:0;text-transform:none;color:var(--faint);margin-top:11px}
.sumkey a{color:var(--ice)}

/* questions, answered in the HTML rather than behind an interaction */
.qa{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(320px,100%),1fr));gap:22px 30px}
.qi h3{font-size:14.5px;font-weight:600;color:var(--straw);line-height:1.35}
.qi p{font-size:14px;color:var(--ash);line-height:1.55;margin-top:6px;max-width:62ch}

.legend{display:flex;flex-wrap:wrap;gap:8px 22px;align-items:center;margin:2px 0 14px;
  font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint)}
.legend span{display:flex;align-items:center;gap:7px}
.lg{width:10px;height:14px;border-radius:2px;display:inline-block}
.lg.red{background:var(--liq-red)}.lg.wht{background:var(--liq-white)}.lg.warn{background:var(--liq-smoke)}
.lgnote{color:var(--ash);letter-spacing:.06em;text-transform:none;font-size:11px}

.wtab{max-width:620px;margin-top:4px}
.wrow{display:grid;grid-template-columns:150px 1fr 46px;gap:14px;align-items:center;margin-bottom:9px}
.wn{font-size:14px;color:var(--ash)}
.wbar{height:14px;border:1px solid var(--line2);border-radius:4px;background:rgba(var(--chalk-rgb),.03);overflow:hidden}
.wbar i{display:block;height:100%;background:var(--liq-red);border-right:2px solid var(--men-red)}
.wp{font-size:12px;color:var(--chalk);text-align:right;font-variant-numeric:tabular-nums}
.calc{font-size:15px;color:var(--ash);margin-top:4px;letter-spacing:.02em}
.calc b{color:var(--straw)}
.lede.small{font-size:14.5px;max-width:78ch}
.sechead h2{scroll-margin-top:var(--clearnav)}


/* ---- The no-JS contract ------------------------------------------------
   An inline script in <head> stamps `js` on <html> before first paint. Without
   it, the controls that only a script can drive are not shown: nine live-looking
   dead buttons and a sentence telling the reader to sweep an instrument that
   will not respond are worse than nothing. The field itself stays — it is
   prerendered at true heights, so it is a chart, not a promise. */
.nojs-only{display:none}
html:not(.js) .nojs-hide{display:none!important}
html:not(.js) .nojs-only{display:block}
html:not(.js) #lenslabel{display:none}   /* no glass to pick up, no label for it */


/* ---- Deep-link destinations clear the sticky bar --------------------------
   Any id on this site is addressable from outside it: a search result, a
   citation, a language model's answer. Three of them landed at exactly top:0,
   which on a 58px sticky header means the section a citation sent someone to
   arrived invisible. `scroll-margin-top` is the whole fix; the absence of it was
   invisible for four rounds because the site links no fragments itself, so
   nothing internal ever exercised the jump. */
/* Every id this site emits is a destination: a citation, a search result, or a
   pasted link can address any of them. The rank rail mints 187 of them itself
   (`history.pushState(null,'','#r'+slug)`) and those were the ones missing --
   `#rows tr[id]` was not in this list, so the one class of anchor the site puts
   into URLs on purpose landed at top:0 behind the header. */
[id]{scroll-margin-top:var(--clearnav)}
/* The one exception, and it is a tripwire rather than a bug today. Listbox
   options carry ids because `aria-activedescendant` requires them, and they live
   inside a ~200px internal scroller. lb.js positions them with manual scrollTop
   arithmetic, which ignores scroll-margin, so the universal rule is inert there
   right now. The day anyone replaces that arithmetic with the idiomatic
   scrollIntoView({block:'nearest'}), every option would drag a phantom 74px
   margin inside a 200px panel and keyboard navigation would pin to the bottom.
   Zero it here, so the rewrite is safe before it happens. */
.lbp [id], .lbp [id] *{scroll-margin-top:0}

/* EVERYTHING with an id, not a curated list of the ids we thought mattered.
   The list started as section/article/heading, and the 187 anchors the rank rail
   mints into URLs were not in it. Replacing it with a longer list only moves the
   judgement call; the judgement call is where both misses lived. `scroll-margin`
   is inert unless something scrolls to the element, so applying it universally
   costs nothing and removes the category "ids we decided are not destinations".
   No site code calls scrollIntoView, so nothing else is affected. */


/* MONO-CAPS IS FOR NUMBERS AND UNITS.
   The three answer cards carried four tracked-caps lines each: the question, the
   district/grape/town, the fact strip and the link. Only one of those four is a
   unit. The question is the most important sentence on the card and it was the
   hardest thing on the card to read. See --t-note in :root. */

/* ---- Three ways in ---------------------------------------------------------
   Three questions, three columns. The dead right half at 1440 is a defensible
   editorial stance for the document sections below; on a row of three answers it
   is an unset table, and the width earns its keep here. */
/* ---------- the hero, and the answer cards ----------
   The fold used to be an h1 that meant nothing to a stranger, a 15.5px lede, and
   55% empty right-hand column at 1440. Nothing was set at display size, so there
   was no number for the eye to land on and no visible answer to "what is this".
   Now: the claim, four computed numerals, and three named answers -- and the
   instrument crests the fold underneath them. */
/* ONE COLUMN, THEN THREE CARDS.
   The two-column hero put the claim on the left and the three answers on the
   right, and the right column was twice the height of the left -- so 1440 showed
   a 450px hole under the stat tiles and pushed the instrument down by exactly that
   much. The cards read better as a row anyway: three answers side by side is the
   shape of "three ways in". */
.hero{padding:26px 0 4px}

/* ---------- THE INSTRUMENT HERO ----------
   Headline left, the instrument's DISPLAY right, the field full width beneath
   both, the console under the field. See build.py for the arithmetic that forces
   the split -- stacked, the display has nowhere to go, and the display is where
   every crosshair, scrub and caliper reading prints. */
.ihero{padding:18px 0 0}
.ihtop{display:grid;grid-template-columns:minmax(0,1.42fr) minmax(0,1fr);
  gap:16px 44px;align-items:end;margin-bottom:14px}
.ihL{min-width:0}
/* The display is a CONTAINER, and that is not a stylistic choice.
   .spec reserves its meta line by height, and the reservation used to be keyed to
   the VIEWPORT -- two lines by default, three below 620px, one at 1100px and up --
   because the slot had always been full width, so viewport and slot were the same
   measurement. In a 500px column on a 1440px viewport the viewport rule reserves
   ONE line for a sentence that needs three, and overflow:hidden amputates it:
   exactly the defect three earlier rounds were spent removing. The reservation
   follows the box it lives in now. */
.ihR{min-width:0;container:specbox / inline-size}
.ihR .spec{margin:0;border:1px solid var(--line);border-radius:12px;
  background:var(--panel);padding:14px 16px 14px;
  box-shadow:inset 0 1px 0 rgba(var(--chalk-rgb),.045)}
.ihfield{border:1px solid var(--line);border-radius:12px;background:var(--panel);
  box-shadow:inset 0 1px 0 rgba(var(--chalk-rgb),.045);padding:6px 0 0}
.ihfield .flightbox{margin-top:0}
.ihconsole{margin-top:14px}
.ihnote{margin-top:16px}
.ihero .tiles{margin-top:12px}
@media(max-width:1000px){
  .ihtop{grid-template-columns:1fr;gap:14px;align-items:start}
  .ihR .spec{padding:12px 14px}
}
/* ON A PHONE THE DISPLAY GOES UNDER THE FIELD.
   Stacked, the hero's right column lands ABOVE the field -- so a thumb is on the
   instrument and the reading it produces is off the top of the screen. The
   readout of a thing belongs after the thing on a single column. `order` on a
   flex container moves the box without moving it in the DOM, so the reading order
   a screen reader and a crawler get is unchanged: headline, display, field.
   Below 760px the console follows both, which is where a control panel belongs
   when there is no room to put it beside anything. */
@media(max-width:1000px){
  .ihero{display:flex;flex-direction:column}
  .ihero > .ihtop{order:1}
  .ihero > .ihfield{order:2}
  .ihero > .tiles{order:4}
  .ihero > .ihconsole{order:5}
  .ihero > .legend{order:6}
  .ihero > .ihnote{order:7}
  .ihtop{display:contents}
  .ihL{order:1}
  .ihR{order:3;margin-top:14px}
}
/* The best voice on this site is Newsreader italic and it was spent entirely on
   two-line verdicts. "The order is the order." is the whole argument in five
   words; at 17px it was a caption under the lede. */
.herokoan{font-family:'Newsreader',Georgia,serif;font-style:italic;font-weight:300;
  font-size:clamp(20px,2.7vw,32px);line-height:1.2;color:var(--ash);margin-top:18px;letter-spacing:-.01em}
.tiles{display:grid;grid-template-columns:repeat(4,1fr);gap:0;margin-top:22px;
  border-top:1px solid var(--line)}
.tile{display:flex;flex-direction:column;gap:5px;padding:16px 18px 14px;
  text-decoration:none;border-right:1px solid var(--line);min-height:44px}
.tile:last-child{border-right:0}
.tile:first-child{padding-left:0}
.tile b{font-size:clamp(38px,3.9vw,56px);font-weight:600;color:var(--chalk);line-height:1;letter-spacing:-.025em}
.tile i{font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:var(--faint);
  font-style:normal;line-height:1.3}
.tile:hover b{color:var(--straw)}
.tile:hover i{color:var(--ash)}

/* A score is a level. Every card that prints one prints the vessel beside it, so
   the chart's grammar is taught before the reader reaches the chart. */
.wv{display:inline-block;width:13px;height:30px;border:1px solid var(--line2);border-radius:2px;
  position:relative;overflow:hidden;background:var(--panel2);vertical-align:-6px}
.wv i{position:absolute;left:0;right:0;bottom:0;background:var(--liq-red);display:block}
.wayscore{display:flex;align-items:center;gap:9px;margin:10px 0 2px}
.wayscore b{font-size:30px;font-weight:600;color:var(--straw);line-height:1;letter-spacing:-.02em}
.wayscore.big b{font-size:44px}
.wayscore.big .wv{width:17px;height:42px;vertical-align:-8px}
.wayscore i{font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--faint);
  font-style:normal}

.ways{display:grid;grid-template-columns:repeat(3,1fr);gap:22px;margin-top:20px}
/* Bounded surfaces. Nothing on this page was contained except the console bezel,
   so nothing ranked: every number sat at the same weight as its neighbours and a
   first-timer could not tell which one was the verdict. */
.way{background:var(--panel);border:1px solid var(--line);border-radius:12px;
  padding:20px;display:flex;flex-direction:column;
  box-shadow:inset 0 1px 0 rgba(var(--chalk-rgb),.045)}
.heroR{margin-top:30px}
.heroR .ways{grid-template-columns:repeat(3,1fr);gap:18px;margin-top:0}
.sechead.tight{margin-bottom:14px}
@media(max-width:900px){ .heroR .ways{grid-template-columns:1fr;gap:14px} }
/* ---- THE ANSWERS BAND -------------------------------------------------
   Three columns, no panels. The bordered card made three different questions
   look like three tiers of the same product, and it put the score -- the thing
   this site sells -- at 30px inside a box. The band divides with the same
   hairline the stat tiles use, and the numeral carries the column. */
.ways.band{display:grid;grid-template-columns:repeat(3,1fr);gap:0;margin-top:18px;
  border-top:1px solid var(--line)}
.ways.band .way{background:none;border:0;border-right:1px solid var(--line);
  border-radius:0;box-shadow:none;padding:20px 26px 22px}
.ways.band .way:first-child{padding-left:0}
.ways.band .way:last-child{border-right:0;padding-right:0}
/* flex, so `margin-top:auto` on the facts strip pushes it and the link to the
   bottom of every column: three verdicts of different lengths otherwise leave
   three links at three heights, and the band stops reading as a row. */
.wayc{display:flex;flex-direction:column}
.wayc .wayq{margin-bottom:8px}
.wayc .wayn{font-size:21px;font-weight:600;letter-spacing:-.02em;line-height:1.15}
.wayc .wayn a{text-decoration:none}
.wayc .wayn a:hover{color:var(--ice)}
.wayc .acm{margin-top:4px}
.wayscore.huge{display:flex;align-items:center;gap:14px;margin:14px 0 0}
.wayscore.huge b{font-size:clamp(64px,6.6vw,120px);line-height:.88;letter-spacing:-.04em;
  color:var(--straw);font-weight:600}
.wayscore.huge i{font-size:11px;letter-spacing:.2em;text-transform:uppercase;
  color:var(--faint);font-style:normal;align-self:flex-end;padding-bottom:10px}
.wv.tall{width:26px;height:78px;border-radius:3px 3px 6px 6px;vertical-align:0}

/* THE SUB-SCORE FAN. Five published sub-scores, drawn at their own values, in the
   served HTML. Static -- the values are printed at rest, not revealed on hover,
   because a card that grows under the pointer moves the two beside it. The hover
   only raises the bar's contrast; nothing changes size and nothing transitions. */
.fan{display:flex;gap:8px;margin-top:14px;align-items:flex-end}
.fanb{position:relative;display:flex;flex-direction:column;align-items:center;gap:4px;
  width:34px}
.fanb > i{display:block;width:100%;height:34px;border-radius:2px;background:var(--panel2);
  border:1px solid var(--line2);position:relative;overflow:hidden}
.fanb > i::after{content:'';position:absolute;left:0;right:0;bottom:0;height:var(--h,0%);
  background:var(--steel)}
.fanb em{font-style:normal;font-family:'IBM Plex Mono',ui-monospace,monospace;font-size:8.5px;
  letter-spacing:.06em;color:var(--faint)}
.fanb b{font-family:'IBM Plex Mono',ui-monospace,monospace;font-size:11.5px;font-weight:600;
  color:var(--ash);letter-spacing:.02em}
.fanb:hover > i::after{background:var(--ice)}
.fanb:hover b{color:var(--chalk)}
.wayc .verdict{font-size:15px;line-height:1.5;margin-top:14px}
.wayc .acf{margin-top:12px}
.wayc .wayg{margin-top:12px}
@media(max-width:900px){
  .ways.band{grid-template-columns:1fr}
  .ways.band .way{border-right:0;border-bottom:1px solid var(--line);padding:18px 0 20px}
  .ways.band .way:last-child{border-bottom:0}
}
/* ---- VINTAGE PAGES ------------------------------------------------------
   The Δ column here is against each producer's OWN ten-year average rather than
   a group median, so the table is narrower than the ranking's and its year
   column carries a four-digit number that must not wrap. */
/* The rank column is 44px; a first column that is a LABEL is not. `th:first-child`
   caught both, so "Westside" wrapped to three lines in a comparison table whose
   first column is the thing being compared. */
.vtbl td.rk{width:44px}
.vtbl:not(.cmp) th:first-child{width:44px}
.cmp th:first-child,.cmp td:first-child{width:120px;white-space:nowrap;
  color:var(--chalk);font-weight:600}
.vtbl td.yr,.vtbl th.yr{width:60px;color:var(--faint);white-space:nowrap}
.vtbl td{vertical-align:middle}
.vtbl .sc{color:var(--straw);font-weight:600;font-size:16px}
.vtbl .dlt{width:92px}
/* The smoke note is the single most important sentence on the 2020 page -- it is
   the difference between "these wines were bad" and "these wines were scored" --
   so it is marked as a note rather than set as one more paragraph. */
.vnote{border-left:2px solid var(--line2);padding:2px 0 2px 16px;margin-top:6px}
.vnote p{font-size:15.5px;line-height:1.6;color:var(--ash);max-width:74ch}
.vnote p b{color:var(--chalk);font-weight:600}
.ihero .tiles,.wrap section .tiles{margin-bottom:2px}

/* The comparison tables on the sides and fees pages carry a secondary label
   column (which districts a side contains, and the like) that must recede. */
.vtbl .sub2{color:var(--faint);font-size:12.5px;max-width:34ch}
.vtbl tr.lit td{background:rgba(var(--ice-rgb),.055)}
.vtbl tr.lit td:first-child{color:var(--chalk);font-weight:600}

/* ---- QUESTIONS, MERGED --------------------------------------------------
   Two consecutive three-column grids -- three questions, then four reasons to
   believe them -- became one two-column section: a sticky index of every
   question on the left, the answers on the right. Sticky is `position:sticky`
   and nothing else: no script, no scroll listener, and so nothing that can move
   while the page is still. */
.qgrid{display:grid;grid-template-columns:minmax(0,300px) minmax(0,1fr);gap:56px;margin-top:20px}
.qnav{align-self:start;position:sticky;top:var(--clearnav)}
.qnav ol{list-style:none;counter-reset:qn;margin:0;padding:0}
.qnav li{counter-increment:qn;border-top:1px solid var(--line)}
.qnav li:last-child{border-bottom:1px solid var(--line)}
.qnav a{display:grid;grid-template-columns:26px 1fr;gap:6px;align-items:baseline;
  padding:11px 0;color:var(--ash);text-decoration:none;font-size:14px;line-height:1.35}
.qnav a::before{content:counter(qn,decimal-leading-zero);
  font-family:'IBM Plex Mono',ui-monospace,monospace;font-size:10px;letter-spacing:.1em;
  color:var(--faint)}
.qnav a:hover{color:var(--chalk)}
.qnav a:hover::before{color:var(--ice)}
.qbody{display:flex;flex-direction:column;gap:0}
.qbody .qi{padding:0 0 26px;margin-bottom:26px;border-bottom:1px solid var(--line)}
.qbody .qi:last-child{border-bottom:0;margin-bottom:0;padding-bottom:0}
/* A question is not a score. Straw is a data colour -- white-wine liquid, and
   the numbers this site sells -- and a heading is neither. */
.qbody .qi h3{font-size:19px;font-weight:600;letter-spacing:-.015em;margin-bottom:8px;
  color:var(--chalk)}
.qbody .qi p{font-size:15.5px;line-height:1.62;color:var(--ash);max-width:70ch}
.qbody .qi p a{color:var(--ice)}
@media(max-width:900px){
  .qgrid{grid-template-columns:1fr;gap:22px}
  /* A sticky index above the thing it indexes is a lid. On one column it is a
     plain list at the top, which is what a table of contents is anyway. */
  .qnav{position:static}
}

/* ---- rules the band did not replace, restored verbatim ---------------- */
.wayq{font-size:var(--t-note);font-family:'Space Grotesk',-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;letter-spacing:0;text-transform:none;color:var(--ash);font-weight:500;
  margin-bottom:12px}
.way h3{font-size:21px;font-weight:600;letter-spacing:-.015em;margin-bottom:5px}
.way h3 a{text-decoration:none}
.way h3 a:hover{text-decoration:underline;text-underline-offset:3px}
.way .verdict{font-size:14.5px;margin:9px 0 12px;line-height:1.5}
.way .acf{display:flex;flex-wrap:wrap;gap:12px;font-size:11px;letter-spacing:.13em;
  text-transform:uppercase;color:var(--faint);margin-top:auto}
.way .acf b{color:var(--chalk);font-size:13px;letter-spacing:0}
.wayg{margin-top:14px;font-size:14px;font-family:'Space Grotesk',-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;letter-spacing:0;text-transform:none}
@media(max-width:900px){.ways{grid-template-columns:1fr;gap:16px}
  .hero{grid-template-columns:1fr;gap:26px;padding:20px 0 0}}
/* ONE SET OF THREE, EVERYWHERE.
   The compact hero cards and the full "Three ways in" section are the same three
   producers. The first fix hid the compact set below 900px and left both on the
   desktop -- where they landed in the SAME viewport, one directly under the other,
   which is the page repeating itself with more whitespace. So the section below is
   gone at every width; the hero carries the three answers, and the section's job --
   the verdict prose and the three destinations -- moves INTO the card. */
@media(max-width:900px){
  .wrap.home{display:flex;flex-direction:column}
  .wrap.home > .ihero{order:1}
  .wrap.home > #answers{order:2}
  .wrap.home > section:not(.ihero):not(#answers){order:3}
}
@media(max-width:760px){
  .tiles{grid-template-columns:1fr 1fr;border-left:1px solid var(--line)}
  .tile{border-bottom:1px solid var(--line);padding:14px 14px 12px}
  .tile:first-child{padding-left:14px}
  .tile:nth-child(2n){border-right:0}
  .tile b{font-size:32px}
  .herokoan{font-size:19px}
  /* Rhythm: the mobile page spent ~600px of its 3,900 on empty bands. */
  section{padding-top:28px}
  .sechead{margin-bottom:16px}
}


/* One empty vessel on the 404: the page had none of the grammar every other page
   is written in, and an empty glass is what "nothing here" looks like on a site
   whose unit of meaning is a filled one. */
.e404{display:flex;gap:26px;align-items:flex-start;padding-top:6px}
.e404v{width:40px;height:120px;flex:0 0 auto;opacity:.5}
.e404v .ghost{fill:rgba(var(--chalk-rgb),.05)}
/* the residue in an empty glass is still wine, so this one stays warm */
.e404m{fill:var(--liq-red);opacity:.55}

/* ---- Print ----------------------------------------------------------------
   A ranked list of wineries is exactly what somebody prints for a Saturday, and
   this site's ground is near-black: printed as shipped it is pages of ink. The
   instruments do not survive paper — a lens you cannot sweep and a rail you
   cannot scroll are furniture — so they are removed, and what is left is the
   thing worth carrying: the ranking, the districts, the reviews, the addresses.
   Colour is inverted to ink-on-paper rather than merely lightened, so the straw
   and claret signals stay distinguishable in greyscale by weight. */
@media print{
  :root{
    /* GENERATED from cruxvine.py -- do not restate these values. */
    --ash:#3D4A47; --ash-rgb:61,74,71;
    --chalk:#0B0F0E; --chalk-rgb:11,15,14;
    --claret:#A31840; --claret-rgb:163,24,64;
    --faint:#5A6B67; --faint-rgb:90,107,103;
    --garnet:#7A1230; --garnet-rgb:122,18,48;
    --ice:#0B4A54; --ice-rgb:11,74,84;
    --ice-dk:#D6EEF1; --ice-dk-rgb:214,238,241;
    --line:#C6CFCD; --line-rgb:198,207,205;
    --line2:#A8B4B1; --line2-rgb:168,180,177;
    --oak:#6B4622; --oak-rgb:107,70,34;
    --panel:#FFFFFF; --panel-rgb:255,255,255;
    --panel2:#F1F4F3; --panel2-rgb:241,244,243;
    --steel:#5A6B67; --steel-rgb:90,107,103;
    --straw:#6E5210; --straw-rgb:110,82,16;
    --void:#FFFFFF; --void-rgb:255,255,255;
    --navh:0px;
  }
  html,body{background:var(--void);color:var(--chalk)}
  body > nav{position:static;border-bottom:1px solid var(--line);padding:0 0 10px}
  .navlinks,.console,.instrument,.ihconsole,.ihR,.ihnote,.legend,.rrail,
  .nojs-only,.cshint,.emptystate,.axisnote,#lensfx,#lens,#vernier{display:none!important}
  /* #the-flight is the FIELD, not the section that contains it. It used to be the
     section id, and when the hero became the instrument that one selector would
     have deleted the headline, the lede, the koan and the four stats from every
     printed page -- while this suite went on passing, because all it asks is
     whether #the-flight is hidden. */
  .ihtop{display:block} .ihero{padding-top:0}
  /* The flight is an instrument, not a picture: without a pointer it is 187
     unlabelled bars, and the table below it says the same thing in words. */
  #the-flight{display:none!important}
  a{color:var(--chalk);text-decoration:underline}
  a[href^="/"]::after{content:" (cruxvine.com" attr(href) ")";font-size:9px;color:var(--faint)}
  .ac,.way,tr,section{break-inside:avoid}
  .verdict{color:var(--chalk)}
  footer{border-top:1px solid var(--line);color:var(--ash)}
  *{box-shadow:none!important}
}
