/* ==========================================================================
   freelist.lol

   One file, no build step, no CDN but the type. The board is set the way a
   newspaper sets a classified page: ruled rather than boxed, agate rather
   than cards, and dense enough that the top of the list is on screen without
   anybody scrolling for it.

   One monospace family does the whole page, masthead included, and is told
   apart by weight, size and tracking alone. Two rules the palette keeps: no
   gradients anywhere, and one accent — press red, spent on exactly three
   things, whoever is #1, the fact that something is live, and the price.

   The one colour that is not ours is --brand on a listing's logo tile, which
   is that site's own theme-color read off its page. Three pixels of it. A
   hundred strangers' palettes let loose on one page is not a page any more.

   Light is the default. Dark is either the system preference or an explicit
   choice, and the explicit choice — a data-theme attribute written by the
   masthead toggle — always wins, so every dark token appears twice below.
   ========================================================================== */

:root{
  --bg:#eae6db;            /* newsprint. greyer and duller than cream */
  --bg-sunk:#e0dbcd;
  --ink:#141310;
  --ink-2:#4c4941;
  --ink-3:#7e7b71;
  --rule:#c6c2b2;          /* hairline rules between entries */
  --rule-strong:#a9a493;   /* a rule that is a rule, without closing anything */
  --rule-2:#141310;        /* the heavy rules that divide sections */
  --red:#b32218;           /* press red. the one accent */
  --red-sunk:#f0e3dc;
  /* The stock the lead entry is printed on. A screen laid over the newsprint,
     warmed very slightly toward the accent so it belongs to the red rule above
     it rather than looking like a highlight somebody dragged over the page.
     Darker than the paper here and lighter than it in the dark theme, because
     what has to stay the same is that it reads as a panel, not which way it
     went to get there. */
  --lead:#e3dbc9;

  /* One family for the whole page, masthead included. Everything is told
     apart by weight, size and tracking alone — which is the constraint a
     wire-service printer worked under, and it holds the page together. */
  --mono:"IBM Plex Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
  --display:var(--mono);
  --text:var(--mono);
  --agate:var(--mono);

  --lane:990px;
  --gutter:20px;
  color-scheme:light;
}

@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]){
    --bg:#121310;--bg-sunk:#1a1b17;
    --ink:#eae7dd;--ink-2:#a6a398;--ink-3:#76746b;
    --rule:#31332c;--rule-strong:#4a4d43;--rule-2:#eae7dd;
    --red:#e0563f;--red-sunk:#26150f;
    --lead:#1c1a15;
    color-scheme:dark;
  }
}
:root[data-theme="dark"]{
  --bg:#121310;--bg-sunk:#1a1b17;
  --ink:#eae7dd;--ink-2:#a6a398;--ink-3:#76746b;
  --rule:#31332c;--rule-strong:#4a4d43;--rule-2:#eae7dd;
  --red:#e0563f;--red-sunk:#26150f;
  --lead:#1c1a15;
  color-scheme:dark;
}

*{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--bg);color:var(--ink);
  font-family:var(--text);font-size:15px;line-height:1.55;
  -webkit-font-smoothing:antialiased;
}
/* A trace of paper tooth. Noise, not a gradient. */
body::before{
  content:"";position:fixed;inset:0;z-index:0;pointer-events:none;opacity:.35;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='150' height='150'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .42 0'/></filter><rect width='150' height='150' filter='url(%23n)'/></svg>");
}
.wrap,.topbar,.foot{position:relative;z-index:1}
a{color:inherit}
:focus-visible{outline:2.5px solid var(--red);outline-offset:2px}

/* ------------------------------------------------------------- the masthead */
/* Three bands, each closed by a rule a little lighter than the one above it,
   so the whole thing reads as one masthead rather than three strips that
   happen to be stacked: the name, then the dateline, then the index.

   The weights are the ones a broadsheet uses and they are the only ones on
   the page. Thick over thin under the name and nowhere else. A double rule
   closes a block. A hairline separates things inside one. Red is #1, and
   nothing else is red. */
.topbar{max-width:var(--lane);margin:0 auto;padding:16px var(--gutter) 0}

.masthead{
  display:grid;grid-template-columns:1fr auto 1fr;align-items:center;
  gap:0 16px;padding-bottom:12px;
}
/* The sections, when there is no room to print them. Hidden on a wide screen,
   where the column it sits in has nothing in it but the job of keeping the
   name in the middle of the page rather than in the middle of what is left
   over beside the toggle. */
/* The columns are pinned rather than left to auto-placement, because on a
   wide screen the hamburger is display:none — and a display:none grid item is
   not placed at all, so the name would fall into the first column and the
   toggle into the second, leaving the third empty and the whole nameplate
   sitting a quarter of the page left of where it belongs. */
.hamburger{
  grid-column:1;
  display:none;justify-self:start;
  border:1px solid var(--rule);background:transparent;color:var(--ink-2);
  width:40px;height:40px;padding:0;cursor:pointer;place-items:center;
  transition:color .14s ease,border-color .14s ease;
}
.hamburger:hover{color:var(--red);border-color:var(--red)}
.hamburger-bars{display:block;width:16px}
.hamburger-bars i{
  display:block;height:2px;background:currentColor;
  transition:transform .2s cubic-bezier(.2,.8,.3,1),opacity .12s ease;
}
.hamburger-bars i + i{margin-top:4px}
/* Three rules become two crossed ones, which is the same three rules the
   masthead is made of doing something else. */
[data-nav="open"] .hamburger-bars i:nth-child(1){transform:translateY(6px) rotate(45deg)}
[data-nav="open"] .hamburger-bars i:nth-child(2){opacity:0}
[data-nav="open"] .hamburger-bars i:nth-child(3){transform:translateY(-6px) rotate(-45deg)}

.brand{
  grid-column:2;
  display:block;justify-self:center;text-align:center;text-decoration:none;
  line-height:.95;white-space:nowrap;
}
.brand-name{
  font-family:var(--mono);font-weight:700;
  font-size:clamp(1.6rem,4.6vw,2.5rem);letter-spacing:-.05em;
}
.brand-tld{color:var(--red)}
.brand-mark{display:none}

.themer{
  grid-column:3;
  justify-self:end;
  border:1px solid var(--rule);background:transparent;color:var(--ink-3);
  width:30px;height:30px;padding:0;cursor:pointer;display:grid;place-items:center;
  transition:color .14s ease,border-color .14s ease;
}
.themer:hover{color:var(--red);border-color:var(--red)}
.themer svg{width:15px;height:15px}
.themer-sun{display:none}
:root[data-theme="dark"] .themer-moon{display:none}
:root[data-theme="dark"] .themer-sun{display:block}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .themer-moon{display:none}
  :root:not([data-theme="light"]) .themer-sun{display:block}
}

/* thick over thin: the rule a masthead always sits on */
.masthead-rule{border-top:4px solid var(--rule-2);border-bottom:1px solid var(--rule-2);height:4px}

/* One line under the nameplate: the index on the left, the dateline on the
   right, which is where a broadsheet puts both of them.

   Neither half may wrap. As the page narrows the dateline gives up what it
   can afford to lose, in order of how much it was saying: the date, then the
   running total, then the word "reading", then today's readers. The sections
   give up their "The" at the same time. What is left at 320 pixels is five
   sections, a live count and the price, on one line, which is the shortest
   honest version of this bar. */
.strip{
  display:flex;align-items:center;justify-content:space-between;gap:0 14px;
  padding:7px 0 8px;border-bottom:1px solid var(--rule-2);
}

.nav{display:flex;align-items:center;flex:0 0 auto;flex-wrap:nowrap;margin-left:-11px}
.nav-link{
  position:relative;
  font-family:var(--agate);font-size:.62rem;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
  text-decoration:none;color:var(--ink-3);padding:5px 11px;white-space:nowrap;
  transition:color .14s ease;
}
.nav-link:hover{color:var(--ink)}
/* the mark under the section you are in, drawn from the middle out */
.nav-link::after{
  content:"";position:absolute;left:11px;right:11px;bottom:1px;height:2px;
  background:var(--red);transform:scaleX(0);transform-origin:50% 50%;
  transition:transform .18s cubic-bezier(.2,.8,.3,1);
}
.nav-link:hover::after{transform:scaleX(.45)}
.nav-link.is-on{color:var(--ink)}
.nav-link.is-on::after{transform:scaleX(1)}

.pulse{
  display:flex;align-items:center;justify-content:flex-end;gap:0 10px;
  /* Neither half of this line shrinks. If they do not fit, the page must say
     so by scrolling rather than by quietly clipping the price off the end,
     which is the one thing on the line worth reading. Everything below is
     tuned so that never happens. */
  flex:0 0 auto;flex-wrap:nowrap;margin:0;
  font-family:var(--agate);font-size:.62rem;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-3);
  white-space:nowrap;
}
.pulse b{color:var(--ink);font-weight:700}
.pulse-sep{color:var(--rule)}
.pulse-live{color:var(--ink-2)}
.pulse-dot{
  display:inline-block;width:6px;height:6px;background:var(--red);border-radius:50%;
  margin-right:6px;vertical-align:baseline;animation:blink 2.6s ease-in-out infinite;
}
.pulse-price{color:var(--red);font-weight:700}
@keyframes blink{0%,100%{opacity:1}50%{opacity:.28}}

/* --------------------------------------------------------- the drawer
   Below the width where the sections and the dateline both fit on one line,
   the dateline keeps the line and the sections move into a drawer. That way
   round because the dateline is the thing worth reading — it says what the
   board costs — and a section index is the thing a reader goes looking for
   rather than reads in passing.

   With no JavaScript the drawer cannot open. Every destination in it is also
   a link in the footer, and the name in the masthead is the way back to the
   list, so nothing here is only reachable through it. */
.nav-head,
.nav-foot,
.scrim{display:none}

@media (max-width:720px){
  .hamburger{display:grid}

  /* The dateline gets the whole line to itself and keeps all of it. */
  .strip{justify-content:center}

  .nav{
    position:fixed;z-index:60;top:0;bottom:0;left:0;
    width:min(78vw,290px);
    display:flex;flex-direction:column;align-items:stretch;margin:0;
    background:var(--bg);border-right:3px double var(--rule-2);
    transform:translateX(-100%);
    /* Off the screen is not out of the way: a drawer that is only translated
       is still in the tab order, and tabbing into something nobody can see is
       worse than not having it. */
    visibility:hidden;
    transition:transform .26s cubic-bezier(.2,.8,.3,1),visibility 0s linear .26s;
    box-shadow:none;
    overflow-y:auto;-webkit-overflow-scrolling:touch;
  }
  [data-nav="open"] .nav{
    transform:none;visibility:visible;
    transition:transform .26s cubic-bezier(.2,.8,.3,1),visibility 0s;
    box-shadow:0 0 0 100vmax rgba(20,19,16,.001);
  }

  .nav-head{
    display:flex;align-items:center;justify-content:space-between;gap:10px;
    padding:14px 16px 12px;border-bottom:4px solid var(--rule-2);
  }
  .nav-title{
    font-family:var(--agate);font-size:.66rem;font-weight:700;
    letter-spacing:.22em;text-transform:uppercase;color:var(--ink);
  }
  .nav-shut{
    border:0;background:transparent;color:var(--ink-3);cursor:pointer;
    width:34px;height:34px;margin:-6px -8px -6px 0;padding:0;display:grid;place-items:center;
  }
  .nav-shut:hover{color:var(--red)}
  .nav-shut svg{width:16px;height:16px}

  .nav-link{
    display:block;font-size:.8rem;letter-spacing:.08em;
    padding:14px 16px;border-bottom:1px solid var(--rule);color:var(--ink-2);
  }
  /* In a drawer the mark goes down the leading edge, where a reader's eye
     already is, rather than under a word in a row of words. */
  .nav-link::after{
    left:0;right:auto;top:0;bottom:0;width:3px;height:auto;
    transform:scaleY(0);transform-origin:50% 50%;
    transition:transform .18s cubic-bezier(.2,.8,.3,1);
  }
  .nav-link:hover::after{transform:scaleY(.4)}
  .nav-link.is-on{color:var(--ink)}
  .nav-link.is-on::after{transform:scaleY(1)}
  /* The drawer is where there is room to say it in full. */
  .nav-the{display:inline!important}

  .nav-foot{
    display:block;margin-top:auto;padding:14px 16px;
    border-top:3px double var(--rule-2);
    font-family:var(--agate);font-size:.62rem;letter-spacing:.06em;text-transform:uppercase;
    color:var(--red);font-weight:700;
  }

  /* The masthead sets z-index:1 on itself so the paper grain sits behind it,
     and that makes a stacking context: the drawer's z-index:60 counts only
     inside the masthead, which as a whole is still at 1. Without this line
     the scrim covers the drawer it is supposed to sit behind and every link
     in it stops being clickable. */
  [data-nav="open"] .topbar{z-index:60}

  .scrim{
    display:block;position:fixed;inset:0;z-index:55;
    background:rgba(20,19,16,.42);
    opacity:0;pointer-events:none;transition:opacity .26s ease;
  }
  [data-nav="open"] .scrim{opacity:1;pointer-events:auto}
  [data-nav="open"]{overflow:hidden}
}

/* --------------------------------------------------------------- the notice */
.wrap{max-width:var(--lane);margin:0 auto;padding:0 var(--gutter)}

.notice{padding:18px 0 14px;border-bottom:3px double var(--rule-2)}

.notice-kicker{
  font-family:var(--agate);font-size:.63rem;letter-spacing:.26em;text-transform:uppercase;
  color:var(--red);margin:0 0 6px;
}
.claim{
  font-family:var(--display);font-weight:900;
  font-size:clamp(1.45rem,3.5vw,2.1rem);line-height:1.1;letter-spacing:-.045em;
  margin:0 0 9px;
}
.claim-price{color:var(--red)}
.lede{
  margin:0 0 7px;max-width:none;color:var(--ink-2);font-size:.84rem;line-height:1.55;
}
.lede-lead{color:var(--ink);font-weight:600}

/* A cross-reference, set the way a paper sets one: agate, quieter than the
   lede above it, and pointing further down the page. */
.lede-note{
  margin:0 0 15px;
  font-family:var(--agate);font-size:.63rem;letter-spacing:.03em;
  color:var(--ink-3);line-height:1.5;
}
.lede-note a{color:var(--red);text-decoration:none;border-bottom:1px solid currentColor}
.lede-note a:hover{background:var(--red);color:var(--bg);border-color:var(--red)}

/* ----------------------------------------------------------------- the press */
.deck{position:relative}

.press{display:flex;gap:0;align-items:stretch;flex-wrap:wrap}
.press-field{
  flex:1 1 260px;display:flex;align-items:center;gap:10px;min-width:0;
  border:1.5px solid var(--rule-2);border-right:0;background:var(--bg-sunk);padding:0 14px;
}
.press-glyph{color:var(--red);display:flex;flex:0 0 auto}
.press-glyph svg{width:17px;height:17px}
.press-input{
  flex:1;min-width:0;border:0;background:transparent;color:var(--ink);
  font-family:var(--agate);font-size:.98rem;padding:14px 0;
}
.press-input::placeholder{color:var(--ink-3)}
.press-input:focus{outline:none}
.press-go{
  flex:0 0 auto;border:1.5px solid var(--rule-2);background:var(--rule-2);color:var(--bg);
  cursor:pointer;font-family:var(--agate);font-weight:700;font-size:.74rem;
  letter-spacing:.14em;text-transform:uppercase;padding:0 30px;position:relative;
}
.press-go:hover{background:var(--red);border-color:var(--red);color:#fff}
.press-go:active{transform:translateY(1px)}
.press-go-busy{display:none}

/* The machine runs through six states and assets/freelist.js is the only
   thing that touches any of them: feed, read, print, done, file, back to
   idle — plus jam when the far end refused, and static for a page that
   arrived with the printout already on it because there was no JavaScript
   to run the sequence. The three timings it hands over as custom properties
   are used here rather than guessed at, so the CSS and the script never
   drift apart. */
.deck[data-state="feed"] .press-go-label,
.deck[data-state="read"] .press-go-label,
.deck[data-state="print"] .press-go-label,
.deck[data-state="done"] .press-go-label,
.deck[data-state="file"] .press-go-label{display:none}
.deck[data-state="feed"] .press-go-busy,
.deck[data-state="read"] .press-go-busy,
.deck[data-state="print"] .press-go-busy,
.deck[data-state="done"] .press-go-busy,
.deck[data-state="file"] .press-go-busy{display:inline}

.press-hint{
  font-family:var(--agate);font-size:.63rem;letter-spacing:.03em;color:var(--ink-3);
  margin:11px 0 0;
}
.trap{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}

/* The press.
   At rest it is nothing at all: the rule that closes this block is the one
   the whole page already has, and a second rule seventeen pixels above it
   reads as an accident rather than as a machine. When something is submitted
   a slot opens here and the printout feeds out of it, which is the only time
   there is anything to see. A newspaper page is made of rules, and one of
   them behaving like a slot is stranger than any illustration of a machine. */
.machine{display:block;margin:0}
.machine-lip{
  display:block;position:relative;height:0;background:var(--rule-2);
  transition:height var(--feed-ms,460ms) cubic-bezier(.3,.8,.4,1);
}
.deck[data-state="feed"] .machine-lip,
.deck[data-state="read"] .machine-lip,
.deck[data-state="print"] .machine-lip,
.deck[data-state="done"] .machine-lip,
.deck[data-state="file"] .machine-lip{height:16px}

.machine-slot{
  position:absolute;left:12px;right:12px;top:50%;height:0;
  background:var(--bg);transform:translateY(-50%);
  transition:height .18s ease;
}
.deck[data-state="read"] .machine-slot,
.deck[data-state="print"] .machine-slot,
.deck[data-state="done"] .machine-slot,
.deck[data-state="file"] .machine-slot{height:3px}

.machine-scan{
  position:absolute;top:50%;left:12px;width:44px;height:3px;background:var(--red);
  transform:translateY(-50%);opacity:0;
}
.deck[data-state="read"] .machine-scan{opacity:1;animation:scan 1.05s linear infinite}
@keyframes scan{from{left:12px}to{left:calc(100% - 56px)}}

/* The print head, laying a line down as the sheet comes out. */
.deck[data-state="print"] .machine-scan{
  opacity:1;left:12px;right:12px;width:auto;
  animation:none;background:var(--red);
}

.machine-leds{
  position:absolute;right:14px;top:50%;transform:translateY(-50%);
  display:flex;gap:4px;opacity:0;transition:opacity .18s ease;
}
.deck[data-state="feed"] .machine-leds,
.deck[data-state="read"] .machine-leds,
.deck[data-state="print"] .machine-leds,
.deck[data-state="done"] .machine-leds,
.deck[data-state="file"] .machine-leds{opacity:1}
.machine-leds i{width:3px;height:3px;background:var(--bg)}
.deck[data-state="read"] .machine-leds i{animation:tick 1.05s steps(1) infinite}
.deck[data-state="read"] .machine-leds i:nth-child(2){animation-delay:.35s}
.deck[data-state="read"] .machine-leds i:nth-child(3){animation-delay:.7s}
@keyframes tick{0%,32%{background:var(--red)}33%,100%{background:var(--bg)}}
.machine-feed{display:none}

/* What the machine says it is doing, in the agate a paper sets a caption in. */
.slip{
  font-family:var(--agate);font-size:.66rem;letter-spacing:.12em;text-transform:uppercase;
  color:var(--ink-3);margin:10px 0 0;min-height:1em;
}
.deck[data-state="idle"] .slip,
.deck[data-state="static"] .slip,
.deck[data-state="jam"] .slip{display:none}

/* The printout, fed out of the slot at the height and speed the script
   measured for this particular sheet rather than a length picked in advance. */
.out{overflow:hidden;max-height:0}
.deck[data-state="print"] .out{
  max-height:var(--sheet-h,560px);
  transition:max-height var(--print-ms,700ms) linear;
}
.deck[data-state="done"] .out{max-height:var(--sheet-h,560px)}
.deck[data-state="file"] .out{max-height:0;transition:max-height 420ms cubic-bezier(.4,0,.7,.4)}
/* No JavaScript: the page arrived with the sheet already printed. */
.deck[data-state="static"] .out{max-height:none;overflow:visible}
.out-beam{display:none}
.out-paper{padding-top:16px}

.sheet{border:1.5px solid var(--rule-2);background:var(--bg-sunk);padding:0}
.sheet-top{
  display:flex;justify-content:space-between;gap:10px;padding:6px 12px;
  border-bottom:1px solid var(--rule-2);
  font-family:var(--agate);font-size:.61rem;letter-spacing:.18em;text-transform:uppercase;
}
.sheet-stamp{color:var(--red);font-weight:700}
.sheet-kind{color:var(--ink-3)}
.sheet-body{display:flex;gap:14px;padding:13px 12px}
.sheet-shot{flex:0 0 168px;border:1px solid var(--rule);overflow:hidden;aspect-ratio:16/9}
.sheet-shot img,.sheet-shot svg{display:block;width:100%;height:100%;object-fit:cover}
.sheet-text{min-width:0;flex:1}
.sheet-id{display:flex;align-items:center;gap:9px;margin-bottom:5px}
.sheet-icon{flex:0 0 auto;width:26px;height:26px;border:1px solid var(--rule);display:grid;place-items:center;overflow:hidden}
.sheet-glyph{font-family:var(--display);font-weight:700;font-size:.78rem}
.sheet-title{font-family:var(--display);font-weight:700;font-size:.94rem;line-height:1.2;letter-spacing:-.02em}
.sheet-tagline{margin:0 0 4px;font-size:.78rem;color:var(--ink-2);line-height:1.45}
.sheet-tagline em{color:var(--ink-3)}
.sheet-url{margin:0;font-family:var(--agate);font-size:.66rem;color:var(--ink-3);letter-spacing:.06em}
.sheet-foot{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  padding:7px 12px;border-top:1px solid var(--rule);
}
.sheet-tags{display:flex;gap:5px;flex-wrap:wrap;list-style:none;margin:0;padding:0}
.sheet-tagchip{
  font-family:var(--agate);font-size:.6rem;letter-spacing:.06em;color:var(--ink-3);
  border:1px solid var(--rule);padding:1px 6px;
}
.sheet-tagchip.is-ok{color:var(--red);border-color:var(--red)}
.sheet-rank{margin-left:auto;font-family:var(--agate);font-weight:700;font-size:.8rem;letter-spacing:.1em;color:var(--red)}

.jam{
  display:flex;align-items:center;gap:8px;margin:12px 0 0;
  font-family:var(--agate);font-size:.74rem;color:var(--red);
  border-left:3px solid var(--red);padding:6px 10px;background:var(--red-sunk);
}
.jam[hidden]{display:none}
.jam-mark svg{width:15px;height:15px;display:block}

/* ------------------------------------------------------------- the list bar */
.listbar{
  display:flex;align-items:baseline;justify-content:space-between;gap:6px 16px;flex-wrap:wrap;
  padding:14px 0 9px;
}
.listbar-title{
  font-family:var(--display);font-weight:700;font-size:.92rem;
  letter-spacing:.14em;text-transform:uppercase;margin:0;
}
.listbar-note{
  margin:0;font-family:var(--agate);font-size:.66rem;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-3);display:flex;gap:0 10px;flex-wrap:wrap;align-items:baseline;
}
.listbar-note b{color:var(--ink)}
.listbar-held{color:var(--ink-2)}
.listbar-note a{color:var(--red);text-decoration:none;border-bottom:1px solid currentColor}

/* ---------------------------------------------------------------- the entries */
.list{
  list-style:none;margin:0;padding:0;
  /* A column of a newspaper ends on a rule. Without this the list trails off
     into the gap above the pile and stops looking like a column. */
  border-bottom:1px solid var(--rule-2);
}
.row{
  display:flex;align-items:flex-start;gap:16px;
  padding:10px 0 11px;border-bottom:1px solid var(--rule);
  position:relative;
}
.row:last-child{border-bottom:0}

.rank{
  flex:0 0 auto;width:42px;padding-top:6px;
  font-family:var(--agate);font-size:.62rem;letter-spacing:0;color:var(--ink-3);
  white-space:nowrap;
}
.rank::before{content:"No."}
.rank-n{color:var(--ink-2);font-weight:700}

.row-logo{
  position:relative;
  flex:0 0 auto;width:46px;height:46px;display:grid;place-items:center;overflow:hidden;
  border:1px solid var(--rule);background:var(--bg-sunk);text-decoration:none;
}

/* The listing's own colour, off its page's theme-color or out of its logo.
   A rule down the leading edge of the tile and nothing else: it is enough to
   say whose listing this is, and small enough that a hundred of them still
   add up to a newspaper rather than a paint chart. */
.row-logo.has-brand{border-left-color:transparent}
.row-logo.has-brand::before{
  content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
  background:var(--brand,var(--rule));
}
.row.is-first .row-logo.has-brand::before{width:4px}
.row-logo img{width:100%;height:100%;object-fit:cover;display:block}
.row-glyph{font-family:var(--display);font-weight:700;font-size:1.1rem;color:var(--ink)}

.row-main{flex:1 1 auto;min-width:0}

/* title, dot leader, price — a contents page, and the price is the joke */
.row-head{display:flex;align-items:baseline;gap:0;margin:0 0 4px;line-height:1.35}
.row-title{
  flex:0 1 auto;min-width:0;
  font-family:var(--display);font-weight:700;font-size:.93rem;line-height:1.35;
  text-decoration:none;letter-spacing:-.025em;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.row-title:hover{color:var(--red)}
.row-dots{
  flex:1 1 auto;min-width:14px;align-self:center;
  height:0;border-bottom:2px dotted var(--rule);margin:0 10px;position:relative;top:-3px;
}
.row-price{
  flex:0 0 auto;
  font-family:var(--agate);font-weight:700;font-size:.68rem;letter-spacing:.06em;text-transform:uppercase;
  color:var(--ink-2);
}

/* The description gets the whole measure to itself, and the details sit
   under it rather than beside it. */
.row-tagline{
  margin:0 0 5px;font-size:.78rem;line-height:1.45;color:var(--ink-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.row-meta{
  margin:0;font-family:var(--agate);font-size:.6rem;letter-spacing:.03em;text-transform:uppercase;
  color:var(--ink-3);display:flex;gap:0 8px;flex-wrap:wrap;line-height:1.4;
}
.row-dot{color:var(--rule)}
.row-clicks{color:var(--ink-2)}
.row-held{color:var(--red);font-weight:700}

/* Whoever is #1: a screened panel with red ink on it and a heavier rule under
   it. The rule is its own weight on purpose. A double rule is what closes a
   block on this page, and using it here put two of them within a few pixels of
   each other and made the lead entry look like the end of something.

   The panel runs a little wider than the measure and bleeds into the gutter,
   which is what a paper does with a boxed lead and is the reason it reads as
   printed rather than as a div somebody gave a background to. */
.row.is-first{
  background:var(--lead);
  border-top:2px solid var(--red);
  border-bottom:2px solid var(--rule-2);
  /* Out to the gutter exactly, which is a twenty-pixel overhang on a desktop
     and a full bleed to the edge of the screen on a phone. The panel comes out
     the same width as the page's own border box either way, so it never makes
     the page scroll. */
  padding:12px var(--gutter) 13px;
  margin:0 calc(var(--gutter) * -1);
}
/* The logo tile is sunk out of the page, and on the panel the page is the
   panel — otherwise it is a lighter square sitting on a darker one. */
.row.is-first .row-logo{background:transparent;border-color:var(--rule-strong)}
/* The brand edge still has to be the only thing on that side, or the border
   this just set draws underneath it. */
.row.is-first .row-logo.has-brand{border-left-color:transparent}
.row.is-first .row-dots{border-bottom-color:var(--red)}
.row.is-first .rank{color:var(--red)}
.row.is-first .rank-n{color:var(--red)}
.row.is-first .row-title{font-size:1.02rem;font-weight:700}
.row.is-first .row-logo{border-color:var(--rule-2)}
.row.is-first .row-price{color:var(--red)}
.row.is-first .row-dots{border-bottom-color:var(--red)}

/* a row that has just landed */
.row.is-new{animation:land .6s ease backwards}
@keyframes land{from{opacity:0;transform:translateY(-8px)}}

/* ------------------------------------------------------------------ the pile */
/* Nothing is deleted for getting old, it just stops being on the list — so
   the rest of the board is drawn as what it is: a heap by the back door.
   Every piece is still a working link. The oldest are at the bottom because
   they landed first, and whatever fell off the list most recently is the last
   thing thrown on, sitting on the crest.

   Layout is a height map: the ground is sampled across the width, each piece
   is thrown at a centre-weighted x, lands on whatever is highest under its
   span, and sinks into it. A mound is what falls out of that — it is not a
   shape anything draws. Placement is seeded off each listing's own id, so a
   resize rebuilds the same heap rather than shuffling it. */
.pile{margin:38px 0 0;padding-top:18px;border-top:3px double var(--rule-2)}
.pile-head{display:flex;align-items:baseline;gap:8px 16px;flex-wrap:wrap;margin-bottom:5px}
.pile-title{
  font-family:var(--display);font-weight:700;font-size:.92rem;
  letter-spacing:.14em;text-transform:uppercase;margin:0;
}
.pile-note{margin:0;font-size:.78rem;color:var(--ink-2);max-width:74ch}
.pile-note b{color:var(--ink);font-weight:600}
.pile-hint{
  margin:9px 0 0;font-family:var(--agate);font-size:.62rem;letter-spacing:.05em;
  text-transform:uppercase;color:var(--ink-3);
}

/* With no JavaScript this is a wall of links that wraps, which is the same
   idea and reads fine. The heap is what JavaScript adds on top. */
.bricks{
  display:flex;flex-wrap:wrap;gap:5px;
  margin-top:16px;padding-bottom:2px;
}
.brick{
  display:inline-block;position:relative;text-decoration:none;
  font-family:var(--agate);font-size:.62rem;line-height:1.25;letter-spacing:0;
  padding:6px 10px 7px;white-space:nowrap;max-width:100%;
  overflow:hidden;text-overflow:ellipsis;
  color:var(--ink-2);background:var(--bg-sunk);border:1px solid var(--rule);
  transition:transform .16s cubic-bezier(.2,.8,.3,1),box-shadow .16s ease,color .16s ease;
}
.brick:hover{color:var(--red);border-color:var(--red)}

/* --- the heap ------------------------------------------------------------ */
.bricks.is-piled{
  display:block;position:relative;margin-top:22px;padding:0;
  border-bottom:2px solid var(--rule-2);   /* the ground it sits on */
}
.bricks.is-piled .brick{
  position:absolute;left:0;bottom:0;margin:0;
  transform:rotate(var(--r,0deg));
  transform-origin:50% 50%;
  opacity:var(--o,1);
  box-shadow:0 1px 0 rgba(20,19,16,.16),0 3px 7px -3px rgba(20,19,16,.4);
}
:root[data-theme="dark"] .bricks.is-piled .brick{
  box-shadow:0 1px 0 rgba(0,0,0,.5),0 3px 8px -3px rgba(0,0,0,.7);
}
@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .bricks.is-piled .brick{
    box-shadow:0 1px 0 rgba(0,0,0,.5),0 3px 8px -3px rgba(0,0,0,.7);
  }
}

/* A piece shows whose it is when it is pulled out, and only then. */
.bricks .brick[data-brand]:hover,
.bricks .brick[data-brand]:focus-visible{
  box-shadow:inset 3px 0 0 var(--brand);
}

/* Pull a piece out of the heap to read it. */
.bricks.is-piled .brick:hover,
.bricks.is-piled .brick:focus-visible{
  transform:rotate(0deg) translateY(-7px) scale(1.07);
  /* the layout writes z-index inline, and an inline style beats a rule — so
     the piece being pulled out has to say so louder */
  z-index:9999!important;opacity:1;
  box-shadow:inset 3px 0 0 var(--brand,transparent),
             0 2px 0 rgba(20,19,16,.2),0 12px 22px -6px rgba(20,19,16,.5);
}

/* Three things end up in a heap like this: flattened card, tied-off sacks and
   loose paper. The material is picked off the listing id, so a piece keeps it. */
.brick.j-card{background:var(--bg-sunk);border-color:var(--rule-2);color:var(--ink-2)}
.brick.j-card::after{
  content:"";position:absolute;left:0;right:0;top:50%;height:1px;
  background:var(--rule);opacity:.85;
}
.brick.j-sack{
  background:var(--ink);border-color:var(--ink);color:var(--bg);
  border-radius:11px 15px 9px 13px / 13px 9px 15px 11px;
}
.brick.j-sack:hover{background:var(--red);border-color:var(--red);color:#fff}
.brick.j-scrap{
  background:var(--bg);border-color:var(--rule);color:var(--ink-3);
  font-size:.6rem;padding:5px 9px 6px;
}
/* the freshest thing on the heap: whatever fell off the list last */
.brick.is-fresh{border-color:var(--red);color:var(--red);border-width:1.5px}
.brick.is-fresh.j-sack{background:var(--red);border-color:var(--red);color:#fff}

.pile-buried{
  margin:14px 0 0;font-family:var(--agate);font-size:.62rem;letter-spacing:.05em;color:var(--ink-3);
}
.pile-buried b{color:var(--ink-2)}

/* Each piece is thrown on, oldest first, so the heap builds the way it built. */
@keyframes drop{
  from{transform:translateY(-64px) rotate(calc(var(--r,0deg) * 2.4));opacity:0}
}
.bricks.is-dropping .brick{
  animation:drop .46s cubic-bezier(.25,.9,.35,1) backwards;
  animation-delay:var(--d,0ms);
}

@media (max-width:640px){
  /* Nothing on the heap goes below about ten pixels — a scrap of paper is
     still something somebody has to be able to read. */
  .brick{font-size:.64rem;padding:6px 9px 7px}
  .brick.j-scrap{font-size:.62rem;padding:5px 8px 6px}
  .bricks.is-piled{margin-top:16px}
}

/* ------------------------------------------------------------------- footer */
/* The colophon, centred the way a paper sets one: the index, then what the
   board is, in two lines of decreasing weight. Centred rather than ranged
   left because there is nothing above it to align to — the list has ended. */
.foot{
  max-width:var(--lane);margin:36px auto 0;padding:16px var(--gutter) 46px;
  border-top:3px double var(--rule-2);text-align:center;
}

.foot-nav{
  display:flex;flex-wrap:wrap;justify-content:center;align-items:baseline;
  gap:0 2px;margin:0 0 14px;
}
.foot-nav a{
  font-family:var(--agate);font-size:.64rem;font-weight:600;
  letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink-2);text-decoration:none;
  padding:6px 11px;white-space:nowrap;
  border-bottom:2px solid transparent;
  transition:color .14s ease,border-color .14s ease;
}
.foot-nav a:hover{color:var(--red);border-bottom-color:var(--red)}

.foot-line{
  margin:0 0 5px;font-family:var(--agate);font-size:.64rem;
  letter-spacing:.1em;text-transform:uppercase;color:var(--ink-2);
}
.foot-fine{margin:0;font-size:.76rem;color:var(--ink-3)}

/* --------------------------------------------------------------- responsive */
@media (max-width:800px){
  /* The field keeps the button beside it rather than above it: stacked, the
     two of them cost about fifty pixels, and fifty pixels here is a whole
     listing further down the page. */
  .press-field{flex-basis:150px}
  .press-go{padding:0 18px;letter-spacing:.1em}
  .notice{padding-bottom:9px}
  .machine{margin-top:0}
}
/* --------------------------------------------------------- narrowing down
   The dateline is the thing worth keeping: it is where the board says what it
   costs. So it is the last thing to give anything up, and the sections go
   into a drawer at 720 rather than squeeze it.

   Above 720 the two share a line and the dateline drops what it can afford.
   Below 720 it has the line to itself and takes some of that back, which is
   why an item can disappear at 1000 and return at 720. That is not a bug in
   the order; it is the line genuinely having more room once the sections are
   no longer on it. */
@media (min-width:721px) and (max-width:884px){
  /* The sections are 326 wide and the whole dateline 505, which with the gap
     between them wants 885. Under that the date goes and nothing else has to. */
  .pulse-date{display:none}
}

/* The sections are a drawer from here down, so what follows is the dateline
   alone, on the full width, giving things up in order of what they cost to
   lose: the date, then today's readers, then the running total, then the word
   after the live count. The price never goes. */
/* Measured, not guessed. The whole dateline is 505px, 327 without the date,
   and 234 without today's readers as well; the line has the viewport less its
   two gutters. Each of these fires a dozen pixels before the fit runs out. */
@media (max-width:560px){
  .pulse-date{display:none}
}

@media (max-width:380px){
  .pulse-today{display:none}
}

/* Five sections at the size above want 287 pixels and a 320 screen has 280.
   Rather than let it wrap and strand one link on a line of its own, it comes
   down a size and holds the line all the way to the narrowest phone made. */
@media (max-width:400px){
  .foot-nav a{font-size:.58rem;padding:6px 8px;letter-spacing:.05em}
}

@media (max-width:344px){
  .foot-nav a{padding:6px 5px;letter-spacing:.02em}
}

@media (max-width:300px){
  /* Narrower than any phone sold, and it still says what it costs. */
  .pulse-total{display:none}
}

/* Every control on a phone is a thumb, not a cursor. */
@media (hover:none){
  .themer{width:40px;height:40px}
  .themer svg{width:16px;height:16px}
  .brand{padding:3px 0}
  .foot-line a{display:inline-block;padding:5px 2px}
  .listbar-note a{display:inline-block;padding:4px 0}

  /* A title set on one line is a 20px target. With no cursor to place, the
     whole entry is the target instead, which is the same link the logo beside
     it already points at, so nothing is lost by covering it. */
  .row-title::after{content:"";position:absolute;inset:0;z-index:1}
  .row-meta{font-size:.64rem}

  /* Nothing gets pulled out of the heap by hovering it, so a tap has to be
     the whole interaction: the piece is followed, not lifted. */
  .bricks.is-piled .brick:active{
    transform:rotate(0deg) scale(1.06);z-index:9999!important;opacity:1;
  }

  /* Safari zooms the whole page in when a field under 16px takes focus, and
     it does not zoom back out. The one field on the site is the last place
     that should happen. */
  .press-input{font-size:16px}
}

/* On a phone the hero is what stands between a reader and the list, so every
   part of it gives up a few pixels and the dateline drops its softest items. */
@media (max-width:640px){
  /* The half of the cross-reference that is an invitation rather than a fact.
     It is the first thing to go when the hero is what stands between somebody
     and the list. */
  .lede-more{display:none}
  .lede-note{font-size:.6rem;margin-bottom:11px;line-height:1.45}
  .topbar{padding-top:10px}
  .masthead{row-gap:3px;padding-bottom:3px}
  .brand-name{letter-spacing:-.055em}
  .pulse{gap:0 9px}
  .strip{padding:6px 0 7px}
  .claim{font-size:1.3rem;margin-bottom:5px}
  .lede{font-size:.76rem;line-height:1.45;margin-bottom:10px}
  .notice{padding:12px 0 10px}
  .listbar{padding:11px 0 7px}
  .press-input{padding:13px 0}
  .press-go{padding:14px 24px}
  .row{padding:10px 0 11px;gap:12px}
  /* The horizontal half of this has to be restated, or the panel keeps its
     negative margin and loses the padding that was cancelling it, and the
     lead entry ends up a gutter to the left of every other row. */
  .row.is-first{padding:11px var(--gutter) 12px}
  .row-logo{width:40px;height:40px}
  .row-glyph{font-size:.98rem}
  .row-tagline{margin-bottom:5px}
  .row-head{margin-bottom:5px}
  .row-meta{font-size:.6rem}
  /* The rank column costs about forty pixels of the line the details sit on,
     which is the difference between them fitting and wrapping. On a phone the
     order is the order they are in, so the column goes and #1 keeps its red
     rule and its held line to say what it is. */
  .rank{display:none}
  .row-meta{gap:0 7px}
  .row{gap:9px}
  .row-title{font-size:.86rem}
  .row.is-first .row-title{font-size:.93rem}
  .row-tagline{font-size:.74rem}
  .sheet-body{flex-direction:column}
  .sheet-shot{flex:none;width:100%}
}




/* A phone on its side is short rather than narrow, so what has to give is
   height: the sections and the name share a line again and the hero loses
   the sentence it can most afford to. */
@media (max-height:460px) and (orientation:landscape){
  .topbar{padding-top:8px}
  .masthead{padding-bottom:6px}
  .nav-link{padding-top:7px;padding-bottom:7px}
  .brand-name{font-size:1.5rem}
  .themer{width:32px;height:32px}
  .claim{font-size:1.3rem;margin-bottom:4px}
  .lede{font-size:.76rem;margin-bottom:9px}
  .press-hint{display:none}
  .notice{padding:9px 0 8px}
  .listbar{padding:9px 0 6px}
}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation:none!important;transition:none!important}
}

/* ==========================================================================
   The pages that are not the board.
   ========================================================================== */

/* ------------------------------------------------------- about and rules */
/* A leader page: one column, set narrower than the board so it reads as
   writing rather than as listings. */
.wrap-prose{max-width:74ch;padding-top:20px}
.prose-title{
  font-family:var(--display);font-weight:700;
  font-size:clamp(1.45rem,3.5vw,2.1rem);line-height:1.1;letter-spacing:-.045em;
  margin:0 0 10px;
}
.prose-lead{
  font-size:.92rem;line-height:1.6;color:var(--ink-2);
  margin:0 0 22px;padding-bottom:16px;border-bottom:3px double var(--rule-2);
}
.prose-back{
  margin:28px 0 0;padding-top:14px;border-top:1px solid var(--rule-2);
  font-family:var(--agate);font-size:.68rem;letter-spacing:.06em;
}
.prose-back a{color:var(--red);text-decoration:none}
.prose-back a:hover{text-decoration:underline}
.mono{font-family:var(--agate);font-size:.9em;color:var(--ink)}

.wrap-prose h2,
.page h2{
  font-family:var(--display);font-weight:700;font-size:.92rem;
  letter-spacing:.14em;text-transform:uppercase;margin:26px 0 8px;
}
.wrap-prose h2:first-of-type,.page h2:first-child{margin-top:0}
.wrap-prose p,.wrap-prose li,.page p,.page li{font-size:.86rem;line-height:1.6;color:var(--ink-2);margin:0 0 10px}
.wrap-prose b,.wrap-prose strong,.page b,.page strong{color:var(--ink);font-weight:600}
.wrap-prose a,.page a{color:var(--red)}
.wrap-prose ul,.wrap-prose ol,.page ul,.page ol{margin:0 0 12px;padding-left:1.2em}
.wrap-prose li{margin-bottom:5px}
code{
  font-family:var(--agate);font-size:.86em;background:var(--bg-sunk);
  border:1px solid var(--rule);padding:1px 5px;
}
.wrap-prose table,.page table{width:100%;border-collapse:collapse;margin:0 0 14px;border-top:3px double var(--rule-2)}
.wrap-prose th,.wrap-prose td,.page th,.page td{
  text-align:left;padding:7px 10px 7px 0;border-bottom:1px solid var(--rule);
  font-size:.8rem;color:var(--ink-2);font-weight:400;
}
.wrap-prose th,.page th{font-family:var(--agate);font-size:.62rem;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-3)}

/* ----------------------------------------------------------- the traffic */
/* Figures set as a paper sets a table of results: ruled, no boxes, the number
   carrying the weight and the label underneath it in agate. */
.figures{
  list-style:none;margin:0;padding:0;
  display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  border-top:3px double var(--rule-2);
}
@media (max-width:720px){.figures{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:420px){.figures{grid-template-columns:minmax(0,1fr)}}
.figure{
  padding:14px 16px 15px 0;border-bottom:1px solid var(--rule);
  border-right:1px solid var(--rule);
}
.figure:nth-child(3n){border-right:0}
@media (max-width:720px){
  .figure:nth-child(3n){border-right:1px solid var(--rule)}
  .figure:nth-child(2n){border-right:0}
}
@media (max-width:420px){.figure{border-right:0}}
.figure-n{
  display:block;font-family:var(--display);font-weight:700;
  font-size:1.7rem;letter-spacing:-.05em;line-height:1;color:var(--ink);
}
.figure-of,
.figure-label{
  display:block;margin-top:5px;
  font-family:var(--agate);font-size:.66rem;font-weight:600;
  letter-spacing:.1em;text-transform:uppercase;color:var(--ink-2);
}
.figure-note{display:block;margin-top:3px;font-size:.74rem;line-height:1.4;color:var(--ink-3)}
.wrap-prose .figures{margin:0 0 22px}
.wrap-prose .figure{padding-bottom:12px}
.figure.is-live .figure-n,.figure.is-live .figure-of{color:var(--red)}

/* A day of traffic, as a row of bars on the ground. */
.bars{
  list-style:none;margin:0;padding:0;position:relative;
  display:flex;align-items:flex-end;gap:2px;height:104px;
  border-top:3px double var(--rule-2);border-bottom:2px solid var(--rule-2);
  padding-top:12px;
}
.bar{position:relative;flex:1 1 0;min-width:0;height:100%;display:flex;align-items:flex-end}
.bar-fill{display:block;width:100%;background:var(--ink-2);min-height:2px}
.bar.is-now .bar-fill{background:var(--red)}
.bar:hover .bar-fill{background:var(--red)}
.bar-hour{
  position:absolute;top:calc(100% + 5px);left:0;
  font-family:var(--agate);font-size:.58rem;letter-spacing:.04em;color:var(--ink-3);white-space:nowrap;
}
.bar-tip{
  position:absolute;bottom:calc(100% + 5px);left:50%;transform:translateX(-50%);
  font-family:var(--agate);font-size:.6rem;letter-spacing:.03em;white-space:nowrap;
  background:var(--ink);color:var(--bg);padding:3px 7px;
  opacity:0;pointer-events:none;transition:opacity .12s ease;z-index:3;
}
.bar:hover .bar-tip,.bar:focus-within .bar-tip{opacity:1}
/* A tip on the first or last bars would hang off the side of the page and
   make it scroll sideways, invisibly, while sitting at zero opacity. */
.bar:nth-child(-n+3) .bar-tip{left:0;transform:none}
.bar:nth-last-child(-n+3) .bar-tip{left:auto;right:0;transform:none}
.bars + .sheets,.bars + .listbar{margin-top:26px}

/* Two ranked lists side by side, each set like a contents page. */
.sheets{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 34px}
@media (max-width:720px){.sheets{grid-template-columns:minmax(0,1fr);gap:0}}
.ranked{list-style:none;margin:0;padding:0;border-top:3px double var(--rule-2)}
.ranked-row{
  display:flex;align-items:baseline;gap:0;
  padding:7px 0 8px;border-bottom:1px solid var(--rule);
}
.ranked-name{
  flex:0 1 auto;min-width:0;font-family:var(--agate);font-size:.74rem;color:var(--ink-2);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.ranked-dots{
  flex:1 1 auto;min-width:14px;align-self:center;
  height:0;border-bottom:2px dotted var(--rule);margin:0 10px;position:relative;top:-3px;
}
.ranked-n{
  flex:0 0 auto;font-family:var(--agate);font-weight:700;font-size:.72rem;color:var(--ink);
}

/* --------------------------------------------------------- the empty board */
.blank{padding:26px 0;border-top:3px double var(--rule-2)}
.blank-line{
  font-family:var(--display);font-weight:700;font-size:1.1rem;letter-spacing:-.03em;
  margin:0 0 5px;color:var(--ink);
}
.blank-note{margin:0;font-size:.84rem;color:var(--ink-2)}
\n