/* ==========================================================================
   MOBILE SHELL — iPhone first                          (loaded after desk.css)
   --------------------------------------------------------------------------
   WHAT THIS FILE IS. Everything that makes the hub usable on a phone lives
   here, in one place, so a later pass can find it and so nothing in desk.css
   had to be re-cut for a 390 px screen. desk.css owns the desk; this file owns
   the phone. It is linked LAST, so where the two disagree at phone width, this
   one wins without a single !important.

   THE TWO JOBS, in the owner's order:
     1. LOG IN EASILY. The iOS rule that breaks more logins than any other:
        an input under 16 px makes Safari ZOOM the page on focus, which leaves
        the user scrolled sideways in the middle of typing a password and the
        Sign-in button off screen. Every field on this site is therefore >= 16
        px at phone width, no exceptions. The login card's own rules live in
        templates/login.html next to the markup they dress; the global input
        floor lives here so a field added anywhere inherits it.
     2. CHECK NUMBERS FAST. A bottom tab bar puts the four money screens one
        thumb-tap from anywhere, and the 244 px sidebar becomes a drawer behind
        a fifth "More" tab. See the NAVIGATION note below.

   RULES THIS FILE KEEPS
     · Not one raw hex. Every colour is a desk.css custom property, so the
       institutional palette repaints this layer for free and the brand-is-
       chrome / blue-gains-red-losses separation is inherited, not re-decided.
     · Every new class is prefixed .m-  (or is an existing desk.css class being
       adjusted inside a media query). Nothing here can collide with a name the
       next pass invents.
     · Two breakpoints only, matching the ones desk.css already has:
       max-width: 900px is "phone or narrow tablet", and a landscape-phone
       query (max-height: 560px) for the case where vertical room is the
       scarce thing. No third mental model.
     · Safe areas are read with env(safe-area-inset-*), which is 0 on every
       device that has no notch, so the same rule is correct everywhere.

   NAVIGATION — WHY A TAB BAR *AND* A DRAWER
     The desk has seventeen destinations, so a tab bar alone cannot replace the
     sidebar and a drawer alone puts every glance two taps away. So: the five
     slots are the ones the owner opens to read a number (or, in Simple view,
     the five pages that exist), and the last slot opens the SAME sidebar as a
     drawer. There is exactly one nav definition in the template — the tab bar
     is built from it by static/mobile.js — so a nav item added later cannot
     go missing on the phone.

   COMPANION FILE: static/mobile.js (drawer behaviour, tab bar, theme-colour
   sync). Neither file is any use without the other.
   ========================================================================== */

:root {
  /* Height of the tab bar itself, before the home-indicator inset. Read by
     .content's bottom padding and by the toast stack so nothing can hide
     underneath it. */
  --m-tabbar-h: 54px;
  --m-tap: 44px;          /* Apple HIG minimum touch target */
  --m-gut: 16px;          /* phone gutter */
  /* Safe areas, named once. env() cannot be used inside a calc() shorthand in
     every engine, but it is fine inside a custom property. */
  --m-safe-t: env(safe-area-inset-top, 0px);
  --m-safe-r: env(safe-area-inset-right, 0px);
  --m-safe-b: env(safe-area-inset-bottom, 0px);
  --m-safe-l: env(safe-area-inset-left, 0px);
}

/* ── Global, every width ───────────────────────────────────────
   These three are corrections to browser defaults, not layout, so they are
   not inside a media query. */

html {
  /* iOS inflates text in landscape unless told not to. A ledger that changes
     size when the phone turns is not a ledger. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  /* The colour the rubber band reveals. Matching --bg is what stops the
     overscroll flashing a white (or black) band that belongs to no theme. */
  background: var(--bg);
}

body {
  /* Kill the rubber-band chain: a swipe past the end of a scrolling card must
     not start dragging the page, and the page must not bounce into a
     mismatched background. contain (not none) keeps pull-to-refresh dead
     without disabling normal scrolling. */
  overscroll-behavior-y: contain;
}

/* Momentum scrolling for anything that scrolls inside the page. */
.t-wrap, pre.log, .sidebar, .content { -webkit-overflow-scrolling: touch; }

/* A wide table scrolls INSIDE its own box. The page itself must never scroll
   sideways — that is the single most common way a mobile dashboard feels
   broken. */
.t-wrap { overflow-x: auto; overscroll-behavior-x: contain; max-width: 100%; }

/* ══ PHONE / NARROW TABLET ═════════════════════════════════════ */

@media (max-width: 900px) {

  /* ── 1. THE 16 px INPUT FLOOR ────────────────────────────────
     Below 16px iOS Safari zooms on focus. This is the fix for the login bug
     the owner would have hit first, and it is set for every control on the
     site rather than just the login card, because the next form somebody adds
     would otherwise reintroduce it. */
  input, select, textarea, .f-input {
    font-size: 16px;
  }
  /* Fields are also targets: 44 px tall, and never narrower than their box. */
  input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
  select, textarea, .f-input {
    min-height: var(--m-tap);
    max-width: 100%;
  }
  textarea, .f-input { min-height: 88px; }

  /* ── 2. TOUCH TARGETS ────────────────────────────────────────
     44x44 is the floor. The buttons keep their visual height by growing their
     hit area, so the desk does not suddenly look like a phone app on a
     tablet. */
  .btn, .btn.sm, .btn.icon, .seg, .nav-item, .pickrow {
    min-height: var(--m-tap);
  }
  .btn.icon { min-width: var(--m-tap); width: var(--m-tap); }
  .btn { padding-left: 15px; padding-right: 15px; }
  /* min-width matters as much as height: the period picker (1D/1W/1M/1Y) and
     the settings "✕" removers measured 38-41 px wide, which is a miss on a
     thumb even though they were tall enough. */
  .btn.sm { min-width: var(--m-tap); padding-left: 13px; padding-right: 13px; font-size: 13px; }
  .seg { height: auto; padding: 0 14px; }
  /* Remove the grey flash iOS paints over anything tappable; the desk has its
     own :active state and the double treatment reads as a glitch. */
  a, button, .nav-item, .seg, .pickrow, .m-tab {
    -webkit-tap-highlight-color: transparent;
  }

  /* ── 3. SHELL + SAFE AREAS ───────────────────────────────────
     One column, and every fixed edge respects the notch, the Dynamic Island
     and the home indicator. */
  .app { grid-template-columns: 1fr; }

  .topbar {
    height: auto;
    min-height: 52px;
    gap: 8px;
    padding: 0 max(12px, var(--m-safe-r)) 0 max(12px, var(--m-safe-l));
    padding-top: var(--m-safe-t);
    flex-wrap: nowrap;
  }
  /* The title takes the leftover room and truncates instead of pushing the
     controls off the bar — which is exactly what it did at 390 px before. */
  .topbar h1 {
    min-width: 0;
    flex: 1 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .topbar .spacer { display: none; }
  .status-strip { flex: none; flex-wrap: nowrap; gap: 6px; }

  /* The machine readouts (inference server, GPU, market clock) are instrument
     detail, and at 390 px they are what pushes Refresh off the bar. They stay
     one tap away in the drawer; the PAPER badge does NOT hide, because it is
     the safety label that says this desk cannot spend money.

     Scoped to .topbar deliberately: mobile.js MOVES these elements into the
     drawer, and the moment they land there this rule stops applying — so the
     hide is "not on the bar", not "not on a phone". It also leaves the
     audience rule (:root[data-mode="simple"] [data-mode-only="advanced"]) in
     charge of whether they exist at all, which an #id rule here would have
     silently overruled. */
  .topbar #chip-market, .topbar #chip-llm, .topbar #chip-gpu { display: none; }

  .content {
    padding: 18px var(--m-gut) 0;
    padding-left: max(var(--m-gut), var(--m-safe-l));
    padding-right: max(var(--m-gut), var(--m-safe-r));
    /* Clear the tab bar AND the home indicator. */
    padding-bottom: calc(var(--m-tabbar-h) + var(--m-safe-b) + 28px);
  }

  /* ── 4. THE DRAWER ───────────────────────────────────────────
     The sidebar element itself, slid in from the left. Same markup, same nav
     items, same one definition of "who is this page for". */
  .sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: min(86vw, 300px);
    z-index: 70;
    transform: translateX(-101%);
    box-shadow: var(--shadow-3);
    padding-top: max(14px, var(--m-safe-t));
    padding-bottom: max(14px, var(--m-safe-b));
    padding-left: max(12px, var(--m-safe-l));
    overscroll-behavior: contain;
    /* visibility, not display: it keeps the closed drawer out of the tab order
       and away from the screen reader (an off-screen panel full of live
       tab-stops is a trap), and it is delayed on close so the slide-out is
       actually seen. */
    visibility: hidden;
    transition: transform .22s cubic-bezier(.2, .7, .3, 1),
                visibility 0s linear .22s;
  }
  .sidebar.open {
    transform: none;
    visibility: visible;
    transition: transform .22s cubic-bezier(.2, .7, .3, 1), visibility 0s;
  }

  .m-scrim {
    position: fixed;
    inset: 0;
    z-index: 65;
    background: rgba(0, 0, 0, .48);
    opacity: 0;
    transition: opacity .22s ease;
    overscroll-behavior: none;
    touch-action: none;
  }
  .m-scrim.on { opacity: 1; }

  /* Scroll lock while the drawer is open. The scroll position is stashed by
     mobile.js and restored on close — position:fixed is the only lock iOS
     Safari actually honours. */
  body.m-locked {
    position: fixed;
    left: 0;
    right: 0;
    width: 100%;
    overflow: hidden;
  }

  /* Nav items are the drawer's whole point, so they get real targets. */
  .nav-item { padding: 11px 12px; gap: 12px; font-size: 14.5px; }
  .nav-item .ico { font-size: 15px; width: 19px; }
  .nav-group { margin-top: 14px; }

  /* The audience switch is moved into the drawer by mobile.js at this width —
     it is a preference, not a per-glance control, and it costs 150 px of a
     390 px bar. This is the slot it lands in. */
  .m-drawer-slot {
    margin-top: 16px;
    padding-top: 14px;
    border-top: 1px solid var(--line);
  }
  .m-drawer-slot .m-slot-label {
    display: block;
    padding: 0 2px 8px;
    font-size: var(--fs-eyebrow);
    font-weight: var(--w-semi);
    letter-spacing: var(--track-eyebrow);
    text-transform: uppercase;
    color: var(--ink-3);
  }
  .m-drawer-slot .seg-group { display: flex; width: 100%; }
  .m-drawer-slot .seg { flex: 1; }

  /* The machine chips, parked in the drawer where they still fit. Nothing to
     un-hide: out of .topbar, the rule above no longer matches them. */
  .m-chip-park { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }

  /* ── 5. THE TAB BAR ──────────────────────────────────────────
     Fixed to the bottom edge because that is where the thumb is on a 6.1"
     phone. Five equal slots, so every target is 390/5 = 78 px wide and the
     whole bar is one row whatever the labels say. */
  .m-tabbar {
    position: fixed;
    z-index: 60;
    inset: auto 0 0 0;
    display: flex;
    align-items: stretch;
    padding-bottom: var(--m-safe-b);
    padding-left: var(--m-safe-l);
    padding-right: var(--m-safe-r);
    background: color-mix(in srgb, var(--bg-elev) 94%, transparent);
    backdrop-filter: blur(14px);
    border-top: 1px solid var(--line);
  }

  .m-tab {
    flex: 1 1 0;
    min-width: 0;
    min-height: var(--m-tabbar-h);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    padding: 6px 2px;
    border: 0;
    background: transparent;
    color: var(--ink-3);
    font-family: inherit;
    font-size: 10.5px;
    font-weight: var(--w-med);
    letter-spacing: .01em;
    cursor: pointer;
    position: relative;
  }
  .m-tab .m-ico { font-size: 17px; line-height: 1; }
  .m-tab .m-lbl {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* Active state is brand CHROME (a nav rail is chrome), and it is carried by
     weight and a hairline as well as by hue — never by colour alone. */
  .m-tab[aria-current="page"] { color: var(--brand-tint); font-weight: var(--w-semi); }
  .m-tab[aria-current="page"]::before {
    content: '';
    position: absolute;
    top: 0; left: 22%; right: 22%;
    height: 2px;
    background: var(--brand-tint);
  }
  .m-tab:focus-visible { outline: 2px solid var(--brand-tint); outline-offset: -3px; }
  .m-tab .m-count {
    position: absolute;
    top: 4px;
    left: 56%;
    min-width: 16px;
    padding: 0 4px;
    border-radius: 99px;
    background: var(--brand-sf);
    border: 1px solid var(--brand-ring);
    color: var(--brand-tint);
    font-size: 9.5px;
    font-weight: var(--w-semi);
    line-height: 15px;
    text-align: center;
  }
  .m-tab .m-count[hidden] { display: none; }

  /* Toasts sit above the bar, not behind it. */
  .toasts {
    right: max(12px, var(--m-safe-r));
    left: max(12px, var(--m-safe-l));
    bottom: calc(var(--m-tabbar-h) + var(--m-safe-b) + 12px);
    max-width: none;
  }

  /* ── 6. CONTENT THAT WAS SIZED FOR A DESK ────────────────────
     Only the shell-level fixes live here; per-card density is a separate
     pass. These are the ones that would otherwise cause a horizontal page
     scroll, which is a shell defect. */
  .grid.k2, .grid.k3, .grid.k4 { grid-template-columns: minmax(0, 1fr); }
  .card, .ticket, .onboard, .venue-card { min-width: 0; max-width: 100%; }
  .view-head h1 { font-size: 22px; }
  .content > * { min-width: 0; }

  /* .row is a section heading plus whatever chips and buttons belong to it,
     laid out as a nowrap flex line. At 390 px the trailing "↓ CSV" links were
     pushed past the right edge and took the whole PAGE sideways with them
     (measured: documentElement.scrollWidth 656 on Reports). Wrapping is the
     whole fix — nothing is hidden, the controls just take a second line. */
  .row { flex-wrap: wrap; row-gap: 8px; }
  .row .spacer { flex: 1 0 auto; }
  .card-h { flex-wrap: wrap; row-gap: 6px; }

  /* A chip is `white-space: nowrap` because on a desk it is two words. In the
     body of a view they are whole clauses ("equity_curve + the legacy
     ledgers", the model name on Inference) and one of them measured 480 px
     wide, which took the page sideways. Only the ones inside .content wrap —
     the topbar's PAPER badge must stay on one line. */
  .content .chip {
    white-space: normal;
    height: auto;
    min-height: 26px;
    padding: 3px 9px;
    max-width: 100%;
    overflow-wrap: anywhere;
  }
  .copyrow { flex-wrap: wrap; }
  .copyrow .lbl { width: auto; }

  /* A stat's sub-line is a SENTENCE ("67 open · 32 closed · $94.33 realized"),
     but it is built as a flex row, so at 390 px each fragment became its own
     block and the clauses came out of order. As text it simply wraps. */
  .stat .s { display: block; }
  .stat .s .delta, .stat .s b { display: inline; }

  /* The onboarding steps are links, and a 20 px link is not a target. */
  .onboard { gap: 10px; }
  .ob-steps { gap: 4px; width: 100%; }
  .ob-step { min-height: var(--m-tap); padding: 2px 0; }
}

/* ── The desktop side of the switch ──────────────────────────── */
@media (min-width: 901px) {
  /* The phone chrome does not exist on a desk. Stated as display:none rather
     than :empty so the slot's own label cannot resurrect it. */
  .m-tabbar, .m-scrim, .m-drawer-slot, .m-chip-park { display: none; }
}

/* ══ LANDSCAPE PHONE ═══════════════════════════════════════════
   844x390 and friends. Vertical room is the scarce thing, so the bar goes
   compact and the notch moves to the side. */

@media (max-width: 900px) and (max-height: 560px) and (orientation: landscape) {
  :root { --m-tabbar-h: 44px; }
  .m-tab { flex-direction: row; gap: 7px; padding: 4px 2px; font-size: 11px; }
  .m-tab .m-ico { font-size: 15px; }
  .m-tab .m-count { position: static; margin-left: 2px; }
  .topbar { min-height: 46px; }
  .content { padding-top: 12px; }
  .sidebar { width: min(52vw, 300px); }
}

/* ══ MOTION ════════════════════════════════════════════════════
   desk.css already zeroes every transition under prefers-reduced-motion; the
   drawer must still be usable when it does, so it snaps rather than slides.
   Stated explicitly because a drawer that relies on its transition to become
   visible would simply never appear. */
@media (prefers-reduced-motion: reduce) {
  .sidebar, .m-scrim { transition: none !important; }
}
