/*
  One stylesheet, no CDN, no framework.

  A market letter is read at 6:30am on a phone by somebody deciding whether to load a truck. That is the whole
  design brief: type that holds up at arm's length, numbers that scan down a column, and nothing that has to be
  fetched from a third party before the page means anything. It also keeps the site honest on shared hosting,
  where one slow external asset is the difference between a page and a spinner.
*/

:root {
    --ink: #16191c;
    --muted: #5f6b72;
    --rule: #e2e7ea;
    --paper: #ffffff;
    --wash: #f6f8f9;
    --brand: #0f766e;
    --brand-dark: #0b5750;
    --warn: #b45309;
    --bad: #b91c1c;
    --good: #15803d;
    --radius: 8px;
    --measure: 68ch;
}

* { box-sizing: border-box; }

body {
    margin: 0;
    background: var(--wash);
    color: var(--ink);
    font: 16px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    -webkit-text-size-adjust: 100%;
}

a { color: var(--brand); text-decoration: none; }
a:hover { text-decoration: underline; }

.wrap { max-width: 1060px; margin: 0 auto; padding: 0 20px; }
.narrow { max-width: var(--measure); }

/* --- Header ------------------------------------------------------------ */
header.site {
    background: var(--paper);
    border-bottom: 1px solid var(--rule);
    position: sticky; top: 0; z-index: 10;
}
/* min-height, not height. A fixed height cannot fail gracefully: the moment the contents need a second line
   they are drawn outside the box rather than pushing it open, which is silent on a desktop and immediately
   visible on a phone. */
header.site .wrap { display: flex; align-items: center; gap: 24px; min-height: 60px; }
.brand { font-weight: 700; letter-spacing: -0.01em; color: var(--ink); font-size: 17px; white-space: nowrap; }
.brand span { color: var(--brand); }

header.site nav.sections { margin-left: auto; display: flex; gap: 20px; align-items: center; font-size: 15px; }
header.site .me { display: flex; gap: 20px; align-items: center; font-size: 15px; }
header.site .wrap a { color: var(--muted); white-space: nowrap; }
header.site .wrap a:hover, header.site .wrap a.on { color: var(--ink); }
header.site .wrap a.brand { color: var(--ink); }

/*
  The phone header.

  Five section labels plus the brand want about 730pt across; an iPhone offers 393pt. In one row the links
  wrap inside the bar and get clipped, which is what made the top of the site look squeezed. Shrinking the
  type until it fits is the other way to lose, so the bar splits into two rows instead.

  Row one is who this is and what the reader holds — brand and standing badge, both always visible, because
  the badge exists precisely so somebody who has just paid can see that it worked.

  Row two is the sections, in a strip that scrolls sideways. Every label keeps its full wording and every tap
  target keeps its full size; the trailing padding is wider than the fade over it, so the last link is crisp
  once the strip is scrolled to its end rather than sitting under a permanent haze.

  It also stops being sticky here. Two rows is ~104pt of an 844pt screen, and this is a site people read a
  page of prose on at 6:30 in the morning — that is a reading surface, not a chrome surface.
*/
@media (max-width: 760px) {
    header.site { position: static; }

    /* The strip on row two has to reach both screen edges to scroll properly, so the padding moves off the
       wrapper and onto the row-one items. The first link then carries the same 20px inset as the brand and
       the two rows line up on a common left margin. */
    header.site .wrap {
        flex-wrap: wrap;
        gap: 0 16px;
        padding-left: 0;
        padding-right: 0;
    }

    header.site .brand { padding-left: 20px; min-height: 52px; display: flex; align-items: center; }

    header.site .me { order: 2; margin-left: auto; padding-right: 20px; min-height: 52px; gap: 14px; }

    header.site nav.sections {
        order: 3;
        flex-basis: 100%;
        margin-left: 0;
        gap: 0;
        border-top: 1px solid var(--rule);
        overflow-x: auto;
        overscroll-behavior-x: contain;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 20px), transparent);
        mask-image: linear-gradient(to right, #000 calc(100% - 20px), transparent);
    }
    header.site nav.sections::-webkit-scrollbar { display: none; }

    header.site nav.sections a { padding: 12px 11px; }
    header.site nav.sections a:first-child { padding-left: 20px; }
    header.site nav.sections a:last-child  { padding-right: 40px; }

    /* Where you are, marked on the strip itself. On a row that scrolls, colour alone is too weak a cue -
       the current section may be half off-screen when the page opens. */
    header.site nav.sections a.on {
        color: var(--ink); font-weight: 600;
        box-shadow: inset 0 -2px 0 var(--brand);
    }
}

/* --- Type -------------------------------------------------------------- */
h1 { font-size: 34px; line-height: 1.2; letter-spacing: -0.02em; margin: 0 0 12px; }
h2 { font-size: 22px; line-height: 1.3; margin: 36px 0 12px; letter-spacing: -0.01em; }
h3 { font-size: 16px; margin: 0 0 8px; }
.lede { font-size: 18px; color: var(--muted); margin: 0 0 24px; }
.eyebrow {
    text-transform: uppercase; letter-spacing: .1em; font-size: 11px;
    font-weight: 700; color: var(--muted); margin-bottom: 6px;
}
.muted { color: var(--muted); }
.small { font-size: 14px; }

main { padding: 36px 0 64px; }

/* --- Cards ------------------------------------------------------------- */
.card {
    background: var(--paper); border: 1px solid var(--rule);
    border-radius: var(--radius); padding: 20px; margin-bottom: 16px;
}
.grid { display: grid; gap: 16px; }
@media (min-width: 720px) { .grid.two { grid-template-columns: 1fr 1fr; } }
@media (min-width: 900px) { .grid.four { grid-template-columns: repeat(4, 1fr); } }

/* --- The headline reading ---------------------------------------------- */
.figure { font-size: 40px; font-weight: 700; line-height: 1.05; letter-spacing: -0.02em; }
.figure small { font-size: 15px; font-weight: 500; color: var(--muted); letter-spacing: 0; }

.pill {
    display: inline-block; padding: 3px 10px; border-radius: 999px;
    font-size: 13px; font-weight: 700;
}
.pill.up { background: #dcfce7; color: #14532d; }
.pill.down { background: #fee2e2; color: #7f1d1d; }
.pill.flat { background: #eef2f4; color: #374151; }

/* --- Tables ------------------------------------------------------------ */
.scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table { border-collapse: collapse; width: 100%; font-size: 15px; }
th, td { text-align: left; padding: 9px 12px; border-bottom: 1px solid var(--rule); white-space: nowrap; }
th { font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); font-weight: 700; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
tr.best td { background: #f0fdf4; }
tbody tr:last-child td { border-bottom: 0; }

/* --- Buttons ----------------------------------------------------------- */
.btn {
    display: inline-block; padding: 10px 18px; border-radius: var(--radius);
    background: var(--brand); color: #fff; font-weight: 600; border: 1px solid var(--brand);
    cursor: pointer; font-size: 15px; font-family: inherit;
}
.btn:hover { background: var(--brand-dark); text-decoration: none; }
.btn.ghost { background: transparent; color: var(--brand); }
.btn.ghost:hover { background: #f0fdfa; }
.btn[disabled] { opacity: .5; cursor: not-allowed; }
/* Irreversible actions only — deleting subscribers. Red so it never reads as the page's main button. */
.btn.danger { background: var(--bad); border-color: var(--bad); color: #fff; }
.btn.danger:hover { background: #991b1b; }

input[type=text], input[type=email], select, textarea {
    width: 100%; padding: 10px 12px; border: 1px solid var(--rule);
    border-radius: var(--radius); font: inherit; background: var(--paper); color: var(--ink);
}
label { display: block; font-size: 14px; font-weight: 600; margin-bottom: 5px; }
.field { margin-bottom: 14px; }

/* --- Notices ----------------------------------------------------------- */
.notice { padding: 12px 16px; border-radius: var(--radius); border: 1px solid var(--rule); margin-bottom: 16px; font-size: 15px; }
.notice.good { background: #f0fdf4; border-color: #bbf7d0; }
.notice.warn { background: #fffbeb; border-color: #fde68a; }
.notice.bad { background: #fef2f2; border-color: #fecaca; }

/* --- The paywall ------------------------------------------------------- */
/*
  A locked section renders as an explicit, honest block: the reader is told what is behind it and what it
  costs. It is never the full content hidden with CSS — the server does not send what has not been paid for,
  so there is nothing here for a curious reader to reveal with developer tools.
*/
.locked {
    border: 1px dashed var(--rule); border-radius: var(--radius);
    padding: 28px 20px; text-align: center; background: var(--paper);
}
.locked h3 { margin-bottom: 6px; }

/* --- Plans ------------------------------------------------------------- */
.plan { display: flex; flex-direction: column; }
.plan .price { font-size: 26px; font-weight: 700; margin: 6px 0 2px; letter-spacing: -0.01em; }
.plan .seats { font-size: 14px; color: var(--muted); }
.plan p { font-size: 15px; color: var(--muted); flex: 1; }
.plan.feature { border-color: var(--brand); box-shadow: 0 0 0 3px #ccfbf1; }

footer.site {
    border-top: 1px solid var(--rule); background: var(--paper);
    padding: 28px 0; font-size: 14px; color: var(--muted);
}
footer.site .wrap { display: flex; flex-wrap: wrap; gap: 18px; }
footer.site a { color: var(--muted); }
.disclaimer { margin-left: auto; }
/* Pushed right only while it shares a line with the links. Once the footer wraps, an auto margin strands it
   alone against the right edge, which reads as a stray fragment rather than a footnote. */
@media (max-width: 760px) { .disclaimer { margin-left: 0; flex-basis: 100%; } }

@media (prefers-color-scheme: dark) {
    :root {
        --ink: #e8eef0; --muted: #9fb0b6; --rule: #2b3438;
        --paper: #1a1f22; --wash: #131719; --brand: #2dd4bf; --brand-dark: #14b8a6;
    }
    .pill.up { background: #14532d; color: #bbf7d0; }
    .pill.down { background: #7f1d1d; color: #fecaca; }
    .pill.flat { background: #2b3438; color: #cbd5e1; }
    tr.best td { background: #14532d33; }
    .notice.good { background: #14532d33; border-color: #14532d; }
    .notice.warn { background: #78350f33; border-color: #78350f; }
    .notice.bad { background: #7f1d1d33; border-color: #7f1d1d; }
    .btn { color: #06231f; }
    .plan.feature { box-shadow: 0 0 0 3px #134e4a; }
}


/* --- Edition tabs -------------------------------------------------------
   A segmented control rather than underlined text. The first version was a
   row of small quiet links and readers missed it entirely — which meant they
   also missed the delivered-cost table, the one thing most likely to make
   them renew. Navigation that is easy to overlook is navigation that is not
   there.

   Progressive enhancement is unchanged: without JavaScript the bar stays
   hidden and every panel renders in order, top to bottom. */
.tabs {
    display: flex;
    gap: 4px;
    margin: 22px 0 24px;
    padding: 5px;
    background: var(--wash);
    border: 1px solid var(--rule);
    border-radius: 12px;
    /* Never wraps to a ragged second row on a phone: it scrolls, keeping the
       control one recognisable object. */
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }

.tabs button {
    font: inherit;
    font-size: 15.5px;
    font-weight: 600;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
    background: none;
    border: 0;
    border-radius: 8px;
    /* A comfortable thumb target on a phone, which the 10px version was not. */
    padding: 13px 20px;
    color: var(--muted);
    transition: background .12s ease, color .12s ease;
}
.tabs button:hover { color: var(--ink); background: rgba(0,0,0,.04); }

.tabs button[aria-selected="true"] {
    background: var(--paper);
    color: var(--brand);
    box-shadow: 0 1px 2px rgba(0,0,0,.10), 0 0 0 1px rgba(0,0,0,.04);
}

.tabs button:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

@media (max-width: 560px) {
    .tabs { border-radius: 10px; }
    .tabs button { padding: 12px 15px; font-size: 15px; }
}

@media (prefers-color-scheme: dark) {
    .tabs button:hover { background: rgba(255,255,255,.06); }
    .tabs button[aria-selected="true"] {
        box-shadow: 0 1px 2px rgba(0,0,0,.5), 0 0 0 1px rgba(255,255,255,.06);
    }
}

.tabbed .panel > h2:first-child { margin-top: 0; }

/* Both sides of the trade, side by side. The layout is the argument: neither
   column is the main one. */
.implication { display: grid; gap: 12px; margin-top: 12px; }
@media (min-width: 720px) { .implication { grid-template-columns: 1fr 1fr; } }
.implication > div { padding: 12px 14px; border-radius: var(--radius); background: var(--wash); font-size: 14px; }
.implication .who {
    text-transform: uppercase; letter-spacing: .08em; font-size: 11px;
    font-weight: 700; color: var(--muted); margin-bottom: 4px;
}
@media (prefers-color-scheme: dark) { .implication > div { background: #0f1315; } }


/* --- History panels ------------------------------------------------------
   Small multiples rather than six lines on one axis. The regions are the
   subject, but they are not being compared point-for-point — each is read
   against its OWN balanced line — so a shared frame beats a shared plot, and
   it sidesteps a six-hue categorical palette that could not clear the
   colour-blind gates on an all-pairs form anyway.

   Diverging blue/red because the index IS polarity data: 0 severe oversupply,
   50 balanced, 100 severe undersupply. Cool and warm, deliberately not green
   and red — a palette where "tight" reads as good is a seller's chart, and
   this one is read by both sides. Both poles validated against the card
   surface in light and dark. */
.viz {
    --tight: #e34948;   /* above 50 — supply tightening */
    --loose: #2a78d6;   /* below 50 — supply lengthening */
    --mid:   #f0efec;
}
.panels { display: grid; gap: 16px; }
@media (min-width: 700px) { .panels { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1000px) { .panels { grid-template-columns: repeat(3, 1fr); } }

.panel-chart { margin: 0; }
.panel-chart figcaption {
    display: flex; align-items: baseline; gap: 8px; margin-bottom: 10px;
}
.panel-chart .reading {
    font-size: 22px; font-weight: 700; margin-left: auto;
    font-variant-numeric: tabular-nums; letter-spacing: -0.01em;
}
.panel-chart svg { width: 100%; height: 96px; display: block; overflow: visible; }
.panel-chart .axis { display: flex; justify-content: space-between; margin-top: 6px; }

/* Thin marks, recessive baseline. The fills carry the polarity, so the trace
   itself stays ink-coloured rather than competing with them. */
.fill-tight { fill: #e34948; fill-opacity: .18; }
.fill-loose { fill: #2a78d6; fill-opacity: .18; }
.baseline { stroke: var(--rule); stroke-width: 1; stroke-dasharray: 3 3; }
.trace { fill: none; stroke: var(--ink); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.latest { fill: var(--ink); stroke: var(--paper); stroke-width: 2; }

/* Movement and price are magnitude, not polarity: one hue each, no midpoint fill. Only the index diverges,
   because only the index has a meaningful centre. */
.trace.movement { stroke: #2a78d6; }
.trace.price { stroke: #1baf7a; }
@media (prefers-color-scheme: dark) {
    .trace.movement { stroke: #3987e5; }
    .trace.price { stroke: #199e70; }
}

.key-swatch {
    display: inline-block; width: 11px; height: 11px; border-radius: 3px; vertical-align: baseline;
}
.key-swatch.tight { background: #e34948; }
.key-swatch.loose { background: #2a78d6; }

@media (prefers-color-scheme: dark) {
    /* Stepped for the dark surface, not flipped: the same two hues re-picked
       for the dark card and validated against it. */
    .fill-tight { fill: #e66767; fill-opacity: .22; }
    .fill-loose { fill: #3987e5; fill-opacity: .22; }
    .key-swatch.tight { background: #e66767; }
    .key-swatch.loose { background: #3987e5; }
}


/* --- The FOB table ------------------------------------------------------
   Item prices across weeks. Dense on purpose: a rep scans down a column
   looking for what moved, so the rows stay tight and the movers carry a
   tint rather than a heavier font, which would fight the numbers. */
table.fob { font-size: 14px; }
table.fob th, table.fob td { padding: 7px 10px; }
table.fob tr.mover td { background: #fffbeb; }
table.fob tbody tr:hover td { background: var(--wash); }
@media (prefers-color-scheme: dark) {
    table.fob tr.mover td { background: #78350f33; }
}


/* --- One block per area -------------------------------------------------
   Each growing area is a self-contained section: heading, what it means for
   each side, movement, price, and the item table. The summary table above
   links into these, so the scan and the detail are the same page. */
.card.area { padding-top: 18px; }
.card.area .area-head {
    display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
    padding-bottom: 12px; margin-bottom: 14px; border-bottom: 1px solid var(--rule);
}
.card.area .area-head h3 { margin: 0; font-size: 19px; letter-spacing: -0.01em; }

/* Anchored jumps must not tuck the heading under the sticky header. */
.card.area { scroll-margin-top: 76px; }

table.mini { width: auto; }
table.mini td { padding: 3px 12px 3px 0; border: 0; }


/* --- Multi-select business types ---------------------------------------
   Checkboxes in a wrapping grid rather than a multi-select list: a native
   multi-select needs ctrl-click to pick a second option, which a good many
   people do not know and nobody should have to. */
.checks { display: grid; gap: 6px; }
@media (min-width: 520px) { .checks { grid-template-columns: 1fr 1fr; } }
.checks .check {
    display: flex; align-items: center; gap: 8px;
    font-weight: 400; font-size: 15px; margin: 0; cursor: pointer;
    padding: 7px 10px; border: 1px solid var(--rule); border-radius: var(--radius);
}
.checks .check:hover { background: var(--wash); }
.checks .check input { width: auto; margin: 0; }


/* --- Standing badge -----------------------------------------------------
   What this reader holds, on every page. The point is the moment just after
   a checkout: somebody who has handed over a card should be able to see it
   worked without hunting for a confirmation email that may not have arrived
   yet. */
.standing {
    display: inline-flex; flex-direction: column; line-height: 1.15;
    padding: 5px 11px; border-radius: 999px; font-size: 13px; font-weight: 700;
    border: 1px solid transparent;
}
.standing .state { font-size: 10.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; opacity: .8; }
.standing.active { background: #dcfce7; color: #14532d; }
.standing.trial  { background: #ccfbf1; color: #0f766e; }
.standing.admin  { background: #ede9fe; color: #4c1d95; }
.standing.none   { background: var(--wash); color: var(--muted); border-color: var(--rule); }
header.site .me a:hover .standing { filter: brightness(.96); }

@media (prefers-color-scheme: dark) {
    .standing.active { background: #14532d; color: #bbf7d0; }
    .standing.trial  { background: #134e4a; color: #99f6e4; }
    .standing.admin  { background: #3b0764; color: #ddd6fe; }
}
@media (max-width: 620px) { .standing .state { display: none; } }

/* ---- The accuracy ledger --------------------------------------------------
   One row per graded forecast: a bar for the range we published, a tick for
   the expected figure, a marker for what shipped.

   Each row is scaled to its OWN band by the page model, so the bar is the same
   width whatever the week's volume. Colour carries the result and is never the
   only thing that does — every row states inside/outside in words and prints
   the miss in cwt, so the table reads identically to somebody who cannot tell
   the two colours apart, and prints in black and white. */
table.ledger td { vertical-align: middle; }
table.ledger .band-note { margin-top: 4px; white-space: nowrap; }

.band {
    position: relative;
    height: 12px;
    min-width: 160px;
    border-radius: 999px;
    background: var(--wash);
    border: 1px solid var(--rule);
}
.band .span {
    position: absolute; top: -1px; bottom: -1px;
    border-radius: 999px;
    background: #cbd5dc;
}
.band .span.hit { background: #bbf7d0; }
.band .span.miss { background: #fecaca; }

/* The expected figure sits inside the band as a hairline: it is the middle of
   a claim, not the claim itself, and drawing it as prominently as the outcome
   would suggest we published a single number. */
.band .expected {
    position: absolute; top: 1px; bottom: 1px; width: 1px;
    margin-left: -0.5px;
    background: var(--muted); opacity: .55;
}
.band .actual {
    position: absolute; top: 50%; width: 11px; height: 11px;
    margin: -5.5px 0 0 -5.5px;
    border-radius: 50%;
    background: var(--ink);
    border: 2px solid var(--paper);
}
/* Pinned to the edge because the outcome fell outside the drawn window. Made a
   different shape, not just a different position, so it cannot be misread as a
   near miss that happened to land at the end of the scale. */
.band .actual.pinned {
    border-radius: 2px;
    background: var(--bad);
    width: 8px; margin-left: -4px;
}

@media (prefers-color-scheme: dark) {
    .band .span { background: #2a3136; }
    .band .span.hit { background: #14532d; }
    .band .span.miss { background: #7f1d1d; }
}

/* ---- Carton picker on the delivered panel ---------------------------------
   A label, a select and a button, on one row where there is room and stacked
   where there is not. It is a GET form, so it also has to look deliberate with
   no script running at all. */
.spec-picker { margin-bottom: 16px; }
.spec-picker label { margin-bottom: 8px; }
.spec-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.spec-row select { margin: 0; flex: 1 1 260px; min-width: 0; }
.spec-row .btn { flex: 0 0 auto; }
