/* Old Flames — Pulp.  Palette values are the validated set; see designs/. */
:root{
  --cream:#FFF7E9; --oxblood:#2B0F16; --deep:#190709;
  --green:#0F7040; /* it ROSE after you sold  · cream on it 5.78:1 */
  --hot:#BE3020;   /* it FELL after you sold  · cream on it 5.43:1 */
  --cold:#0B6373;  /* no longer a verdict ground — see body.rose below */
  --gold:#F0B429; --goldD:#C0850C; --goldL:#FFE08A; --timber:#7A4A22;
  --pos:#0E6B34;   /* the outcome you'd rather have had · 6.22:1 on cream */
  --neg:#A8452E;   /* the worse one                     · on cream        */
  --negS:#A8221C;  /* stronger red, for figures on cream · 6.78:1         */
  /* The card's mat. A box-shadow SPREAD, so it paints outside the border box
     and takes no space in layout — which is why anything meant to line up
     with the card's visible edge has to subtract it by hand. */
  --mat:5px;
  /* How far the card row hangs past the reading column, each side. The
     masthead and the button row take the same pull, so the page has one left
     edge and one right edge — they were on -112 while the row moved to -118
     and nobody could see the six pixels until they were looked for. */
  --rowpull:118px;
  --sans:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  --slab:'Archivo',var(--sans);
  /* Was Bodoni Moda. A didone's whole character is the thick/thin contrast,
     and at 42px uppercase it reads as a fashion masthead rather than a pulp
     paperback — the stems go heavy while the hairlines nearly vanish. The
     display voice is now the same grotesque as the labels, one weight class
     up, so the card has two faces instead of three and neither of them
     modulates stroke. Italics for the same face are loaded in base.html. */
  --display:'Archivo',var(--sans);
}
*{box-sizing:border-box}

/* The `hidden` attribute has to WIN, and this is not defensive tidying — it
   was broken. Browsers implement hidden as `[hidden]{display:none}` in the UA
   stylesheet, and ANY author rule setting display beats a UA rule regardless
   of specificity. So `.ghost` and `.shelfnav`, which both set
   display:inline-block, quietly cancelled it: "← Back to portfolio" was on
   screen with an empty shelf, where pressing it bounces off /shelf to the
   demo. Everything toggled by `el.hidden` in shelf.js and share.js depends on
   this line. */
[hidden]{display:none !important}
html,body{margin:0}
body{background:var(--oxblood);color:var(--cream);font-family:var(--sans);
  font-size:15px;line-height:1.5;-webkit-font-smoothing:antialiased;min-height:100vh}
/* THE VERDICT GROUNDS, reversed 2026-09-23 on Nalin's call.
   Until now: a stock that ROSE after you sold was red, because that is bad
   news for YOU, and SPEC.md said finance's green-up/red-down "would actively
   mislead". The card now reads as the market rather than as a feeling — the
   verdict WORDS (Tragedy, Woohoo!, Phew!) still carry how it went for you,
   so colour was saying the same thing twice and disagreeing with every other
   chart anyone has ever seen.

   Classes say what the STOCK did (`rose`/`fell`); tokens say what colour
   they are. That split is the point: `.spine.hot` was a hue wearing a
   meaning, which is how it stayed unnoticed that hot meant "you lost out".

   --cold keeps its place in the palette and no longer grounds a verdict. */
/* SCOPED TO .cardpage, and that is not tidiness. `plain` is already the
   bodyclass of the portfolio, the landing page and all three account pages
   — it means "no verdict here", not "neutral verdict". An unscoped
   body.plain painted every one of them timber, which on the portfolio is
   the exact colour of a neutral spine, so those rows vanished into the
   page. Third time this shape of bug has appeared: `.plain` on a link,
   `.modes` on two controls, and now this. A name is only safe where it is
   scoped. */
.cardpage.rose{background:var(--green)}
.cardpage.fell{background:var(--hot)}
.cardpage.plain{background:var(--timber)}
.grain{position:fixed;inset:0;pointer-events:none;z-index:0;opacity:.28;
  background-image:radial-gradient(var(--deep) .9px,transparent .9px);
  background-size:6px 6px;
  mask-image:linear-gradient(198deg,transparent 44%,#000 100%)}
.col{position:relative;z-index:1;max-width:470px;margin:0 auto;
  padding:22px 26px 70px}

/* type */
.brand{font-family:var(--display);font-weight:900;font-size:15px;letter-spacing:.04em;
  text-transform:uppercase;color:var(--cream);text-decoration:none;display:block}
.brand span{color:var(--gold)}
.imprint{font-family:var(--slab);font-size:8.5px;font-weight:900;
  letter-spacing:.34em;text-transform:uppercase;opacity:.62}
.kick{font-family:var(--display);font-style:italic;font-weight:500;
  font-size:14.5px;margin:0;opacity:.9}
h1.tt{font-family:var(--display);font-size:39px;font-weight:900;text-transform:uppercase;
  letter-spacing:-1.1px;line-height:.94;margin:6px 0 0;
  /* Two short lines rather than one long and one orphan — REGISTER OR SIGN
     IN broke after SIGN and left IN on a line by itself. Every heading
     benefits, long stock names included, and a browser without it wraps
     exactly as it did before. */
  text-wrap:balance}
h1.tt em{display:block;font-style:italic;font-weight:500;text-transform:none;
  font-size:21px;letter-spacing:-.2px;line-height:1.25;margin-top:7px;opacity:.9}
.lede{font-size:14px;opacity:.86;margin:10px 0 0}
.eyebrow{font-family:var(--slab);font-size:10px;font-weight:900;letter-spacing:.19em;
  text-transform:uppercase;opacity:.72}
.rule{height:1px;background:var(--cream);opacity:.24;margin:22px 0;border:0}

/* the chart's readout. `.tip` is positioned against .chart, and b/span were
   running together on one line — "₹1.17LNov 2015" — because neither had a
   rule to make them stack. */
/* The gap under the card. Deliberately a MARGIN and not a rule: a rule is
   45px and an edge the card does not need, since its mat is already one.
   28px measured — enough to read as separation past the 5px mat, and on a
   1280x800 laptop it still leaves ~130px of a 160px chart above the fold. */
.chart{position:relative;margin-top:28px}
.tip{position:absolute;transform:translate(-50%,calc(-100% - 9px));
  pointer-events:none;background:var(--cream);color:var(--oxblood);
  border-radius:3px;padding:5px 9px 6px;white-space:nowrap;text-align:center;
  opacity:0;transition:opacity .12s;box-shadow:0 3px 0 rgba(25,7,9,.22)}
.tip.on{opacity:1}
.tip b{display:block;font-size:14.5px;font-weight:800;letter-spacing:-.4px;
  line-height:1.1;font-variant-numeric:tabular-nums}
.tip span{display:block;font-family:var(--slab);font-size:8px;font-weight:800;
  letter-spacing:.11em;text-transform:uppercase;opacity:.62;margin-top:2px}

/* the key. Three items were butting into one sentence — "while you held it
   after you sold hover the line" — for want of any layout. The swatches
   mirror the chart: a thin line for the part you held, a bold one for the
   part you didn't. */
.legend{display:flex;flex-wrap:wrap;align-items:center;gap:5px 15px;
  margin-top:11px;font-family:var(--slab);font-size:8.5px;font-weight:800;
  letter-spacing:.13em;text-transform:uppercase;opacity:.7}
.legend span{display:inline-flex;align-items:center;gap:7px}
.legend i{width:17px;flex:0 0 auto;border-top:1.5px solid var(--cream);
  opacity:.5}
.legend i.k-after{border-top-width:3px;opacity:1}
/* what you actually took. Gold rather than dimmed cream: cream at 50% is
   1.56:1 against the hot ground, which is not a contrast ratio. */
.legend i.k-got{border-top-style:dashed;border-top-width:2px;opacity:1;
  border-top-color:var(--goldL)}
.legend .muted{opacity:.5;letter-spacing:.1em}

/* one line, right-aligned against the brand above it */
.topline{display:flex;justify-content:flex-end;margin-top:-2px}

/* the sticker — both figures, labelled, colour-coded */
/* Was a flex row of the sticker and the verdict column. The sticker is gone —
   the animation at the top of the page carries worth-today, you-took and the
   seal, and printing them again one screen later was the whole reason the
   masthead went too. What is left is one block of what the animation cannot
   say, so it reads LEFT, like the prose it sits between, rather than hugging
   a right edge that no longer has anything on the other side of it. */
.payoff{margin-top:24px}
.sticker{width:150px;height:150px;border-radius:50%;background:var(--cream);
  color:var(--oxblood);display:flex;flex-direction:column;align-items:center;
  justify-content:center;text-align:center;transform:rotate(-9deg);
  box-shadow:0 5px 0 rgba(25,7,9,.24);flex:0 0 auto}
.sticker .lab{font-family:var(--slab);font-size:7px;font-weight:900;
  letter-spacing:.19em;text-transform:uppercase;opacity:.66}
.sticker .big{font-family:var(--sans);font-size:23px;font-weight:800;
  letter-spacing:-.9px;line-height:1.05;margin-top:2px}
.sticker .small{font-family:var(--sans);font-size:18px;font-weight:800;
  letter-spacing:-.7px;line-height:1.05;margin-top:2px}
/* The column beside the sticker. It had no rules at all, so it behaved as a
   plain block inside the flex row, claimed the full width and wrapped
   underneath — which is how the seal ended up below the circle rather than
   beside it. Bottom-aligned with the sticker and set right, so the stamp sits
   opposite the money and the numbers read inward. */
.verdictcol{display:flex;flex-direction:column;align-items:flex-start;
  text-align:left}
.verdictcol .delta{margin-top:13px}
.because{font-size:12.5px;opacity:.78;line-height:1.45;margin-top:5px}

.sticker .base{font-family:var(--sans);font-size:13px;font-weight:800;
  letter-spacing:-.4px;line-height:1.05;margin-top:1px;opacity:.62}
.sticker .hr{width:56px;height:1px;background:rgba(43,15,22,.2);margin:8px 0 6px}
.pos{color:var(--pos)} .neg{color:var(--negS)}
.seal{font-family:var(--slab);font-size:17px;font-weight:900;letter-spacing:.15em;
  text-transform:uppercase;border:2.5px double var(--cream);padding:8px 15px;
  display:inline-block;transform:rotate(-3deg)}
.blurb{font-family:var(--slab);font-size:9.5px;font-weight:700;letter-spacing:.07em;
  text-transform:uppercase;line-height:1.75;opacity:.8;margin-top:12px}
.verdictcol .blurb{text-align:left;margin-top:6px;opacity:.62}
/* The stake, in the quiet register — a reference point, not an outcome. */
.stake{font-family:var(--slab);font-size:9.5px;font-weight:700;
  letter-spacing:.07em;text-transform:uppercase;opacity:.62;margin-top:14px}
.delta{font-family:var(--sans);font-size:23px;font-weight:800;letter-spacing:-.6px;
  margin-top:16px}
.verdictcol .delta{margin-top:0}

/* chart */
.spark{margin-top:8px}
.spark svg{display:block;width:100%;height:auto;overflow:visible}
.axis{display:flex;justify-content:space-between;margin-top:2px}
.axis span{font-family:var(--slab);font-size:9px;font-weight:800;
  letter-spacing:.12em;text-transform:uppercase;opacity:.6}

/* story */
ul.story{list-style:none;margin:14px 0 0;padding:0;display:flex;
  flex-direction:column;gap:11px}
ul.story li{font-family:var(--display);font-style:italic;font-weight:500;
  font-size:14.5px;line-height:1.5;display:flex;gap:10px}
ul.story li b{font-style:normal;font-weight:700;font-variant-numeric:tabular-nums}
ul.story .dot{opacity:.42;font-style:normal;font-size:11px;line-height:1.9;flex:0 0 auto}

/* form */
form{margin-top:22px}
fieldset{border:0;padding:0;margin:0 0 18px}
legend{font-family:var(--slab);font-size:10px;font-weight:900;letter-spacing:.19em;
  text-transform:uppercase;opacity:.72;padding:0;margin-bottom:9px}
.grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.grid3{display:grid;grid-template-columns:1fr 1fr 1.2fr;gap:8px}
.grid4{display:grid;grid-template-columns:1fr 1fr 1fr 1.1fr;gap:7px}
@media (max-width:430px){.grid4{grid-template-columns:1fr 1fr}}
/* A sale row carries one more field than a purchase row — its own profit.
   Under 430px it is 2+2+1 rather than four-across, so the last input runs
   full width instead of sitting half-empty beside nothing. */
.grid5{display:grid;grid-template-columns:1fr 1fr 1fr 1fr 1.1fr;gap:7px}
@media (max-width:560px){.grid5{grid-template-columns:1fr 1fr}
  .grid5 > :last-child{grid-column:1 / -1}}
.sale{margin-bottom:12px}
.sale .l{font-family:var(--slab);font-size:9px;font-weight:900;
  letter-spacing:.14em;text-transform:uppercase;opacity:.66;display:block;
  margin-bottom:5px}
/* The row-adding buttons, sale side and buy side. Keyed on the CLASS, not on
   #addsale — an id rule meant the buy button inherited the global button{}
   instead: cream on cream, full width, a white pill with invisible text.
   `.more` alone was never enough, because it sets no background and no width
   and so leaves both to that global rule. */
button.more{display:inline-block;margin:2px 0 20px;background:none;border:0;
  padding:0;cursor:pointer;width:auto}
button.more:hover{background:none}
/* The name-search dropdown. Absolute inside the label, which is the only
   element wrapping just this input — hence `position:relative` on the label
   rather than a new wrapper div, so the markup the form submits is unchanged.

   z-index because the duplicate warning sits directly below and would
   otherwise paint over the list. */
label{display:block;position:relative}
.finds{position:absolute;left:0;right:0;top:100%;z-index:20;
  margin-top:4px;max-height:264px;overflow-y:auto;
  background:var(--deep);border:1px solid rgba(255,247,233,.22);
  border-radius:4px;box-shadow:0 10px 30px rgba(0,0,0,.45)}
.find{display:flex;justify-content:space-between;align-items:baseline;
  gap:12px;padding:9px 12px;cursor:pointer}
.find + .find{border-top:1px solid rgba(255,247,233,.10)}
/* `.on` is the keyboard's highlight and hover is the mouse's — the same
   paint for both, so arrowing and pointing cannot disagree about which row
   is live. */
.find.on,.find:hover{background:rgba(240,180,41,.13)}
.find .fn{font-family:var(--sans);font-size:14px;font-weight:600;
  line-height:1.3}
.find .ft{font-family:var(--slab);font-size:10px;font-weight:800;
  letter-spacing:.09em;opacity:.6;white-space:nowrap}
label .l{font-family:var(--slab);font-size:9px;font-weight:900;letter-spacing:.14em;
  text-transform:uppercase;opacity:.66;display:block;margin-bottom:5px}
input{width:100%;font-family:var(--sans);font-size:15px;font-weight:600;
  color:var(--cream);background:rgba(255,247,233,.07);
  border:1px solid rgba(255,247,233,.28);border-radius:9px;padding:11px 12px}
input::placeholder{color:rgba(255,247,233,.4);font-weight:400}
input:focus{outline:0;border-color:var(--gold);background:rgba(255,247,233,.12)}
button{width:100%;font-family:var(--slab);font-size:14px;font-weight:900;
  letter-spacing:.06em;text-transform:uppercase;background:var(--cream);
  color:var(--oxblood);border:0;border-radius:10px;padding:15px;cursor:pointer;
  margin-top:6px}
button:hover{background:var(--goldL)}
.hint{font-size:12.5px;opacity:.66;margin-top:12px;line-height:1.5}
.hint code{font-family:var(--sans);background:rgba(255,247,233,.1);
  border-radius:4px;padding:1px 5px}

/* candidates + problems */
.opt{display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  border:1px solid rgba(255,247,233,.3);border-radius:11px;padding:14px 16px;
  color:var(--cream);text-decoration:none;margin-bottom:9px}
.opt:hover{border-color:var(--gold);background:rgba(255,247,233,.07)}
/* Dates here are DATA — the user has to read them exactly, and picking the
   wrong era changes the share count by the split ratio. This row used to
   inherit the display face while that face was a didone, whose hairlines go
   so thin at weight 900 that the diagonal of a 4 all but disappears and
   "Oct 2024" reads as "Oct 2021". The display face is a grotesque now so the
   hazard is gone, but the rule stays: tabular figures so the years line up
   down the column, which makes an odd one out visible at a glance. */
.opt .when{font-family:var(--slab);font-weight:800;font-size:17px;
  letter-spacing:.005em;font-variant-numeric:tabular-nums}
.opt .meta{font-family:var(--slab);font-size:9.5px;font-weight:800;
  letter-spacing:.12em;text-transform:uppercase;opacity:.68;text-align:right}
/* the "or was it —" row under an inferred buy date. Dates again, so the
   slab face and tabular figures, same reasoning as .opt .when above. */
.alsobuy{margin-top:7px;display:flex;flex-wrap:wrap;gap:4px 10px;align-items:baseline}
.alsobuy .l{font-family:var(--slab);font-size:9px;font-weight:900;
  letter-spacing:.14em;text-transform:uppercase;opacity:.55}
.alsobuy a{font-family:var(--slab);font-size:12.5px;font-weight:700;
  font-variant-numeric:tabular-nums;color:var(--cream);opacity:.8;
  text-decoration:underline;text-underline-offset:3px}
.alsobuy a:hover{opacity:1;color:var(--gold)}

.warn{border-left:2px solid var(--gold);padding:11px 0 11px 14px;margin-top:16px;
  font-size:13px;line-height:1.55;opacity:.92}
.warn b{color:var(--gold)}
/* "Show all 11 — 2 more". Had no rule at all, so it fell through to the
   browser default: #0000EE on oxblood is about 1.6:1, which is not a contrast
   ratio so much as a rumour. Every other link here is cream and underlined. */
.more{display:inline-block;margin-top:6px;font-family:var(--slab);font-size:10px;
  font-weight:900;letter-spacing:.14em;text-transform:uppercase;
  color:var(--cream);opacity:.8;text-decoration:underline;text-underline-offset:3px}
.more:hover{opacity:1;color:var(--gold)}

/* the "or pick one" divider — also ruleless, so it sat as a bare line of
   body text between the year field and the list instead of separating them */
.or{display:flex;align-items:center;gap:12px;margin:20px 0 14px;
  font-family:var(--slab);font-size:9px;font-weight:900;letter-spacing:.19em;
  text-transform:uppercase;opacity:.5}
.or::before,.or::after{content:"";flex:1;height:1px;background:var(--cream);
  opacity:.25}

/* the card's foot: share beside add-another, as the wireframes drew it */
.foot{display:flex;align-items:center;justify-content:space-between;gap:14px;
  flex-wrap:wrap;margin-top:22px}
.foot .back{margin-top:0}
/* Written for an <a>. On a <button> the global button rule underneath it
   supplied `background:var(--cream)` and `width:100%`, and .ghost's own
   `color:var(--cream)` then painted cream on cream — a full-width blank pill
   with an invisible label. A ghost has to state its own background. */
.ghost{display:inline-block;width:auto;background:transparent;margin-top:6px;
  font-family:var(--slab);font-size:10px;
  font-weight:900;letter-spacing:.14em;text-transform:uppercase;
  color:var(--cream);text-decoration:none;border:1px solid rgba(255,247,233,.34);
  border-radius:20px;padding:8px 15px}
.ghost:hover{border-color:var(--gold);color:var(--gold)}
.ghost:disabled{opacity:.45;cursor:default;border-color:rgba(255,247,233,.2)}
.ghost span{opacity:.6;letter-spacing:.08em}

/* The post-this block on the card page. A rule of its own so it reads as a
   section rather than as more of the card above it — the reader has finished
   the analysis by this point and is being shown a different kind of thing. */
.postit{margin-top:38px;padding-top:26px;
  border-top:1px solid rgba(255,247,233,.14)}
/* The same block leading the page instead of closing it: no rule above it,
   because there is nothing above it to be separated from. */
/* The way out, above the artefact. Reads as chrome rather than as a call to
   action: the page's one loud button is Share, and two more competing with it
   in gold would make the first screen a row of demands. */
/* Back on the left, onward on the right — pushed apart, so which is which is
   answered by position before either is read. With nothing on the shelf the
   back button is hidden and `Add another` falls to the left on its own, which
   is correct: there is nowhere to go back to. */
.topnav{display:flex;gap:9px;flex-wrap:wrap;justify-content:space-between;
  margin-top:14px}
.topnav .ghost{margin-top:0}
/* Room under the nav for whatever comes next. `h1.tt` carries margin:6px,
   which was enough beneath an italic `.kick` and is nothing beneath a row of
   pills — and the heading is wrapped in a <section>, so an `h1` adjacency
   selector matches nothing. The SECTION is what follows the nav. */
.topnav + section{margin-top:28px}

.postit.lead{margin-top:14px;padding-top:0;border-top:0}
/* The mat is a box-shadow SPREAD — it paints 16px outside the border box and
   takes no space in layout — so a margin has to clear the ring before it
   counts as a gap at all. At 14px the mat sat 2px OVER the button row above
   and clipped the bottom of both pills. 24 leaves 8. */
.postit.lead .shareframe{margin:0 auto 22px}
@media (min-width:880px){
  /* #shareui below it is the phone's button and is display:none here, so this
     margin only made the card's COLUMN taller than the card — which the code
     column then stretched to match, ending 17px below it. */
  .leadrow .postit.lead .shareframe{margin-bottom:0}
}

/* Card left, code right, and the row alone is allowed out past the reading
   column. `width:max-content` with symmetric auto margins centres it and lets
   it exceed .col's 418px of content; the prose below is untouched, which is
   the whole reason this is not the page widening.

   The breakpoint is measured, not chosen: the row is 642px wide (400 card +
   24 gap + 218 code) and the scrollbar takes ~15px, so at exactly 860 the
   viewport was 845 of usable width and the page scrolled sideways. 880 is the
   first width with room to spare. Below it a phone gets the share button and
   never sees the code. */
.leadrow{display:block}
@media (min-width:880px){
  /* A GRID, not a flex row, so the mode switch can sit above the card in a
     row of its own while the card and the code still start on the same line.
     Inside the card's column it pushed the card down by its own height and
     the two tops stopped agreeing.

     654 = 400 card + 36 gap + 218 code. The column gives 418 of content, so
     the row hangs 236 over it — split as -118 a side, which centres it on the
     page. `margin:auto` does not: an element wider than its container stays
     put at the start, so the row was pinned to the column's left edge and the
     code stuck out to the right of everything else on the page. */
  .leadrow{display:grid;grid-template-columns:400px 218px;column-gap:36px;
    width:654px;max-width:none;
    margin-left:calc(var(--rowpull) * -1);
    margin-right:calc(var(--rowpull) * -1)}
  .leadrow > .modes{grid-column:1;grid-row:1;justify-self:end;margin:0 0 12px}
  .leadrow > .postit.lead{grid-column:1;grid-row:2;min-width:0}
  /* Up by the mat, so the two columns share a visible top edge. The grid
     aligns the BOXES; the mat paints outside the card's, so without this the
     code sat --mat lower than the thing it stands beside. */
  .leadrow > .sharefoot{grid-column:2;grid-row:2;
    margin-top:calc(var(--mat) * -1)}
  /* The masthead and the button row take the same width, so the page has one
     left edge and one right edge instead of two grids a hundred pixels
     apart: the brand and "Back to portfolio" now start where the card starts,
     and "Add another" ends where the code ends. */
  .cardpage .masthead,
  .cardpage .topnav{margin-left:calc(var(--rowpull) * -1);
    margin-right:calc(var(--rowpull) * -1)}
  /* Beside the card, the separator that made sense above it is noise. */
  /* Beside the card the separator is noise, and the top margin was pushing
     the code 24px below the card it sits next to. */
  .leadrow .sharefoot > #qrbox{margin-top:0;padding-top:0;border-top:0}
  /* Both columns end together. The code and its caption stay at the top,
     where they belong to each other; AirDrop drops to the foot, which is
     honest about it being the second-best route and stops the column
     trailing off into 200px of nothing beside a 500px card. */
  .leadrow > .sharefoot,
  .leadrow #qrbox,
  .leadrow .qrsays{display:flex;flex-direction:column}
  .leadrow #qrbox,
  .leadrow .qrsays{flex:1;min-height:0}
  .leadrow .qrsays #airdrop{margin-top:auto}
  /* Stacked beside the card, and a flex COLUMN rather than a block: it is a
     link in the chain that lets AirDrop drop to the foot, and as a block the
     chain stopped here without saying so. */
  .leadrow .qrrow{display:flex;flex-direction:column;flex:1}
  .leadrow .qrsays{margin-top:14px}
  /* Both columns start at the same offset. `.postit.lead` carries its own
     14px top margin, which was pushing the card down 14px relative to the
     code beside it — one row, two different starting lines. */
  .leadrow .postit.lead{margin-top:0}
  /* The plaque FILLS its column, left edge to right edge, instead of sitting
     190px wide and centred inside 218 — which left it inset 14px from the
     text directly beneath it. It is also simply a bigger code, which is the
     one dimension that helps a camera. */
  .leadrow .qr{justify-content:flex-start;margin:0}
  .leadrow .qr svg{width:218px;height:218px}
}

/* The separator belongs to the CODE, not to the block wrapping it. On a phone
   #qrbox is hidden — the share button is up beside the card instead — and a
   wrapper carrying the border left a rule with 64px of nothing under it. Hung
   on the element that disappears, the separator disappears with it. */
.sharefoot > #qrbox{margin-top:20px;padding-top:18px;
  border-top:1px solid rgba(255,247,233,.14)}
.qrrow{display:flex;gap:16px;align-items:flex-start;flex-wrap:wrap}
.qrrow .qr{margin:0;flex:0 0 auto}
.qrsays{flex:1 1 168px;min-width:158px}
.qrsays .hint:first-child{margin-top:0}
.qrsays .ghost{margin-top:12px}

/* The download fallback under the share button. `.getfile` and not `.plain`:
   that name is taken by the NEUTRAL VERDICT ground on a spine, and a second
   unrelated rule of one name is the same trap `.modes` was last week. */
.getfile{color:var(--cream);opacity:.8;text-decoration:underline;
  text-underline-offset:3px}
.getfile:hover{opacity:1;color:var(--gold)}
.postit .kick{margin-bottom:0}
.postit .shareframe{margin:32px auto 40px}

/* The fallback address is one unbroken token. Without permission to break it
   overflowed the reading column by 171px on the card page — narrower there
   than on the share page, which is why it only showed up once the code moved
   in beside the preview. */
#qrbox a.back{overflow-wrap:anywhere;line-height:1.6}

/* ═══════════════════════════ the shelf ═══════════════════════════════
   Direction A2 from designs/radical.html — every flame a book spine, spine
   width carrying time held, "the sorting principle made physical".

   Two departures from the mock, both deliberate. It specified Bodoni Moda,
   which is retired, so the name is set in --display (Archivo). And the mock's
   `Ah`/`Woohoo` predate the five-word ladder; the word comes from
   verdict_word now and is sometimes rightly absent.
   ═════════════════════════════════════════════════════════════════════ */
/* line-height 22px, not the inherited 1.5. At the body's 15px that computes
   to 22.5px, and the half pixel propagated: the masthead was 22.50 tall, so
   the button row below it started at y=72.50 and its 1px border straddled two
   device rows — rendering at half strength, which read as the bottom of the
   pill being cut off. An integer here puts every row beneath it back on whole
   pixels. */
.masthead{display:flex;justify-content:space-between;align-items:baseline;
  gap:14px;line-height:22px}
/* The masthead spreads its children to the two ends, which is right for one
   nav item and wrong for two — they would straddle the brand. */
.mastnav{display:flex;align-items:baseline;gap:14px}
/* Was an underlined 9px caption at .6 opacity, which read as a status line
   rather than as a control. It is the same destination as the card page's
   button, so it wears the same clothes — just smaller, since in the masthead
   it is chrome rather than an action. */
.shelfnav{display:inline-block;font-family:var(--slab);font-size:9px;
  font-weight:900;letter-spacing:.14em;text-transform:uppercase;
  color:var(--cream);text-decoration:none;
  border:1px solid rgba(255,247,233,.34);border-radius:20px;padding:6px 12px}
.shelfnav:hover{border-color:var(--gold);color:var(--gold)}

/* One row of currencies, when there is more than one. The selected tab is a
   filled pill rather than an underline: this switches which SHELF you are
   looking at, not which section of one page, and an underline reads as the
   lighter of those two. */
/* The app's own confirm, so the one moment somebody is destroying something
   is not also the one moment the page stops looking like this app. */
.askwrap{position:fixed;inset:0;z-index:50;display:flex;align-items:center;
  justify-content:center;padding:26px;background:rgba(9,3,4,.72)}
.askbox{position:relative;background:var(--oxblood);
  border:1px solid rgba(255,247,233,.24);
  border-radius:6px;padding:24px 24px 20px;max-width:380px;width:100%;
  box-shadow:0 18px 0 rgba(9,3,4,.35)}
/* Its own rules, not .ghost's: a dismissal is not an action to weigh against
   the one the dialog is asking about, so it carries no border and no weight.
   A 34px box around a 20px glyph, because the tap target has to be real. */
/* z-index and margin:0 are both load-bearing, and neither is obvious:
   without the first the .kick paragraph wins the hit test and the button is
   decorative — visible, hoverable, and dead to a click. Without the second
   the global button{margin-top:6px} lands it 6px below where `top` says. */
.askx{position:absolute;top:8px;right:8px;z-index:1;width:34px;height:34px;
  margin:0;padding:0;border:0;background:none;color:var(--cream);opacity:.55;
  font-size:20px;line-height:1;cursor:pointer;border-radius:4px}
.askx:hover{opacity:1;background:rgba(255,247,233,.10)}
.askbox .tt2{margin-top:4px}
.askrow{display:flex;gap:9px;margin-top:20px;justify-content:flex-end}
.askrow .ghost{margin-top:0}
/* The destructive option is named and coloured; it is never the quiet one. */
.ghost.danger{border-color:rgba(190,48,32,.75);color:#F0A28E}
.ghost.danger:hover{background:var(--hot);border-color:var(--hot);
  color:var(--cream)}

/* ── the docked action ───────────────────────────────────────────────────
   The landing page's one button, pinned rather than trusted to land in the
   fold. See the block comment in landing.html for why it is not in flow.

   The scrim is what makes it legible over a scrolling list: without it the
   bar slices whichever spine it happens to cross, which reads as a rendering
   fault rather than as "there is more below". It fades to an EXPLICIT
   transparent oxblood, not `transparent` — that keyword is rgba(0,0,0,0), and
   a browser interpolating in non-premultiplied space fades this through grey.

   Width matches .col's 470px so the button lines up with the column it
   belongs to; at full width on a laptop it read as a cookie banner. */
.dock{position:fixed;left:0;right:0;bottom:0;z-index:41;
  padding:14px 18px calc(16px + env(safe-area-inset-bottom));
  background:linear-gradient(to top,
    var(--oxblood) 0%, var(--oxblood) 62%, rgba(43,15,22,0) 100%)}
.dock .bigshare{display:block;width:min(470px, calc(100vw - 36px));margin:0 auto}
/* The dock caps its button at the reading column and centres it; a second
   button beside it has to obey the same cap or the pair comes out 470 and
   864. Constrain the ROW, and both children fill it. */
.dock .twoways{width:min(470px, calc(100vw - 36px));margin:0 auto}
.dock .twoways .bigshare{width:100%}
/* Room for the last line of the page to clear the bar. Only the pages that
   carry a dock pay it. */
body.docked{padding-bottom:104px}

/* The same segmented control as the shelf's currency tabs, so two switches
   in one app look like one idea. Right-aligned and tight to the card,
   because it belongs to the card rather than to the page. */
.tabs.modes{justify-content:flex-end;margin:0 0 10px;gap:6px}
.tabs.modes .tab{padding:6px 12px;font-size:9px;text-decoration:none}

.tabs{display:flex;gap:8px;margin:18px 0 4px;flex-wrap:wrap}
.tab{width:auto;margin:0;padding:7px 15px;border-radius:20px;
  font-family:var(--slab);font-size:10px;font-weight:900;letter-spacing:.14em;
  text-transform:uppercase;cursor:pointer;
  background:transparent;color:var(--cream);
  border:1px solid rgba(255,247,233,.34)}
.tab:hover{border-color:var(--gold);color:var(--gold);background:transparent}
.tab[aria-selected="true"]{background:var(--cream);color:var(--oxblood);
  border-color:var(--cream)}
.tab[aria-selected="true"]:hover{background:var(--cream);color:var(--oxblood)}

.shelfhead{margin-top:24px}
.shelfhero{margin:14px 0 2px}
.shelfhero .imprint{opacity:.6;margin-bottom:14px}
.pill{font-size:8px;letter-spacing:.14em;border:1px solid rgba(255,247,233,.34);
  border-radius:9px;padding:2px 7px;margin-left:7px;opacity:.75}
.hero{font-family:var(--sans);font-weight:800;font-size:42px;
  letter-spacing:-1.5px;line-height:1;margin-top:7px}
.tookout{font-family:var(--display);font-style:italic;font-size:14.5px;
  opacity:.8;margin-top:9px}
.tookout s{text-decoration-thickness:1px}
.grouped{font-family:var(--sans);font-weight:800;font-size:19px;
  letter-spacing:-.5px;margin-top:7px;color:var(--goldL)}

.grouphead{display:flex;justify-content:space-between;align-items:baseline;
  gap:12px;font-family:var(--slab);font-size:9px;font-weight:800;
  letter-spacing:.17em;text-transform:uppercase;opacity:.55;
  margin:20px 0 11px;padding-top:16px;border-top:1px solid rgba(255,247,233,.16)}
.micro{opacity:.75}

/* The width legend. Marks are placed by the SAME calc() the spines use, so a
   legend that disagreed with a spine would have to be a CSS bug rather than a
   constant that drifted. It is hidden on narrow screens, where `min-width`
   below starts binding and the two genuinely would stop agreeing — no legend
   is honest, a wrong one is not. */
.ticks{position:relative;height:15px;margin:0 0 9px}
.ticks i{position:absolute;left:calc(51% + 49% * var(--t));
  transform:translateX(-50%);display:flex;flex-direction:column;
  align-items:center;gap:3px}
.ticks i b{width:1px;height:4px;background:var(--cream);opacity:.34}
.ticks i em{font-family:var(--slab);font-style:normal;font-size:8px;
  font-weight:800;letter-spacing:.13em;text-transform:uppercase;opacity:.45}
/* 206px is 51% of a 404px track, which is a 456px viewport. Below that the
   floor starts binding, the marks stop agreeing with the spines, and no
   legend beats a wrong one. */
@media (max-width:456px){ .ticks{display:none} }

.spines{list-style:none;margin:0;padding:0}
/* The row is the positioning context for the remove button, which sits over
   the spine's right edge rather than in the flow — a column of crosses would
   compete with the money for the eye, and this is a rare action. */
.spinerow{position:relative;margin-bottom:7px}
.drop,.edit{position:absolute;top:50%;right:-2px;transform:translateY(-50%);
  width:26px;height:26px;padding:0;border:0;border-radius:50%;
  background:transparent;color:var(--cream);font-size:15px;line-height:1;
  opacity:0;cursor:pointer;transition:opacity .12s}
/* The anchor needs what the button gets for free. 28px clears the drop's
   own 26px disc with a hair of dark between them, so the pair reads as two
   controls rather than one smear. */
.edit{right:28px;display:flex;align-items:center;justify-content:center;
  text-decoration:none}
.edit svg{width:13px;height:13px;display:block}
.spinerow:hover .drop,.drop:focus,
.spinerow:hover .edit,.edit:focus{opacity:.9;background:rgba(25,7,9,.4)}
.drop:hover,.edit:hover{opacity:1;background:rgba(25,7,9,.62)}
/* Two glyphs land on the widest spines' money, where one used to. So the
   value steps back while they are showing: it is a reveal, and you are
   reaching for a control, not reading the figure. The value comes back the
   moment the pointer leaves — nothing here is a mode. */
.spinerow:hover .sv{opacity:.2;transition:opacity .12s}
/* Always visible where there is no hover to reveal them, and there the
   money has to stay readable — so they keep the disc and stay faint
   instead, and nothing is dimmed underneath. */
@media (hover:none){
  .drop,.edit{opacity:.55;background:rgba(25,7,9,.4)}
  .spinerow:hover .sv{opacity:1}
}
.spinelink{display:block;text-decoration:none;color:inherit}

/* Width is `calc(51% + 49% * --t)`, so a year of holding is the same number
   of pixels on every page of every shelf — pagination cannot rescale an
   absolute ramp, and two currency groups on one screen cannot end up with two
   scales. `min-width` is the floor: below it the name, the duration, the
   value and the word stop fitting and the width stops meaning anything. */
/* flex-START, not center. Centred, a row with no verdict word had its single
   line of value floating between the name and the duration, which read as
   broken alignment rather than as absent information — and three legitimate
   cases carry no word. Aligned to the top, the value sits on the name's line
   whether or not a word follows it. */
.spine{position:relative;display:flex;align-items:flex-start;gap:12px;
  width:calc(51% + 49% * var(--t));min-width:206px;max-width:100%;
  padding:13px 15px;border-radius:2px 4px 4px 2px;color:var(--cream);
  box-shadow:inset -6px 0 0 rgba(25,7,9,.22)}
.spine.rose{background:var(--green)}
.spine.fell{background:var(--hot)}
/* The third ground needs a value of its own HERE, and the reason is specific
   to a shelf. On a card, `neutral` is the oxblood page itself — which works,
   because a card is the whole screen. A shelf sits ON oxblood, so a neutral
   spine drawn in it is a spine you cannot see: Nalin's first two flames both
   landed on Windfall and the page read as broken rather than as calm.
   Timber is already in the palette, reads as a book, and says nothing about
   direction — which is the whole job of the third ground. Cream on it is
   7.0:1. */
.spine.plain{background:var(--timber)}
/* A row that could not be priced, which is not a verdict and must not look
   like one. */
.spine.dud{background:rgba(255,247,233,.07);
  box-shadow:inset 0 0 0 1px rgba(255,247,233,.22)}
/* Nothing known about the duration, so nothing claimed about the width: a
   dashed edge reads as "we don't know how thick this book is", where a solid
   floor-width spine would read as a three-month hold. */
.spine.unknown{box-shadow:inset 0 0 0 1px rgba(255,247,233,.2);
  border-right:2px dashed rgba(255,247,233,.45)}

.spine .sp{min-width:0;flex:1}
/* The name is sized off the ramp too — 13.5px on the thinnest spine, 16px on
   the widest. Not a trick to win back characters, though it does: a thin book
   has smaller lettering on its spine, and "ATHER ENER…" at full size on a
   seven-month hold was the width encoding eating the name it was labelling. */
.spine .h{display:block;font-family:var(--display);font-weight:900;
  font-size:calc(13.5px + 2.5px * var(--t));letter-spacing:-.2px;
  line-height:1.05;text-transform:uppercase;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.spine .m{display:block;font-family:var(--slab);font-size:9px;font-weight:600;
  letter-spacing:.13em;text-transform:uppercase;opacity:.75;margin-top:5px;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.spine .sv{margin-left:auto;text-align:right;flex:0 0 auto}
.spine .v{display:block;font-family:var(--sans);font-weight:800;font-size:16px;
  letter-spacing:-.4px;line-height:1}
.spine .w{display:block;font-family:var(--slab);font-size:8px;font-weight:800;
  letter-spacing:.16em;text-transform:uppercase;opacity:.8;margin-top:4px}
.spinerow.waiting .spine{opacity:.5}
.spinerow[data-state="lost"] .spine .m,
.spinerow[data-state="ask"] .spine .m,
.spinerow[data-state="broken"] .spine .m,
.spinerow[data-state="offline"] .spine .m{opacity:.9;letter-spacing:.06em;
  text-transform:none;font-size:10.5px;font-weight:500;font-family:var(--sans)}

/* Direction in words, for the rows that legitimately carry no verdict word.
   Off-screen, not display:none — it has to reach a screen reader. */
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);
  white-space:nowrap}

.divider{font-family:var(--slab);font-size:8.5px;font-weight:800;
  letter-spacing:.16em;text-transform:uppercase;opacity:.4;margin:16px 0 9px}
.pager{margin-top:12px}

/* The share, at the foot of each currency group. Its own row rather than a
   third child of .grouphead, which is a two-child space-between and put the
   currency label in the middle the moment anything joined it. */
.shareshelf{margin-top:18px}
.shareshelf button[hidden]{display:none}


.tt2{font-family:var(--display);font-size:24px;font-weight:900;
  letter-spacing:-.6px;line-height:1.05;margin:5px 0 0}
.golink{display:block;text-align:center;text-decoration:none;margin-top:18px}
.keepit{margin-top:34px;padding-top:24px;
  border-top:1px solid rgba(255,247,233,.16)}
.keepit .ghost{margin-top:14px}

/* An address where a headline usually goes. */
.tt.addr{font-size:24px;letter-spacing:-.4px;text-transform:none;
  overflow-wrap:anywhere;line-height:1.15}

/* A .back inside a paragraph is a word in a sentence, not a block. The
   22px block margin was breaking the line it sat in. */
.hint .back{margin-top:0}

/* `details` had no rules at all — the long-standing gap in .working, visible
   now that the account page opens one. The marker is the browser's, resized
   and coloured to sit in the app's register rather than replaced: a custom
   triangle is a lot of CSS to reinvent something every browser already
   draws correctly. */
details.working{margin-top:26px;border-top:1px solid rgba(255,247,233,.14);
  padding-top:18px}
details.working > summary{font-family:var(--slab);font-size:10px;
  font-weight:900;letter-spacing:.14em;text-transform:uppercase;opacity:.7;
  cursor:pointer;list-style-position:outside}
details.working > summary:hover{opacity:1;color:var(--gold)}
details.working[open] > summary{margin-bottom:10px}
details.working label{display:block;margin-top:12px}
details.working input{max-width:220px}
details.working .ghost{margin-top:12px}

.says{margin-top:20px}
.says dt{font-family:var(--slab);font-size:9px;font-weight:900;
  letter-spacing:.17em;text-transform:uppercase;opacity:.55;margin-top:16px}
.says dd{margin:5px 0 0;font-size:13.5px;line-height:1.55;opacity:.9}

/* ── a shared card, as a stranger sees it ─────────────────────────────
   Card and invitation side by side on a laptop, stacked on a phone — the
   same shape as .sharecols, which this deliberately does not reuse: that
   one is sized around a QR box this page does not have. */
/* The hand-copy fallback: a browser that refused the clipboard still has
   to show the address, or the button is a dead end. */
.linkout{width:100%;max-width:330px;margin-top:6px;font-size:12px;
  padding:5px 7px}
#linknote{opacity:.7}

/* Wider than the reading column, for the same reason the share page is:
   a 400px card beside prose does not fit 640px, and stacked it pushes the
   invitation — the only thing asked of a viewer — below the fold. */
body.seenpage .col{max-width:880px}
.seen{display:flex;flex-wrap:wrap;gap:40px;align-items:center;
  justify-content:center;margin-top:30px}
.seen .shareframe{flex:0 0 400px;margin:0}
.seennext{flex:1 1 300px;min-width:280px;max-width:420px}
.seennext .tt{font-size:31px;line-height:1.08;margin:6px 0 12px}
.seennext .lede{margin-bottom:22px}
.seennext .bigshare{display:inline-block;text-align:center;text-decoration:none}
.seennext .hint{margin-top:10px}
@media (max-width:860px){
  .seen{gap:26px;margin-top:16px}
  .seen .shareframe{flex-basis:100%}
  .seennext{flex-basis:100%;max-width:none}
  .seennext .tt{font-size:26px}
}
/* The story under the card. Narrower than the card row above it, because
   prose wants a reading measure and the card row does not. */
.seenstory{max-width:560px;margin:46px auto 0;
  border-top:1px solid rgba(255,247,233,.14);padding-top:22px}
.seenstory .who{font-family:var(--slab);font-size:11px;font-weight:900;
  letter-spacing:.14em;text-transform:uppercase;opacity:.72;margin-top:18px}
.seenstory .who:first-of-type{margin-top:10px}
.seenstory .story{margin-bottom:0}
/* A deck is already a horizontal scroller; it must not also be centred by
   the flex parent, or the first card starts half off-screen. */
/* A DECK ON THE VIEWER'S PAGE, sized so the next card's edge always shows.
   Two full cards and a sliver at 880px, one and a sliver on a phone — the
   same 86% the share page uses there, for the same reason: a card nobody
   can see the edge of is a card nobody knows to swipe for.

   Cards are SCALED to these widths by fit.js, not clipped. Without it a
   291px frame cropped a quarter off every 400px card. */
.seen .seendeck{flex:1 1 100%;justify-content:flex-start}
.seen .seendeck .shareframe{flex:0 0 42%;max-width:none}
@media (max-width:860px){
  .seen .seendeck .shareframe{flex:0 0 86%}
}
.seen .deckhint{flex:1 1 100%;text-align:center;margin-top:10px}

/* The viewer is not the owner: there is no shelf of theirs to go back to,
   and a nav item pointing at an empty portfolio is worse than none. */
body.seenpage .mastnav{display:none}

/* the share page */
/* The card inside is a fixed 400x500 render source — it cannot reflow, since
   what is previewed has to be what gets captured. So it is SCALED instead:
   the box takes whatever width is going, keeps 4:5, and share.js sets --s to
   the ratio. A phone viewport is ~338px once the column padding is off, so an
   unscaled 400px iframe overflowed and lost the seal and the imprint. */
/* The mat. The page ground follows the verdict — body.hot is --hot, body.cold
   is --cold — and the share card's own ground uses the SAME two tokens, so on
   an ah or a phew the card and the page were the identical colour, measured:
   rgb(190,48,32) on rgb(190,48,32), rgb(11,99,115) on rgb(11,99,115). The
   card had no edge. Only the plain ground escaped, and by accident.

   Done as a box-shadow spread rather than padding or a wrapper: it draws
   outside the border box without touching layout, so `fit()` keeps reading
   clientWidth as the card's own width and needs no adjustment.

   And it is the ONLY edge. There was a drop shadow under it, which with no
   spread was just the card's own box offset 22px down — a dark band below
   the mat rather than a shadow around it. Between that band, the mat, and
   the card's own double hairline, one object had four outlines. The mat
   separates the card from the page on its own; the rest was noise. */
/* THE DECK. A horizontal snap row, because a collection past six stocks is
   more than one card and they are meant to be flicked through rather than
   paged with buttons. `overscroll-behavior-x:contain` so a swipe at the end
   does not become a browser back-gesture on iOS. */
.deck{display:flex;gap:14px;overflow-x:auto;scroll-snap-type:x mandatory;
  overscroll-behavior-x:contain;padding-bottom:6px;
  scrollbar-width:none;-webkit-overflow-scrolling:touch}
.deck::-webkit-scrollbar{display:none}
/* 86%, so the NEXT card's edge shows. A deck where card 2 sits entirely
   off-screen is a deck nobody knows they have — the swipe has to be visible
   before anyone makes it. The preview scales by --s = clientWidth/400, so a
   narrower frame is a smaller card and not a cropped one. */
.deck .shareframe{flex:0 0 86%;scroll-snap-align:center;margin:16px 0 0}
.deck .shareframe:last-child{margin-right:0}
.deckhint{text-align:center}

.shareframe{width:100%;max-width:400px;aspect-ratio:4/5;margin:36px auto;
  position:relative;overflow:hidden;border-radius:4px;
  box-shadow:0 0 0 var(--mat) var(--oxblood)}
.shareframe iframe{position:absolute;top:0;left:0;width:400px;height:500px;
  border:0;transform:scale(var(--s,1));transform-origin:top left}
/* The share page is the one screen that is not a column of prose. Stacked,
   the 400x500 preview pushed the QR a full fold down, and a code you have to
   scroll to find does not get scanned. So the card and the code sit side by
   side and the reading column widens to hold them.

   It collapses back to one column at 760px — and nothing is lost there,
   because a phone gets the share button and never sees the QR at all. */
body.sharepage .col{max-width:880px}
.sharecols{display:flex;flex-wrap:wrap;gap:34px;align-items:flex-start;
  margin-top:24px}
.sharecols .shareframe{flex:0 0 400px;margin:16px 0 24px}
.sharenext{flex:1 1 300px;min-width:280px}
#qrbox > .hint:first-child{margin-top:0}   /* align with the card's top edge */
@media (max-width:760px){
  body.sharepage .col{max-width:470px}
  .sharecols{gap:0;margin-top:0}
  .sharecols .shareframe{flex-basis:100%;margin:32px auto 40px}
  .sharenext{flex-basis:100%}
}

.bigshare{font-family:var(--slab);font-size:15px;font-weight:900;
  letter-spacing:.15em;text-transform:uppercase;width:100%;
  background:var(--gold);color:var(--oxblood);border:0;border-radius:26px;
  padding:16px 20px;cursor:pointer}
.bigshare:disabled{background:rgba(255,247,233,.18);color:var(--cream);cursor:default}

/* TWO WAYS IN, side by side and the same size. The upload was a hint line
   under the button — quieter than the thing it is meant to replace for
   anybody with more than a couple of exits, and SPEC.md:195 is explicit that
   typing one is the ON-RAMP to fetching the file, not a substitute for it.

   Same padding, same radius, same type as `.bigshare`, so they read as peers.
   The fill is the only difference left, and it goes to the manual one because
   it is the step that needs no trip to a broker — a lead, not a ranking.

   Stacks under 430px, where two of these on a row would each be too narrow
   to hold their own label. */
/* STACKED, not side by side. Two on a row leaves each about 200px, which is
   not enough for "UPLOAD A TRADEBOOK" at this tracking — it wrapped to two
   lines and the pair stopped reading as equals. Full width each is the
   honest way to make them peers, and it costs one button's height.

   `text-decoration:none` because `.bigshare` was written for a <button> and
   is worn by an <a> here; `.golink` used to supply it. Same shape as `.ghost`
   on a <button>, which painted cream on cream for the same reason. */
.twoways{display:flex;flex-direction:column;gap:10px;margin-top:18px}
.twoways > *{margin-top:0;text-decoration:none;box-sizing:border-box}
.twoways .bigshare{display:block;text-align:center}
.twoways .alt{font-family:var(--slab);font-size:15px;font-weight:900;
  letter-spacing:.15em;text-transform:uppercase;text-align:center;
  display:block;width:100%;
  background:transparent;color:var(--cream);border-radius:26px;
  padding:16px 20px;box-shadow:inset 0 0 0 2px rgba(255,247,233,.5)}
.twoways .alt:hover{box-shadow:inset 0 0 0 2px var(--gold);color:var(--gold)}
/* The code needs a light ground of its own — the page is near-black, and a
   dark-on-dark QR is invisible to the eye and to a camera. The padding is
   the quiet zone the spec asks for, on top of the factory's border. */
.qr{display:flex;justify-content:center;margin:18px 0}
.qr svg{display:block;width:190px;height:190px;background:var(--cream);
  padding:14px;border-radius:6px;box-shadow:0 4px 0 rgba(25,7,9,.45)}

.back{display:inline-block;margin-top:22px;font-family:var(--slab);font-size:10px;
  font-weight:900;letter-spacing:.14em;text-transform:uppercase;color:var(--cream);
  opacity:.7;text-decoration:underline;text-underline-offset:3px}

/* ═══════════════════════ the tradebook importer ═══════════════════════
   Two screens on one page — the drop and the review — swapped by `hidden`
   rather than by navigation, because the file is read in the browser and
   there is no request between them to hang a second page on.

   `.well`, not `.drop`: `.drop` is a spine's remove button (above), which is
   position:absolute at opacity:0, so a dropzone wearing that name renders
   invisible. `.found .sv` and `.well .big` are scoped for the same reason —
   both bare names belong to the spine and the sticker. */
.well{border:2px dashed rgba(255,247,233,.34);border-radius:4px;
  padding:30px 18px;text-align:center;margin-top:20px;transition:.15s;
  cursor:pointer}
.well.over{border-color:var(--gold);background:rgba(240,180,41,.07)}
.well .big{font-family:var(--display);font-weight:800;font-size:15px}
.well .sm{font-size:11.5px;opacity:.6;margin-top:4px}
.well input[type=file]{display:none}

/* The broker's own instructions, inline. The errand IS the drop-off point,
   so this cannot be a link to a help page. */
.steps{border-left:2px solid rgba(255,247,233,.22);padding:2px 0 2px 14px;
  margin-top:20px}
.steps ol{margin:7px 0 0;padding-left:16px;font-size:13px;line-height:1.75}
.steps li::marker{color:var(--gold)}
.cap{display:inline-block;font-family:var(--slab);font-size:8.5px;font-weight:900;
  letter-spacing:.13em;text-transform:uppercase;background:rgba(240,180,41,.16);
  color:var(--gold);padding:2px 6px;border-radius:2px;margin-left:5px}

/* One imported stock. Deliberately NOT a .spine — a spine's width carries
   time held and none of this is priced yet, so borrowing it would promise an
   answer the import cannot give. */
.found{display:flex;align-items:baseline;gap:12px;padding:11px 0;
  border-bottom:1px solid rgba(255,247,233,.1)}
.found .nm{font-family:var(--display);font-weight:900;font-size:14px;
  text-transform:uppercase;letter-spacing:-.2px}
.found .det{font-size:11.5px;opacity:.68;margin-top:3px;line-height:1.45}
.found .sv{margin-left:auto;text-align:right;flex:0 0 auto}
.found .qty{font-weight:800;font-size:13px;font-variant-numeric:tabular-nums}
.found .whn{font-family:var(--slab);font-size:8.5px;font-weight:800;
  letter-spacing:.12em;text-transform:uppercase;opacity:.6;margin-top:2px}

/* Whether we know when it was bought — the one thing on the review that
   changes what the portfolio can do with the row, because a buy date is what
   it sorts by. */
.knew{font-family:var(--slab);font-size:8.5px;font-weight:900;letter-spacing:.1em;
  text-transform:uppercase;padding:1px 5px;border-radius:2px;margin-left:7px;
  vertical-align:1px;white-space:nowrap}
.knew.full{background:rgba(15,112,64,.34);color:#9FE3BE}
.knew.part{background:rgba(255,247,233,.12);opacity:.72}
.tally{font-family:var(--slab);font-size:9px;font-weight:900;letter-spacing:.16em;
  text-transform:uppercase;opacity:.6;margin-top:18px}
