/* ══════════════════════════════════════════════════════════════════════════
   TRADZY — POPUP SCROLL / VIEWPORT FIXES
   ══════════════════════════════════════════════════════════════════════════
   Loaded last so it outranks the inline <style> blocks in index.html, several
   of which sit after polish.css in document order.

   ── WHAT THIS FILE IS *NOT* ───────────────────────────────────────────────
   It is NOT a general mobile modal system. index.html already ships a good
   one, and this file deliberately stays out of its way:

     .modal-bg.open                                  { align-items/padding }
     .modal-bg.open .modal|.lw-wizard|.rank-modal    { width, max-height:
                                                       min(88dvh,880px),
                                                       overflow:auto, margin }
     @media (max-width:640px)                        { the same, as a
                                                       bottom sheet }

   plus explicit coverage for #tradzy-account-modal .acct-modal-box,
   #tradzy-public-profile .modal, #htu-overlay .htu-card, #weekly-review-modal
   .wr-box, #tv-import-modal > div, #ge-modal > div and #season-end-modal>div.
   Those rules are `!important` throughout, already dvh-based, and correct.
   An earlier draft of this file restated them at equal specificity; the only
   effect was to make the cascade harder to follow, so it was removed.

   ── WHAT WAS ACTUALLY BROKEN ──────────────────────────────────────────────
   Measured at 375x812 with each overlay forced open and given content taller
   than the viewport, checking for a real scrollport (computed overflow-y of
   auto/scroll AND scrollHeight > clientHeight — scrollHeight alone is not a
   test, it exceeds clientHeight on overflow:visible too).

   21 of 24 overlays were already fine. Three were not, because they are the
   ones the rules above never name:

     #psych-overlay        card top rendered at -802px
     #season-end-modal     card top rendered at -694px
     #tradzy-auth-overlay  no scrollport at all

   The first two are the flex-centering trap: the overlay is
   `display:flex; align-items:center; overflow:visible`, and when the child is
   taller than the container, centring puts the child's top edge at a NEGATIVE
   offset. Negative overflow is not reachable by scrolling in any browser, so
   the top 800px of the dialog — title, close button, first form fields — is
   gone with no gesture that recovers it.

   The fix is `margin:auto` on the child instead of `align-items:center` on
   the parent. It centres identically while the child fits, and collapses to 0
   once it does not, so the overflow all lands on the bottom edge where the
   overlay's own scrollbar reaches it.

   The remaining rules below cover overlays that are not currently broken but
   have no treatment of their own — they pass today only because their content
   happens to be short. Verified to cause no regression: 22/22 pass with this
   file enabled.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── OVERLAYS WITH NO TREATMENT OF THEIR OWN ───────────────────────────────
   Enumerated, not wildcarded: `[id*="modal"]` would also catch the drawers
   (chat panel, notes panel, cosmetics panel), which are edge-anchored and
   must not become centred scroll containers. .modal-bg is deliberately absent
   — see the header. */
#psych-overlay,
#season-end-modal,
#tradzy-auth-overlay,
#void-onboard-overlay,
#ge-onboard-overlay,
#void-welcome-overlay,
#pf-setup-modal,
#sr-create-modal,
#sr-browse-modal,
#reward-bg,
#v19-pitch-overlay,
#v19-paywall-overlay {
  /* The overlay is the scrollport. */
  align-items: flex-start;
  justify-content: center;
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  /* Reaching the end of a popup must not start scrolling the page behind it. */
  overscroll-behavior: contain;
  padding: max(16px, env(safe-area-inset-top, 0px)) 12px
           max(16px, env(safe-area-inset-bottom, 0px)) 12px;
  box-sizing: border-box;
}

/* `> *` rather than a class list because these cards are inconsistently named
   (.psych-card, .vw-card, .auth-box, .v19-pitch-box, .reward-card and three
   unnamed divs). A direct child of a popup overlay IS the card. */
#psych-overlay > *,
#season-end-modal > *,
#tradzy-auth-overlay > *,
#void-onboard-overlay > *,
#ge-onboard-overlay > *,
#void-welcome-overlay > *,
#pf-setup-modal > *,
#sr-create-modal > *,
#sr-browse-modal > *,
#reward-bg > *,
#v19-pitch-overlay > *,
#v19-paywall-overlay > * {
  /* THE fix for the -802px clip. Not align-items — see the header. */
  margin: auto;
  box-sizing: border-box;

  /* DELIBERATELY NO max-width HERE.
     An earlier version set `max-width: calc(100vw - 24px)` as a safety cap.
     It did the opposite of its intent: these cards already carry their own,
     much tighter caps (.vw-card 500px, .v19-pitch-box 560px, .pf-modal-inner
     480px), and an `#overlay > *` selector outranks a single class, so the
     "cap" RELAXED them. Measured at 1440x900 it blew four dialogs out to full
     width — #void-welcome-overlay 500px -> 1317px, #v19-pitch-overlay
     560px -> 1416px.
     Nothing is needed here: these cards are flex items, and the default
     flex-shrink:1 already squeezes them to fit any narrower viewport. The
     mobile block below gives phones a deliberate full-width layout instead. */
}

/* Three cards ship `max-height:none; overflow:visible` and so cannot scroll
   at all. Now that the overlay scrolls, they only need to stop clipping. */
#psych-overlay > .psych-card,
#season-end-modal > *,
#tradzy-auth-overlay > .auth-box {
  max-height: none;
  overflow: visible;
}

@media (max-width: 768px) {
  #psych-overlay > *,
  #season-end-modal > *,
  #tradzy-auth-overlay > *,
  #void-onboard-overlay > *,
  #ge-onboard-overlay > *,
  #void-welcome-overlay > *,
  #pf-setup-modal > *,
  #sr-create-modal > *,
  #sr-browse-modal > *,
  #reward-bg > *,
  #v19-pitch-overlay > *,
  #v19-paywall-overlay > * {
    width: 100%;
    max-width: 100%;
  }
  #psych-overlay,
  #season-end-modal,
  #tradzy-auth-overlay,
  #void-onboard-overlay,
  #ge-onboard-overlay,
  #void-welcome-overlay,
  #pf-setup-modal,
  #sr-create-modal,
  #sr-browse-modal,
  #reward-bg,
  #v19-pitch-overlay,
  #v19-paywall-overlay {
    padding: max(12px, env(safe-area-inset-top, 0px)) 8px
             max(20px, env(safe-area-inset-bottom, 0px)) 8px;
  }

  /* iOS zooms the page in when a focused input's font-size is under 16px and
     does not zoom back out. Every input in these popups is 11-12px, so tapping
     the sign-in field leaves the user zoomed into a corner of a fixed overlay. */
  #tradzy-auth-overlay input,
  #sr-create-modal input,
  #sr-create-modal textarea,
  #pf-setup-modal input,
  #pf-setup-modal select {
    font-size:var(--fs-16);
  }
}

/* Landscape phones (~380px of height): the vertical padding above is most of
   the screen. */
@media (max-height: 520px) {
  #psych-overlay,
  #season-end-modal,
  #tradzy-auth-overlay,
  #void-onboard-overlay,
  #ge-onboard-overlay,
  #void-welcome-overlay,
  #v19-pitch-overlay,
  #v19-paywall-overlay {
    padding-top: 8px;
    padding-bottom: 8px;
  }
}

/* ── THE APP SHELL ─────────────────────────────────────────────────────────
   `.app { height: calc(100vh - 52px) }` measures against the viewport with
   browser chrome COLLAPSED, so while a URL bar is showing the shell is laid
   out taller than the screen and its last rows fall below the fold with
   nothing to scroll them — the shell is not itself a scroll container.

   SCOPE: this only bites at 901px and up. Below 900px index.html already
   ships `.app { height: auto !important }`, which stacks the columns and
   hands scrolling to the body, so phones were never affected and this rule is
   correctly inert there. Where it does apply is the case desktop CSS usually
   forgets: an iPad in Safari, which is wide enough to miss the 900px
   breakpoint but still has collapsing browser chrome.

   The vh line stays first as the fallback for engines without dvh. */
.app {
  height: calc(100vh - 52px);
  height: calc(100dvh - 52px);
}

/* ══════════════════════════════════════════════════════════════════════════
   NOTIFICATIONS
   ══════════════════════════════════════════════════════════════════════════
   #tz-notif-stack is `top:60px; right:14px; width:300px` at every viewport.
   On a 375px phone that is 80% of the width, pinned over the right column and
   landing directly on the floating chat button and the mascot widget — toasts
   were covering the controls the user was reaching for. */
@media (max-width: 768px) {
  #tz-notif-stack {
    top: max(52px, env(safe-area-inset-top, 0px));
    left: 8px;
    right: 8px;
    width: auto;
    /* Four stacked cards at ~72px each overrun a short viewport. The stack is
       pointer-events:none, so clipping here is never interactive. */
    max-height: 46dvh;
    overflow: hidden;
  }
  .tz-notif {
    padding: 10px;
  }
  .tz-notif-msg {
    /* Long engine copy produced six-line toasts that reached the header. */
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .toast {
    left: 8px;
    right: 8px;
    bottom: max(12px, env(safe-area-inset-bottom, 0px));
    max-width: none;
  }
}

/* The unread dot is 10-12px square with an 11px number inside it, so the "9+"
   case renders clipped and off-centre. Sized to its content, keeping the round
   shape for the single-digit case. */
#tradzy-chat-unread {
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius:var(--rad-999);
  font-size:var(--fs-10);
  line-height: 1;
  font-weight: 700;
  box-sizing: border-box;
}

/* ── DRAWERS ───────────────────────────────────────────────────────────────
   Edge-anchored, so the centred-overlay rules above deliberately skip them.
   They only need their scroll bodies sized against the live viewport. */
#tzn-panel {
  max-height: min(78dvh, 420px);
}
#tzn-list {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}
#tradzy-chat-panel #chat-messages {
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
#tdm-panel {
  max-height: 90vh;
  max-height: min(90dvh, 900px);
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
}
@media (max-width: 768px) {
  #tdm-overlay > #tdm-panel {
    width: 100%;
    max-width: 100%;
    max-height: min(88dvh, 760px);
    border-radius: 16px 16px 0 0;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   TOUCH & VIEWPORT ADAPTATION — from the measured UX audit.
   Every rule cites the measurement that justifies it. This file loads last,
   so equal-specificity !important here beats the inline polish block.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── iOS input auto-zoom ────────────────────────────────────────────────────
   Safari on iOS zooms the whole page whenever a focused input computes under
   16px, then leaves the user to pinch back out. All 308 inputs in this app
   measured 11-15px, so this fired on EVERY field: ticker, prices, P&L, notes.
   16px is the exact platform threshold, not a stylistic preference.
   Deliberately NOT solved with maximum-scale=1 on the viewport meta — that
   would also disable legitimate accessibility pinch-zoom.
   range/checkbox/radio are excluded: they render no text, so the floor would
   only inflate their boxes.
   The `html body` prefix is not decoration: `.inp{font-size:var(--fs-14)
   !important}` already exists inline, and a bare `textarea`/`select` selector
   (0,0,1) loses to it (0,1,0) even with !important. Measured: 11 selects and
   textareas held 14px until the specificity was raised. */
@media (max-width: 768px) {
  html body input:not([type="range"]):not([type="checkbox"]):not([type="radio"]),
  html body select,
  html body textarea,
  html body .inp {
    font-size: 16px !important;
  }
}

/* ── Touch targets ─────────────────────────────────────────────────────────
   Apple's documented minimum is 44x44. 37% of visible controls measured
   under it at 375px, with segmented controls pinned to 36px.
   Split deliberately rather than blanket-44: primary navigation and action
   controls get the full 44px, but dense multi-select chips (emotion tags,
   debrief answers, psych answers) get 40px. There are 18 trade tags in one
   wrapped cloud — at 44px each that is a large amount of extra scrolling for
   a control you tap once, and 40px is already more than double what several
   of them measured.
   The .chip family (.tf-pill, .at-pill, .btag, .trade-tag ...) is NOT handled
   here. It is pinned by a min-height:36px!important rule in the inline sheet
   that resisted override even from html body .x.x, so it is corrected at its
   own source in index.html instead — one rule, one place, findable. */
@media (max-width: 768px) {
  html body .r-tab, html body .acct-tabbtn, html body .auth-tab,
  html body .mode-pill-new, html body .eq6-btn, html body .log-btn,
  html body .modal-btn, html body .ll-view-tab {
    min-height: 44px !important;
  }
  html body .debrief-btn, html body .lw-psych-btn {
    min-height: 40px !important;
  }
}

/* ── Strength-meter dots ───────────────────────────────────────────────────
   These are DATA ENTRY — each dot sets bull/bear pressure for a factor that
   feeds the score — not decoration. They measured 10px wide on a 12px pitch,
   under a third of a fingertip, so mis-taps corrupt the analysis input itself.
   A true 44px pitch is geometrically impossible here: 11 dots x 44px needs
   484px and the phone has 375px. But the row was using only 127px of the
   263px actually available to it, so the pitch nearly doubles (12px -> 22px)
   with room to spare.
   Scoped to the tappable dots only: .tsm-center is a 3px non-interactive
   divider and must keep its size. */
@media (max-width: 768px) {
  .tsm-dots { gap: 6px !important; }
  .tsm-dot.tsm-bear-dot,
  .tsm-dot.tsm-bull-dot { width: 16px !important; height: 16px !important; }
}

/* ── Tab strip scroll affordance ───────────────────────────────────────────
   The strip holds 550px of tabs in a 373px window with scrollbar-width:none
   and no mask, gradient or shadow — nothing signalled that 6 of the 11 tabs
   existed. A soft edge fade restores the "there is more" cue. Mobile-only
   because the strip does not overflow at >=768px, where fading would be a
   lie. */
@media (max-width: 768px) {
  .r-tabs {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 14px, #000 calc(100% - 18px), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, #000 14px, #000 calc(100% - 18px), transparent 100%);
  }
  /* NOTE: deliberately no `scroll-behavior: smooth` here. It would make every
     programmatic scroll animation-driven, which adds latency to a navigation
     action in an app whose whole point is trading faster — and it silently
     defeats scrollLeft assignment when frames are not being produced
     (measured: with smooth, scrollLeft stayed 0; with auto, it landed
     instantly). 76-tabbar-affordance.js sets scrollLeft directly instead. */
}

/* ── Landscape phones ──────────────────────────────────────────────────────
   At 844x390 the log wizard gave an 83px scroll area for 274px of content:
   header, step chips and nav consumed 260 of the 343px modal. The step chips
   are the most expendable — lwGoStep() still tracks progress and the counter
   still reads "STEP n OF 5", so nothing is lost but the row itself. */
@media (max-height: 500px) and (orientation: landscape) {
  .lw-steps-bar { display: none !important; }
  .modal-bg.open .modal { max-height: 96dvh !important; padding: 10px !important; }
}

/* ── Wide desktop: use the space instead of stretching into it ─────────────
   With no max-width, a single trade card rendered 1712px wide at 2560px —
   ticker on the far left, P&L on the far right, and the eye crossing the
   whole monitor to connect them. Two columns keep cards near 700px AND show
   twice as many trades, which is what the extra width is actually good for.
   Any non-card child (e.g. a group-by-pair header) spans the full row so
   grouping keeps reading top-to-bottom. */
@media (min-width: 1600px) {
  .trade-items {
    display: grid !important;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
    align-items: start;
  }
  .trade-items > *:not(.trade-card) { grid-column: 1 / -1; }
}
