/* ═══════════════════════════════════════════════════════════════════
   PORTFOLIO TRACKER — UI refresh (calmer visuals + mobile)
   ═══════════════════════════════════════════════════════════════════
   Loaded AFTER style.css so plain-specificity overrides win.

   SCOPE CONTRACT — every rule in this file is scoped to either
     .app > .pane   → the 8 Portfolio tabs only
     .app > .tabs   → the Portfolio tab strip only
   The other trackers (gym, fishing, budget, ffb, macro, calendar,
   poker) nest their own .pane / .tabs deeper inside #*-tracker
   containers, so they are never matched. Do not remove the child
   combinator — it is what keeps this file isolated.
   =================================================================== */

/* ───────────────────────────────────────────────────────────────────
   1. SCOPED TOKENS
   Redefined on the pane so they cascade to descendants without
   touching :root (which every other tracker shares).
   ─────────────────────────────────────────────────────────────────── */
.app > .pane,
.app > .tabs {
  --pt-line: rgba(0, 0, 0, .07);
  --pt-line-soft: rgba(0, 0, 0, .045);
  --pt-r: 12px;
  --pt-r-lg: 16px;
  /* softer than the base greens/reds — less shouting on a wall of numbers */
  --pos: #2f7d55;
  --neg: #b04747;
}
@media (prefers-color-scheme: dark) {
  .app > .pane,
  .app > .tabs {
    --pt-line: rgba(255, 255, 255, .085);
    --pt-line-soft: rgba(255, 255, 255, .05);
    --pos: #58c98a;
    --neg: #e8807f;
  }
}

/* ───────────────────────────────────────────────────────────────────
   2. TYPOGRAPHY — stop shouting
   The base sheet uses 10px ALL-CAPS letterspaced labels in a dozen
   places. Sentence case at a readable size is the single biggest
   "easier on the eyes" win here.
   ─────────────────────────────────────────────────────────────────── */
.app > .pane .sec-lbl {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: -.05px;
  text-transform: none;
  color: var(--tx);
  margin-bottom: 12px;
}

.app > .pane thead th {
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--tx2);
  background: transparent;
  border-bottom: 1px solid var(--pt-line);
  padding: 12px 16px;
}

.app > .pane .mc .ml {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--tx2);
  margin-bottom: 5px;
}

/* Numbers line up column-to-column instead of jittering */
.app > .pane table,
.app > .pane .mc .mv,
.app > .pane .nw-chip .val {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ───────────────────────────────────────────────────────────────────
   3. SURFACES — fewer boxes, softer lines
   ─────────────────────────────────────────────────────────────────── */
.app > .pane {
  padding: 28px 28px 64px;
}

.app > .pane .mc {
  background: var(--bg2);
  border-radius: var(--pt-r);
  padding: 16px 18px;
}
.app > .pane .mc .mv {
  font-size: 21px;
  font-weight: 600;
  letter-spacing: -.3px;
}
.app > .pane .sum-grid {
  gap: 12px;
  margin-bottom: 24px;
}

.app > .pane .tbl-wrap {
  border: 1px solid var(--pt-line);
  border-radius: var(--pt-r-lg);
}

.app > .pane tbody td {
  padding: 13px 16px;
  border: none;
}
.app > .pane tbody tr {
  border-bottom: 1px solid var(--pt-line-soft);
}
.app > .pane tbody tr:last-child {
  border-bottom: none;
}

.app > .pane .notice {
  border-left-width: 3px;
  border-radius: 0 var(--pt-r) var(--pt-r) 0;
  padding: 12px 16px;
  line-height: 1.6;
}

.app > .pane .badge {
  font-weight: 500;
  padding: 3px 9px;
  border-radius: 6px;
}

.app > .pane .chart-card,
.app > .pane .alloc-card,
.app > .pane .equity-card,
.app > .pane .ocard,
.app > .pane .ci-card {
  border-radius: var(--pt-r-lg);
}

/* ───────────────────────────────────────────────────────────────────
   4. CONTROLS — consistent shape, calmer weight
   ─────────────────────────────────────────────────────────────────── */
.app > .pane .pbtn {
  padding: 6px 13px;
  font-size: 12px;
  border-radius: 999px;
  min-height: 30px;
}
.app > .pane .btn-secondary,
.app > .pane .addbtn {
  border-radius: 10px;
  min-height: 36px;
}
.app > .pane .col-tog-btn {
  border-radius: 999px;
}

/* Injected by portfolio-ui.js for mobile; the real <thead> handles
   sorting at desktop widths, so it stays out of the way there. */
.pt-sortbar { display: none; }

/* ═══════════════════════════════════════════════════════════════════
   5. TAB STRIP
   Fade edges signal "there are more tabs". The mask stops are driven
   by --ptfl/--ptfr, set from portfolio-ui.js on scroll. With JS off
   both default to 0 → no mask, no visual change.
   ═══════════════════════════════════════════════════════════════════ */
.app > .tabs {
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  -ms-overflow-style: none;
  gap: 2px;
  -webkit-mask-image: linear-gradient(90deg,
      transparent 0, #000 var(--ptfl, 0px),
      #000 calc(100% - var(--ptfr, 0px)), transparent 100%);
  mask-image: linear-gradient(90deg,
      transparent 0, #000 var(--ptfl, 0px),
      #000 calc(100% - var(--ptfr, 0px)), transparent 100%);
  transition: -webkit-mask-image .2s ease, mask-image .2s ease;
}
.app > .tabs::-webkit-scrollbar { display: none; }
.app > .tabs .tab {
  scroll-snap-align: center;
  border-radius: 8px 8px 0 0;
}

/* ═══════════════════════════════════════════════════════════════════
   6. MOBILE
   ═══════════════════════════════════════════════════════════════════ */
@media (max-width: 760px) {

  /* ── 6a. Shell ────────────────────────────────────────────────── */
  .app > .pane {
    padding: 16px 14px 96px;
  }
  .app > .pane .sec-lbl {
    font-size: 14px;
    margin-top: 28px;
  }
  body { font-size: 15px; }

  .app > .tabs {
    padding: 0 12px;
    top: 0;
  }
  .app > .tabs .tab {
    padding: 0 16px;
    min-height: 48px;
    font-size: 14.5px;
    display: flex;
    align-items: center;
  }

  /* ── 6a-ii. Topbar ────────────────────────────────────────────────
     The topbar is shared chrome, so these rules are gated on a
     Portfolio pane being active — selectTracker() clears .active from
     every .app>.pane when you switch to another tracker, so this
     matches only while the Portfolio tracker is on screen.

     Why it's needed: .topbar-right is a nowrap flex row that wants
     419px intrinsically (69 Trackers + 35 eye + 129 user chip +
     116 net worth, + gaps + padding) on a 375-430px phone, so it
     overflowed the viewport and dragged the whole document wider. */
  /* Two deterministic rows: title on top (with the net-worth chip
     pinned to its right), all the buttons below.

     Net worth is taken out of flow rather than being made a flex item.
     A flex approach can't reliably force the row break here: wrapping
     is decided by each item's flex-basis, not its final width, so any
     basis that pushes the buttons down on a 360px screen still lets
     "Trackers" slip up onto the title row at 393px. Absolute
     positioning makes the layout identical at every width. */
  body:has(.app > .pane.active) .topbar {
    position: relative;
    flex-direction: column;
    align-items: stretch;
    padding: 10px 14px;
    gap: 9px;
  }
  body:has(.app > .pane.active) .topbar-left {
    /* Reserves the strip the net-worth chip occupies so the two can't
       collide. 118px fits the chip up to a 7-figure value; the title
       ellipsises rather than wrapping, so the bar keeps one stable
       height whatever the title or screen width. */
    padding-right: 118px;
    min-width: 0;
  }
  body:has(.app > .pane.active) .topbar-title {
    font-size: 15px;
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  body:has(.app > .pane.active) .nw-chip {
    position: absolute;
    top: 12px;
    right: 14px;
    align-items: flex-end;
  }
  body:has(.app > .pane.active) .nw-chip .val { font-size: 19px; }
  body:has(.app > .pane.active) .nw-chip .lbl { font-size: 9.5px; }

  body:has(.app > .pane.active) .topbar-right {
    flex-wrap: wrap;
    width: 100%;
    gap: 8px;
    row-gap: 8px;
  }
  body:has(.app > .pane.active) #user-chip {
    flex-wrap: wrap;
    gap: 6px;
    min-width: 0;
  }
  /* Hidden rather than truncated: the display name is the one element
     here with unbounded width, and letting it vary (up to its 110px
     cap) is what decides whether the auth buttons fit on the same row
     as Trackers/eye. Dropping it makes the topbar lay out identically
     no matter whose account is signed in. The avatar still identifies
     the session, and the full name remains on desktop. */
  body:has(.app > .pane.active) .user-name { display: none; }

  body:has(.app > .pane.active) .hub-back-btn,
  body:has(.app > .pane.active) .privacy-btn {
    min-height: 36px;
    padding: 7px 11px;
  }
  body:has(.app > .pane.active) .btn-signout {
    min-height: 34px;
    padding: 7px 9px;
    font-size: 10.5px;
    white-space: nowrap;
  }

  /* Summary cards: 2-up instead of one giant column */
  .app > .pane .sum-grid {
    grid-template-columns: 1fr 1fr;
    gap: 10px;
  }
  .app > .pane .mc { padding: 14px; }
  .app > .pane .mc .mv { font-size: 19px; }
  /* Let a lone trailing card fill the row */
  .app > .pane .sum-grid .pnl-grand-total { grid-column: 1 / -1; }

  /* Toolbars wrap left-aligned instead of cramming to the right */
  .app > .pane .pane-toolbar {
    justify-content: flex-start;
    gap: 8px;
  }

  /* ── 6b. Touch targets ────────────────────────────────────────── */
  .app > .pane .pbtn {
    min-height: 44px;
    padding: 11px 17px;
    font-size: 13.5px;
  }
  .app > .pane .col-tog-btn {
    min-height: 44px;
    padding: 11px 15px;
    font-size: 13.5px;
  }
  .app > .pane .btn-secondary,
  .app > .pane .addbtn,
  .app > .pane .btn-fetch,
  .app > .pane .btn-test {
    min-height: 46px;
    padding: 12px 18px;
    font-size: 14.5px;
    border-radius: 12px;
  }
  .app > .pane .delbtn {
    min-width: 44px;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 19px;
    border-radius: 10px;
  }
  .app > .pane .svbtn,
  .app > .pane .cxbtn {
    min-height: 44px;
    padding: 11px 18px;
    font-size: 14.5px;
    border-radius: 10px;
  }
  .app > .pane .period-btns { gap: 6px; }

  /* Full-width primary actions read better than a lone left-aligned pill */
  .app > .pane > .addbtn {
    display: flex;
    width: 100%;
    justify-content: center;
    margin-top: 12px;
  }

  /* ── 6c. Tables → cards ───────────────────────────────────────────
     .pt-cards is applied by portfolio-ui.js only to tables that
     actually have a wide header row, so narrow/layout tables and
     the .alloc-tbl modal keep their normal rendering.
     ─────────────────────────────────────────────────────────────── */
  .app > .pane .tbl-wrap:has(> .pt-cards) {
    border: none;
    border-radius: 0;
    overflow: visible;
    background: none;
  }
  .app > .pane table.pt-cards { display: block; }
  .app > .pane table.pt-cards > thead { display: none; }
  .app > .pane table.pt-cards > tbody {
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
  .app > .pane table.pt-cards > tbody > tr {
    display: block;
    background: var(--bg);
    border: 1px solid var(--pt-line);
    border-radius: var(--pt-r-lg);
    padding: 14px 16px;
  }
  .app > .pane table.pt-cards > tbody > tr:hover { background: var(--bg); }

  /* Generic label/value row — used by Orders, P&L, Watchlist, etc. */
  .app > .pane table.pt-cards > tbody > tr > td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding: 7px 0;
    border: none;
    white-space: normal;
    text-align: right;
  }
  .app > .pane table.pt-cards > tbody > tr > td::before {
    content: attr(data-label);
    flex: 0 0 auto;
    max-width: 48%;
    color: var(--tx2);
    font-size: 13px;
    font-weight: 500;
    text-align: left;
  }
  /* Cells whose header was blank (action columns) get no label */
  .app > .pane table.pt-cards > tbody > tr > td[data-label=""]::before,
  .app > .pane table.pt-cards > tbody > tr > td:not([data-label])::before {
    content: none;
  }
  /* Action cell sits at the bottom of the card */
  .app > .pane table.pt-cards > tbody > tr > td.pt-actions {
    justify-content: flex-end;
    gap: 6px;
    padding-top: 12px;
    margin-top: 6px;
    border-top: 1px solid var(--pt-line);
  }

  /* ── 6d. Holdings table — the two-line summary card ───────────── */
  #port-body > tr:not(.ir) {
    display: grid;
    grid-template-columns: auto auto 1fr auto 16px;
    align-items: baseline;
    column-gap: 8px;
    row-gap: 3px;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
  #port-body > tr:not(.ir) > td {
    display: block;
    padding: 0;
    text-align: left;
  }
  #port-body > tr:not(.ir) > td::before { content: none; }

  /* line 1 — symbol · value */
  #port-body > tr:not(.ir) > td[data-col="symbol"] {
    grid-column: 1 / 4;
    grid-row: 1;
    font-size: 16.5px;
    font-weight: 700;
    letter-spacing: -.2px;
  }
  #port-body > tr:not(.ir) > td[data-col="value"] {
    grid-column: 4;
    grid-row: 1;
    justify-self: end;
    text-align: right;
    font-size: 16.5px;
    font-weight: 600;
  }
  /* line 2 — shares · sector · G/L% */
  #port-body > tr:not(.ir) > td[data-col="shares"] {
    grid-column: 1;
    grid-row: 2;
    font-size: 13px;
    color: var(--tx2);
  }
  #port-body > tr:not(.ir) > td[data-col="sector"] {
    grid-column: 2;
    grid-row: 2;
    font-size: 13px;
    color: var(--tx2);
    max-width: 15ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  #port-body > tr:not(.ir) > td[data-col="glpct"] {
    grid-column: 4;
    grid-row: 2;
    justify-self: end;
    text-align: right;
    font-size: 13.5px;
    font-weight: 600;
  }
  /* "8 sh · Technology" — suffix skipped on the CASH row, where the
     shares cell holds a dollar amount rather than a share count */
  #port-body > tr:not(.ir):not(.cash-row) > td[data-col="shares"]::after {
    content: " sh";
    color: var(--tx3);
  }
  #port-body > tr:not(.ir) > td[data-col="sector"]::before {
    content: "· ";
    color: var(--tx3);
  }

  /* Disclosure chevron, spanning both summary lines */
  #port-body > tr:not(.ir)::after {
    content: "";
    grid-column: 5;
    grid-row: 1 / 3;
    align-self: center;
    justify-self: end;
    width: 7px;
    height: 7px;
    border-right: 1.5px solid var(--tx3);
    border-bottom: 1.5px solid var(--tx3);
    transform: rotate(45deg);
    transition: transform .18s ease;
  }
  #port-body > tr.pt-open::after { transform: rotate(-135deg); }

  /* Detail cells — collapsed by default */
  #port-body > tr:not(.ir) > td[data-col="price"],
  #port-body > tr:not(.ir) > td[data-col="cost"],
  #port-body > tr:not(.ir) > td[data-col="pct"],
  #port-body > tr:not(.ir) > td[data-col="gl"],
  #port-body > tr:not(.ir) > td[data-col="daypct"],
  #port-body > tr:not(.ir) > td[data-col="rgl"],
  #port-body > tr:not(.ir) > td[data-col="tag"],
  #port-body > tr:not(.ir) > td[data-col="acct"],
  #port-body > tr:not(.ir) > td.pt-actions {
    display: none;
    grid-column: 1 / -1;
  }
  #port-body > tr.pt-open > td[data-col="price"],
  #port-body > tr.pt-open > td[data-col="cost"],
  #port-body > tr.pt-open > td[data-col="pct"],
  #port-body > tr.pt-open > td[data-col="gl"],
  #port-body > tr.pt-open > td[data-col="daypct"],
  #port-body > tr.pt-open > td[data-col="rgl"],
  #port-body > tr.pt-open > td[data-col="tag"],
  #port-body > tr.pt-open > td[data-col="acct"] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 9px 0;
    font-size: 14px;
    text-align: right;
    border-top: 1px solid var(--pt-line-soft);
  }
  /* First detail row gets a little air under the summary */
  #port-body > tr.pt-open > td[data-col="price"] { margin-top: 10px; }

  #port-body > tr.pt-open > td[data-col]::before {
    content: attr(data-label);
    color: var(--tx2);
    font-size: 13px;
    font-weight: 500;
    text-align: left;
    flex: 0 0 auto;
  }
  /* Friendlier than the cramped column headers */
  #port-body > tr.pt-open > td[data-col="pct"]::before { content: "% of portfolio"; }
  #port-body > tr.pt-open > td[data-col="cost"]::before { content: "Cost per share"; }
  #port-body > tr.pt-open > td[data-col="rgl"]::before { content: "Realized G/L"; }
  #port-body > tr.pt-open > td[data-col="gl"]::before { content: "Unrealized G/L"; }

  #port-body > tr.pt-open > td.pt-actions {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 8px;
    padding-top: 12px;
    margin-top: 8px;
    border-top: 1px solid var(--pt-line);
  }

  /* ── 6e. Edit / add row → bottom sheet ────────────────────────────
     The .ir row keeps every input id and every inline handler the
     existing code wired up (saveEditedPortRow, savePortRow,
     saveCashPosition, cancelEditPort). This is presentation only.
     ─────────────────────────────────────────────────────────────── */
  .app > .pane table.pt-cards > tbody > tr.ir {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 320;
    display: block;
    background: var(--bg);
    border: none;
    border-radius: 20px 20px 0 0;
    box-shadow: 0 -10px 44px rgba(0, 0, 0, .26);
    padding: 8px 18px calc(18px + env(safe-area-inset-bottom, 0px));
    max-height: 86vh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    animation: pt-sheet-up .22s cubic-bezier(.32, .72, 0, 1);
    /* grab handle */
    background-image: linear-gradient(var(--bs), var(--bs));
    background-repeat: no-repeat;
    background-position: center 9px;
    background-size: 38px 4px;
  }
  @keyframes pt-sheet-up {
    from { transform: translateY(100%); }
    to   { transform: translateY(0); }
  }
  .app > .pane table.pt-cards > tbody > tr.ir::before {
    content: attr(data-sheet-title);
    display: block;
    font-size: 17px;
    font-weight: 650;
    padding: 22px 0 14px;
    margin-bottom: 4px;
    border-bottom: 1px solid var(--pt-line);
  }
  .app > .pane table.pt-cards > tbody > tr.ir > td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    padding: 9px 0;
    border: none;
    white-space: normal;
  }
  .app > .pane table.pt-cards > tbody > tr.ir > td::before {
    content: attr(data-label);
    flex: 0 0 38%;
    color: var(--tx2);
    font-size: 14px;
    font-weight: 500;
    text-align: left;
  }
  /* Computed-value placeholders ("auto"/"live") carry no control — drop
     them from the sheet. .pt-auto-cell is set by portfolio-ui.js. */
  .app > .pane table.pt-cards > tbody > tr.ir > td.pt-auto-cell { display: none; }

  .app > .pane table.pt-cards > tbody > tr.ir > td input,
  .app > .pane table.pt-cards > tbody > tr.ir > td select,
  .app > .pane table.pt-cards > tbody > tr.ir > td textarea {
    width: auto !important;
    flex: 1 1 auto;
    min-width: 0;
    /* 16px keeps iOS Safari from zooming the viewport on focus */
    font-size: 16px;
    padding: 12px 13px;
    min-height: 48px;
    border-radius: 11px;
    background: var(--bg2);
  }
  .app > .pane table.pt-cards > tbody > tr.ir > td.pt-actions,
  .app > .pane table.pt-cards > tbody > tr.ir > td:last-child {
    display: flex;
    gap: 10px;
    padding-top: 16px;
    margin-top: 8px;
    border-top: 1px solid var(--pt-line);
  }
  .app > .pane table.pt-cards > tbody > tr.ir > td.pt-actions::before,
  .app > .pane table.pt-cards > tbody > tr.ir > td:last-child::before {
    content: none;
  }
  .app > .pane table.pt-cards > tbody > tr.ir .svbtn {
    flex: 1 1 auto;
    min-height: 50px;
    font-size: 15.5px;
    border-radius: 12px;
  }
  .app > .pane table.pt-cards > tbody > tr.ir .cxbtn {
    flex: 0 0 92px;
    min-height: 50px;
    font-size: 15.5px;
    border-radius: 12px;
    margin-left: 0;
  }

  .pt-sheet-backdrop {
    position: fixed;
    inset: 0;
    z-index: 310;
    background: rgba(0, 0, 0, .42);
    animation: pt-fade .2s ease;
  }
  @keyframes pt-fade { from { opacity: 0; } to { opacity: 1; } }

  /* ── 6f. Mobile sort control ──────────────────────────────────────
     thead is hidden in card mode, which would strand the sortable
     column headers. portfolio-ui.js injects this bar and proxies the
     selection back to the original <th> click handlers.
     ─────────────────────────────────────────────────────────────── */
  .pt-sortbar {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 10px;
  }
  /* A table that stayed a table (too few columns) keeps its own
     header row, so the proxy bar would be redundant there. */
  .pt-sortbar:has(+ .tbl-wrap > table:not(.pt-cards)) { display: none; }
  .pt-sortbar > label {
    font-size: 13px;
    color: var(--tx2);
    flex: 0 0 auto;
  }
  .pt-sortbar select {
    flex: 1 1 auto;
    min-height: 44px;
    font-family: var(--f);
    font-size: 15px;
    color: var(--tx);
    background: var(--bg2);
    border: 1px solid var(--pt-line);
    border-radius: 11px;
    padding: 0 12px;
    outline: none;
  }
  .pt-sortbar button {
    flex: 0 0 auto;
    min-height: 44px;
    min-width: 44px;
    font-family: var(--f);
    font-size: 16px;
    color: var(--tx2);
    background: var(--bg2);
    border: 1px solid var(--pt-line);
    border-radius: 11px;
    cursor: pointer;
  }

  /* ── 6g. Charts, equity curve, misc grids ─────────────────────── */
  .app > .pane .charts-row,
  .app > .pane .alloc-row,
  .app > .pane .other-grid,
  .app > .pane .ci-cards {
    grid-template-columns: 1fr;
    gap: 12px;
  }
  .app > .pane .alloc-toolbar {
    flex-wrap: wrap;
    gap: 8px;
  }
  .app > .pane .alloc-seg-btn,
  .app > .pane .csb {
    min-height: 44px;
    padding: 11px 16px;
    font-size: 13.5px;
  }
  .app > .pane #equity-curve-body { height: 200px; }

  /* P&L → Invested Breakdown builds its two-column chart grid with an
     inline style attribute, so stacking it needs !important. Chart.js
     also stamps a pixel width on the canvas, which forces the grid
     track open unless the items are allowed to shrink. */
  #inv-breakdown-card > div[style*="1fr 1fr"] {
    grid-template-columns: 1fr !important;
    gap: 16px !important;
  }
  #inv-breakdown-card > div[style*="1fr 1fr"] > div { min-width: 0; }
  #inv-breakdown-card canvas { max-width: 100% !important; }
  #inv-breakdown-card .chart-legend { min-width: 0; }
  .app > .pane .heatmap-controls {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }
  .app > .pane .div-calendar { grid-template-columns: repeat(2, 1fr); }

  /* Modals get room to breathe and never trap content off-screen */
  .app > .pane .modal-box,
  .modal-overlay .modal-box {
    width: 100%;
    max-width: 100%;
    max-height: 88vh;
    overflow-y: auto;
  }
}

/* Very narrow phones only — most Androids are 360-412px and read better
   with the summary cards still 2-up, so this stays below that range. */
@media (max-width: 330px) {
  .app > .pane .sum-grid { grid-template-columns: 1fr; }
  .app > .pane { padding: 14px 11px 96px; }
}

/* ───────────────────────────────────────────────────────────────────
   7. Respect reduced-motion
   ─────────────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .app > .pane table.pt-cards > tbody > tr.ir { animation: none; }
  .pt-sheet-backdrop { animation: none; }
  #port-body > tr:not(.ir)::after { transition: none; }
  .app > .tabs { transition: none; }
}
