/* ============================================================
   PALETTE & TYPE — "a beautiful ledger from 1885"

   The art style sheet governs this file as much as it governs the
   engravings, and it is blunt about what a printed page does not
   have: no rounded "app icon" framing, no drop shadows, no tonal
   gradients. So separation here is done the way a press does it —
   with a RULE. A card is bounded by a hairline, not lifted off the
   paper by a blur; a corner is square because a plate is square.

   Two consequences worth knowing:

   - The body face is the SERIF. This is a ledger, and the reading
     face of a ledger is the same face as its headings. The sans
     survives only as a fallback inside `--label`, and any place it
     still shows through is a bug, not a choice.

   - `--shadow` is kept as a token and set to `none` rather than
     deleted, because a dozen rules downstream reference it and the
     next person to add a card will reach for it too. One place to
     put a shadow back if this turns out to be wrong; nowhere to
     accidentally reintroduce one if it is right.
   ============================================================ */
:root{
  --paper:#f4ece0;      --card:#fdf8ef;      --card-2:#f3e9d8;
  --ink:#2b2118;        --ink-soft:#6d5c48;  --ink-faint:#9c8a73;
  --rule:#d8c7ab;       --rule-strong:#b9a284;
  --iron:#3f4a52;       --wood:#8a5a2b;      --brass:#a8781f;
  --green:#4c6b3c;      --red:#a3402c;       --sky:#3d6076;
  --shadow:none;
  --radius:2px;
  --serif:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,"Times New Roman",serif;
  --sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
  --label:var(--serif);
}
@media (prefers-color-scheme: dark){
  :root{
    --paper:#17130f;    --card:#221c16;      --card-2:#2b241c;
    --ink:#ece0cc;      --ink-soft:#b3a189;  --ink-faint:#7d6e5c;
    --rule:#40352a;     --rule-strong:#5c4c3c;
    --iron:#9fb0bd;     --wood:#c98a4b;      --brass:#d8a83f;
    --green:#89b06d;    --red:#d9705a;       --sky:#7fa9c4;
    --shadow:none;
  }
}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;padding:0}
body{
  background:var(--paper); color:var(--ink);
  font-family:var(--serif); font-size:16.5px; line-height:1.5;
  overscroll-behavior-y:contain;
  font-variant-numeric:oldstyle-nums;
}
h1,h2,h3,.serif{font-family:var(--serif); font-weight:600; letter-spacing:.01em}
/* Anything that has to line up in a column gets lining, tabular figures.
   Old-style figures are the right reading face for a sentence and the
   wrong one for a price list — they hop above and below the baseline. */
.num,.purse,.energy-num,.chip,.trophy .v,.mkt-price,.stepper .qty,.log .t,
.row .meta,.btn .cost{font-variant-numeric:lining-nums tabular-nums}
#app{max-width:540px;margin:0 auto;padding:0 12px calc(84px + env(safe-area-inset-bottom));}

/* ---------- Header: the masthead ----------
   A masthead, not a toolbar. The paper is flat behind it and it is
   closed with the printer's double rule — one heavy, one hair, which
   is how every weekly of the decade separated its nameplate from its
   columns. The old fade-to-transparent is gone: it was a soft tonal
   ramp, which is the one thing the style sheet will not have. */
header{
  position:sticky; top:0; z-index:40; margin:0 -12px 10px; padding:9px 14px 0;
  padding-top:calc(9px + env(safe-area-inset-top));
  background:var(--paper);
  border-bottom:1px solid var(--ink);
  /* The second half of the double rule: a 3px band of paper, then a
     hairline. Painted as shadows because they must not take part in
     layout — the header is sticky and the plate scrolls under it. */
  box-shadow:0 3px 0 var(--paper), 0 4px 0 var(--rule-strong);
}
.hdr-top{display:flex;align-items:baseline;justify-content:space-between;gap:8px}
.town-name{font-family:var(--serif);font-size:21px;font-weight:600;letter-spacing:.06em}
.town-name .brand-mark{margin-right:6px}
.town-name small{display:block;font-size:10px;letter-spacing:.17em;text-transform:uppercase;color:var(--ink-faint);font-family:var(--label);font-weight:600}
.purse{font-family:var(--serif);font-size:20px;font-weight:600;color:var(--brass);white-space:nowrap}
.energy-row{display:flex;align-items:center;gap:10px;margin:7px 0 8px}
.energy-num{font-size:13px;color:var(--ink-soft);white-space:nowrap}
.energy-num b{color:var(--ink);font-size:15px}

/* ---------- Bars: ruled and hatched ----------
   A bar is a box ruled in ink and filled with hatching, the way a
   quantity is shaded on an engraved chart. The stripes are hard-edged
   repeating stops — no blending anywhere in the ramp — so this stays
   inside the rule against tonal gradients while giving each bar its
   own texture: energy hatches one way, growth the other, wear is
   solid iron, experience is close-ruled.

   `.bar` sets the angle and pitch per kind; the fill itself is always
   the same recipe, so a new bar is one line. */
.bar{
  flex:1;height:13px;background:var(--card-2);
  border:1px solid var(--ink);border-radius:0;overflow:hidden;
}
.bar > i{
  display:block;height:100%;border-radius:0;
  transition:width .45s cubic-bezier(.2,.8,.2,1);
  background-image:repeating-linear-gradient(
    var(--hatch-angle,45deg),
    var(--hatch-ink) 0 var(--hatch-line,1.5px),
    transparent var(--hatch-line,1.5px) var(--hatch-gap,4px));
  background-color:var(--hatch-bg,transparent);
}
.bar.energy > i{--hatch-ink:var(--brass);--hatch-bg:rgba(168,120,31,.16);--hatch-angle:45deg}
.bar.xp     > i{--hatch-ink:var(--sky);  --hatch-bg:rgba(61,96,118,.15); --hatch-angle:45deg;--hatch-gap:3px}
.bar.grow   > i{--hatch-ink:var(--green);--hatch-bg:rgba(76,107,60,.16); --hatch-angle:-45deg}
.bar.dur    > i{--hatch-ink:var(--iron); --hatch-bg:var(--iron);         --hatch-angle:90deg}
.bar.sm{height:8px}
/* Below about 8px a 4px hatch pitch reads as mush. The small bars
   tighten the pitch so the texture survives the size. */
.bar.sm > i{--hatch-line:1px;--hatch-gap:3px}

/* ---------- Cards ---------- */
.card{
  background:var(--card); border:1px solid var(--rule-strong); border-radius:var(--radius);
  padding:12px 14px 13px; margin-bottom:10px; box-shadow:var(--shadow);
}
.card.tight{padding:10px 12px 11px}
/* The heading of a card is a column head: set in the reading face,
   closed with a hairline that runs the full width of the plate. */
.card-h{
  display:flex;align-items:baseline;justify-content:space-between;gap:8px;
  margin:0 -14px 9px;padding:0 14px 7px;border-bottom:1px solid var(--rule);
}
.card.tight .card-h{margin:0 -12px 8px;padding:0 12px 6px}
.card-h h2{font-size:17px;margin:0;letter-spacing:.02em}
/* A card-h that opens on a piece of town chrome (Heat's flame, the
   Bank's vault, a wanted star) rather than straight into its h2 —
   baseline alignment would sink an inline-block mark below the
   letters next to it, so it gets centred against the row instead. */
.card-h > .mark{align-self:center;margin-right:2px}
.card-h .lvl{font-family:var(--label);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-faint)}
/* A sum of money is not a label: it keeps its own case and its own
   figures. "62 s 8 b" set as "62 S 8 B" is a different thing. */
.card-h .lvl.money{font-size:15px;letter-spacing:0;text-transform:none;color:var(--brass);font-family:var(--serif);font-variant-numeric:lining-nums tabular-nums}
.sub{font-size:13.5px;color:var(--ink-soft)}
.hint{font-size:13px;color:var(--ink-soft);margin-top:7px;display:flex;gap:6px;align-items:flex-start;line-height:1.42}
.hint.warn{color:var(--red)}
/* Standing's own hint, recoloured the same way a mood word already is
   — the badge mark inherits this via .mark's own color:inherit, so
   one class colours the icon and the sentence together. */
.hint.good{color:var(--green)}
.hint .ic{flex:none;opacity:.75}
.rule{height:1px;background:var(--rule);margin:11px -14px}
/* A section head above the plate it introduces. Letterspaced caps in
   the reading face, closed with a rule — a standing head, not a chip. */
.eyebrow{
  font-family:var(--label);font-size:11px;letter-spacing:.19em;text-transform:uppercase;
  color:var(--ink-soft);font-weight:700;margin:18px 0 7px;
  display:flex;align-items:center;gap:9px;
}
.eyebrow::after{content:"";flex:1;height:1px;background:var(--rule-strong)}

/* ---------- Buttons ---------- */
/* ---------- Buttons: pressed, not filled ----------
   The style sheet allows one accent per image, under a tenth of the
   area, and "the image must read completely with the accent removed".
   Three saturated button fills competing on one screen broke both
   halves of that, so colour has moved off the face and onto the RULE:
   a sell button is bounded and lettered in brass, a harvest button in
   field green, and the paper shows through both. Exactly one button
   is allowed to be solid — the primary — because a press only ever
   reversed out the one line it most wanted read.

   Everything is square. The press of a button is a 1px settle, not a
   lift, because nothing on a page floats. */
button{font-family:inherit;font-size:15px;color:inherit;cursor:pointer}
.btn{
  display:flex;align-items:center;justify-content:center;gap:8px;width:100%;
  min-height:46px;padding:10px 14px;border-radius:var(--radius);
  border:1px solid var(--ink-soft); background:var(--card); color:var(--ink);
  font-family:var(--serif); font-size:15.5px; font-weight:600; letter-spacing:.015em;
  transition:transform .06s ease, background .15s;
}
.btn:active{transform:translateY(1px)}
.btn.primary{background:var(--ink);border-color:var(--ink);color:var(--paper)}
.btn.gold{border-color:var(--brass);color:var(--brass);background:var(--card)}
.btn.green{border-color:var(--green);color:var(--green);background:var(--card)}
/* Disabled buttons drop their colour entirely — a faded-but-still-green
   button reads as tappable, which is exactly the confusion to avoid. */
.btn[disabled]{opacity:1;cursor:default;transform:none;background:var(--card-2)!important;border-color:var(--rule)!important;border-style:dashed!important;color:var(--ink-faint)!important}
.btn .cost{font-weight:600;font-size:13px;opacity:.75}
.btn-row{display:flex;gap:8px}
.btn-row > .btn{flex:1}
.btn.sm{min-height:38px;font-size:14.5px;padding:6px 12px}
.btn.xs{min-height:34px;min-width:38px;font-size:14px;padding:4px 9px}
.link{background:none;border:none;color:var(--sky);text-decoration:underline;text-underline-offset:2px;padding:6px 2px;font-size:13.5px;font-family:var(--serif)}

/* ---------- Chat and telegrams ---------- */
.chat-log{max-height:220px;overflow-y:auto;margin-bottom:8px}
.chat-line{font-size:14px;line-height:1.5;padding:3px 0;border-bottom:1px solid var(--rule)}
.chat-line:last-child{border-bottom:none}
.chat-line b{font-weight:700}
.chat-line .report{float:right;font-size:11px;opacity:.6}
.chat-form,.tg-form,.rename-form{display:flex;gap:6px}
.tg-form,.rename-form{flex-wrap:wrap;margin-top:4px}
.tg-form input:first-child,.rename-form input{flex:1 1 100px}
.tg-form input:nth-child(2){flex:1 1 auto}
.chat-input,.tg-form input,.rename-form input{
  flex:1;min-width:0;padding:9px 10px;font-family:var(--serif);font-size:14.5px;color:var(--ink);
  background:var(--card);border:1px solid var(--rule-strong);border-radius:var(--radius);
}
.chat-input:focus,.tg-form input:focus,.rename-form input:focus{outline:2px solid var(--sky);outline-offset:1px}
.chat-form .btn,.tg-form .btn,.rename-form .btn{width:auto;flex:none}

/* ---------- The front desk (sign in / create account) ---------- */
#authGate{padding-top:22px}
.card.auth .link{display:block;text-align:center;margin-top:2px}
.field{
  display:block;margin-bottom:12px;font-family:var(--label);font-size:12px;
  letter-spacing:.1em;text-transform:uppercase;color:var(--ink-soft);
}
.field input{
  display:block;width:100%;margin-top:5px;padding:11px 12px;
  font-family:var(--serif);font-size:16px;color:var(--ink);
  background:var(--card);border:1px solid var(--rule-strong);border-radius:var(--radius);
}
.field input:focus{outline:2px solid var(--sky);outline-offset:1px}
/* .hint's own display:flex outranks the UA [hidden] default, same as nav
   above, so an empty, hidden authError still needs this said explicitly. */
#authError[hidden]{display:none}

/* ---------- Tier pickers (the ladder) ---------- */
.tiers{display:flex;gap:6px;overflow-x:auto;padding:2px 0 6px;margin:0 -2px;-webkit-overflow-scrolling:touch;scrollbar-width:none}
.tiers::-webkit-scrollbar{display:none}
.tier{
  flex:none;display:inline-flex;flex-direction:column;align-items:center;gap:1px;
  min-height:40px;padding:5px 12px;border-radius:var(--radius);white-space:nowrap;
  border:1px solid var(--rule-strong); background:var(--card); color:var(--ink-soft);
  font-family:var(--serif);font-size:14px;font-weight:600;
}
/* The chosen rung is the one solid mark in the ladder. */
.tier.on{background:var(--ink);border-color:var(--ink);color:var(--paper)}
.tier.locked{opacity:1;border-style:dashed;color:var(--ink-faint);background:var(--card-2)}
.tier i{font-family:var(--label);font-style:normal;font-size:9.5px;letter-spacing:.11em;text-transform:uppercase;opacity:.85}

/* ---------- Segmented control ----------
   A row of tabs cut into the sheet: the live one is joined to the
   plate below by having no bottom rule, the dead ones are closed off.
   That is how a printed index tab reads, and it needs no fill. */
.seg{display:flex;gap:0;background:none;border:none;border-bottom:1px solid var(--rule-strong);border-radius:0;padding:0;margin:0 0 -1px}
.seg button{
  flex:1;border:1px solid transparent;border-bottom:none;background:none;
  padding:9px 4px 8px;border-radius:0;
  font-family:var(--label);font-size:11.5px;font-weight:700;letter-spacing:.11em;
  text-transform:uppercase;color:var(--ink-faint);
}
.seg button.on{
  background:var(--card);color:var(--ink);box-shadow:none;
  border-color:var(--rule-strong);
  position:relative;z-index:1;
}

/* ---------- Chips / inventory ----------
   A tally slip, not a pill. Square, ruled, and the count set in
   lining figures so a sackful of things reads as a column. */
.chips{display:flex;flex-wrap:wrap;gap:6px}
.chip{
  display:inline-flex;align-items:baseline;gap:6px;padding:4px 9px;border-radius:var(--radius);
  background:var(--card-2);border:1px solid var(--rule-strong);font-size:13.5px;color:var(--ink-soft);
  font-family:var(--serif);
}
.chip b{color:var(--ink);font-weight:700}

/* ---------- The marks ----------
   The engravings from `art/marks.ts`, worn by whatever they depict.

   The sizes are the art style sheet's sizing table, and the weights
   under them are the reason the table exists. A stroke inside a
   viewBox scales with the box, so the 2-unit outline that reads
   correctly on a 320px plate arrives at the 20px chip as a fifth of a
   pixel and disappears. The marks therefore set their widths from
   custom properties, and these classes supply them: a "unit" is meant
   to be about 1px at chip size and 4px at plate size, so the number
   here is that figure times 320 over the size.

   Below 32px the texture weight is set to zero — which draws nothing
   — because the style sheet is explicit that at chip size what must
   read is "the silhouette, and nothing else". Hatching that survives
   to 20px is not tone any more, it is grit. */
.mark{
  display:inline-block;flex:none;vertical-align:-.16em;
  color:inherit;fill:none;stroke:currentColor;
  stroke-linecap:round;stroke-linejoin:round;
}
/* These are lighter than the sizing table's own figures, and the tint
   is why. The table was written for a mark that is nothing but line,
   where a heavy contour is all that holds a shape together at 20px.
   With a wash underneath, the same weights close the form up and the
   colour never gets out — a slender pick head becomes solid black at
   every grade, which is the ladder lost. The contour still has to
   carry the silhouette, so this is as light as it goes. */
.mark.m20{width:20px;height:20px;--w-o:17;--w-s:11; --w-t:0}
.mark.m28{width:28px;height:28px;--w-o:13;--w-s:8.5;--w-t:0}
.mark.m32{width:32px;height:32px;--w-o:12;--w-s:7.5;--w-t:0}
.mark.m44{width:44px;height:44px;--w-o:8.5;--w-s:5.5;--w-t:3}
/* 96px is the style sheet's own "card / list row" tier (unit 1.5px),
   the first size a wash has room to breathe in — so unlike the sizes
   above, this one uses the table's real weights rather than a lighter
   hand-tuned figure: unit-px * 320/size, for outline (2u), structure
   (1.25u) and texture (0.75u) alike. It is also the smallest size the
   touchmark is ever allowed to sit on. */
.mark.m96{width:96px;height:96px;--w-o:10;--w-s:6.25;--w-t:3.75}
/* The touchmark carries no wash, so even this small it can hold real
   weight without the fill closing it up the way a tinted mark would. */
.mark.m22{width:22px;height:22px;--w-o:28;--w-s:18;--w-t:0}
/* A rung of a ladder carries its mark above its name. */
.tier .mark{margin-bottom:1px}
/* On the shelf the mark is the leftmost thing in the row. */
.mkt-top > .mark,.mkt-top > .mark-card{margin:0 12px 0 0}

/* A card-size mark plus its touchmark, stamped over the corner the
   same way every time — see docs/art-style-sheet.md's "The touchmark"
   for why it never appears smaller than this. */
.mark-card{position:relative;display:inline-flex;flex:none}
.mark-card .stamp{position:absolute;right:-3px;bottom:-3px}
.row > .mark{margin-right:10px}

/* ---------- Work-place scenes ----------
   The one deliberate exception to this file's own "no gradients"
   rule at the top: docs/art-style-sheet.md carries the same exception
   for exactly this reason — a scene's sky is allowed atmosphere the
   ink-only marks and the ledger chrome around it never are. The FRAME
   stays plain — a hairline rule, square corners, the ledger's own
   paper — same as every other card; only what's pasted inside it
   breaks the flat-ink rule, the way an 1890s ledger might carry an
   actual tipped-in photograph plate between its ruled pages. */
.place{
  margin:0 0 14px;background:var(--card);border:1px solid var(--rule-strong);
  border-radius:var(--radius);overflow:hidden;
}
.place-img{display:block;width:100%;height:auto;aspect-ratio:480/320;background:var(--card-2)}

/* ---------- Faction halls: the crest ----------
   A hall has no scene to head it the way a work place does — there's
   no headframe or field to stand in front of, only membership and
   dues — so its own crest stands in the same slot a `.place` figure
   would, framed the same plain way. */
.crest{
  display:flex;justify-content:center;padding:18px 0;margin:0 0 14px;
  background:var(--card);border:1px solid var(--rule-strong);border-radius:var(--radius);
}
.place figcaption{
  padding:6px 14px 8px;font-family:var(--label);font-size:11px;font-weight:700;
  letter-spacing:.12em;text-transform:uppercase;color:var(--ink-faint);
  border-top:1px solid var(--rule);
}

/* ---------- Identity: the ledger's own masthead ----------
   art/portrait.ts's plain arrival bust, square like every other
   object frame the style sheet sets, next to the name it belongs to.
   Everyone's `.portrait` src is the same file today, which is the
   whole point of the line underneath it — said once here rather than
   hidden, so nobody mistakes "plain" for a bug. */
.identity{display:flex;align-items:center;gap:12px}
.portrait{
  width:56px;height:56px;flex:none;background:var(--card-2);
  border:1px solid var(--rule-strong);border-radius:var(--radius);
}
.p-name{font-family:var(--serif);font-size:17px;font-weight:600;letter-spacing:.02em}

/* ---------- Rows ---------- */
.row{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:9px 0}
.row + .row{border-top:1px solid var(--rule)}
.row .lead{min-width:0}
.row .name{font-family:var(--serif);font-size:16px}
.row .meta{font-size:12.5px;color:var(--ink-soft);font-variant-numeric:tabular-nums}
.num{font-variant-numeric:tabular-nums}
.up{color:var(--green);font-weight:700}
.down{color:var(--red);font-weight:700}
.flat{color:var(--ink-faint);font-weight:700}

/* ---------- Market ---------- */
.mkt-item{padding:11px 0}
.mkt-item + .mkt-item{border-top:1px solid var(--rule)}
.mkt-top{display:flex;align-items:center;justify-content:space-between;gap:10px}
.mkt-price{text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums}
.mkt-price .big{font-family:var(--serif);font-size:18px;font-weight:600}
.mkt-price .tiny{font-size:11.5px;color:var(--ink-faint)}
.stepper{display:flex;gap:6px;align-items:center;margin-top:9px}
.stepper .qty{
  flex:1;text-align:center;font-family:var(--serif);font-size:17px;font-weight:600;
  border:1px solid var(--rule-strong);border-radius:var(--radius);padding:6px 4px;background:var(--card-2);
}
.trade{display:flex;gap:8px;margin-top:7px}
.trade .btn{flex:1}

/* ---------- Stats / trophies ----------
   The record is a ruled table, so the trophies are cells in a grid
   that shares its rules rather than twelve separate boxes with
   twelve separate borders. One hairline between neighbours, drawn by
   the gap showing the plate behind it. */
.trophies{
  display:grid;grid-template-columns:repeat(2,1fr);gap:1px;
  background:var(--rule);border:1px solid var(--rule);
}
.trophy{background:var(--card);border:none;border-radius:0;padding:9px 11px 10px}
.trophy .v{font-family:var(--serif);font-size:22px;font-weight:600;line-height:1.15}
.trophy .k{font-size:12px;color:var(--ink-soft);letter-spacing:.01em;line-height:1.3;margin-top:1px}
.streak{
  display:flex;align-items:center;gap:12px;background:var(--card);
  border:1px solid var(--ink);border-radius:var(--radius);
  padding:11px 13px;margin-bottom:12px;
  box-shadow:3px 3px 0 -1px var(--rule-strong);
}
.streak .n{font-family:var(--serif);font-size:28px;font-weight:600;color:var(--brass);font-variant-numeric:lining-nums}

/* ---------- Log ---------- */
.log{list-style:none;margin:0;padding:0;font-size:13.5px}
.log li{display:flex;gap:9px;padding:6px 0;border-top:1px solid var(--rule);color:var(--ink-soft)}
.log li:first-child{border-top:none}
.log .t{flex:none;font-size:11.5px;color:var(--ink-faint);width:52px;font-variant-numeric:tabular-nums;padding-top:2px}
.log .x{color:var(--ink)}

/* ---------- Bottom nav ---------- */
/* The foot of the sheet: closed with a heavy rule, its divisions
   marked by hairlines between the tabs rather than by a lifted bar.
   The live tab is stamped with a heavy rule over its head. */
nav{
  position:fixed;left:0;right:0;bottom:0;z-index:50;
  display:flex;background:var(--card);border-top:1px solid var(--ink);
  padding-bottom:env(safe-area-inset-bottom);
  box-shadow:none;
}
/* nav's own display:flex above outranks the UA [hidden] default, so the
   auth gate (which hides #nav while signed out) needs this said explicitly. */
#nav[hidden]{display:none}
nav button{
  flex:1;background:none;border:none;border-left:1px solid var(--rule);padding:14px 1px 13px;
  font-family:var(--label);
  color:var(--ink-faint);font-size:11px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
}
nav button:first-child{border-left:none}
nav button.on{color:var(--ink);background:var(--card-2);box-shadow:inset 0 3px 0 -1px var(--ink)}

/* ---------- Main Street: the doors ----------
   Fixed order, never sorted by state. A shut door is readable and
   unpressable, and states its own trigger. Colour appears on the three
   faction halls and nowhere else on the screen. */
.doors{padding:0}
.door{
  display:grid;grid-template-columns:1fr auto;gap:1px 10px;width:100%;
  text-align:left;background:none;border:none;border-bottom:1px solid var(--rule);
  padding:11px 12px;color:inherit;font:inherit;
}
.door:last-child{border-bottom:none}
.door-n{font-family:var(--serif);font-size:15px;font-weight:600;color:var(--ink)}
.door-tag{font-family:var(--label);font-size:10px;font-weight:700;letter-spacing:.15em;text-transform:uppercase;
  color:var(--ink-faint);white-space:nowrap;align-self:center}
.door-d{grid-column:1/-1;font-size:13px;line-height:1.4;color:var(--ink-soft)}
.door.shut{background:var(--card-2)}
.door.shut .door-n{color:var(--ink-soft);font-weight:500}
.door.company .door-n{color:var(--iron)}
.door.growers .door-n{color:var(--green)}
.door.dockside .door-n{color:var(--red)}
.link.back{display:inline-block;margin:0 0 6px 2px}

/* ---------- Toasts / feedback ---------- */
#toasts{position:fixed;left:0;right:0;bottom:calc(74px + env(safe-area-inset-bottom));z-index:60;display:flex;flex-direction:column;align-items:center;gap:6px;pointer-events:none;padding:0 14px}
/* A slip of paper handed across the counter: the app's own ground,
   ruled in ink, with a hard offset rule behind it for weight. Not a
   coloured lozenge — the good and bad slips say which they are with
   a heavier rule and their lettering, so a mono screenshot reads. */
.toast{
  max-width:520px;width:100%;background:var(--card);color:var(--ink);
  border:1px solid var(--ink);border-left-width:4px;
  border-radius:var(--radius);padding:10px 13px;font-size:14.5px;
  font-family:var(--serif);
  box-shadow:3px 3px 0 -1px var(--rule-strong);
  animation:toastIn .22s cubic-bezier(.2,.9,.3,1.2), toastOut .3s ease 2.5s forwards;
}
.toast.good{border-left-color:var(--green)}
.toast.bad{border-left-color:var(--red);color:var(--ink)}
@keyframes toastIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}
@keyframes toastOut{to{opacity:0;transform:translateY(-6px)}}

.floater{
  position:fixed;z-index:70;pointer-events:none;font-family:var(--serif);font-weight:700;
  font-size:17px;color:var(--sky);text-shadow:0 1px 0 var(--paper);
  animation:floatUp 1.1s cubic-bezier(.2,.7,.3,1) forwards;
}
.floater.gold{color:var(--brass)}
.floater.green{color:var(--green)}
@keyframes floatUp{0%{opacity:0;transform:translate(-50%,0) scale(.9)}15%{opacity:1;transform:translate(-50%,-8px) scale(1)}100%{opacity:0;transform:translate(-50%,-46px) scale(1)}}

/* The flash was a white bloom over the whole screen, which is a
   photographic effect and belongs to a different century. What a
   press does instead is print the page a shade heavier: this washes
   the paper's own colour across the sheet and lifts. */
#flash{position:fixed;inset:0;z-index:65;pointer-events:none;background:var(--brass);opacity:0}
#flash.go{animation:flashy .5s ease-out}
@keyframes flashy{0%{opacity:.16}100%{opacity:0}}

/* The double-ruled box a paper set an announcement in. */
#levelup{
  position:fixed;left:50%;top:16%;transform:translateX(-50%);z-index:70;pointer-events:none;
  background:var(--card);border:1px solid var(--brass);border-radius:var(--radius);
  outline:1px solid var(--brass);outline-offset:3px;
  padding:15px 24px;text-align:center;box-shadow:4px 4px 0 -1px var(--rule-strong);
  opacity:0;max-width:300px;
}
/* Both halves need `forwards` — without it the banner snaps back to
   opacity:0 the moment luIn finishes and the celebration is invisible. */
#levelup.go{animation:luIn .35s cubic-bezier(.2,.9,.3,1.3) forwards, luOut .5s ease 2.6s forwards}
#levelup .k{font-family:var(--label);font-size:10.5px;letter-spacing:.24em;text-transform:uppercase;color:var(--brass);font-weight:700}
#levelup .v{font-family:var(--serif);font-size:26px;font-weight:600;margin-top:3px;letter-spacing:.01em}
#levelup .s{font-size:13.5px;color:var(--ink-soft);margin-top:4px}
/* An unlock is the bigger announcement: a third rule around the box. */
#levelup.unlock{outline-width:3px;outline-offset:2px}
@keyframes luIn{from{opacity:0;transform:translateX(-50%) translateY(-14px) scale(.94)}to{opacity:1;transform:translateX(-50%) none}}
@keyframes luOut{to{opacity:0;transform:translateX(-50%) translateY(-10px)}}

/* ---------- Modal ---------- */
#modal{position:fixed;inset:0;z-index:80;display:none;align-items:center;justify-content:center;padding:20px;background:rgba(30,22,14,.45)}
#modal.on{display:flex}
#modal .box{background:var(--card);border:1px solid var(--ink);border-radius:var(--radius);padding:18px;max-width:360px;width:100%;box-shadow:5px 5px 0 -1px var(--rule-strong)}
#modal h3{margin:0 0 6px;font-size:19px}
#modal p{margin:0 0 14px;font-size:14.5px;color:var(--ink-soft)}

/* ---------- Misc ---------- */
/* The welcome notice: brass rule, no fill. The gradient it used to
   carry was the only tonal ramp left on the page. */
.welcome{border:1px solid var(--brass);background:var(--card)}
.welcome .card-h{border-bottom-color:var(--brass)}
.welcome h2{color:var(--brass)}
.empty{text-align:center;color:var(--ink-faint);font-size:13.5px;padding:14px 0;font-style:italic}
/* What just happened, set as a pull quote against a heavy rule. */
.result{margin-top:9px;font-size:14px;font-family:var(--serif);font-style:italic;color:var(--ink);border-left:3px solid var(--ink-soft);padding:1px 0 1px 10px}
.footnote{text-align:center;font-family:var(--label);font-size:11px;letter-spacing:.06em;color:var(--ink-faint);padding:10px 0 2px}


/* ============================================================
   THE MAP — the bird's-eye plate, and the card under a shopfront.

   The plate is an engraving the server cut once and will not cut
   again until the town changes; the client pans and zooms it and
   nothing else. Every word on the card came down the wire.

   The SVG is inlined rather than put in an <img> for one reason:
   a tap has to find the lot it landed on, and only the document
   can do that. Ink and accent are handed to it from the palette
   here, so the plate flips with the theme like everything else.
   ============================================================ */
.map-wrap{margin:0 -12px}
.map-bar{
  display:flex;gap:4px;align-items:center;padding:8px 12px 6px;
  overflow-x:auto;-webkit-overflow-scrolling:touch;
}
.map-bar button{
  flex:none;background:var(--card);border:1px solid var(--rule-strong);border-radius:var(--radius);
  padding:6px 11px;font-family:var(--label);font-size:11px;font-weight:700;
  letter-spacing:.1em;text-transform:uppercase;color:var(--ink-soft);
}
.map-bar button.on{background:var(--ink);border-color:var(--ink);color:var(--paper);box-shadow:none}
.map-bar button[disabled]{opacity:.45}
.map-bar .sp{flex:1}

/* ---------- Work: the quick-jump bar ----------
   Nine stations and an odd-jobs shelf in one long scroll is a lot of
   thumb work to get to the one you want. Sticks under the masthead
   (--hdr-h, measured in JS since the header's own height isn't fixed)
   so a tap reaches any station without scrolling back up first. */
.work-nav{
  display:flex;gap:4px;align-items:center;
  overflow-x:auto;-webkit-overflow-scrolling:touch;
  position:sticky;top:var(--hdr-h,74px);z-index:30;
  background:var(--paper);margin:0 -12px 10px;padding:6px 12px;
  border-bottom:1px solid var(--rule);
}
.work-nav button{
  flex:none;background:var(--card);border:1px solid var(--rule-strong);border-radius:var(--radius);
  padding:6px 11px;font-family:var(--label);font-size:11px;font-weight:700;
  letter-spacing:.1em;text-transform:uppercase;color:var(--ink-soft);
}
/* Land below both sticky bars, not under them. */
[id^="wsec-"]{scroll-margin-top:calc(var(--hdr-h,74px) + var(--worknav-h,44px))}

/* Each station is closed by default (bar the first): the scene plate
   plus a full skill card, nine times over, is what actually made the
   page long. A tap on the summary — or a jump from the bar above —
   opens just that one. */
details.wsec{margin-bottom:10px}
details.wsec > summary{
  cursor:pointer;list-style:none;
  display:flex;align-items:center;gap:8px;
  background:var(--card);border:1px solid var(--rule-strong);border-radius:var(--radius);
  padding:12px 14px;box-shadow:var(--shadow);
  font-family:var(--serif);font-size:16px;font-weight:600;color:var(--ink);
}
details.wsec > summary::-webkit-details-marker{display:none}
details.wsec > summary > span:first-child{flex:1}
details.wsec > summary .lvl{font-family:var(--label);font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-faint)}
details.wsec > summary::after{
  content:"›";font-family:var(--label);font-size:18px;line-height:1;color:var(--ink-faint);
  transform:rotate(90deg);transition:transform .15s;
}
details.wsec[open] > summary::after{transform:rotate(-90deg)}
details.wsec[open] > summary{margin-bottom:10px}
.wsec-body{animation:wsec-in .12s ease-out}
@keyframes wsec-in{from{opacity:0}to{opacity:1}}
.map-note{padding:0 12px 8px;font-size:12px;color:var(--ink-soft)}
.map-frame{
  position:relative;background:var(--card);border-top:1px solid var(--rule);
  border-bottom:1px solid var(--rule);height:56vh;min-height:280px;overflow:hidden;
  touch-action:none;cursor:grab;
}
.map-frame.drag{cursor:grabbing}
.map-frame svg{display:block;width:100%;height:100%;color:var(--ink);--accent:var(--brass)}
.map-frame .lot{cursor:pointer}
.map-frame .lot.sel>path:first-child{fill:var(--brass);fill-opacity:.26}
.map-frame .figure{cursor:pointer}
.map-busy{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  font-family:var(--serif);font-size:15px;color:var(--ink-soft);text-align:center;padding:20px;
}
.map-zoom{position:absolute;right:8px;bottom:8px;display:flex;flex-direction:column;gap:5px}
.map-zoom button{
  width:38px;height:38px;border-radius:var(--radius);border:1px solid var(--ink);
  background:var(--card);color:var(--ink);font-size:18px;font-weight:700;
  box-shadow:2px 2px 0 -1px var(--rule-strong);
}
.map-scale{
  position:absolute;left:10px;bottom:10px;font-family:var(--label);font-size:10.5px;
  color:var(--ink-faint);letter-spacing:.12em;text-transform:uppercase;
}
.lotcard{
  border:1px solid var(--ink);border-radius:var(--radius);background:var(--card);
  box-shadow:3px 3px 0 -1px var(--rule-strong);margin:12px 0 0;padding:12px 13px 13px;
}
.lotcard h3{font-family:var(--serif);font-size:17px;margin:0 0 2px}
.lotcard .sub{font-family:var(--label);font-size:11px;color:var(--ink-faint);text-transform:uppercase;letter-spacing:.13em;font-weight:700}
.lotcard dl{display:grid;grid-template-columns:auto 1fr;gap:3px 12px;margin:10px 0 0;font-size:13.5px}
.lotcard dt{color:var(--ink-faint)}
.lotcard dd{margin:0;color:var(--ink)}
.lotcard .note{margin:10px 0 0;font-size:13px;color:var(--ink-soft)}
.lotcard .flag{display:inline-block;margin-top:8px;font-size:11.5px;font-weight:700;
  letter-spacing:.08em;text-transform:uppercase;color:var(--brass)}
.lotcard .shut{margin-top:10px;font-size:12.5px;color:var(--ink-faint);font-style:italic}
