/* Drop Radar — collector workspace layer.
   Loaded AFTER site.css and scoped to .ws-app / .ws-* / .dash-* so it adds a
   dense application shell without editing the shared stylesheet every other
   page depends on. Palette, type and the 2px-ink-with-hard-shadow treatment all
   come from site.css's :root tokens — nothing new is invented here. */

.ws-app {
  --ws-nav-h: 148px;
  --ws-gap: clamp(14px, 1.6vw, 22px);
  --ws-radius: 14px;
  --ws-shadow: 3px 3px 0 var(--ink);
  --ws-row-gap: 10px;
}
@media (max-width: 900px) { .ws-app { --ws-nav-h: 196px; } }
@media (max-width: 560px) { .ws-app { --ws-nav-h: 232px; } }

/* Visible-on-focus skip link: site.css's .sr-only never reveals itself, and a
   sighted keyboard user needs to see where Tab has landed. */
.ws-skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--acid); color: var(--ink); border: 2px solid var(--ink);
  border-radius: 0 0 10px 0; padding: 10px 16px;
  font-weight: 800; font-size: 0.8rem; text-transform: uppercase; text-decoration: none;
}
.ws-skip:focus { left: 0; }

/* ============================ fixed application nav ====================== */
.ws-nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 40;
  display: flex; align-items: center; gap: 12px;
  flex-wrap: wrap; justify-content: flex-start;
  min-height: var(--ws-nav-h);
  padding: 8px clamp(12px, 2.4vw, 28px);
  background: var(--paper);
  border-bottom: 2px solid var(--ink);
  box-shadow: 0 2px 0 rgba(28, 16, 48, 0.08);
}
/* --ws-nav-h-real is measured by workspace.js; --ws-nav-h is the no-JS
   fallback, and is only ever right when the nav does not wrap. */
.ws-app > main { padding-top: calc(var(--ws-nav-h-real, var(--ws-nav-h)) + 10px); padding-bottom: 60px; }

.ws-brand { display: inline-flex; align-items: center; gap: 8px; text-decoration: none; color: var(--ink); }
.ws-brand .logo-mark { width: 30px; height: 30px; }
.ws-brand .logo-word { font-weight: 900; font-stretch: 75%; font-size: 1.06rem; text-transform: uppercase; white-space: nowrap; }
@media (max-width: 420px) { .ws-brand .logo-word { display: none; } }

/* Freshness badge. Green ONLY when the catalogue's own state_last_seen is
   inside the 30-minute window wall.js already uses; stale is a muted pill that
   names the time instead of pretending. */
.ws-live {
  display: inline-flex; align-items: center; gap: 7px;
  border: 2px solid var(--ink); border-radius: 999px;
  padding: 4px 11px; background: var(--card);
  font-family: 'Space Mono', monospace; font-size: 0.72rem; white-space: nowrap;
}
.ws-live[data-fresh="yes"] { background: color-mix(in srgb, var(--in), #fff 86%); }
.ws-live[data-fresh="no"] { background: var(--card); color: var(--muted); }
.ws-live .status-dot { width: 8px; height: 8px; }
@media (max-width: 720px) { .ws-live { font-size: 0.66rem; padding: 3px 9px; } }

/* Specificity note: site.css styles `nav[aria-label="Primary"]` (0,1,1), so
   these are scoped through .ws-nav (0,2,0) to win without !important. */
.ws-nav .ws-views {
  display: flex; align-items: center; gap: 6px 10px;
  flex: 1 0 100%; order: 3; margin: 0;
  flex-wrap: wrap; justify-content: flex-start;
}
.ws-nav .ws-views a {
  text-decoration: none; font-weight: 800; font-size: 0.78rem;
  text-transform: uppercase; letter-spacing: 0.02em; color: var(--ink);
  padding: 7px 10px; border-radius: 999px; border: 2px solid transparent;
  white-space: nowrap;
}
.ws-nav .ws-views a.ws-views__pro {
  background: var(--acid); border-color: var(--ink); letter-spacing: 0;
}
.ws-nav .ws-views a:hover { border-color: var(--ink); color: var(--ink); background: var(--card); text-decoration: none; }
.ws-nav .ws-views a:active { transform: translate(1px, 1px); }
.ws-nav .ws-views a[aria-current="page"] { background: var(--acid); border-color: var(--ink); color: var(--ink); text-decoration: none; }

.ws-nav-right { display: flex; align-items: center; gap: 8px; margin-left: auto; }
.ws-nav-right .motion-toggle { font-size: 0.64rem; padding: 5px 10px; }
@media (max-width: 1080px) { .ws-nav-right .motion-toggle { display: none; } }

.ws-shelf-pill {
  display: inline-flex; align-items: center; gap: 6px; text-decoration: none;
  border: 2px solid var(--ink); border-radius: 999px; padding: 5px 12px;
  background: var(--card); color: var(--ink); font-weight: 800; font-size: 0.76rem;
}
.ws-shelf-pill:hover { background: var(--acid); color: var(--ink); }

/* Stateful profile zone: skeleton -> guest tokens -> dashboard token. */
.ws-auth { display: inline-flex; align-items: center; gap: 8px; min-height: 34px; }
.ws-auth[data-state="checking"] .ws-auth__skel {
  display: block; width: 104px; height: 30px; border-radius: 999px;
  background: linear-gradient(90deg, var(--line), color-mix(in srgb, var(--line), #fff 55%), var(--line));
  background-size: 200% 100%; animation: wsShimmer 1.2s linear infinite;
}
@keyframes wsShimmer { to { background-position: -200% 0; } }
.motion-paused .ws-auth__skel { animation: none; }
@media (prefers-reduced-motion: reduce) { .ws-auth__skel { animation: none; } }

.ws-token {
  display: inline-flex; align-items: center; gap: 6px; text-decoration: none;
  border: 2px solid var(--ink); border-radius: 999px; padding: 7px 14px;
  font-weight: 800; font-size: 0.76rem; text-transform: uppercase; letter-spacing: 0.04em;
  background: var(--card); color: var(--ink); white-space: nowrap;
  transition: transform 120ms ease-out, box-shadow 120ms ease-out;
}
.ws-token:hover { transform: translate(-1px, -1px); box-shadow: 3px 3px 0 var(--ink); color: var(--ink); }
.ws-token:active { transform: translate(1px, 1px); box-shadow: none; }
.ws-token--strong { background: var(--acid); }
.ws-token--dash { background: var(--hot); color: var(--paper); }
.ws-token--dash:hover { color: var(--paper); }
@media (max-width: 560px) { .ws-token { padding: 6px 10px; font-size: 0.7rem; } }

@media (max-width: 900px) {
  .ws-nav { row-gap: 6px; }
  .ws-nav-right { margin-left: auto; }
}

/* ============================== command strip =========================== */
.ws-command {
  position: sticky; top: var(--ws-nav-h-real, var(--ws-nav-h)); z-index: 22;
  background: var(--paper);
  border: 2px solid var(--ink); border-radius: var(--ws-radius);
  box-shadow: var(--ws-shadow);
  padding: 10px 12px; margin: 0 0 var(--ws-gap);
  display: flex; flex-direction: column; gap: 8px;
}
.ws-command__line { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.ws-command__line--scroll {
  flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
  scrollbar-width: thin;
  /* The clip edge was flush with the pill stroke, so the capsule ends
     looked jagged. This padding is inside the clip, which leaves the
     2px border and the hover shadow intact. */
  padding: 4px 2px 8px;
}
.ws-command__line--scroll > * { flex: 0 0 auto; }
.ws-command input[type="search"] {
  flex: 1 1 240px; min-width: 0; padding: 9px 14px;
  border: 2px solid var(--ink); border-radius: 999px; background: var(--card);
  font-family: 'Archivo', sans-serif; font-size: 0.9rem; color: var(--ink);
}
.ws-command input[type="search"]::placeholder { color: var(--muted); }
.ws-command .sort-select { font-size: 0.78rem; padding: 7px 12px; }
.ws-command .pill { padding: 8px 16px; font-size: 0.78rem; }
.ws-command .pill[aria-pressed="true"], .ws-command .pill.active { background: var(--acid); }
.ws-command .pill:disabled { opacity: 0.45; cursor: not-allowed; transform: none; box-shadow: none; }
.ws-scope-label {
  font-weight: 800; font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--muted); padding-right: 2px;
}
@media (max-width: 900px) {
  /* The nav is two rows tall here. Keeping the filter strip stuck under it as
     well would leave a phone reading drops through a letterbox. */
  .ws-command { position: static; }
  .ws-command input[type="search"] { flex: 1 0 100%; }
  .ws-command .quick-filters {
    flex: 1 1 100%; min-width: 0; flex-wrap: wrap; overflow: visible;
    padding: 4px 2px 8px;
  }
  .ws-command .quick-filters .pill { flex: 0 0 auto; white-space: nowrap; }
}

/* ============================== desk layout ============================= */
.ws-desk {
  display: grid; grid-template-columns: minmax(0, 2fr) minmax(280px, 1fr);
  gap: var(--ws-gap); align-items: start;
}
@media (max-width: 1080px) { .ws-desk { grid-template-columns: 1fr; } }

.ws-col { min-width: 0; }
#drops, #feed { scroll-margin-top: calc(var(--ws-nav-h) + 74px); }
.ws-col__head {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin: 0 0 10px;
}
.ws-col__head h1, .ws-col__head h2 { font-size: 1.05rem; letter-spacing: 0.01em; }
.ws-count { font-family: 'Space Mono', monospace; font-size: 0.78rem; color: var(--muted); }

.ws-rail { display: flex; flex-direction: column; gap: var(--ws-gap); position: sticky; top: calc(var(--ws-nav-h) + 78px); }
@media (max-width: 1080px) { .ws-rail { position: static; } }

.ws-panel {
  background: var(--card); border: 2px solid var(--ink); border-radius: var(--ws-radius);
  box-shadow: var(--ws-shadow); padding: 14px;
}
.ws-panel > h2 { font-size: 0.86rem; letter-spacing: 0.06em; margin-bottom: 10px; }
.ws-panel__sub { margin: -6px 0 10px; font-size: 0.74rem; color: var(--muted); line-height: 1.45; }
.ws-panel__more { display: inline-block; margin-top: 10px; font-size: 0.76rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.04em; }
.ws-panel--hot { background: var(--hot); color: var(--paper); }
.ws-panel--hot h2, .ws-panel--hot p { color: var(--paper); }
.ws-panel--hot .ws-panel__sub { color: color-mix(in srgb, var(--paper), var(--hot) 24%); }
.ws-panel--scroll { max-height: 420px; overflow-y: auto; }

/* The stat tiles wall.js renders into #stat-strip, restyled for the rail. */
.ws-panel .stat-strip { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; margin: 0; }
.ws-panel .stat-tile { padding: 9px 10px; box-shadow: none; border-radius: 10px; background: var(--paper); }
.ws-panel .stat-tile .label { font-size: 0.6rem; letter-spacing: 0.08em; margin-bottom: 2px; }
.ws-panel .stat-tile .value { font-size: 1.15rem; }

/* ============================== product rows ============================ */
.ws-rows { display: flex; flex-direction: column; gap: var(--ws-row-gap); }

.ws-row {
  position: relative;
  display: grid; grid-template-columns: 78px minmax(0, 1fr) auto;
  gap: 12px; align-items: start;
  background: var(--card); border: 2px solid var(--ink); border-radius: var(--ws-radius);
  box-shadow: var(--ws-shadow); padding: 10px;
  transition: transform 110ms ease-out, box-shadow 110ms ease-out;
}
.ws-row:hover { transform: translate(-1px, -1px); box-shadow: 5px 5px 0 var(--ink); }
.ws-row:focus-within { box-shadow: 5px 5px 0 var(--ink); }
.ws-row.is-featured { grid-template-columns: 122px minmax(0, 1fr) auto; background: color-mix(in srgb, var(--acid), var(--card) 82%); }
/* Mobile density (DR-WORKSPACE-5). Measured at 390x844: the command strip ate
   ~340px and the featured row's double-height treatment ate most of the rest,
   so a phone saw ONE product on the first screen. Nothing below changes what a
   row says or which actions it offers — only how much room it takes. */
@media (max-width: 640px) {
  .ws-row { grid-template-columns: 80px minmax(0, 1fr); gap: 10px; padding: 8px; }
  .ws-row__side { grid-column: 1 / -1; flex-direction: row; align-items: center; justify-content: space-between; }

  /* The "featured" treatment is a desktop luxury: at 390px a bigger thumb, a
     3-line title and the mascots turned the first row into a screenful. The row
     keeps its tint so it still reads as the lead item — it just stops costing a
     whole screen. */
  .ws-row.is-featured { grid-template-columns: 80px minmax(0, 1fr); }
  .ws-row.is-featured .ws-row__title { font-size: 0.95rem; -webkit-line-clamp: 2; }
  .ws-row.is-featured .ws-price { font-size: 1.05rem; }
  .ws-row.is-featured .card-mascots { display: none; }
}

@media (max-width: 560px) {
  /* Tighter filter strip: same controls, same order, less chrome before the
     first product. Search and sort share a row instead of stacking — the sort
     control is narrow, and a full-width search line was a wasted row.
     (The row-compaction half of this breakpoint lives AFTER the row rules it
     overrides — see "mobile row compaction" below. Equal specificity, so
     source order decides.) */
  .ws-command { padding: 8px 10px; gap: 6px; }
  .ws-command__line { gap: 6px; }
  .ws-command input[type="search"] { flex: 1 1 auto; min-width: 0; padding: 8px 10px; font-size: 0.82rem; }
  .ws-command .sort-select { flex: 0 0 auto; max-width: 42%; padding: 6px 10px; font-size: 0.72rem; }
  .ws-command .pill { padding: 8px 14px; font-size: 0.74rem; }
  .ws-scope-label { font-size: 0.6rem; }
  /* NOT DONE: reordering so search and sort share a line. Tried, measured, and
     reverted — flex `order` on these children made the strip TALLER (221px ->
     230px), because .quick-filters is flex:1 1 100% and reordering around it
     produced an extra wrap rather than removing one. Collapsing the category /
     shop line behind a "Filters" disclosure is the real fix, and that is a
     markup + behaviour change with its own tests, not a stylesheet tweak. */
}

.ws-row__media {
  position: relative; display: block; width: 100%; aspect-ratio: 1 / 1;
  border: 2px solid var(--ink); border-radius: 10px; overflow: hidden;
  background: var(--line); text-decoration: none;
}
.ws-row__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ws-row__media.is-placeholder { display: flex; align-items: center; justify-content: center; }
.ws-row__media.is-placeholder img { width: 52%; height: 52%; object-fit: contain; }

.ws-row__main { min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.ws-row__title {
  font-weight: 800; font-size: 0.95rem; line-height: 1.3; color: var(--ink); text-decoration: none;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.ws-row__title:hover { color: var(--hot); text-decoration: underline; }
.ws-row.is-featured .ws-row__title { font-size: 1.12rem; -webkit-line-clamp: 3; }
.ws-row__meta { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; font-size: 0.72rem; }
.ws-row__meta .tier-pill { font-size: 0.6rem; padding: 2px 8px; }
.ws-store { font-weight: 800; text-transform: uppercase; letter-spacing: 0.04em; font-size: 0.66rem; }
.ws-seen { color: var(--muted); font-size: 0.7rem; }
.ws-row__why { font-size: 0.72rem; color: var(--muted); line-height: 1.4; }

/* Underneath the picture, not on top of it. At 78px (and 53px on a phone) an
   overlaid pill covered the product it was describing. */
.ws-row__mediawrap { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.ws-chip {
  display: block; text-align: center;
  border: 2px solid var(--ink); border-radius: 999px; padding: 1px 6px;
  font-family: 'Space Mono', monospace; font-size: 0.56rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.04em;
  white-space: nowrap;
}
.ws-chip--in { background: var(--in); color: #fff; }
.ws-chip--out { background: var(--card); color: var(--muted); }
.ws-rare {
  border: 2px solid var(--ink); border-radius: 999px; padding: 1px 8px;
  background: var(--rare); color: #fff; font-size: 0.6rem; font-weight: 900;
  text-transform: uppercase; letter-spacing: 0.05em;
}

.ws-row__side { display: flex; flex-direction: column; align-items: flex-end; gap: 8px; }
.ws-price { font-size: 1.05rem; font-weight: 700; white-space: nowrap; }
.ws-row.is-featured .ws-price { font-size: 1.3rem; }

.ws-row__actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 2px; }
.ws-act {
  display: inline-flex; align-items: center; gap: 6px; text-decoration: none;
  border: 2px solid var(--ink); border-radius: 999px; padding: 6px 13px;
  font-weight: 800; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em;
  background: var(--card); color: var(--ink);
  transition: transform 110ms ease-out, box-shadow 110ms ease-out;
}
.ws-act:hover { transform: translate(-1px, -1px); box-shadow: 3px 3px 0 var(--ink); color: var(--ink); }
.ws-act:active { transform: translate(1px, 1px); box-shadow: none; }
.ws-act--cop { background: var(--acid); }
.ws-act--buy { background: var(--card); }
.ws-act--plain { border-style: dashed; color: var(--muted); font-weight: 600; }
.ws-alt { font-size: 0.72rem; color: var(--muted); line-height: 1.5; }
.ws-alt a { font-weight: 700; }
.ws-alt.is-muted { opacity: 0.85; }

/* ---- the watch control: present on every product, in every state ---- */
.ws-watch {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 34px; padding: 6px 12px;
  border: 2px solid var(--ink); border-radius: 999px; background: var(--card);
  font-family: 'Archivo', sans-serif; font-weight: 800; font-size: 0.72rem;
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--ink); cursor: pointer;
  transition: transform 110ms ease-out, box-shadow 110ms ease-out, background 110ms ease-out;
}
.ws-watch .watch-icon { font-size: 0.95rem; line-height: 1; }
.ws-watch:hover { transform: translate(-1px, -1px); box-shadow: 3px 3px 0 var(--ink); background: var(--acid); }
.ws-watch:active { transform: translate(1px, 1px); box-shadow: none; }
.ws-watch[aria-pressed="true"] { background: var(--acid); }
.ws-watch[aria-busy="true"] { opacity: 0.6; cursor: progress; transform: none; box-shadow: none; }
.ws-watch:disabled { opacity: 0.5; cursor: not-allowed; transform: none; box-shadow: none; }
.ws-watch-error {
  grid-column: 1 / -1;
  margin: 2px 0 0; font-size: 0.72rem; font-weight: 700; color: var(--out);
  border-top: 2px dashed var(--line); padding-top: 6px;
}
/* The once-per-device note after a signed-out visitor's first watch. On the
   desktop wall (a grid) it spans the full width, and dense packing lets the
   rest of the clicked card's row fill in, so it sits under that row. */
.ws-rows:has(> .ws-nudge) { grid-auto-flow: row dense; }
.ws-nudge {
  grid-column: 1 / -1;
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
  border: 2px dashed var(--ink); border-radius: var(--ws-radius); background: var(--paper);
  padding: 12px 44px 12px 14px; position: relative;
}
.ws-nudge__copy { flex: 1 1 260px; margin: 0; font-size: 0.86rem; line-height: 1.5; color: var(--ink); }
.ws-nudge__go { padding: 9px 16px; }
.ws-nudge__close {
  position: absolute; top: 8px; right: 8px;
  border: 2px solid var(--ink); background: var(--card); border-radius: 999px;
  width: 28px; height: 28px; cursor: pointer;
  font-family: 'Archivo', sans-serif; font-weight: 900; font-size: 0.8rem; line-height: 1; color: var(--ink);
}
.ws-nudge__close:hover { background: var(--hot); color: var(--paper); }

/* ---- mobile row compaction (DR-WORKSPACE-5) ----
   Placed AFTER the row rules above on purpose: these override them at equal
   specificity, so source order is what makes them win. An earlier attempt sat
   above .ws-row__meta / .ws-store and silently did nothing (288px -> 286px).

   Measured at 390x844 before: a normal row was 288px. 86px of that was the two
   seller actions wrapping onto separate lines (152px + 186px into a 264px
   column) and 71px was store / tier / checked wrapping onto three. Nothing is
   removed and no action is hidden — the pieces are made small enough to sit on
   one line each. */
@media (max-width: 560px) {
  .ws-row { grid-template-columns: 76px minmax(0, 1fr); gap: 8px; }
  /* Sized by the grid column, not pinned: the column now has to hold the
     picture and the In stock chip beneath it. */
  .ws-row__media { width: 100%; height: auto; }
  .ws-row__meta { gap: 5px; font-size: 0.68rem; }
  .ws-row__meta .tier-pill { font-size: 0.55rem; padding: 1px 6px; }
  .ws-store { font-size: 0.6rem; }
  .ws-seen { font-size: 0.64rem; }
  .ws-row__actions { gap: 6px; margin-top: 0; }
  .ws-act { padding: 5px 10px; font-size: 0.64rem; gap: 4px; }
  /* The seller action carries the shop name, so its width follows the shop.
     Cap and ellipsize rather than let one long name push the pair onto a second
     line — the shop is already named in the meta row directly above it. */
  .ws-act--buy, .ws-act--plain {
    max-width: 44%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  /* The locked lead is a sentence ("Open at {shop} — day pass"), not a short
     shop name. Ellipsizing it hid the price. Let it use the row and wrap. */
  .ws-act.is-locked {
    max-width: 100%; overflow: visible; text-overflow: unset; white-space: normal;
    line-height: 1.25;
  }
  /* The cart action is the wider of the pair and sets whether both fit on one
     line; measured 132px + 6px gap against a 274px column, so the seller link
     gets the remainder rather than a share that forces a wrap. */
  .ws-act--cop { padding: 5px 9px; }
  .ws-row__side { gap: 6px; }
  /* The watch control keeps a 44px hit target even as the row tightens:
     density must not cost tappability. */
  .ws-watch { min-height: 44px; }

  /* The column header cost 89px before the first product: a heading, the
     result count and the seller-trust line each on their own row. The trust
     line is the one claim that must survive — it is kept, just set to share
     the row with the count instead of owning one. */
  .ws-col__head { gap: 4px 8px; margin-bottom: 8px; }
  .ws-col__head h1, .ws-col__head h2 { font-size: 0.95rem; }
  .ws-count { font-size: 0.68rem; line-height: 1.35; }
}

/* ---- states: skeleton / empty / error ---- */
.ws-skeleton { display: flex; flex-direction: column; gap: var(--ws-row-gap); }
.ws-skeleton__row {
  height: 96px; border: 2px solid var(--line); border-radius: var(--ws-radius);
  background: linear-gradient(90deg, var(--line), color-mix(in srgb, var(--line), #fff 60%), var(--line));
  background-size: 200% 100%; animation: wsShimmer 1.4s linear infinite;
}
.motion-paused .ws-skeleton__row { animation: none; }
@media (prefers-reduced-motion: reduce) { .ws-skeleton__row { animation: none; } }

.ws-empty, .ws-error {
  border: 2px dashed var(--line); border-radius: var(--ws-radius);
  padding: 22px 16px; text-align: center; color: var(--muted); font-size: 0.9rem;
  background: var(--card);
}
.ws-error { border-color: var(--out); color: var(--out); border-style: solid; }
.ws-empty img { width: 58px; height: 58px; display: block; margin: 0 auto 8px; }

.ws-more {
  width: 100%; margin-top: var(--ws-row-gap);
  border: 2px solid var(--ink); border-radius: 999px; background: var(--card);
  padding: 11px 16px; font-family: 'Archivo', sans-serif; font-weight: 800; font-size: 0.78rem;
  text-transform: uppercase; letter-spacing: 0.05em; cursor: pointer;
}
.ws-more:hover { background: var(--acid); }
.ws-more[aria-busy="true"] { opacity: 0.6; cursor: progress; }

/* ============================ modal overlay ============================= */
.ws-overlay {
  position: fixed; inset: 0; z-index: 90;
  display: flex; align-items: center; justify-content: center;
  padding: 18px; background: rgba(28, 16, 48, 0.62);
}
.ws-overlay[hidden] { display: none; }
.ws-modal {
  width: min(520px, 100%); max-height: 86vh; overflow-y: auto;
  background: var(--card); color: var(--ink);
  border: 3px solid var(--ink); border-radius: 20px; box-shadow: 6px 6px 0 var(--ink);
  padding: 22px clamp(18px, 4vw, 28px);
  animation: wsModalIn 180ms cubic-bezier(0.2, 0.9, 0.3, 1.2) both;
}
@keyframes wsModalIn { from { opacity: 0; transform: translateY(10px) scale(0.98); } }
.motion-paused .ws-modal { animation: none; }
@media (prefers-reduced-motion: reduce) { .ws-modal { animation: none; } }
.ws-modal__head { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 10px; }
.ws-modal__head h2 { font-size: 1.25rem; flex: 1 1 auto; }
.ws-modal__mascot { width: 46px; height: 46px; transform: rotate(-6deg); flex: 0 0 auto; }
.ws-modal__copy { margin: 0 0 10px; font-size: 1rem; line-height: 1.55; }
.ws-modal__note {
  margin: 0 0 16px; padding: 10px 12px;
  border: 2px dashed var(--line); border-radius: 10px; background: var(--paper);
  font-size: 0.82rem; line-height: 1.5; color: var(--muted);
}
.ws-modal__note b { color: var(--ink); }
.ws-modal__cta { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.ws-modal__fine { margin: 14px 0 0; font-size: 0.76rem; color: var(--muted); line-height: 1.5; }
.ws-modal__close {
  border: 2px solid var(--ink); background: var(--card); border-radius: 999px;
  width: 32px; height: 32px; flex: 0 0 auto; cursor: pointer;
  font-family: 'Archivo', sans-serif; font-weight: 900; font-size: 0.9rem; line-height: 1; color: var(--ink);
}
.ws-modal__close:hover { background: var(--hot); color: var(--paper); }

.ws-btn {
  display: inline-flex; align-items: center; justify-content: center;
  border: 2px solid var(--ink); border-radius: 999px; padding: 11px 20px;
  font-family: 'Archivo', sans-serif; font-weight: 800; font-size: 0.8rem;
  text-transform: uppercase; letter-spacing: 0.04em; text-decoration: none;
  background: var(--card); color: var(--ink); cursor: pointer;
  transition: transform 110ms ease-out, box-shadow 110ms ease-out;
}
.ws-btn:hover { transform: translate(-1px, -1px); box-shadow: 3px 3px 0 var(--ink); color: var(--ink); }
.ws-btn:active { transform: translate(1px, 1px); box-shadow: none; }
.ws-btn:disabled, .ws-btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; transform: none; box-shadow: none; }
.ws-btn--primary { background: var(--acid); }
.ws-btn--ghost { background: transparent; }

/* ============================== dashboard =============================== */
/* width:100% — on phones site.css makes <main> a flex COLUMN, where auto side
   margins stop an item stretching and size it to its content instead. The
   shelf's 640px unit then pushed the whole dashboard off the side of the screen
   (silently clipped by body's overflow-x) rather than scrolling inside itself. */
.dash-shell { max-width: 1080px; margin: 0 auto; width: 100%; }
.dash-head { display: flex; align-items: flex-end; gap: 14px; flex-wrap: wrap; margin-bottom: 18px; }
.dash-head h1 { font-size: clamp(1.5rem, 3.6vw, 2.1rem); }
.dash-who { font-size: 0.84rem; color: var(--muted); }
.dash-badge {
  display: inline-flex; align-items: center; gap: 6px;
  border: 2px solid var(--ink); border-radius: 999px; padding: 4px 12px;
  font-family: 'Space Mono', monospace; font-size: 0.72rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em; background: var(--card);
}
.dash-badge--pro { background: var(--acid); }
.dash-badge--free { background: var(--card); color: var(--muted); }

.dash-tabs {
  display: flex; gap: 8px; flex-wrap: wrap;
  border-bottom: 2px solid var(--ink); padding-bottom: 10px; margin-bottom: 18px;
}
.dash-tab {
  border: 2px solid var(--ink); border-radius: 999px; background: var(--card);
  padding: 9px 16px; font-family: 'Archivo', sans-serif; font-weight: 800; font-size: 0.78rem;
  text-transform: uppercase; letter-spacing: 0.04em; cursor: pointer; color: var(--ink);
  transition: transform 110ms ease-out, box-shadow 110ms ease-out;
}
.dash-tab:hover { transform: translate(-1px, -1px); box-shadow: 3px 3px 0 var(--ink); }
.dash-tab[aria-selected="true"] { background: var(--acid); box-shadow: 3px 3px 0 var(--ink); }
.dash-panel[hidden] { display: none; }
.dash-panel { display: flex; flex-direction: column; gap: 14px; }

.dash-card {
  background: var(--card); border: 2px solid var(--ink); border-radius: var(--ws-radius);
  box-shadow: var(--ws-shadow); padding: 16px;
}
.dash-card h2 { font-size: 0.95rem; margin-bottom: 8px; }
.dash-card h3 { font-size: 0.82rem; margin-bottom: 6px; }
.dash-card p { margin: 0 0 10px; font-size: 0.88rem; line-height: 1.55; }
.dash-card p:last-child { margin-bottom: 0; }
.dash-note { font-size: 0.8rem; color: var(--muted); line-height: 1.5; }

.dash-rows { display: flex; flex-direction: column; gap: 10px; }
.dash-row {
  display: grid; grid-template-columns: 56px minmax(0, 1fr) auto; gap: 12px; align-items: center;
  border: 2px solid var(--line); border-radius: 12px; padding: 10px; background: var(--paper);
}
@media (max-width: 600px) {
  .dash-row { grid-template-columns: 48px minmax(0, 1fr); }
  .dash-row__side { grid-column: 1 / -1; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
}
.dash-row__media { width: 100%; aspect-ratio: 1/1; border: 2px solid var(--ink); border-radius: 8px; overflow: hidden; background: var(--line); }
.dash-row__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.dash-row__main { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.dash-row__title { font-weight: 800; font-size: 0.9rem; line-height: 1.3; text-decoration: none; color: var(--ink); }
.dash-row__title:hover { color: var(--hot); }
.dash-row__meta { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; font-size: 0.72rem; color: var(--muted); }
.dash-row__side { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; }

.dash-field { display: flex; gap: 12px; align-items: flex-start; padding: 12px 0; border-top: 2px dashed var(--line); }
.dash-field:first-of-type { border-top: 0; }
.dash-field__body { flex: 1 1 auto; min-width: 0; }
.dash-field__label { font-weight: 800; font-size: 0.86rem; display: block; }
.dash-field__help { font-size: 0.78rem; color: var(--muted); line-height: 1.5; margin-top: 2px; }
.dash-field input[type="checkbox"] { width: 20px; height: 20px; margin-top: 3px; flex: 0 0 auto; accent-color: var(--hot); }
.dash-field input[type="checkbox"]:disabled { cursor: not-allowed; }
.dash-field.is-unavailable { opacity: 0.72; }

.dash-status {
  display: inline-flex; align-items: center; gap: 6px;
  border: 2px solid var(--line); border-radius: 999px; padding: 2px 9px;
  font-family: 'Space Mono', monospace; font-size: 0.64rem; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--muted); background: var(--card);
}
.dash-status--off { border-style: dashed; }
.dash-status--on { border-color: var(--ink); color: var(--ink); background: color-mix(in srgb, var(--in), #fff 84%); }
.dash-msg { font-size: 0.8rem; min-height: 1.2em; margin: 6px 0 0; }
.dash-msg.is-ok { color: var(--in); font-weight: 700; }
.dash-msg.is-error { color: var(--out); font-weight: 700; }
.dash-msg.is-pending { color: var(--muted); }

.dash-select {
  border: 2px solid var(--ink); border-radius: 999px; padding: 6px 12px; background: var(--card);
  font-family: 'Archivo', sans-serif; font-weight: 700; font-size: 0.74rem; color: var(--ink);
}
.dash-select:disabled { opacity: 0.5; cursor: not-allowed; }

.dash-skeleton { display: flex; flex-direction: column; gap: 10px; }
.dash-skeleton__row {
  height: 74px; border-radius: 12px; border: 2px solid var(--line);
  background: linear-gradient(90deg, var(--line), color-mix(in srgb, var(--line), #fff 60%), var(--line));
  background-size: 200% 100%; animation: wsShimmer 1.4s linear infinite;
}
.motion-paused .dash-skeleton__row { animation: none; }
@media (prefers-reduced-motion: reduce) { .dash-skeleton__row { animation: none; } }

/* ============================ below-the-fold trim ======================= */
.ws-secondary { margin-top: 44px; border-top: 2px solid var(--line); padding-top: 28px; }
.ws-secondary > h2 { font-size: 1rem; margin-bottom: 14px; }

/* ---- the locked one-tap basket (DR-PAYWALL-1) ----
   Free visitors see this in place of the cart button. It must read as a real
   thing they do not have yet, not as a broken or disabled control: muted
   surface, dashed edge, intact contrast, and it is a live link to /pricing/ so
   it is keyboard reachable and says where it goes. Seeing the faster path is
   the pitch; making it look greyed-out and dead would waste it. */
.ws-act--cop.is-locked,
.card-buy.cop.is-locked {
  background: color-mix(in srgb, var(--acid), var(--card) 72%);
  border-style: dashed;
  color: var(--ink);
  cursor: pointer;
}
.ws-act--cop.is-locked:hover,
.card-buy.cop.is-locked:hover {
  background: var(--acid);
  border-style: solid;
  transform: translate(-1px, -1px);
  box-shadow: 3px 3px 0 var(--ink);
}
.ws-act--cop.is-locked:focus-visible,
.card-buy.cop.is-locked:focus-visible {
  outline: 3px solid var(--ink);
  outline-offset: 2px;
}

/* ---- the trial strip and the freeze card (DR-PAYWALL-4) ----
   Both read as STATUS, not as an error. A free account being three-quarters
   through its week has done nothing wrong, and a frozen one still owns
   everything on its shelf — styling either like a failure would be a lie about
   what happened, and would make people defensive rather than curious. */
.dash-card--trial {
  border-left: 6px solid var(--acid);
}
.dash-card--warn {
  border-left: 6px solid var(--hot);
  background: color-mix(in srgb, var(--hot), var(--card) 92%);
}
.dash-card--trial h2,
.dash-card--warn h2 { margin-top: 0; }

/* ---- the shelf (PLAN-DASHBOARD §4a) ----
   A free account's watches as objects standing on a plank. Empty slots are
   dashed outlines ("room for more", not "nothing here"); the locked slot is the
   same dashed acid as the locked basket, so a limit reads as one idea sitewide.
   Sold out = desaturated photo, tipped 2deg on its base corner. The words
   "Sold out" stay: tilt is a second channel, never the only one. */
.dash-shelf {
  background: var(--card); border: 2px solid var(--ink); border-radius: var(--ws-radius);
  box-shadow: var(--ws-shadow); padding: 16px 16px 20px;
}
.dash-shelf__head { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.dash-shelf__head h2 { font-size: 0.95rem; margin: 0; }
.dash-shelf__scroll { overflow-x: auto; padding: 12px 6px 10px; margin: 0 -6px; scroll-snap-type: x proximity; }
.dash-shelf__unit { min-width: 640px; }
.dash-shelf__row {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px;
  align-items: end; padding: 0 12px;
}
.dash-shelf__plank {
  height: 14px; border: 2px solid var(--ink); border-radius: 4px;
  background: var(--acid); box-shadow: 4px 4px 0 var(--ink);
}

.dash-slot {
  display: flex; flex-direction: column; gap: 6px; min-height: 250px;
  border: 2px solid var(--ink); border-radius: 12px 12px 4px 4px; padding: 10px;
  background: var(--paper); scroll-snap-align: start;
  transform-origin: bottom left;
  animation: dashSettle 260ms ease-out both;
}
.dash-slot--item { box-shadow: 3px 0 0 var(--ink); }
.dash-slot__media { aspect-ratio: 1 / 1; border: 2px solid var(--ink); border-radius: 8px; overflow: hidden; background: var(--line); }
.dash-slot__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.dash-slot__title {
  font-weight: 800; font-size: 0.84rem; line-height: 1.3; color: var(--ink); text-decoration: none;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
a.dash-slot__title:hover { color: var(--hot); }
.dash-slot__meta { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin: 0; font-size: 0.7rem; color: var(--muted); }
.dash-slot__controls { margin-top: auto; display: flex; flex-direction: column; gap: 6px; }
.dash-slot__controls .dash-select,
.dash-slot__remove { min-height: 44px; width: 100%; }
.dash-slot__remove { padding: 6px 12px; font-size: 0.72rem; }

.dash-slot.is-soldout { transform: rotate(-2deg); }
.dash-slot.is-soldout .dash-slot__media img { filter: grayscale(0.9); opacity: 0.75; }

.dash-slot--empty,
.dash-slot--locked {
  align-items: center; justify-content: center; text-align: center;
  border-style: dashed; background: transparent; box-shadow: none;
}
.dash-slot--empty { border-color: var(--muted); }
.dash-slot__add {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-height: 44px; padding: 8px 14px; border-radius: 999px;
  font-weight: 800; font-size: 0.78rem; color: var(--ink); text-decoration: none;
}
.dash-slot__add span { font-size: 1.4rem; line-height: 1; }
.dash-slot__add:hover { background: var(--line); }
.dash-slot__hint { font-family: 'Space Mono', monospace; font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }
.dash-slot--locked {
  gap: 10px; color: var(--ink); text-decoration: none;
  background: color-mix(in srgb, var(--acid), var(--card) 72%);
}
.dash-slot--locked:hover { background: var(--acid); border-style: solid; }
.dash-slot__cta { font-weight: 800; font-size: 0.82rem; }
.dash-slot__add:focus-visible,
.dash-slot--locked:focus-visible,
.dash-shelf__scroll:focus-visible { outline: 3px solid var(--ink); outline-offset: 2px; }

@keyframes dashSettle { from { opacity: 0; translate: 0 -8px; } }
.motion-paused .dash-slot { animation: none; }
@media (prefers-reduced-motion: reduce) { .dash-slot { animation: none; } }

/* A pass is neither a subscription nor free, so it does not borrow either
   badge: Pro is the acid yellow, free is muted, a pass is the pink accent with
   a dashed edge to say "time-limited" without saying "lesser". */
.dash-badge--pass {
  background: color-mix(in srgb, var(--hot), var(--card) 78%);
  border-style: dashed;
  color: var(--ink);
}

/* ============================== the finder ============================== */
/* The front door for someone who knows one word. Deliberately generous at the
   top of the page and deliberately NOT a hero: no mascot stage, no marketing
   headline, just the question and a box. The dense wall still starts within a
   screen of it. */
.ws-finder {
  background: var(--card);
  border: 2px solid var(--ink); border-radius: var(--ws-radius);
  box-shadow: var(--ws-shadow);
  padding: clamp(16px, 2.4vw, 26px); margin: 0 0 var(--ws-gap);
}
.ws-finder__title {
  font-size: clamp(1.15rem, 2.4vw, 1.6rem); font-weight: 900;
  text-transform: uppercase; letter-spacing: 0.01em; margin: 0 0 12px;
}
.ws-finder__box { display: flex; }
.ws-finder__box input[type="search"] {
  flex: 1 1 auto; min-width: 0;
  padding: 14px 18px;
  border: 2px solid var(--ink); border-radius: 999px; background: var(--paper);
  font-family: 'Archivo', sans-serif; font-size: clamp(0.95rem, 1.6vw, 1.1rem); color: var(--ink);
}
.ws-finder__box input[type="search"]:focus-visible {
  outline: 3px solid var(--hot); outline-offset: 2px;
}
.ws-finder__box input[type="search"]::placeholder { color: var(--muted); }

/* The answer only appears once something has been typed, so it must not
   reserve space while empty. */
.ws-finder__answer { margin: 12px 0 0; font-size: 0.92rem; line-height: 1.5; }
.ws-finder__answer:empty { display: none; }
.ws-finder__answer[data-state="in"]   { color: var(--ink); }
.ws-finder__answer[data-state="out"]  { color: var(--ink); }
.ws-finder__answer[data-state="none"] { color: var(--muted); }
.ws-finder__hint { margin: 10px 0 0; font-size: 0.78rem; color: var(--muted); }

@media (max-width: 560px) {
  .ws-finder { padding: 14px; }
  .ws-finder__title { margin-bottom: 10px; }
  .ws-finder__box input[type="search"] { padding: 12px 15px; }
  .ws-finder__hint { font-size: 0.72rem; }
}

/* ---------------------------- hero, in the workspace ---------------------- */
/* site.css sizes the hero for the page it used to own, where it WAS the first
   screen. Here it sits ABOVE an application, so the type is tightened. The
   sticker stage stays at the site.css height: a shorter box clipped the peel
   and the resting rotation. */
.ws-app .hero {
  margin-bottom: var(--ws-gap);
  padding: clamp(20px, 3vw, 34px);
  border-radius: var(--ws-radius);
}
.ws-app .hero h1 {
  font-size: clamp(1.6rem, 2.7vw, 2.3rem);
  line-height: 0.98;
  max-width: 30ch;
  margin-bottom: 12px;
}
.ws-app .hero-copy > p { font-size: 0.92rem; line-height: 1.55; max-width: 58ch; }
.ws-app .hero-cta { margin-top: 14px; }
.ws-app .hero-cta-btn { padding: 11px 20px; font-size: 0.86rem; }
.ws-app .sticker-stage { min-height: 340px; height: clamp(340px, 34vw, 420px); }

.also-look-count { margin: 0 0 8px; font-size: 0.84rem; font-weight: 800; }
.also-look-row { padding: 8px 0; border-top: 1px solid var(--line, rgba(28,16,48,0.12)); }
.also-look-row:first-of-type { border-top: 0; }
.also-look-name { display: block; font-weight: 800; color: var(--ink); text-decoration: none; }
a.also-look-name:hover { color: var(--radar); }
.also-look-kind, .also-look-reading, .also-look-more { display: block; margin: 2px 0 0; font-size: 0.75rem; line-height: 1.4; color: var(--muted); }
.also-look-row.is-locked { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; }
.also-look-pro { font-size: 0.72rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.06em; color: var(--radar); text-decoration: none; }
@media (max-width: 860px) {
  .ws-app .hero { padding: 20px; }
  .ws-app .sticker-stage { min-height: 340px; height: 340px; }
}

/* Dashboard rail: shops you follow (dashboard.js renderFollows). */
.dash-follows__list { list-style: none; margin: 10px 0 0; padding: 0; display: grid; gap: 8px; }
.dash-follow {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 8px;
  border: 1px solid var(--line); border-radius: 10px; padding: 8px 10px; background: var(--paper);
}
.dash-follow__name { display: grid; gap: 2px; min-width: 0; overflow-wrap: anywhere; }
.dash-follow__host { font-size: 0.74rem; color: var(--muted); }
.dash-follow__remove { padding: 6px 12px; min-height: 36px; font-size: 0.72rem; }
.dash-follows > .ws-btn { margin-top: 12px; min-height: 44px; width: 100%; white-space: normal; text-align: center; }
