/* Local palette: one-off colors for this page, named by role. Global tokens live in style.css. */
:root {
  --ec-dome-bg: #dcf0ff;
  --ec-tide-pool-bg: #effcff;
  --ec-tide-pool-bg-2: #c6f1ff;
  --ec-rack-red: #ff5b5b;      /* Pride rack stripe */
  --ec-rack-orange: #ff9a3c;   /* Pride rack stripe, Gourdlight rack felt */
  --bl-desk: #f1d3a8;          /* Drafting Loft: Archie's wood desk */
  --bl-desk-grain: #e2b985;    /* its grain lines */
  --bl-sheet: #eaf4ff;         /* blueprint sheets and rolled plans */
}
/* Player economy pages: stalls, price map, bottles, sealed auctions, town builds, the geode. Loaded via page({head}). */
.pickers { display: grid; gap: 10px; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); margin: 12px 0; }
.stack-form { display: grid; gap: 12px; max-width: 420px; }
.lots { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
.lot {
  display: grid; gap: 4px; padding: 12px; color: var(--ink); text-decoration: none;
  background: var(--card); border: 2px solid var(--ink); border-radius: var(--r-tile); box-shadow: var(--shadow-sm);
}
.lot small { color: var(--muted); }
.lot-items { display: flex; flex-wrap: wrap; gap: 4px; }
.spark polyline { stroke: var(--grape); }
.spark.up polyline { stroke: var(--gain); }
.spark.down polyline { stroke: var(--loss); }
.gain { color: var(--gain); font-weight: 800; }
.loss { color: var(--loss); font-weight: 800; }
.lot { align-content: start; } /* grid rows share a height; keep each lot's text at the top instead of spread out */
.stack-form .pickers { margin: 0; }
.stack-form > p:last-child { margin: 0; }
.item-tile form { margin-top: auto; padding-top: 4px; } /* Buy buttons line up along the bottom of a row of tiles */
.haggle .bank-figures { margin: 0; }
.grid-2 > form > .btn { justify-self: start; } /* bank: buttons keep their natural width */
.grid-2 > form.search-row { display: flex; } /* your shop: name/till fields sit beside their buttons */

/* Tables: numbers right-aligned and unbroken; boxes and buttons on one center line; labels beside their boxes */
.table .num { text-align: right; white-space: nowrap; }
.table .inline-form { align-items: center; }
.table .inline-form label { display: flex; align-items: center; gap: 6px; }

/* Phones: .table-stack rows turn into wrapped strips so prices and buttons never scroll off the right edge.
   .stack-main grows to fill the first line (.stack-wide fills all of it), .stack-full takes a line of its own, data-label names a cell once the header is gone. */
@media (max-width: 560px) {
  .table-wrap .table-stack { min-width: 0; }
  .table-stack, .table-stack tbody { display: block; }
  .table-stack thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .table-stack tr { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; padding: 10px 12px; }
  .table-stack td { padding: 0; }
  .table-stack td:has(> svg:only-child) { width: auto; } /* the 1% icon-column trick collapses the icon in a flex row */
  .table-stack td:empty { display: none; }
  .table-stack tbody tr + tr { border-top: 1px solid var(--rule); }
  .table-stack tbody tr + tr > * { border-top: 0; }
  .table-stack .stack-main { flex: 1 1 55%; min-width: 0; }
  .table-stack .stack-wide { flex: 1 1 calc(100% - 64px); } /* fills the line beside an icon, pushing the rest to line two */
  .table-stack .stack-full { flex-basis: 100%; }
  .table-stack td:last-child { margin-left: auto; } /* the row's action or price keeps to the right edge */
  .table-stack .num { text-align: left; }
  .table-stack td[data-label]::before { content: attr(data-label) ' '; font: 700 var(--fs-xs) var(--display); color: var(--muted); }
}

/* Vault Burrow geode */
.geode-body { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; }
.geode-body > div { flex: 1 1 260px; display: grid; gap: 8px; }
.geode-body p { margin: 0; }
.geode { flex: none; max-width: 100%; height: auto; }

/* Bargain Owl price map: glows over the world map, brighter = cheaper */
.owl-glows { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.owl-glow { pointer-events: auto; cursor: pointer; }
.owl-glow circle { animation: owl-pulse 2.4s ease-in-out infinite alternate; transform-box: fill-box; transform-origin: center; }
.owl-glow:hover rect, .owl-glow:focus-visible rect { fill: var(--sun); }
@keyframes owl-pulse { to { transform: scale(1.12); } }
@media (prefers-reduced-motion: reduce) { .owl-glow circle { animation: none; } }
.reduce-motion .owl-glow circle { animation: none; }
.owl-map { margin: 8px 0 12px; }

/* Starlight Auction: a lot under its glass dome, glowing brighter with each sealed bid (--glow 0-1) */
.dome { display: inline-grid; place-items: center; padding: 10px 8px 4px; border: 3px solid var(--ink); border-bottom-width: 6px;
  border-radius: 999px 999px 10px 10px; background: radial-gradient(circle at 50% 60%, rgb(255 226 138 / calc(var(--glow) * .9)), transparent 70%), color-mix(in srgb, var(--ec-dome-bg) 40%, transparent); /* style-ok: color computed from a per-element custom property */
  box-shadow: 0 0 calc(var(--glow) * 22px) rgb(255 210 63 / var(--glow)); } /* style-ok: color computed from a per-element custom property */
.reveal-bids { display: grid; gap: 6px; padding-left: 1.4em; }
.reveal-bids .row-hi { font-weight: 800; }

/* Swap Meet Pier: bottles bobbing in the tide pool, glass glowing with the thrower's pet */
.tide-pool { background: linear-gradient(180deg, var(--ec-tide-pool-bg), var(--ec-tide-pool-bg-2)); }
.bottle-tile { grid-template-columns: auto 1fr; align-items: center; gap: 4px 12px; }
.bottle-tile > span { display: grid; gap: 4px; min-width: 0; }
.bottle-tile .bottle { animation: bottle-bob 3s ease-in-out infinite alternate; }
.lot:nth-child(3n+2) .bottle { animation-delay: -1.2s; }
.lot:nth-child(3n) .bottle { animation-delay: -2.1s; }
@keyframes bottle-bob { from { transform: translateY(2px) rotate(-6deg); } to { transform: translateY(-3px) rotate(6deg); } }
@media (prefers-reduced-motion: reduce) { .bottle-tile .bottle { animation: none; } }
.reduce-motion .bottle-tile .bottle { animation: none; }
h1 .bottle { vertical-align: middle; }

/* Geode card: the label on its own line, then one row of input, Add and Crack it open, both full-size buttons on one center line.
   Phones stack input, Add and Crack it open full width, so the two buttons line up edge to edge. */
.geode-label { font-weight: 700; }
.geode-actions { align-items: center; }
.geode-add { display: flex; flex: 1 1 260px; gap: 12px; align-items: center; min-width: 0; }
.geode-add input { flex: 1 1 8em; min-width: 0; width: auto; }
.geode-card .btn-row .btn { white-space: normal; } /* the long Crack it open label wraps instead of pushing the page wide */
.geode-body > div { min-width: 0; }
@media (max-width: 767px) {
  .geode-body { justify-content: center; text-align: center; }
  .geode-actions, .geode-add { display: grid; grid-template-columns: minmax(0, 1fr); gap: 12px; } /* grid: the shared flex-basis 40% rule would stretch a column's height */
  .geode-actions form, .geode-actions .btn, .geode-add input { width: 100%; }
}
/* The crystals' glints twinkle one after another (--d); the global reduced-motion rules stop them. */
.geode-glint { transform-box: fill-box; transform-origin: center; animation: geode-twinkle 2.6s ease-in-out var(--d, 0s) infinite; }
@keyframes geode-twinkle { 0%, 60%, 100% { transform: none; opacity: 1; } 80% { transform: scale(.4) rotate(45deg); opacity: .5; } }

/* Desktop: stall/auction/bottle forms use two columns instead of a narrow strip beside empty space; the recipe list and lots use the width. */
@media (min-width: 1100px) {
  .stack-form { max-width: 760px; grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
  .stack-form > :first-child, .stack-form > p, .stack-form > fieldset { grid-column: 1 / -1; }
  .recipes { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
}

/* NPC shop head (/shop/*) and the Drafting Loft (/builds): coins, backpack and next-cart pills, then one line and a closed How it works */
.sh-pills, .bl-pills { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0; }
.sh-how, .bl-how { margin-top: 12px; text-align: left; }
.sh-how ul, .bl-how ul { margin: 4px 0 0; padding-left: 20px; }
@media (max-width: 767px) { .sh-pills, .bl-pills { justify-content: center; } }
/* Drafting Loft (/builds), layout B "Archie's work table" (2026-10-15 pick): one warm-wood desk card with the builds taking shares
   pinned on as tilted blueprint sheets (a pink pin on top), a build going up as a smaller sheet, Up next as rolled-up plans (pills
   with sky end caps), then Standing in Glimmeria as a strip of postcards. Art: src/art/builds.js. The desk colors (--bl-*) are in
   the :root block at the top; every page theme keeps cards light (Plum night darkens only the page), so the wood and paper stay
   as they are and the theme's own ink and muted text sit on them (test/builds-opening.test.js checks the contrast). */
.bl-desk { background: var(--bl-desk); background-image: repeating-linear-gradient(0deg, transparent 0 22px, var(--bl-desk-grain) 22px 24px); }
.bl-desk > h2, .bl-cards > h2 { margin-top: 0; }
.bl-art svg { display: block; width: 100%; height: auto; }
.bl-sheets { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin-top: 18px; }
.bl-sheet { position: relative; background: var(--bl-sheet); border: var(--line) solid var(--ink); border-radius: var(--r-sm); padding: 14px; box-shadow: var(--shadow-sm); rotate: calc(var(--tilt, 0) * .6deg); }
.bl-sheet::before { content: ''; position: absolute; top: -10px; left: 50%; translate: -50% 0; width: 18px; height: 18px; border-radius: 50%; background: var(--pink); border: 2px solid var(--ink); z-index: 1; }
.bl-sheet .bl-art { margin-bottom: 10px; }
.bl-sheet h3 { margin: 0; }
.bl-blurb { margin: 6px 0 10px; font-size: var(--fs-sm); }
.bl-meter { margin: 6px 0 0; font-size: var(--fs-sm); }
.bl-held { margin: 8px 0 0; }
.bl-sheet-small { margin-top: 24px; display: grid; grid-template-columns: 200px 1fr; gap: 16px; align-items: center; }
.bl-sheet-small .bl-art { margin: 0; }
.bl-rolls-h { margin: 24px 0 4px; }
.bl-rolls-note { margin: 0 0 10px; color: var(--ink); }
.bl-rolls { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 12px; }
.bl-rolls li { display: grid; padding: 8px 20px; background: var(--bl-sheet); border: 2px solid var(--ink); border-radius: 999px; box-shadow: inset 10px 0 0 var(--sky), inset -10px 0 0 var(--sky), var(--shadow-sm); }
.bl-rolls li span { font-size: var(--fs-xs); color: var(--muted); }
.bl-postcards { display: flex; gap: 16px; overflow-x: auto; padding: 4px 4px 8px; }
.bl-postcard { flex: 0 0 220px; background: var(--card); border: 2px solid var(--ink); border-radius: var(--r-sm); padding: 8px; box-shadow: var(--shadow-sm); }
.bl-postcard p { margin: 8px 4px 0; }

/* Buy chips A, pick then buy: radio pills 10 / 50 / 250 / All N (unaffordable ones disabled), the picked size's cost and pay line
   and its full-width Buy button shown by :has() on data-i (no JS). Another amount folds under it. */
.bl-buy { margin-top: 14px; padding-top: 12px; border-top: 2px dashed var(--rule); }
.bl-buy fieldset { border: 0; padding: 0; margin: 0; min-width: 0; }
.bl-buy legend { font: 600 var(--fs-sm) var(--display); margin-bottom: 8px; padding: 0; }
.bl-chips { gap: 12px; }
.bl-chip { border-radius: 999px; min-height: 44px; min-width: 64px; padding: 8px 16px; align-content: center; font-size: 1rem; }
.bl-chip:has(input:disabled) { opacity: .45; cursor: not-allowed; }
.bl-buy .bl-quote, .bl-buy .bl-go { display: none; }
.bl-buy .bl-short { display: block; }
.bl-buy:has(input[data-i="0"]:checked) [data-i="0"]:is(.bl-quote, .bl-go),
.bl-buy:has(input[data-i="1"]:checked) [data-i="1"]:is(.bl-quote, .bl-go),
.bl-buy:has(input[data-i="2"]:checked) [data-i="2"]:is(.bl-quote, .bl-go),
.bl-buy:has(input[data-i="3"]:checked) [data-i="3"]:is(.bl-quote, .bl-go) { display: block; }
.bl-quote { margin: 12px 0; }
.bl-go { width: 100%; }
.bl-custom { margin-top: 16px; }
.bl-custom .inline-form { margin-top: 12px; gap: 16px; align-items: center; }

@media (max-width: 767px) {
  .bl-desk > h2, .bl-cards > h2, .bl-rolls-h, .bl-rolls-note { text-align: center; }
  .bl-sheets { grid-template-columns: 1fr; }
  .bl-sheet .head-row { justify-content: center; text-align: center; }
  .bl-sheet-small { grid-template-columns: 1fr; }
  .bl-rolls { display: grid; }
  .bl-chips { display: grid; grid-template-columns: repeat(4, 1fr); }
  .bl-chip { min-width: 0; padding: 8px 4px; }
}

/* Shop Buy buttons (routes/shops.js tiles) stay on one line: "Buy for 126" used to wrap to "Buy / for" in narrow tiles. A tile with
   under 130px of room (the 6-across desktop grid, 2-across on small phones) drops the " for" and reads "Buy 126". A container query,
   so it follows the tile's width and every tile in one grid gets the same label. Markup: Buy<span class="buy-for"> for</span> 126.
   ponytail: 130px fits 3-digit prices (the tailor tops out at 809); a 4-digit shop needs a wider threshold. */
.item-tile:has(.buy-btn) { container-type: inline-size; }
.buy-btn { white-space: nowrap; padding-inline: 10px; }
@container (max-width: 129px) { .buy-for { display: none; } }

/* A shop with racks (Thimble & Thread): Cart / Pride / Gourdlight tabs as fabric swatches (2026-10-12 pick B), each pinked (zigzag-cut)
   top and bottom with a sewn-on name label. data-rack picks the cloth: Cart blue gingham, Pride rainbow stripes, Gourdlight orange
   felt; any other rack gets lilac polka dots. The picked swatch tilts and gets a dashed stitch inside its edge (not color alone).
   The cloth is ::after under the zigzag mask; ::before is the same shape in ink, peeking out right and below as outline and shadow,
   and its shadow slides on hover and press. The row pins to the top while the picked section scrolls
   under it; #racks (a tab link, a buy) lands just above it. */
#racks { scroll-margin-top: 8px; }
.rack-tabs { position: sticky; top: 0; z-index: 5; display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 12px;
  margin: -6px -6px 12px; padding: 8px 6px 12px; background: var(--card); }
@media (min-width: 768px) { .rack-tabs { grid-auto-columns: minmax(0, 220px); justify-content: center; } } /* tag-sized, not stretched */
.rack-tabs a {
  --cloth: radial-gradient(var(--grape) 3px, transparent 3.5px) 0 0 / 14px 14px, var(--hi); /* a rack with no cloth of its own */
  --t: 5px; /* pinking tooth depth */
  --pinked: conic-gradient(from 135deg at 50% 0, #000 90deg, transparent 0) top / calc(2 * var(--t)) var(--t) round no-repeat,
    conic-gradient(from -45deg at 50% 100%, #000 90deg, transparent 0) bottom / calc(2 * var(--t)) var(--t) round no-repeat, /* style-ok: mask stencil, black is opaque */
    linear-gradient(#000 0 0) center / 100% calc(100% - 2 * var(--t) + 1px) no-repeat; /* style-ok: mask stencil, black is opaque */
  position: relative; isolation: isolate; display: grid; grid-template-columns: minmax(0, 1fr); justify-items: center; align-content: center;
  min-height: 44px; padding: 10px 10px 12px; color: var(--ink); text-decoration: none; text-align: center; transition: transform .08s; }
.rack-tabs a[data-rack="cart"] { --cloth: linear-gradient(90deg, color-mix(in srgb, var(--sky) 45%, transparent) 50%, transparent 0) 0 0 / 16px 16px,
  linear-gradient(color-mix(in srgb, var(--sky) 45%, transparent) 50%, transparent 0) 0 0 / 16px 16px, var(--card); }
.rack-tabs a[data-rack="pride"] { --cloth: repeating-linear-gradient(-35deg, var(--ec-rack-red) 0 7px, var(--ec-rack-orange) 0 14px, var(--sun) 0 21px,
  var(--lime) 0 28px, var(--sky) 0 35px, var(--grape) 0 42px); }
.rack-tabs a[data-rack="gourdlight"] { --cloth: radial-gradient(rgb(255 255 255 / .35) 1px, transparent 1.5px) 0 0 / 7px 7px,
  radial-gradient(rgb(0 0 0 / .12) 1px, transparent 1.5px) 3px 4px / 5px 5px, var(--ec-rack-orange); }
.rack-tabs a::before, .rack-tabs a::after { content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none; -webkit-mask: var(--pinked); mask: var(--pinked); }
.rack-tabs a::before { background: var(--ink); translate: 3px 3px; transition: translate .08s; }
.rack-tabs a::after { background: var(--cloth); border-inline: 2px solid var(--ink); }
/* The item keeps a card-white halo so it reads on any cloth; the name label is a card-white chip, so its contrast never depends on the cloth. */
.rack-tabs svg { width: 30px; height: 30px; filter: drop-shadow(0 0 2px var(--card)) drop-shadow(0 0 1px var(--card)); }
.rack-tabs :is(b, small) { justify-self: stretch; margin-inline: -4px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  background: var(--card); border: 2px solid var(--ink); }
.rack-tabs b { margin-top: 4px; padding: 2px 2px 0; border-bottom: 0; border-radius: var(--r-sm) var(--r-sm) 0 0; font: 600 .95rem/1.2 var(--display); }
.rack-tabs small { margin-top: -1px; padding: 0 2px 2px; border-top: 0; border-radius: 0 0 var(--r-sm) var(--r-sm); font-size: var(--fs-xs); color: var(--muted); } /* -1px: no cloth seam between the two halves */
/* Three tabs on a 320px phone leave about 55px inside a swatch (the container is its content box): the label widens to the swatch's edges and the name drops to --fs-xs. */
.rack-tabs a { container-type: inline-size; }
@container (max-width: 64px) { .rack-tabs :is(b, small) { margin-inline: -10px; } .rack-tabs b { font-size: var(--fs-xs); } }
/* The stitch is the cloth's own outline, so it runs behind the label (never across its text) and the tab's outline stays the global focus ring. */
.rack-tabs a[aria-current] { rotate: -2deg; }
.rack-tabs a[aria-current]::after { outline: 2px dashed var(--ink); outline-offset: -9px; }
.rack-tabs a[aria-current] :is(b, small) { background: var(--pick); color: var(--ink); }
@media (hover: hover) and (pointer: fine) { .rack-tabs a:hover { transform: translate(-2px, -2px); } .rack-tabs a:hover::before { translate: 5px 5px; } }
.rack-tabs a:active { transform: translate(2px, 2px); }
.rack-tabs a:active::before { translate: 1px 1px; }
@media (prefers-reduced-motion: reduce) { .rack-tabs a:is(:hover, :active) { transform: none; } }
.reduce-motion .rack-tabs a:is(:hover, :active) { transform: none; }
.rack-line { margin: 0 0 12px; }
@media (max-width: 767px) { .rack-line { text-align: center; } } /* "just make sure the description is centered on mobile" */
/* Vault Burrow: "Where your Glimmers grow", the geode against the closest town build (src/services/build-nudge.js) */
.vb-card > p { margin: 8px 0; }
.vb-vs { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 12px 0; }
.vb-way { display: grid; gap: 4px; align-content: start; padding: 12px; border: 2px solid var(--ink); border-radius: var(--r-tile); background: var(--card); }
.vb-way small { font: 600 var(--fs-xs) var(--display); text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.vb-way b { font: 600 1.3rem/1.1 var(--display); }
.vb-way span { font-size: var(--fs-sm); }
.vb-win { background: var(--sun-cream); }
.vb-mini { list-style: none; margin: 0 0 12px; padding: 0; display: grid; gap: 10px; }
.vb-mini li { display: grid; gap: 4px; }
@media (max-width: 767px) { .vb-card h2, .vb-card > p { text-align: center; } .vb-card .btn-row .btn { width: 100%; } }

/* Shop tiles: what the item does, one row per effect in a tinted inset under the name (routes/shops.js fxInset, 2026-10-16 pick B).
   Icon, then the short number in bold (rule lines regular); the small print sits under it, lined up past the icon. Left-aligned
   so a wrapped line reads as one line; the inset is ink so it reads as the answer. */
.item-tile .it-fx { display: grid; gap: 4px; align-self: stretch; margin: 4px 0 2px; padding: 6px; background: var(--tint); border-radius: var(--r-sm); font-size: var(--fs-sm); line-height: 1.25; color: var(--ink); text-align: left; }
.it-fx .fx-row { display: grid; grid-template-columns: 16px minmax(0, 1fr); column-gap: 6px; align-items: start; }
.it-fx .fx-ic { display: block; margin-top: 1px; } /* on the first line of a wrapped rule */
.it-fx .fx-lead { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; min-width: 0; }
.it-fx .fx-lead b { font: 700 var(--fs-sm)/1.2 var(--display); }
.it-fx .fx-rest { grid-column: 2; font-size: var(--fs-xs); color: var(--muted); }
/* Icon fills reuse the meter colors (style.css .stat-*), so Health, Hunger and Mood match the pet panel; the ink outline keeps
   the pale ones (sun, glow, silver) readable on the tint. */
.fx-ic path { fill: var(--fx, var(--tint-2)); stroke: var(--ink); stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; }
.fx-ic path.fx-line { fill: none; }
.fx-hp { --fx: var(--ui-stat-hp-fill); }
.fx-hunger { --fx: var(--ui-stat-hunger-fill); }
.fx-mood, .fx-morph { --fx: var(--pink); }
.fx-glow, .fx-lure { --fx: var(--ui-stat-glow-fill); }
.fx-zip, .fx-luck { --fx: var(--sun); }
.fx-grit, .fx-bad { --fx: color-mix(in oklab, var(--ui-stat-hunger-fill), var(--pink) 30%); }
.fx-guard, .fx-def, .fx-element { --fx: var(--sky); }
.fx-teach { --fx: var(--pick); }
.fx-atk { --fx: var(--rule); }
.fx-color { --fx: var(--grape); }
/* Hunger: "+2" and ten pips on one line, the ones this food fills lit in the meter's orange */
.it-fx .fx-hunger + .fx-lead { flex-wrap: nowrap; }
.fx-pips { display: inline-flex; gap: 1px; }
.fx-pips i { display: block; width: 5px; height: 10px; border: 1.5px solid var(--ink); border-radius: 2px; background: var(--card); }
.fx-pips i.on { background: var(--ui-stat-hunger-fill); }

/* Vault Burrow: "Ways to earn Glimmers" (logic/earn.js, #earn; every "not enough Glimmers" error points here). Each row is one
   link, the place name over its one line, at least 44px tall. */
.earn-list { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 8px; }
.earn-list a { display: grid; gap: 2px; min-height: 44px; padding: 8px 12px; background: var(--tint); border-radius: var(--r-sm); color: var(--ink); text-decoration: none; }
.earn-list a:hover, .earn-list a:focus-visible { background: var(--tint-2); }
.earn-list b { font-family: var(--display); color: var(--link); }
.earn-list span { font-size: var(--fs-sm); }

/* Market Stalls list (/market, routes/myshop.js, 2026-10-15 pick A): players' stalls, then the town regulars'. Each row is the
   keeper's portrait, the stall's name, who runs it and where, and what is on the shelves. */
.ms-head > h1, .ms-head > .lede { text-align: center; margin-inline: auto; }
.ms-top { justify-content: center; }
.ms h2 { display: flex; align-items: center; gap: 8px; }
.ms-list { list-style: none; margin: 0; padding: 0; display: grid; }
.ms-list li + li { border-top: 2px dashed var(--rule); }
.ms-row { display: flex; gap: 12px; align-items: center; min-height: 64px; padding: 10px 4px; color: var(--ink); text-decoration: none; border-radius: var(--r-sm); }
.ms-row > svg { flex: none; width: 52px; height: 52px; }
.ms-txt { display: grid; gap: 2px; min-width: 0; }
.ms-txt b { font: 700 1.05rem/1.2 var(--display); color: var(--link); overflow-wrap: anywhere; }
.ms-txt small { color: var(--muted); font-size: var(--fs-sm); }
@media (hover: hover) and (pointer: fine) { .ms-row:hover { background: var(--hi-soft); } }
@media (min-width: 900px) { .ms-list { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 24px; } .ms-list li:nth-child(2) { border-top: 0; } }
@media (max-width: 767px) { .ms-top .btn { flex-basis: 100%; } }
