/* =============================================================================
   HoldOrSell / Signal Desk — responsive + PWA layer.

   WHAT THIS FILE IS
   -----------------
   An ADDITIVE override layer. It is loaded LAST, after index.html's inline
   <style> block and after site.css, and it never redefines a colour token or a
   component's identity — only the geometry that breaks below ~900px, plus the
   styling for the PWA bars that pwa.js injects.

   WHY IT IS A SEPARATE FILE
   -------------------------
   index.html is 3,326 lines with one inline <script>; four lanes touch it in
   this wave and one syntax error there kills every handler on the page. This
   lane writes nothing into it. Everything below layers on from outside.

   HOW IT IS SCOPED
   ----------------
   index.html and the marketing pages share class names (.topbar, .panel, .grid,
   .btn, .chip, .conf, .spark) with DIFFERENT definitions. Where a rule would
   collide, it is scoped by something structural that only one of them has:

     div.topbar     -> the desk (index.html)   header.topbar -> marketing
     #mainView / #pageView / #resultsWrap / #<x>Panel -> the desk only
     body > .wrap   -> the desk's page wrapper only

   Anything unscoped here is safe for both.

   "MOBILE-FIRST" IN A LAYER
   -------------------------
   The base it layers over is desktop-first, so this file states the phone
   behaviour unconditionally wherever the rule is also correct on desktop
   (min-width:0, minmax(min(..,100%)), overflow-wrap), and uses max-width
   queries only where desktop must keep its own value. Two breakpoints do almost
   all the work — 640px (phone) and 900px (tablet/small laptop) — chosen to sit
   alongside the values already in the base sheets (520/560/640/720/760/880)
   rather than introduce a competing scale.

   NOTHING HERE HIDES OVERFLOW ON <body>. Clipping the page would make the 375px
   test pass while the layout was still broken. Every overflow below is fixed at
   its cause.
   ========================================================================== */

/* ---------------------------------------------------------------- tokens -- */
:root{
  --sd-tap:44px;                                   /* minimum touch target */
  --sd-gut:16px;                                   /* phone gutter */
  --sd-safe-b:env(safe-area-inset-bottom, 0px);
  --sd-safe-l:env(safe-area-inset-left, 0px);
  --sd-safe-r:env(safe-area-inset-right, 0px);
  --sd-safe-t:env(safe-area-inset-top, 0px);
  --sd-pwa-bar:0px;      /* pwa.js raises this while an install/update bar is up */
}

/* ========================================================================== */
/* 1. GLOBAL OVERFLOW DISCIPLINE                                              */
/* ========================================================================== */

html{ -webkit-text-size-adjust:100%; text-size-adjust:100%; }

/* iOS/Android browser chrome makes 100vh taller than the visible viewport, so
   the page always ends a chrome-height below the fold. dvh tracks the real one. */
@supports (height:100dvh){ body{ min-height:100dvh; } }

/* Media can never be the thing that widens the page. */
img, svg, video, canvas, iframe{ max-width:100%; }

/* Flex and grid children default to min-width:auto (= min-content), which is
   what actually causes almost every "page is wider than the screen" bug: one
   nowrap child sets a floor the container cannot go below. */
body > .wrap,
#mainView, #pageView, #resultsWrap,
.hero, .hero-copy, .dropzone,
.signal, .signal .body, .signal .head,
.riskbar .rb-body, .cohort .co-read,
.vcard-id, .setrow .ctl{ min-width:0; }

/* Long unbroken strings — card keys, TCGplayer ids, set slugs, error payloads —
   are the other half of it. They are data, so they can be any width. */
.signal .name, .q-title, .q-meta, .kv dd, .rt-name, .rt-dim,
.pg-title, .pg-sub, .slabline, .grade-box, .resolve,
.admin-tbl td, .sr-name, .stat .v, .val-metric .v,
.ci-name, .ci-meta, .ci-reason{ overflow-wrap:anywhere; }

/* A row that must not become a horizontal scroller. */
.priceline, .footer-row, .spark-dates{ flex-wrap:wrap; gap:6px; }

/* Event flags are white-space:nowrap and carry a whole headline ("Reprint
   confirmed — Surging Sparks reprint set announced 12 Aug"). At EVERY width
   that is wider than the .signal card holding it, and .signal is
   overflow:hidden, so today the text is chopped mid-word with no ellipsis and
   no clue there was more. Cap it and ellipse it instead. */
.flags .flag{ max-width:100%; }
.flags .flag .fl-label{ min-width:0; overflow:hidden; text-overflow:ellipsis; }

/* ========================================================================== */
/* 2. THE TOPBAR — the 375px overflow bug (STATUS.md, KNOWN ISSUES)           */
/*                                                                            */
/*    "at a 375px viewport the page measures 482px wide because of            */
/*     #accountChip in the topbar when signed in."                            */
/*                                                                            */
/*    Cause: div.topbar is a single-line flex row (no wrap) whose children are */
/*    all nowrap and all default to min-width:auto. Signed out it is brand +   */
/*    modepill + "Sign in" and it just fits. Signing in APPENDS #accountChip   */
/*    (display name + plan badge + "sign out") as a fifth child, and since     */
/*    nothing may shrink and nothing may wrap, the flex line's min-content     */
/*    width becomes the page's min width — 482px — and the whole document      */
/*    scrolls sideways, sticky topbar and all.                                 */
/*                                                                            */
/*    Fix, in this order:                                                      */
/*      a. let the row wrap;                                                   */
/*      b. let every child shrink (min-width:0) and truncate rather than       */
/*         define a floor;                                                     */
/*      c. give the phone an explicit two-row order so the wrap is designed,   */
/*         not accidental.                                                     */
/*    Scoped to div.topbar so the marketing header.topbar is untouched.        */
/* ========================================================================== */

div.topbar{
  flex-wrap:wrap;
  row-gap:8px;
  column-gap:12px;
  padding-top:10px;
  padding-bottom:10px;
  padding-left:max(14px, var(--sd-safe-l));
  padding-right:max(14px, var(--sd-safe-r));
  padding-top:max(10px, var(--sd-safe-t));   /* notch, when viewport-fit=cover */
  min-width:0;
}
/* (b) — the load-bearing line. Without it the children keep a min-content floor
   and wrapping alone would not be enough for a long display name. */
div.topbar > *{ min-width:0; }
div.topbar .brand{ flex:0 1 auto; overflow:hidden; gap:8px; }
div.topbar .brand > *{ min-width:0; }
div.topbar .brand h1{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
div.topbar .brand .sub{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
div.topbar .modepill{ overflow:hidden; }
div.topbar .modepill > span:last-child{
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
/* The account chip is the specific child STATUS.md names. Cap the display name
   rather than the chip, so the plan badge and "sign out" never get clipped. */
div.topbar .account-chip{ min-width:0; max-width:100%; }
div.topbar .account-chip > span:first-child{
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:14ch;
}

@media (max-width:640px){
  /* (c) — a designed two-row topbar:
       row 1   brand ....................... account (chip or "Sign in")
       row 2   data-mode pill  ...  IT console
     The inline style="margin-left:10px" on #accountBtn/#modeToggle can only be
     beaten by !important; index.html is fenced this wave, so that is the tool
     available. Both should move into a class when the file next opens. */
  div.topbar{ column-gap:10px; }
  div.topbar .brand{ order:1; }
  div.topbar .brand h1{ font-size:15px; }
  div.topbar .brand .sub{ display:none; }
  div.topbar #accountBtn,
  div.topbar .account-chip{ order:2; margin-left:auto !important; flex:0 1 auto; }
  div.topbar .modepill{ order:3; margin-left:0; flex:1 1 auto; font-size:11px; padding:5px 10px; }
  div.topbar #modeToggle{ order:4; margin-left:0 !important; flex:0 0 auto; }
  div.topbar .account-chip{ padding:5px 10px; gap:6px; }
  div.topbar .account-chip .expand{ padding:0 2px; }
}

/* Below ~330px the brand and the chip genuinely cannot share a row without both
   ellipsing, so give the chip its own. Measured: at 320px and 375px they fit
   side by side, which is why this is NOT a 400px rule — a three-row sticky
   topbar on the commonest phone width costs more than it buys. */
@media (max-width:330px){
  div.topbar .account-chip{ flex:1 1 100%; margin-left:0 !important; justify-content:space-between; }
}

/* Marketing header keeps its own layout; it only needs the notch. */
header.topbar .topbar-in{
  padding-left:max(var(--pad, 18px), var(--sd-safe-l));
  padding-right:max(var(--pad, 18px), var(--sd-safe-r));
}

/* ========================================================================== */
/* 3. PAGE GUTTERS + SAFE AREA                                               */
/* ========================================================================== */

@media (max-width:640px){
  body > .wrap{
    padding-left:max(var(--sd-gut), var(--sd-safe-l));
    padding-right:max(var(--sd-gut), var(--sd-safe-r));
    padding-top:18px;
    padding-bottom:calc(56px + var(--sd-pwa-bar) + var(--sd-safe-b));
  }
}
.legal-footer{ padding-bottom:calc(2rem + var(--sd-pwa-bar) + var(--sd-safe-b)); }

/* ========================================================================== */
/* 4. GRIDS THAT ARE WIDER THAN A PHONE                                      */
/*                                                                            */
/*    minmax(340px, 1fr) does NOT collapse on a narrow container: auto-fill    */
/*    still lays one 340px track, so a 323px content box overflows by 17px.    */
/*    minmax(min(340px,100%), 1fr) keeps the desktop track and lets the phone  */
/*    fall back to the container width. Every re-declaration below repeats the */
/*    base value exactly — only the min() wrapper is new.                      */
/* ========================================================================== */

/* the desk: verdict card grid (#resultsWrap only — .grid means something else
   on the marketing pages) */
#resultsWrap .grid{ grid-template-columns:repeat(auto-fill, minmax(min(340px,100%), 1fr)); }

/* catalogue grid view — all three of its declarations must be repeated, because
   media queries carry no specificity and this file wins on source order alone */
.cgrid{ grid-template-columns:repeat(auto-fill, minmax(min(236px,100%), 1fr)); }
@media (max-width:560px){ .cgrid{ grid-template-columns:repeat(auto-fill, minmax(min(150px,100%), 1fr)); } }
@media (min-width:1600px){ .cgrid{ grid-template-columns:repeat(auto-fill, minmax(min(292px,100%), 1fr)); } }

.stats{ grid-template-columns:repeat(auto-fit, minmax(min(150px,100%), 1fr)); }
.val-grid{ grid-template-columns:repeat(auto-fit, minmax(min(180px,100%), 1fr)); }
.ci-stats{ grid-template-columns:repeat(auto-fit, minmax(min(118px,100%), 1fr)); }
@media (max-width:640px){ .ci-stats{ grid-template-columns:repeat(auto-fit, minmax(min(104px,100%), 1fr)); } }

/* marketing (site.css) — same treatment, so a 320px device never scrolls */
.plans{ grid-template-columns:repeat(auto-fit, minmax(min(290px,100%), 1fr)); }
.g2{ grid-template-columns:repeat(auto-fit, minmax(min(280px,100%), 1fr)); }
.g3{ grid-template-columns:repeat(auto-fit, minmax(min(240px,100%), 1fr)); }
.g4{ grid-template-columns:repeat(auto-fit, minmax(min(190px,100%), 1fr)); }
.fork{ grid-template-columns:repeat(auto-fit, minmax(min(260px,100%), 1fr)); }
footer.site .cols{ grid-template-columns:repeat(auto-fit, minmax(min(190px,100%), 1fr)); }

/* key/value list on the card page: "auto 1fr" lets a long card_key set the
   column width; minmax(0,1fr) does not. */
.kv{ grid-template-columns:auto minmax(0, 1fr); }
@media (max-width:520px){
  .kv{ grid-template-columns:1fr; gap:0; }
  .kv dt{ margin-top:10px; }
  .kv dd{ margin-bottom:2px; }
}

/* ========================================================================== */
/* 5. WIDE CONTENT SCROLLS INSIDE ITSELF, NEVER THE BODY                     */
/*                                                                            */
/*    The rtable and ci-tbl tables are already wrapped in their own            */
/*    overflow-x:auto div by index.html. The tables in the grade / patterns /  */
/*    validation / brain / admin panels are NOT, and cannot be wrapped from a  */
/*    stylesheet — so the scroll goes on the panel that holds them. Listed by  */
/*    id rather than blanket .panel, so #collectrPanel (which manages its own  */
/*    scrollers) and #resultsWrap are left alone.                              */
/* ========================================================================== */

@media (max-width:900px){
  #gradePanel .panel,
  #patternsPanel .panel,
  #valPanel .panel,
  #brainPanel .panel,
  #adminPanel .panel,
  #pageView .panel{
    overflow-x:auto;
    -webkit-overflow-scrolling:touch;
    overscroll-behavior-x:contain;   /* no back-swipe hijack mid-table */
  }
  /* Give the data tables a real floor so the columns scroll rather than crush
     into unreadable slivers. */
  #gradePanel table, #patternsPanel table, #valPanel table, #brainPanel table{ min-width:520px; }
  .admin-tbl{ min-width:460px; }
  .rtable{ min-width:600px; }
}
/* the two scroll wrappers index.html already emits */
.ci-scroll, .tablescroll{ overscroll-behavior-x:contain; }

/* ========================================================================== */
/* 6. TOUCH TARGETS + INPUT BEHAVIOUR                                        */
/* ========================================================================== */

@media (pointer:coarse){
  /* iOS Safari zooms the whole viewport when a focused field is under 16px —
     which is itself a horizontal-overflow bug, not just a nuisance. */
  input, select, textarea{ font-size:16px; }

  /* 44px is the floor Apple and Google both publish. Anything the user is meant
     to press gets it; text links keep their own hit area. */
  .btn, .seg button, .mini-btn, .it-tab, .viewtog button, .pg-back,
  .expand, .chart-tabs .ct, .themebtn, .auth-tab, .sr-add,
  .rb-x, .auth-close, .job-card .jclose, .nav a, .nav .cta{
    min-height:var(--sd-tap);
    display:inline-flex;
    align-items:center;
    justify-content:center;
  }
  /* icon-only controls also need the width */
  .rb-x, .auth-close, .job-card .jclose, .themebtn{ min-width:var(--sd-tap); }
  /* a whole search-result row is the target, not the little "add" label */
  .sr-item{ min-height:56px; }
  .rtable tbody td{ padding-top:14px; padding-bottom:14px; }

  /* kill the 300ms click delay and the grey flash */
  a, button, .seg button, .sr-item, .gcard, .rtable tbody tr, .signal{
    touch-action:manipulation;
    -webkit-tap-highlight-color:transparent;
  }
}

/* index.html has no :focus-visible style at all. :where() gives this zero
   specificity, so site.css's own (better-scoped) rule still wins on the
   marketing pages and this only fills the gap in the desk. */
:where(:focus-visible){
  outline:2px solid var(--mint, #4fe3a3);
  outline-offset:2px;
  border-radius:4px;
}
/* fields whose base rule sets outline:none on :focus need a real-specificity
   counterpart, or keyboard focus is invisible */
.auth-input:focus-visible, .cardsearch input:focus-visible,
.search:focus-visible, .toolbar input:focus-visible, .field:focus-visible{
  outline:2px solid var(--mint, #4fe3a3);
  outline-offset:1px;
}

/* Hover-only affordances do not exist on a touch screen. The floating 220px
   preview image is triggered by mouseover and would be stranded open. */
@media (hover:none){
  #hoverImg{ display:none !important; }
  .card-thumb, .sr-thumb{ cursor:default; }
}

/* ========================================================================== */
/* 7. THE DESK AT 375px                                                       */
/*    Brokerage clarity: the verdict is the first thing you read, buttons are  */
/*    full-width thumb targets, nothing is a two-column compromise.            */
/* ========================================================================== */

@media (max-width:640px){

  /* --- hero + loader ---------------------------------------------------- */
  .hero{ gap:18px; margin-bottom:22px; }
  .hero-copy h2{ font-size:clamp(26px, 8vw, 34px); }
  .hero-copy p{ font-size:14.5px; max-width:none; }
  .hero-copy .legend{ gap:12px; }
  .dropzone{ padding:20px 16px; border-radius:14px; }
  .btn-row{ gap:9px; }
  .dropzone .btn-row .btn,
  #runRow .btn,
  .ci-entry .btn{ flex:1 1 100%; width:100%; }
  .cur-row{ flex-wrap:wrap; gap:8px; }
  .cur-row .seg{ flex:1 1 auto; }
  .cur-seg button{ flex:1; }
  .search-results{ max-height:min(420px, 52vh); }

  /* --- stat strip ------------------------------------------------------- */
  .stat{ padding:13px 14px; }
  .stat .v{ font-size:22px; }

  /* --- controls --------------------------------------------------------- */
  .controls{ gap:8px; }
  .controls .search{ margin-left:0; width:100%; min-width:0; flex:1 1 100%; }
  .controls .seg{ width:100%; }
  .controls .seg button{ flex:1; padding-left:6px; padding-right:6px; }
  .toolbar{ gap:8px; }
  .toolbar input[type=text]{ flex:1 1 100%; min-width:0; }

  /* --- verdict cards ---------------------------------------------------- */
  /* The colour semantics are the product: mint = HOLD LONG, amber = HOLD
     SHORT, coral = SELL NOW. On a phone the 4px strip and the 10.5px badge are
     the only carriers of that, so both get bigger rather than smaller. */
  .signal .verdict-strip{ height:5px; }
  .signal .body{ padding:15px 15px 17px; }
  .signal .head{ gap:10px; }
  .signal .name{ font-size:15.5px; }
  .verdict-badge{ font-size:11.5px; padding:6px 10px; letter-spacing:.055em; }
  .signals-mini .sbar{ height:30px; }
  .cohort{ gap:7px; }

  /* --- card detail: the money page -------------------------------------- */
  /* A full-bleed card scan is ~480px tall at 375 — the verdict would be two
     screens down. Cap it and centre it. */
  .card-grid > div:first-child{ max-width:240px; margin-left:auto; margin-right:auto; }
  /* price now / 30d / 90d / 1y read as a row of four. At minmax(180px) they
     stack into four full-width slabs and the comparison is lost; 2x2 keeps it. */
  .val-grid{ grid-template-columns:repeat(auto-fit, minmax(min(130px,100%), 1fr)); gap:10px; }
  .pg-head{ margin:18px 0 16px; gap:10px; }
  .pg-title{ font-size:19px; }
  .panel{ padding:16px; border-radius:12px; }
  .panel h3{ font-size:15px; }

  /* --- admin console ---------------------------------------------------- */
  .it-tabs{ gap:0; }
  .it-tab{ padding:9px 11px; }
  .q-acts{ gap:7px; }

  /* --- floating furniture ------------------------------------------------ */
  .job-dock{
    left:10px; right:10px; max-width:none;
    bottom:calc(10px + var(--sd-pwa-bar) + var(--sd-safe-b));
  }
  .toast{
    left:12px; right:12px; transform:none; text-align:center;
    bottom:calc(16px + var(--sd-pwa-bar) + var(--sd-safe-b));
  }
  .auth-modal{ width:100%; max-width:none; margin:0 12px; border-radius:14px; }
  .auth-body{ padding:20px 18px; }
  .riskbar{ padding:12px 13px; font-size:12px; }
}

/* Very small phones (320px) — the last places that still touch the edge. */
@media (max-width:360px){
  .hero-copy .legend{ gap:9px; font-size:11px; }
  .stats{ gap:9px; }
  div.topbar .modepill{ font-size:10.5px; }
}

/* Phone in landscape: the sticky topbar plus a full-height hero leaves no room
   for content, so tighten the vertical rhythm rather than the horizontal. */
@media (max-height:520px) and (orientation:landscape){
  body > .wrap{ padding-top:14px; }
  .hero{ margin-bottom:16px; }
  .search-results{ max-height:40vh; }
}

/* ========================================================================== */
/* 8. PWA BARS (injected by pwa.js — see .sdpwa-* there)                      */
/*    One component, two uses: "update ready" and "install". Docked bottom     */
/*    centre, above the safe area, below the auth overlay (z-index 200) and    */
/*    above the toast (120) and job dock (130).                                */
/* ========================================================================== */

.sdpwa-bar{
  position:fixed;
  z-index:150;
  left:12px; right:12px;
  bottom:calc(12px + var(--sd-safe-b));
  margin:0 auto;
  max-width:520px;
  display:flex;
  align-items:center;
  gap:12px;
  flex-wrap:wrap;
  padding:12px 14px;
  border-radius:12px;
  border:1px solid var(--line, #2b3543);
  background:var(--desk-3, #1f2733);
  color:var(--ink, #e8edf2);
  box-shadow:var(--shadow, 0 8px 30px rgba(0,0,0,.45));
  font-family:var(--sans, -apple-system, system-ui, sans-serif);
  font-size:13.5px;
  line-height:1.45;
  opacity:0;
  transform:translateY(14px);
  transition:opacity .2s ease, transform .2s ease;
}
.sdpwa-bar.on{ opacity:1; transform:translateY(0); }
.sdpwa-bar .sdpwa-text{ flex:1 1 200px; min-width:0; }
.sdpwa-bar .sdpwa-acts{ display:flex; gap:8px; flex:0 0 auto; margin-left:auto; }
.sdpwa-bar .sdpwa-btn{
  font-family:inherit;
  font-size:13px;
  font-weight:600;
  cursor:pointer;
  border:1px solid var(--line, #2b3543);
  background:transparent;
  color:var(--ink-dim, #94a3b4);
  border-radius:9px;
  padding:9px 14px;
  min-height:var(--sd-tap);
  display:inline-flex;
  align-items:center;
  justify-content:center;
  touch-action:manipulation;
}
.sdpwa-bar .sdpwa-btn:hover{ color:var(--ink, #e8edf2); border-color:var(--ink-faint, #5f6f80); }
.sdpwa-bar .sdpwa-primary{
  background:var(--mint, #4fe3a3);
  border-color:transparent;
  color:#0d1219;
}
.sdpwa-bar .sdpwa-primary:hover{ color:#0d1219; filter:brightness(1.05); }
/* An update is news; an install offer is a suggestion. Colour says which. */
.sdpwa-bar.sdpwa-update{ border-color:var(--v-accent-bd, rgba(157,140,255,.32)); }

@media (max-width:640px){
  .sdpwa-bar{ font-size:13px; padding:11px 12px; }
  .sdpwa-bar .sdpwa-acts{ flex:1 1 100%; margin-left:0; }
  .sdpwa-bar .sdpwa-btn{ flex:1 1 auto; }
}

/* Light theme (marketing pages can toggle it; the desk is dark-only today). */
:root[data-theme="light"] .sdpwa-bar .sdpwa-primary{ color:#fff; }
@media (prefers-color-scheme: light){
  :root:not([data-theme="dark"]) .sdpwa-bar .sdpwa-primary{ color:#fff; }
}

/* Installed / standalone: no browser chrome, so the app owns the whole notch. */
html.sd-standalone body{ overscroll-behavior-y:none; }
html.sd-standalone div.topbar{ padding-top:max(10px, var(--sd-safe-t)); }

@media (prefers-reduced-motion: reduce){
  .sdpwa-bar{ transition:none; transform:none; }
}
