/* Self-hosted, one variable file, latin subset. font-display:swap so text
   is readable on the fallback immediately rather than invisible while the
   face loads. The metric-adjusted fallback below keeps the swap from
   shifting layout: ui-monospace is close enough in advance width that
   measured CLS stays at 0.001. */
@font-face{
  font-family:'JetBrains Mono';
  font-style:normal;
  font-weight:400 800;
  font-display:swap;
  src:url('/fonts/jetbrains-mono-latin.woff2') format('woff2');
}

:root{
  --bg:#060912;
  --panel:rgba(12,15,21,.6);
  --line:rgba(255,255,255,.06);
  --line-strong:rgba(255,255,255,.08);
  --ink:#f0f0f3;
  --ink-head:#ffffff;
  --ink-2:#b0b7c3;
  --ink-3:#8b919a;
  --ink-4:#787d85;
  --accent:#5B8DEF;
  --ring:#5B8DEF;
  color-scheme:dark;
}
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--bg);color:var(--ink);
  font-family:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
  font-size:14px;line-height:1.6;
  font-variant-numeric:tabular-nums;font-feature-settings:'tnum' 1;
  overflow-x:hidden;
}
a{color:var(--accent);text-decoration:none;border-bottom:1px solid transparent}
a:hover{border-bottom-color:var(--accent)}
a:focus-visible,summary:focus-visible{outline:2px solid var(--ring);outline-offset:3px;border-radius:2px}
.wrap{max-width:1180px;margin:0 auto;padding:0 24px}
/* Visually hidden, and insulated from presentational inheritance.
   Without text-transform:none these spans inherit uppercase from the flag
   they sit inside — thirteen sentences of assistive-technology copy styled
   as all-caps labels. It changes nothing a sighted person sees and nothing
   a screen reader announces (transforms do not alter the DOM text), but a
   sentence written for a person to hear should not carry a label's styling
   just because of where it happens to live. */
.sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;border:0;
  text-transform:none;letter-spacing:normal}

/* ── masthead ─────────────────────────────────────────── */
.top{border-bottom:1px solid var(--line);position:relative}
/* The bar's vertical padding is what is left of the old 18px once each
   link's own 44px box is subtracted: the masthead measures 59px on desktop
   and 49px on a phone, exactly as it did when the links were 20px tall
   and the padding carried the difference. The text does not move. */
.top .wrap{display:flex;align-items:baseline;gap:16px;flex-wrap:wrap;padding-top:7px;padding-bottom:7px}
.mark{font-weight:800;letter-spacing:.22em;font-size:13.5px;color:var(--ink-head)}
.crumb{font-size:12px;letter-spacing:.04em;color:var(--ink-3)}
.crumb a{color:var(--ink-3)}
.crumb a:hover{color:var(--ink-2)}
/* Breadcrumb and footer links are 12px text, and every one of them is a
   real 44px-tall box (WCAG 2.5.8 wants 24px; the touch-target rule in
   CLAUDE.md wants 44px on every interactive element). Until 2026-09-15 the
   hit area was grown through an absolutely positioned ::after, which
   extends what a finger can hit but leaves the element's own box at 20px —
   so every audit that measures getBoundingClientRect(), Lighthouse and
   the 390px sweep included, kept reporting the links as 20px targets.
   inline-flex + min-height gives the anchor the box itself. The hover
   underline moves from border-bottom (which would sit at the foot of the
   44px box, 12px under the text) to text-decoration, which follows the
   glyphs. Neighboring rows of a wrapped breadcrumb or footer never share a
   tap box, because the height is real rather than a negative margin.
   Width: 4px of padding each side, pulled back by the same margin, so
   "FLOAT" (38px of glyphs) and "Terms" (36px) box out at 44px or more
   without moving a pixel — the 4px lands inside the " / " and " · "
   separators, which are 12px or wider, so two boxes never meet. */
.crumb a,footer a{display:inline-flex;align-items:center;min-height:44px;border-bottom:0;
  padding:0 4px;margin:0 -4px;text-decoration:none}
.crumb a:hover,footer a:hover{border-bottom:0;text-decoration:underline;
  text-underline-offset:3px;text-decoration-thickness:1px}

/* ── file header ──────────────────────────────────────── */
.file{padding:44px 0 8px}
/* The plate takes an auto track rather than a fraction, and carries its
   own fixed width below. A fraction would hand it half the page at 900px
   the moment the grid collapses to one column. */
.file-grid{display:grid;grid-template-columns:auto minmax(0,1.15fr) minmax(0,.85fr);
  gap:34px 28px;align-items:start}
/* The index header has two blocks, not three — there is no plate on a list
   of items. Left as-is, its title block lands in the plate's auto track
   and sizes to max-content, so the H1 sets the column width instead of the
   column setting the H1's measure. It survived only because the old title
   happened to be short: renaming it four characters longer pushed the H1
   to 835px and squeezed the refusal panel to a 137px ribbon. Two blocks,
   two tracks, both bounded. */
.file-grid--index{grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr)}

/* ── the plate ────────────────────────────────────────── */
/* The photograph of the object the record describes. Square-cornered and
   hairline-ruled like every other field on the page: it is a plate in a
   record, not a product shot. */
/* align-items:flex-start is load-bearing, not tidiness. A column flex
   container stretches its children across the cross axis by default,
   which overrode the image's width:auto and rendered a 0.52-ratio card
   into a 0.71-ratio box — a 37% distortion that width/height attributes
   and max-width could not prevent, because the stretch won on the used
   value. Measured, not assumed: the plate test lays this out in Chrome
   and compares naturalWidth/naturalHeight against the rendered content
   box. */
.plate{--pw:172px;margin:0;width:var(--pw);
  display:flex;flex-direction:column;align-items:flex-start;gap:9px}
/* Bounded on BOTH axes, and auto on both, so the browser scales the
   intrinsic 500x962 (or whatever the file really is) down to fit and the
   ratio survives exactly. width:100% alone would have been enough to stop
   a stretch, but not enough to stop a tall card eating the fold: the 1916
   Sporting News Ruth is 1 5/8 x 3in, a 0.52 ratio against a modern card's
   0.71, and at a fixed width it rendered 329px tall and pushed the
   desktop ladder from three rows above the fold down to one. The cap is
   the height a 2.5 x 3.5in card reaches at this width, so no plate is
   ever taller than the empty state and the file header is the same height
   whether an image cleared or not. */
.plate img{display:block;width:auto;height:auto;max-width:100%;
  max-height:calc(var(--pw) * 1.4);border:1px solid var(--line-strong)}
/* A trading card is 2.5 x 3.5in. The empty state holds that ratio so the
   header does not change height depending on whether an image cleared,
   but it is deliberately NOT a card: no rounded corner, no gradient, no
   silhouette. It is a ruled field with a sentence in it. */
.plate-empty{
  aspect-ratio:5/7;border:1px solid var(--line-strong);background:var(--panel);
  display:flex;align-items:center;justify-content:center;padding:14px;
  font-size:12px;letter-spacing:0;line-height:1.7;
  color:var(--ink-4);text-align:center;
}
/* 12px: this caption runs a full 87-character sentence explaining why a
   plate may be empty, so it is body copy rather than a caption label. */
.plate figcaption{font-size:12px;line-height:1.6;color:var(--ink-4);max-width:60ch}
h1{
  margin:0 0 18px;font-size:clamp(1.5rem,1rem + 2.2vw,2.5rem);line-height:1.12;
  font-weight:700;letter-spacing:-.02em;color:var(--ink-head);text-wrap:balance;
}
/* A field strip, not a grid of boxes. Rules run top and bottom of the
   whole band rather than around each cell, so a row that does not fill
   its last column leaves no orphan block hanging in the layout. */
.ident{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(132px,1fr));
  gap:0 22px;margin:0;padding:12px 0 0;
  border-top:1px solid var(--line-strong);border-bottom:1px solid var(--line-strong);
}
.ident>div{padding:0 0 12px}
.ident dt{
  font-size:12px;letter-spacing:.15em;text-transform:uppercase;color:var(--ink-3);
  margin:0 0 4px;
}
.ident dd{margin:0;font-size:13.5px;color:var(--ink-head)}

.refusal{
  border:1px solid var(--line-strong);background:var(--panel);
  padding:20px 22px;
}
.refusal h2{
  margin:0 0 10px;font-size:12px;letter-spacing:.18em;text-transform:uppercase;
  color:var(--accent);font-weight:600;
}
.refusal p{margin:0;font-size:12px;line-height:1.7;color:var(--ink-2);max-width:60ch}
.refusal .fig{color:var(--ink);white-space:nowrap}

/* ── the spread axis: the page's one authored moment ───── */
.axis-block{margin:30px 0 6px}
.axis-head{
  display:flex;justify-content:space-between;gap:16px;flex-wrap:wrap;
  font-size:12px;letter-spacing:0;color:var(--ink-3);
  padding-bottom:10px;
}
.axis{position:relative;height:66px;border-bottom:1px solid var(--line-strong)}
.grid{position:absolute;bottom:0;top:18px;width:1px;background:var(--line-strong)}
.grid-label{
  position:absolute;top:-15px;left:0;transform:translateX(-50%);
  font-size:12px;letter-spacing:.1em;color:var(--ink-4);white-space:nowrap;
}
.tick{
  position:absolute;bottom:0;width:1px;height:26px;background:var(--ink-3);
  transform-origin:50% 100%;
}
.tick.end{background:var(--accent);height:44px;width:2px}
.tick-label{
  position:absolute;bottom:48px;font-size:12px;letter-spacing:.1em;
  color:var(--ink-4);white-space:nowrap;transform:translateX(-50%);
}
.tick.end .tick-label{color:var(--accent)}
/* The two endpoint labels sit at 0% and 100%, so a centered transform
   hangs them off both edges of the axis — visibly clipped on a phone.
   They anchor inward instead. */
.tick.lo .tick-label{transform:translateX(0)}
.tick.hi .tick-label{transform:translateX(-100%)}
.flat-note{margin:0;font-size:12px;line-height:1.7;color:var(--ink-2);max-width:70ch}
.flat-note b{color:var(--ink-head);font-weight:600}
.flat-note .fig{color:var(--ink-head)}
.axis-ends{display:flex;justify-content:space-between;padding-top:8px;font-size:12px;color:var(--ink-2)}
.axis-ends b{color:var(--ink-head);font-weight:600}
@media (prefers-reduced-motion:no-preference){
  .tick{animation:rise .78s cubic-bezier(.16,1,.3,1) both}
}
@keyframes rise{from{transform:scaleY(.06)}to{transform:scaleY(1)}}

/* ── the grade surface ────────────────────────────────── */
/* The ramp is the app's --series-* table, which is categorical rather than
   status-carrying: none of these five says good or bad, they say "this
   company, not that one". Ordered so the company with the most sales
   behind it gets the accent. Ratios are recorded beside the tokens in
   ThemeContext.jsx; the lowest is 6.16:1, clear of both the 3:1 floor
   WCAG 1.4.11 sets for a graphic a reader must tell apart and the 4.5:1
   text floor the legend keys need. */
.gs{margin:26px 0 6px;padding:0}
/* The box declares the plot's height through aspect-ratio and the label
   row through a fixed line, so the whole figure occupies its final height
   before anything paints. Nothing here can shift after first paint. */
.gs-box{position:relative}
/* aspect-ratio is set inline per view, from that view's own viewBox, so
   the box and the drawing can never disagree. */
.gs-plot{display:block;width:100%;height:auto}
.gs-view--narrow{display:none}
@media (max-width:560px){
  .gs-view--wide{display:none}
  .gs-view--narrow{display:block}
}
.gs-grid{fill:none;stroke:rgba(255,255,255,.08);stroke-width:1}
.gs-frame{fill:none;stroke:rgba(255,255,255,.06);stroke-width:1}
/* Positioned as a percentage of the same coordinate space the plot is
   drawn in, so a label and its gridline cannot drift apart at any width.
   The gutter each view reserves is its own viewBox left padding as a
   share of its width. */
.gs-yl{position:absolute;left:0;padding-right:7px;text-align:right;
  transform:translateY(-50%);font-size:12px;line-height:1;
  color:#8b919a;pointer-events:none}
.gs-view--wide .gs-yl{width:8.75%}
.gs-view--narrow .gs-yl{width:12.19%}
.gs-xrow{position:relative;height:20px;margin-top:2px}
.gs-xl{position:absolute;top:0;transform:translateX(-50%);
  font-size:12px;line-height:1;color:#8b919a}
.gs-line{fill:none;stroke-width:1.5;stroke-linejoin:round;stroke-linecap:round;opacity:.55}
.gs-dot{stroke:#060912;stroke-width:1.25;transform-box:fill-box;transform-origin:center}
/* Opt-IN, like the tick strip above it: a visitor who asked for less
   motion gets the finished surface on the first paint, with every point
   at full size. The animation carries no information the static figure
   does not already show, so there is nothing to substitute for it —
   which is exactly why it is safe to simply not run.
   IT ANIMATES SCALE AND NOT OPACITY, and that is not a style choice.
   A fill-mode of "both" holds the from-state through the delay, so a
   from-state of opacity:0 renders every price as nothing at all until
   its turn comes — and if the animation never runs, which is what a
   screenshot tool, a print stylesheet or a paused tab can each produce,
   the points never appear. Measured: a headless capture of this page with
   an opacity keyframe showed the connecting lines and ONE of fifteen
   points. Scaling from .55 leaves every point visible in that same
   frozen state, which is the whole reason the floor is .55 and not 0. */
@media (prefers-reduced-motion:no-preference){
  .gs-dot{animation:gsPop .42s cubic-bezier(.16,1,.3,1) both}
}
@keyframes gsPop{from{transform:scale(.55)}to{transform:scale(1)}}
.gs-s1{stroke:#5B8DEF}.gs-dot.gs-s1{fill:#5B8DEF}
.gs-s2{stroke:#f59e0b}.gs-dot.gs-s2{fill:#f59e0b}
.gs-s3{stroke:#22d3ee}.gs-dot.gs-s3{fill:#22d3ee}
.gs-s4{stroke:#f472b6}.gs-dot.gs-s4{fill:#f472b6}
.gs-s5{stroke:#f0f0f3}.gs-dot.gs-s5{fill:#f0f0f3}
.gs-s6{stroke:#8b919a}.gs-dot.gs-s6{fill:#8b919a}
.gs-cap{display:flex;justify-content:space-between;align-items:baseline;gap:14px;flex-wrap:wrap;
  margin:10px 0 0;font-size:12px;color:#b0b7c3}
.gs-keys{display:flex;gap:14px;flex-wrap:wrap}
.gs-key{display:inline-flex;align-items:center;gap:6px;font-size:12px;letter-spacing:.04em;
  color:#b0b7c3}
.gs-key::before{content:"";width:9px;height:9px;border-radius:50%;background:currentColor;flex:none}
.gs-key i{font-style:normal;color:#8b919a}
.gs-key.gs-s1::before{background:#5B8DEF}
.gs-key.gs-s2::before{background:#f59e0b}
.gs-key.gs-s3::before{background:#22d3ee}
.gs-key.gs-s4::before{background:#f472b6}
.gs-key.gs-s5::before{background:#f0f0f3}
.gs-key.gs-s6::before{background:#8b919a}
/* At 288px the grade numbers land about nine pixels apart, so the narrow
   view keeps every other one. The labels themselves never shrink — they
   are HTML, and 12px is 12px at every width. */
@media (max-width:400px){
  .gs-view--narrow .gs-xl:nth-child(even){display:none}
}

/* ── the ladder ───────────────────────────────────────── */
.ladder{margin:34px 0 0}
.g-last{font-size:12px;font-weight:600;white-space:normal;overflow-wrap:anywhere;color:var(--ink-3)}
.ladder h2,.notes h2,.method h2,.pages h2{
  margin:0 0 6px;font-size:13.5px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--ink-head);font-weight:700;
}
.lede{margin:0 0 20px;font-size:12px;color:var(--ink-2);max-width:66ch;line-height:1.7}
.warn{color:var(--ink-head)}
.scroller{overflow-x:auto;max-width:100%}
.scroller:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
table.grades{width:100%;border-collapse:collapse;min-width:660px}
/* Row rules are load-bearing on a seven-column table, so they are NOT
   --line: that token composites to 1.12:1 on this ground and left 35
   rows of figures floating with no structure to read across. Scoped
   here rather than changed globally — the app's own token is fine for
   the surfaces it was measured on. */
table.grades th,table.grades td{
  padding:8px 14px 8px 0;border-bottom:1px solid rgba(255,255,255,.13);
  text-align:right;white-space:nowrap;font-weight:400;
}
tbody.group tr:hover td,tbody.group tr:hover th{background:rgba(255,255,255,.05)}
table.grades th:first-child,table.grades td:first-child{text-align:left;padding-left:0}
table.grades td:last-child,table.grades th:last-child{padding-right:0}
/* 11px, not 9.5px: these are the column keys of the page's primary
   instrument, and the whole task is reading across them. No sticky:
   .scroller's overflow-x makes it the sticky scrollport, and it never
   scrolls vertically, so top:0 here was dead code. */
table.grades thead th{
  font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-2);
  border-bottom:1px solid rgba(255,255,255,.18);padding-bottom:9px;
}
/* One header rule for the whole ladder, then a labeled rule per group.
   Columns stay measured against each other all the way down the page —
   which is the only way a PSA 8 can be read against an SGC 8. */
tbody.group>tr.group-head>th{
  text-align:left;white-space:normal;border-bottom:1px solid var(--line-strong);
  padding:26px 0 7px;
}
tbody.group:first-of-type>tr.group-head>th{padding-top:14px}
.gh{
  font-size:12px;letter-spacing:.18em;text-transform:uppercase;
  color:var(--ink-head);font-weight:600;margin-right:12px;
}
.count{font-size:12px;letter-spacing:0;color:var(--ink-3)}
.group-note{display:block;margin:9px 0 2px;font-size:12px;line-height:1.7;
  color:var(--ink-3);max-width:70ch;letter-spacing:0;text-transform:none}
/* Prefixed with table.grades so they outrank the font-weight:400 in
   the cell reset above, which is (0,1,2) to a bare class's (0,1,0) and
   was silently flattening every figure in the ladder to 400 — the one
   weight step that separates a value from its own metadata, and half of
   what separates a confident figure from a demoted one. */
table.grades .g-grade{color:var(--ink-head);font-weight:600;font-size:13.5px}
table.grades .g-value{color:var(--ink-head);font-weight:600;font-size:14.5px}
.g-meta{color:var(--ink-3);font-size:12px}
.g-range{color:var(--ink-2);font-size:12px}
table.grades .g-date{color:var(--ink-2);font-size:12px;font-weight:500}
.g-none{color:var(--ink-3);font-size:12px;font-style:normal}
/* The per-cell column key. Silent above 640px, where the real <thead>
   is doing the job; the phone tier reveals it. Sized and colored
   exactly like that <thead>, because it is that <thead>. */
table.grades .k{display:none}
/* A figure the reader must not take at face value is demoted in its own
   cell. The previous treatment was a 1.02:1 background wash — invisible,
   and two columns away from the number it was qualifying. */
table.grades tr.no-sale .g-value,table.grades tr.disputed .g-value{color:var(--ink-3);font-weight:400}
.g-flag{
  display:inline-block;margin-left:9px;font-size:12px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-4);border:1px solid rgba(255,255,255,.16);
  border-radius:2px;padding:3px 6px;vertical-align:1px;
}

/* ── narrative as annotation ──────────────────────────── */
.notes{margin:52px 0 0;padding-top:26px;border-top:1px solid var(--line-strong)}
.note{
  display:grid;grid-template-columns:minmax(0,22ch) minmax(0,1fr);
  gap:28px;padding:22px 0;border-bottom:1px solid var(--line);
}
.note-anchor{font-size:12px;line-height:1.7}
.note-anchor .k{
  display:block;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-4);
  margin-bottom:6px;
}
.note-anchor .v{color:var(--ink);font-size:12px}
.note-body{font-size:13.5px;line-height:1.75;color:var(--ink-2);max-width:64ch}
.note-body p{margin:0 0 12px}
.note-body p:last-child{margin:0}
.note-body h3{
  margin:0 0 10px;font-size:12px;letter-spacing:.16em;text-transform:uppercase;
  color:var(--ink-3);font-weight:600;
}
.qa{margin:0}
.qa dt{color:var(--ink-head);font-weight:600;margin:0 0 6px}
.qa dd{margin:0 0 18px;color:var(--ink-2)}
.qa dd:last-child{margin:0}

/* ── method / footer ──────────────────────────────────── */
/* Jump nav. Sticky, so the bypass stays reachable once a reader is deep in
   the story. Chips are 44px tall per the touch-target rule and carry the
   --ring token (the audited focus color) rather than the per-vertical glow, which composites as
   low as 2.4:1 and fails WCAG 1.4.11. */
.jump{position:sticky;top:0;z-index:20;margin:26px 0 0;padding:12px 0;
  background:rgba(6,9,18,.92);backdrop-filter:blur(12px);
  border-bottom:1px solid var(--line)}
.jump-label{display:block;margin-bottom:8px;font-size:12px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--ink-4)}
.jump ul{display:flex;flex-wrap:wrap;gap:6px;list-style:none;margin:0;padding:0}
.jump a{display:inline-flex;align-items:center;min-height:44px;padding:0 13px;
  border:1px solid var(--line-strong);border-radius:2px;
  font-size:12px;color:var(--ink-2);text-decoration:none;white-space:nowrap}
.jump a:hover{border-color:var(--accent);color:var(--ink)}
.jump a:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
/* An anchored heading has to clear the sticky bar rather than hide beneath
   it, which is the failure that makes jump navs feel broken. */
section[id]{scroll-margin-top:104px}
@media (prefers-reduced-motion:no-preference){html{scroll-behavior:smooth}}
/* A link inside running prose is underlined, not just recoloured. Color
   alone fails WCAG 1.4.1 and Lighthouse flags it: a reader who cannot
   distinguish the accent from the body color has no cue at all. Links in
   navigation, tables and lists are excluded, where position and context
   already identify them. */
p a,.lede a,dd a{text-decoration:underline;text-underline-offset:2px;
  text-decoration-thickness:1px;text-decoration-color:rgba(91,141,239,.55)}
p a:hover,.lede a:hover,dd a:hover{text-decoration-color:var(--accent)}
.method{margin:48px 0 0;padding-top:26px;border-top:1px solid var(--line-strong)}
.method dl{
  display:grid;grid-template-columns:minmax(0,22ch) minmax(0,1fr);
  gap:10px 28px;margin:18px 0 0;font-size:12px;line-height:1.75;
}
.method dt{letter-spacing:0;font-size:12px;font-weight:700;color:var(--ink-2)}
.method dd{margin:0;color:var(--ink-2);max-width:66ch}
/* Padding is 12px less top and bottom than it was (22/56), because each
   link now carries 12px of its own box above and below its text: the
   whitespace a reader sees is unchanged. align-items:center keeps the
   plain-text span level with the 44px link row beside it on desktop; the
   8px row-gap does the same job between rows once they wrap on a phone. */
footer{margin:52px 0 0;padding:10px 0 44px;border-top:1px solid var(--line);
  font-size:12px;letter-spacing:0;color:var(--ink-4);
  display:flex;gap:8px 20px;flex-wrap:wrap;justify-content:space-between;align-items:center}

/* ── index page ───────────────────────────────────────── */
/* Flex, not block, and that is structural rather than stylistic: the sort
   control works by setting each row's order property, which only a flex or
   grid formatting context reads. Column direction with no explicit order on the
   list itself leaves the default reading exactly as the document is
   written, so an unsorted page is byte-for-byte the arrangement a crawler
   receives. */
.index-list{list-style:none;margin:22px 0 0;padding:0;border-top:1px solid var(--line-strong);
  display:flex;flex-direction:column}
.index-list li{border-bottom:1px solid var(--line)}
.index-list a{
  display:grid;
  grid-template-columns:34px minmax(0,1fr) 176px 150px;
  grid-template-areas:"plate name rail spread" "plate sub rail spread";
  gap:4px 22px;align-items:center;padding:13px 0;border-bottom:none;color:var(--ink);
}
.index-list a:hover{color:var(--ink-head)}
.index-list a:hover .il-spread{color:var(--accent)}
.il-name{grid-area:name;align-self:end;font-size:13.5px}
.il-spread{grid-area:spread;font-size:12px;color:var(--ink-3);text-align:right;white-space:nowrap}
/* The index sub-line is a data summary, not a label: "34 graded prices ·
   244 verified sales · BGS · PSA · SGC" runs 60-67 characters, once per row.
   Uppercase at 11px with .13em tracking made twenty-two of those on one page
   — the shape of every word removed, on the copy that tells a visitor what
   the row actually contains. Sentence case at body size. */
.il-sub{grid-area:sub;align-self:start;font-size:12px;letter-spacing:0;color:var(--ink-3)}

/* ── the plate slot ───────────────────────────────────── */
/* Fixed box whether or not a photograph is licensed for the row, because
   a slot that collapses when empty indents twenty rows one way and one
   row another. 34x48 is the 0.71 ratio a slabbed card presents, so a real
   plate drops in without object-fit having to crop anything meaningful. */
.il-plate{grid-area:plate;position:relative;display:grid;place-items:center;
  width:34px;height:48px;
  border:1px solid var(--line-strong);background:var(--panel);overflow:hidden}
/* Absolute, not width/height:100%. A percentage height resolves against a
   definite containing block, and the grid row here is content-sized, so
   the percentage went indefinite and the image fell back to its intrinsic
   box: a 500x962 scan rendered 32x62 inside a 48px slot and was saved only
   by overflow:hidden. Positioned against the plate's own edges the box is
   definite by construction, and object-fit crops rather than distorts. */
.il-plate img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block}
.il-plate--none b{font-weight:400;font-size:12px;letter-spacing:0;color:var(--ink-4)}

/* ── the row rail ─────────────────────────────────────── */
/* Height is fixed in CSS and the viewBox is stretched to it, so the rail
   occupies its grid track before the SVG is laid out and cannot shift the
   row. non-scaling-stroke is what survives that stretch: without it a
   1-unit tick renders at 0.35px on a 176px track. */
.il-rail{grid-area:rail}
.rail{display:block;width:100%;height:24px}
.rail path{fill:none;vector-effect:non-scaling-stroke;stroke-linecap:butt}
.rb{stroke:var(--line-strong);stroke-width:1}
.rs{stroke:var(--ink-4);stroke-width:3}
.rk{stroke:var(--ink-3);stroke-width:1;opacity:.5}
.rc{stroke:var(--ink-3);stroke-width:1}
.index-list a:hover .rs,.index-list a:focus-visible .rs{stroke:var(--accent)}
.index-list a:hover .rc,.index-list a:focus-visible .rc{stroke:var(--accent)}

/* ── the sort control ─────────────────────────────────── */
/* The inputs are .sr — visually hidden but present and focusable, which is
   the difference between a hidden radio group and a broken one. Every
   checked and focused state below is expressed through a sibling
   combinator from the input, so the control needs no script and no ARIA
   beyond the group label: it IS a radio group. */
.sortbar{display:flex;flex-wrap:wrap;align-items:center;gap:7px 6px;margin:32px 0 0}
.sortbar-k{font-size:12px;letter-spacing:.15em;text-transform:uppercase;
  color:var(--ink-4);margin-right:10px}
.sortbar label{font-size:12px;color:var(--ink-3);padding:5px 11px;
  border:1px solid var(--line);cursor:pointer;-webkit-user-select:none;user-select:none}
.sortbar label:hover{color:var(--ink);border-color:var(--line-strong)}
#s-record:checked~.sortbar label[for="s-record"],#s-spread:checked~.sortbar label[for="s-spread"],#s-sales:checked~.sortbar label[for="s-sales"],#s-year:checked~.sortbar label[for="s-year"]
{color:var(--ink-head);border-color:var(--accent);cursor:default}
#s-record:focus-visible~.sortbar label[for="s-record"],#s-spread:focus-visible~.sortbar label[for="s-spread"],#s-sales:focus-visible~.sortbar label[for="s-sales"],#s-year:focus-visible~.sortbar label[for="s-year"]
{outline:2px solid var(--ring);outline-offset:2px}
#s-spread:checked~.index-list li{order:var(--o-spread)}
#s-sales:checked~.index-list li{order:var(--o-sales)}
#s-year:checked~.index-list li{order:var(--o-year)}

/* ── the spread field ─────────────────────────────────── */
/* The 52px head is the hover label's room. A bar whose top sits at the
   scale's ceiling puts its label above the plot, and without the padding
   that label is clipped by the figure or grows the page. Reserving the
   space is cheaper than flipping the label below the bar, which would put
   it over the very rows it is meant to point at. */
.field{margin:14px 0 0;padding:52px 0 0}
.field-plot{position:relative;width:100%;aspect-ratio:1180/300;
  border-top:1px solid var(--line);border-bottom:1px solid var(--line-strong)}
/* 12px on the field's axis labels, not the app's 10.5px label token.
   The Spread Axis on the dossier pages is the same job on the same
   stylesheet — a value scale with gridline figures and tick labels — and its
   .grid-label and .tick-label have run at 12px/.1em/--ink-4 since it was
   built. The first version of this plot took its sizes from the app's dense
   label role instead, which is right for a field key inside a 900px-wide
   admin panel and wrong for a numeral a reader has to pick off an axis. One
   surface, one size for one job. */
.fg{position:absolute;left:0;right:0;bottom:var(--y);height:0;
  border-top:1px dashed var(--line);pointer-events:none}
.fg i{position:absolute;left:0;bottom:2px;font-style:normal;font-size:12px;
  letter-spacing:.08em;color:var(--ink-4);background:var(--bg);padding-right:7px}
/* The gradient is the grade ladder, abbreviated: a card's poor copies are
   the foot of the bar and its best are the head, so the bar lightens as it
   climbs. The 2px cap is the one figure the whole page is built to protect
   from being read as "the price of the card" — what the DEAREST graded
   copy fetched — and putting it in accent across twenty-one bars draws a
   horizon of top-of-market marks that is worth reading on its own. */
.fb{position:absolute;left:var(--x);bottom:var(--b);height:var(--h);width:6px;
  transform:translateX(-50%);border-bottom:none;
  background:linear-gradient(to top,var(--line-strong),var(--ink-4))}
.fb::after{content:"";position:absolute;left:0;right:0;top:0;height:2px;
  background:var(--accent);opacity:.55}
/* The drawn bar is 6px because the data wants it thin; the target is not.
   This grows the hit area to 20x(height+12) without moving a pixel of ink. */
.fb::before{content:"";position:absolute;inset:-6px -7px}
.fb:hover,.fb:focus-visible{background:var(--ink-2);z-index:3}
.fb:hover::after,.fb:focus-visible::after{opacity:1}
.fb-lab{position:absolute;left:50%;bottom:calc(100% + 9px);transform:translateX(-50%);
  display:none;flex-direction:column;gap:2px;white-space:nowrap;
  font-size:12px;line-height:1.45;color:var(--ink-2);background:var(--bg);
  border:1px solid var(--line-strong);padding:6px 10px;pointer-events:none;z-index:4}
.fb-lab b{color:var(--ink-head);font-weight:700;font-size:12px}
.fb:hover .fb-lab,.fb:focus-visible .fb-lab{display:flex}
.fb--l .fb-lab{left:0;transform:none}
.fb--r .fb-lab{left:auto;right:0;transform:none}
.field-x{position:relative;height:20px;margin-top:5px}
.fx{position:absolute;left:var(--x);transform:translateX(-50%);
  font-size:12px;letter-spacing:.08em;color:var(--ink-4)}
.field figcaption{margin:16px 0 0;font-size:12px;line-height:1.7;
  color:var(--ink-3);max-width:72ch}
@media (prefers-reduced-motion:no-preference){
  .fb{transition:background .12s ease}
  .fb::after{transition:opacity .12s ease}
}

/* ── not-found page ───────────────────────────────────── */
/* Deliberately NOT .file/.file-grid. Those two go display:contents below
   640px so the dossier can lift its columns into main's flex flow and
   put the ladder first — correct there, wrong here: it would drop the
   sentence explaining the error BELOW the list of cards it introduces.
   A one-column block needs no reordering, so it opts out by not
   claiming the classes that do it. */
.nf{padding:44px 0 8px;max-width:66ch}
.nf .refusal{margin-top:26px}
.pages{margin:44px 0 0}

@media (max-width:900px){
  /* Two columns, not one: the plate keeps its place beside the
     identification it identifies, and the refusal — the widest prose on
     the page — takes the full measure underneath rather than being
     squeezed into a 300px column. */
  .file-grid{grid-template-columns:auto minmax(0,1fr);gap:26px 24px}
  /* Same reason as the desktop rule: with the refusal panel spanning both
     tracks the index's title block would sit alone in the plate's
     max-content track and the H1 would size the page. */
  .file-grid--index{grid-template-columns:minmax(0,1fr)}
  .file-grid>div:last-child{grid-column:1/-1}
  .file{padding-top:30px}

  /* The rail drops to its own row before the row is squeezed, because the
     sub-line is what breaks first: at 768px the four-column row left 275px
     for a 60-character summary and every row in the list wrapped to two,
     which turned a scannable column of names into a ragged block. The rail
     gains from the move — full width is more scale than 176px ever was. */
  .index-list a{
    grid-template-columns:34px minmax(0,1fr) auto;
    grid-template-areas:"plate name spread" "plate sub sub" "rail rail rail";
    gap:4px 20px}
  .il-rail{margin-top:8px}
}
@media (max-width:640px){
  .wrap{padding:0 16px}
  body{font-size:13.5px}
  /* Every pixel above the ladder is a pixel the visitor scrolls past to
     reach what they came for, so the header runs tighter on a phone:
     denser identification, a shorter masthead, no oversized title. */
  .top .wrap{padding-top:2px;padding-bottom:2px;gap:8px}
  .crumb{font-size:12px;letter-spacing:.04em}
  /* The last crumb is the item's own name and the H1 repeats it a line
     later. Three wrapped lines of masthead is a high price for a
     duplicate; the JSON-LD BreadcrumbList still carries the level. */
  .crumb-here{display:none}
  h1{margin-bottom:14px;letter-spacing:-.025em}
  .refusal{padding:15px 16px}
  .refusal p{font-size:12px;line-height:1.65}
  /* On a phone the endpoint labels collide with the nearest gridline
     label, and the line directly beneath the axis already names both
     ends with their values. Drop the duplicate, keep the scale. */
  .tick-label{display:none}
  .nf{padding-top:22px}
  .pages{margin-top:34px}
  .note,.method dl{grid-template-columns:minmax(0,1fr);gap:12px}
  .note{padding:18px 0}
  /* The row unstacks rather than shrinking. A 176px rail beside a 150px
     range beside a wrapping card name is four columns inside 358px, and
     the name — the only part a visitor scans for — is the one that loses.
     The plate keeps its column and the row becomes: name, range, what is
     behind the range, then the range drawn. */
  .index-list a{
    grid-template-columns:34px minmax(0,1fr);
    grid-template-areas:"plate name" "plate spread" "sub sub" "rail rail";
    gap:3px 14px;align-items:start;padding:14px 0}
  .il-name{align-self:start}
  .il-spread{text-align:left;align-self:start}
  .il-sub{margin-top:5px}
  .il-rail{margin-top:7px}

  /* A phone has no hover, so the label a bar exists to reveal can never
     be read — a tap would navigate straight past it, and at this pitch it
     would be the wrong bar as often as the right one. The field stays,
     because the picture is the point and it reads at any size; only the
     targets go. Every card in it is a full-width row two scrolls down. */
  .field{padding-top:14px;margin-top:30px}
  .field-plot{aspect-ratio:auto;height:230px}
  .fb{pointer-events:none}
  /* Ten decade marks inside 358px run together into one grey stripe. Every
     other one still names the axis and the gap between two labels is
     unambiguous, because a decade is the only interval on it. */
  .fx:nth-child(even){display:none}
  .field figcaption{margin-top:14px}
  .sortbar{margin-top:26px;gap:6px}
  .sortbar-k{width:100%;margin:0 0 2px}

  /* ── the answer first ─────────────────────────────────
     Measured on the shipped page at 390x844: the first ladder
     row began at y=963, 119px below the fold, and at 360 it
     began at y=1022. A visitor arriving from "1986 Fleer
     Michael Jordan value" met a masthead, a title, six
     identification fields, a refusal panel and a log axis
     before a single price.

     The order is now: the item named, the ladder, then
     everything that explains the ladder. display:contents
     lifts the file header's two columns into main's flex
     flow, so the blocks reorder without being moved, copied
     or duplicated — the DOM order, and with it the screen
     reader's reading order, is unchanged. The only focusable
     element among the reordered blocks is the ladder's own
     scroller, which moves UP, so focus order still tracks
     what is on screen. */
  main.wrap{display:flex;flex-direction:column}
  .file,.file-grid{display:contents}
  /* :first-of-type, not :first-child — the plate is now the grid's first
     child, and :first-child silently stopped matching the identification
     column the moment it was added, dropping 18px of intended breathing
     room without failing anything. */
  .file-grid>div:first-of-type{padding-top:18px}
  /* Mobile order follows the DOM now, and that is a deliberate reversal.
     The ladder used to be order:1 so a visitor who searched "<item> value"
     met a price before anything else. The owner's instruction is that the
     page opens with the story and the data sits below it, on every width,
     and the jump nav is what keeps that from costing the price-seeker
     anything: it is the second thing on the screen and one tap reaches the
     ladder. Without the nav this reordering would be a straight downgrade
     for the most common visit. */
  /* The field, the sort control and the list share order:1 with the
     refusal panel above them. Equal order falls back to document order, so
     the four stay in the sequence they are written in and only the file
     header's columns move — which is the whole point of the reordering. */
  .index-list{order:1}
  .field{order:1}
  .sortbar{order:1}
  .file-grid>div:last-child{order:1}
  .jump{order:2}
  .notes{order:3}
  /* The identification strip is data, so it goes below the story with the
     rest of it. It also has to: measured on a phone it pushed the jump nav
     to y=1193 against an 844px fold, which made the bypass something you
     had to scroll past the page to find. display:contents lets it leave its
     title block and take an order of its own without changing the desktop
     grid, where it still sits under the H1. (No backticks in this comment:
     the whole stylesheet is a JS template literal and one would end it.) */
  .titleblock{display:contents}
  .titleblock h1{order:0}
  .identwrap{order:4}
  .refusal{margin-top:34px}
  /* The plate shares order:4 with the axis rather than taking a number of
     its own. Equal order falls back to DOM order, and the plate is the
     earlier element, so it lands immediately before the grade chart, which
     is where an item's picture belongs once the story has been read.

     What it must never do is land above the story or the jump nav. An
     image in the file header is 240px of masthead between the visitor and
     the first thing they came for, and on this page that is now either the
     story or a tap on the nav. */
  .plate{order:4;margin-top:30px;--pw:150px}
  .axis-block{order:4;margin-top:26px}
  .ladder{order:5}
  .method{order:6}
  footer{order:7}
  .ladder{margin-top:24px}
  /* Identification stays above the ladder — the contract asks for the
     item identified first — but as a wrapping run of key/value pairs
     rather than a 96px column grid, which cost 127px of height for six
     short fields. */
  .ident{display:flex;flex-wrap:wrap;gap:0 18px;padding:9px 0 0}
  .ident>div{display:flex;align-items:baseline;gap:6px;padding-bottom:7px}
  .ident dt{font-size:12px;letter-spacing:.12em;margin:0}
  .ident dd{font-size:12px}

  /* ── one record per grade ─────────────────────────────
     Seven columns cannot be read across 358px of content
     width. On the shipped page only Grade and Value were on
     screen; Last verified sale, Realized range, Sold/comps,
     Depth and Basis were reachable only by panning the
     scroller sideways — and Last verified sale and Basis are
     where this page keeps its honesty. Below 640px the table
     stops being a grid and becomes one block per grade:
     grade and value on the first line, the sale date on the
     second, the remaining evidence as labeled pairs beneath.
     Nothing is dropped, nothing is duplicated, and no
     horizontal scroll remains.

     display:block would strip the table from the
     accessibility tree, so every table element carries an
     explicit ARIA role in the markup; the <thead> stays in
     the DOM, visually hidden, so each cell still resolves its
     own column header. */
  .scroller{overflow-x:visible}
  table.grades{display:block;min-width:0;font-size:12px}
  table.grades>thead{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
    overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}
  /* Selectors carry the full table.grades>tbody.group>tr path on
     purpose: the shorter forms lose to the base sheet's own
     table.grades descendant rules on specificity, and a row that
     silently stays display:table-row lays its seven cells out
     sideways again. */
  table.grades>tbody.group{display:block}
  table.grades>tbody.group>tr.group-head{display:block}
  table.grades>tbody.group>tr.group-head>th{display:block;padding:24px 0 8px}
  table.grades>tbody.group:first-of-type>tr.group-head>th{padding-top:10px}
  table.grades>tbody.group>tr:not(.group-head){
    display:flex;flex-wrap:wrap;align-items:baseline;line-height:1.45;
    column-gap:12px;row-gap:2px;padding:9px 0 10px;
    border-bottom:1px solid rgba(255,255,255,.13);
  }
  /* :not(.group-head) throughout — a bare >th here outranks the base
     sheet's group-head rule and re-imposes nowrap on the group
     headings, which then run off the side of the page. */
  table.grades>tbody.group>tr:not(.group-head)>th,
  table.grades>tbody.group>tr:not(.group-head)>td{
    padding:0;border-bottom:0;text-align:left;white-space:nowrap;min-width:0;
  }
  table.grades>tbody.group>tr:not(.group-head)>th.g-grade{flex:0 0 auto;min-width:60px}
  table.grades>tbody.group>tr:not(.group-head)>td.g-value{flex:1 1 auto;text-align:right}
  /* The date holds a line of its own: it is the column that turns a
     figure into evidence, and it is the one a zero-sold row replaces
     with "no verified sale on record" rather than a borrowed date. */
  table.grades>tbody.group>tr:not(.group-head)>td.g-date{flex:0 0 100%;white-space:normal}
  /* The remaining evidence flows as a wrapping run. Each pair stays
     whole — a figure never breaks across lines — and the run is allowed
     to spill onto a third line rather than shrink a figure to fit. */
  table.grades>tbody.group>tr:not(.group-head)>td.g-range,
  table.grades>tbody.group>tr:not(.group-head)>td.g-meta{flex:0 1 auto;max-width:100%}
  /* 11px in --ink-2 (9.86:1) — the same size and color as the <thead>
     these keys come from. The Column-Key Exception applies here more
     than anywhere: on a phone these ARE the column headers. */
  table.grades .k{display:inline;margin-right:5px;font-size:12px;font-weight:400;
    letter-spacing:.14em;text-transform:uppercase;color:var(--ink-2)}
}