/* ============================================================
   THE PHONE SCALE — phone.css
   Loaded by index.html on EVERY product, and inert everywhere it
   does not apply: every rule is inside
   `@media (max-width: 900px) and (pointer: coarse)`.

   IT USED TO BE GATED ON `html.has-phone-nav`, WHICH WAS WRONG.
   That class means "this product armed the bottom bar", and the
   Nova Assistant session made the argument that settles it: their
   product has no rail and five gated routes, the chat's own
   sidebar IS the chrome, and arming a tab bar on a chat app purely
   to inherit a type scale is the tail wagging the dog. A scale is
   not a navigation. It belongs to the POINTER, which is what
   `(pointer: coarse)` asks about — so a phone browser gets it too,
   not only the three apps, and every product improves at once.

   The thing that does still belong to the bar — `--hn-clear`, what
   a bottom-anchored control has to clear — lives in homenav.css
   with the bar itself, and is 0px wherever the bar is not mounted.

   WHY A SCALE AND NOT A LIST OF FIXES
   A live sweep of all 40 Nova OS routes at 375pt
   (mobile/os/scripts/parity-sweep.mjs) measured interactive controls
   at 26, 27, 30, 33, 34, 36 and 38 pixels tall. Fixing them view by
   view would be forty edits and a fortieth-first the next time
   somebody adds a chip.

   Almost none of those controls set their own height. They inherit
   --ctl-h-sm / --ctl-h-md / --ctl-h-lg from tokens.css, which are
   designed for a mouse. So the lever is the TOKENS: raise them here
   and every button, chip, field and select in the product grows at
   once, in the proportions its designer chose. Same for type.

   This is the same argument the native bridge makes about 200 fetch
   sites: the fix belongs at the one place the value is born.

   THE FLOOR IS NOT THE TARGET. Apple's 44×44 is a minimum, and the
   brief was "bigger and way easier for mobile", so the default button
   lands at 48 rather than 44 and the primary at 52. A thumb is about
   45–57px of contact; designing to the legal minimum means every tap
   is a near miss.
   ============================================================ */

@media (max-width: 900px) and (pointer: coarse) {
  html {
    /* ---- CONTROL HEIGHTS ----
       32/38/44 on a desktop become 42/48/54. The ratio is kept so a
       dense toolbar still reads as denser than a primary CTA; it is
       the whole ladder that moves, not one rung. */
    --ctl-h-sm: 42px;
    --ctl-h-md: 48px;
    --ctl-h-lg: 54px;
    /* Fields get their own rung because ::before cannot pad a replaced
       element (tokens.css says so) — an input's real height is the only
       lever it has. */
    --ctl-h-field: 50px;
    --tap-pad: 48px;
    --tap-pad-sm: 44px;

    /* ---- TYPE ----
       Read at arm's length, one-handed, often outdoors. Body goes to
       16px, which is also the number below which iOS zooms the page on
       focus — see the input rule further down. The 12px floor becomes
       13px: on a phone 12px is a squint, not a caption. */
    --fs-display: clamp(2rem, 8vw, 2.4rem);
    --fs-h1: 1.6rem;      /* 25.6px */
    --fs-h2: 1.3rem;      /* 20.8px */
    --fs-h3: 1.125rem;    /* 18px   */
    --fs-body: 1rem;      /* 16px   */
    --fs-sm: 0.9375rem;   /* 15px   */
    --fs-xs: 0.875rem;    /* 14px   */
    --fs-2xs: 0.8125rem;  /* 13px — the phone floor */

    /* ---- ROOM ----
       Denser is not calmer on a small screen; it is just harder to hit.
       Every gap in the product is a multiple of these. */
    --sp-3: 14px;
    --sp-4: 18px;
    --sp-5: 24px;
  }

  /* ---- ICONS ----
     16px glyphs beside 16px type were balanced on a desktop. At phone
     scale they read as decoration on a label rather than as the thing
     the label names. */
  #view .btn svg,
  #view .btn .ico { width: 19px; height: 19px; }
  #view .icon-btn svg,
  #view .btn-icon svg { width: 21px; height: 21px; }

  /* ---- THE SAFETY NET ----
     For controls that never took the tokens — a view-local chip, a
     hand-rolled tab, a bare <button>. min-height rather than height, so
     nothing that is already taller is squashed, and it is deliberately
     NOT applied to the whole document: the topbar and the rail are
     fixed-height lockups whose height is not ours to spend (Findings
     F7 — a min-height there is what broke the brand block). */
  #view button,
  #view [role="button"],
  #view a.btn,
  #view label.btn,
  #view select {
    min-height: 44px;
  }
  /* Both axes. A control can clear 44 tall and still be a 24pt-wide sliver —
     `.fnl-lane-collapse` measured 24x44, `.cal-new` 40x44 — and a thumb misses
     a narrow target as reliably as a short one. */
  #view .btn,
  #view button,
  #view [role="button"],
  #view .icon-btn,
  #view .btn-icon { min-width: 44px; }

  /* A checkbox is the smallest thing anyone is ever asked to hit, and it
     is usually the one that matters (select this row, mark this done). */
  #view input[type="checkbox"],
  #view input[type="radio"] { width: 24px; height: 24px; }

  /* ---- FIELDS ----
     iOS zooms the whole page when a field under 16px takes focus, then
     leaves it zoomed. It reads as the app breaking. 16px is not a
     preference here, it is the threshold — see --fs-ios-nozoom in tokens.css. */
  #view input,
  #view textarea,
  #view select,
  .modal input,
  .modal textarea,
  .drawer input,
  /* Both halves of this arrived at once from two sessions, and each had
     something the other did not. `max(…, var(--fs-body))` never renders SMALLER
     than the product's body type; `--fs-ios-nozoom` names what the 16 actually
     is, so the next person does not read it as a type choice and "tidy" it.
     Together: never below Safari's threshold, never below body type, and the
     threshold is a token the ship gate can see. */
  .drawer textarea { font-size: max(var(--fs-ios-nozoom), var(--fs-body)); }
  #view .input,
  #view input:not([type="checkbox"]):not([type="radio"]) {
    min-height: var(--ctl-h-field);
  }

  /* ---- NOTHING SITS UNDER THE KEYBOARD ----
     assets/js/native/shell.js publishes the live keyboard height as
     --kb-height inside the app (0px in a browser, and 0px whenever the
     keyboard is down). Any layer that owns the bottom edge adds it. */
  .modal,
  .drawer,
  .me-sheet { padding-bottom: var(--kb-height, 0px); }

  /* ---- SHEETS, NOT CENTRED DIALOGS ----
     A centred desktop dialog on a phone is a small box in the middle of
     a dark screen with its actions out of thumb reach. The same modal
     becomes a bottom sheet: full width, rounded at the top, actions at
     the bottom where the hand already is. `views/wins.js` shipped this
     for one modal; it belongs to all of them. */
  .modal-backdrop .modal {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto;
    width: 100%; max-width: none; margin: 0;
    /* 92dvh measured from the BOTTOM puts the top of a tall sheet at 8% of the
       screen — which on a phone is underneath the clock and the Dynamic Island.
       Photographed on an iPhone 17e: "Add transaction" opened with its title
       level with the wifi and battery icons. A sheet may use the whole screen
       EXCEPT the status bar, so the inset comes out of its height. */
    max-height: calc(92dvh - var(--safe-top)); overflow-y: auto;
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    padding-bottom: calc(env(safe-area-inset-bottom) + var(--kb-height, 0px) + var(--sp-4));
  }
  /* The grab handle that says "this pulls down" — the affordance a
     bottom sheet needs and a centred dialog never had. */
  .modal-backdrop .modal::before {
    content: ""; display: block; width: 38px; height: 4px; margin: 2px auto 12px;
    border-radius: var(--r-pill); background: var(--border-strong); flex: none;
  }

  /* ---- A FULL-SCREEN DRAWER STARTS AT THE TOP OF THE SCREEN ----
     `.drawer-scrim` is `position: fixed; inset: 0` and components.css makes
     `.drawer` full-width under 640px, so on a phone a drawer covers the whole
     screen — including the status bar. Its header pads from y=0, which puts the
     title level with the clock and the battery.

     Photographed on an iPhone 17e: budget's "Add transaction" — which is an
     openDrawer(), not an openModal(), so the sheet rule above never touched it
     — opened with its title overlapping the wifi and battery icons. The CRM
     record drawer is the same component, and NOVA-MOBILE.md names that one
     specifically.

     Full-screen is the right shape for a long form, so the drawer keeps it and
     only its content moves down. Same fix as the topbar in #1015: spend
     --safe-top, do not shrink the surface. */
  /* Not keyed to has-phone-nav, for the same reason the scale is not: a status
     bar is a property of the DEVICE, not of whether a product armed a bottom
     bar. Nova Assistant has no bar and its drawers sit under the clock exactly
     as everyone else's did. */
  .drawer-head { padding-top: calc(var(--sp-5) + var(--safe-top)); }

  /* The calendar rules below carried html.has-phone-nav too. Harmless on the
     three apps — all of them arm the bar — but EMPIRE has a calendar and no
     bar, so on a phone it kept the desktop toolbar: a month strip and six
     controls on one 375pt line. Same argument as everything else in this file.
     A calendar toolbar is not a navigation decision. */

  /* ---- AND THE CALENDAR COMPOSER IS A DESKTOP DRAWER ----
     The rule above catches `.modal`. The most-used creation surface in the
     product is not one: `.cal-detail-panel` is `position: absolute; right: 0;
     bottom: 0; width: 360px; max-width: 88vw`, sliding in from the RIGHT. On a
     phone that is a desktop side-drawer squeezed to 88% of the screen, with a
     strip of calendar still showing down the left edge — photographed on an
     iPhone 17e, which is what put it here.

     Two things were wrong with it and only one of them is visible.

     The visible one is the shape. The plan is explicit: sheets, not modals,
     because a bottom sheet puts its actions where the hand already is.

     The invisible one is worse. openComposer() focuses the title field 60ms
     after it opens, so the keyboard comes up EVERY time — and this panel is not
     `.modal`, `.drawer` or `.me-sheet`, so it was in none of the lists that add
     `--kb-height`. Its date, duration and save controls sat under the keyboard
     with nothing to scroll them into view.

     `position: fixed`, not absolute: as a sheet it belongs to the screen rather
     than to `.cal-main`, and it has to sit above the bottom bar (z-index 70)
     the way a sheet does. `--z-modal` is 100. */
  .cal-detail-panel {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto;
    width: 100%; max-width: none;
    max-height: calc(88dvh - var(--safe-top)); overflow-y: auto;
    border-left: none; border-top: 1px solid var(--border);
    border-radius: var(--r-xl) var(--r-xl) 0 0;
    box-shadow: 0 -8px 32px -12px rgba(0, 0, 0, 0.5);
    transform: translateY(100%);
    z-index: var(--z-modal);
    padding-bottom: calc(env(safe-area-inset-bottom) + var(--kb-height, 0px));
  }
  .cal-detail-panel.open { transform: translateY(0); }
  /* The same grab handle the modals get, so it reads as a sheet rather than as
     a panel that happens to be at the bottom. */
  .cal-detail-panel.open::before {
    content: ""; display: block; width: 38px; height: 4px; margin: 8px auto 4px;
    border-radius: var(--r-pill); background: var(--border-strong); flex: none;
  }

  /* ---- THE PAGE HEADER IS A DESKTOP HEADER ----
     Measured on Pipelines at 375pt: the eyebrow, a 38px display title, the
     subtitle and two full-size buttons ate about 60% of the screen before a
     single pipeline card. On a desktop that header is a generous entrance; on a
     phone it is a wall you scroll past every single time.

     Nothing is deleted — the eyebrow is Nova explaining what the page is, and
     PHILOSOPHY.md is explicit that plain language is not decoration. It is
     compressed: the title steps down from --fs-display to --fs-h1 (which is
     itself bigger here than on a desktop), the vertical rhythm halves, and the
     two header buttons share one row instead of stacking. That is roughly 120px
     of screen back, on every view in the product. */
  #view .view-head {
    gap: var(--sp-3); margin-bottom: var(--sp-4);
  }
  #view .view-head h1 {
    font-size: var(--fs-h1); line-height: 1.15;
  }
  #view .view-head .eyebrow { margin-bottom: 4px; }
  #view .view-head .view-sub {
    margin-top: 5px; font-size: var(--fs-sm); max-width: none;
  }
  #view .view-body { margin-top: var(--sp-4); }
  /* Header actions share a row when they fit and take a row each when they do
     not. `flex: 1 1 auto` — NOT `1 1 0`: a zero basis forces both into half the
     screen whatever they say, and "Import from a spreadsheet" then hangs its
     icon off the left edge and eats its own last word. With an auto basis the
     button will not shrink below its own text, so it wraps to its own line
     instead of truncating, and short pairs still sit side by side. */
  #view .view-head .view-actions {
    width: 100%; gap: var(--sp-2);
  }
  #view .view-head .view-actions > .btn { flex: 1 1 auto; }

  /* ---- ACTION ROWS WRAP, AND THEY SHRINK ----
     `.view-head .view-actions` already sets flex-wrap:wrap, and it was not
     enough: it also sets flex-shrink:0, and a flex item that refuses to shrink
     takes its max-content width no matter what wrapping it allows. Measured at
     401px inside a 377pt screen on Pipelines and Automations — the last button
     in the row is simply off the edge, and since the parent clips, it is gone
     rather than scrolled away. Allowing the shrink is what makes the wrap
     happen at all. */
  #view .view-actions,
  #view .view-head .view-actions {
    flex-wrap: wrap; flex: 0 1 auto; min-width: 0; max-width: 100%;
    justify-content: flex-start;
  }

  /* ---- STRIPS SCROLL, THEY DO NOT CLIP ----
     A row of tabs or filter chips wider than the screen inside a
     non-scrolling parent is content that is GONE, not content that is
     off to the side. Measured on five routes. */
  #view .tabs,
  #view .tabstrip,
  #view .seg,
  #view .chips,
  #view .filter-row {
    overflow-x: auto; flex-wrap: nowrap;
    scrollbar-width: none; -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity;
  }
  #view .tabs::-webkit-scrollbar,
  #view .tabstrip::-webkit-scrollbar,
  #view .seg::-webkit-scrollbar,
  #view .chips::-webkit-scrollbar,
  #view .filter-row::-webkit-scrollbar { display: none; }

  /* ---- THE CALENDAR TOOLBAR IS A DESKTOP TOOLBAR ----
     Nine controls in two flex halves: sidebar toggle, the date, Today, prev,
     next, a timezone picker, Select, Plan day, Tasks, New, and a Month/Week/Day
     switch. On a desktop they sit on one line. calendar.js already had a
     `@media (max-width:900px)` rule that let both halves WRAP, and wrapping is
     what produced the damage — measured on an iPhone 17, the toolbar became
     FIVE stacked rows and, with the Google banner under it, ate ~40% of the
     screen. Two hours of the day were visible.

     Wrapping is the wrong answer for a strip of controls on a phone; scrolling
     is the house answer (see `.segmented` in components.css and the strips
     above). So each half takes a full row and does not wrap, and the actions
     half scrolls sideways. That is TWO rows instead of five, and — this is the
     point, and why nothing here is `display: none` — every control is still
     reachable. The parity law is that no feature may be desktop-only, and a
     control hidden to save space is a feature deleted on a phone. */
  .cal-topbar { flex-wrap: wrap; gap: 6px; padding: 8px 10px; }
  .cal-topbar-l,
  .cal-topbar-r {
    flex: 1 0 100%; min-width: 0; flex-wrap: nowrap;
  }
  .cal-topbar-l { overflow: hidden; }
  /* Only the date may shrink. Everything beside it is a control, and a control
     that shrinks below its text wraps mid-word — "Today" rendered as "Toda /
     y" on the first pass, which is a squeezed label, not a button. */
  .cal-topbar-l > *:not(.cal-period) {
    flex-shrink: 0; white-space: nowrap;
  }
  /* The date is the one thing that may shrink: everything beside it is a
     control, and a control that ellipsises is a control you cannot read. */
  .cal-period {
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .cal-topbar-r {
    overflow-x: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch;
    scroll-snap-type: x proximity;
  }
  .cal-topbar-r::-webkit-scrollbar { display: none; }
  .cal-topbar-r > * { flex-shrink: 0; }
  /* WHAT IS VISIBLE WITHOUT SCROLLING IS A DECISION, SO MAKE IT.
     Source order is a desktop's order, and it put the timezone picker and
     three tool buttons in front of the two controls people actually reach for.
     On the first pass the Month/Week/Day switch was entirely off-screen — still
     reachable by swiping, which satisfies the parity law, but invisible, which
     satisfies nobody. The view switch and New lead; the timezone, Select, Plan
     day and Tasks scroll. */
  .cal-topbar-r .cal-viewseg { order: -2; }
  .cal-topbar-r .cal-new { order: -1; }
  /* The connect banner is a third row on a screen that has none to give. */
  .cal-banner { padding: 7px 10px; gap: 7px; line-height: 1.3; }

  /* ---- MOMENTUM ----
     The view is the scroll container; without this it stops dead
     instead of gliding, which is the single most "this is a website in
     a box" feeling there is. */
  #view { -webkit-overflow-scrolling: touch; overscroll-behavior-y: contain; }

  /* ---- NO HOVER-ONLY AFFORDANCES ----
     There is no hover on a phone. A control that only appears on
     :hover does not exist here, so anything the product reveals that
     way is shown outright. */
  #view .row-actions,
  #view .hover-actions,
  #view .show-on-hover { opacity: 1 !important; visibility: visible !important; }
}

/* REDUCE MOTION IS ALREADY HANDLED, GLOBALLY.

   There was a copy of the standard clamp here. motion.css already carries the
   same rule at `@media (prefers-reduced-motion: reduce)` with no width or
   pointer scoping, so it covers phones too — and it does more than the copy did
   (animation-delay, iteration-count, scroll-behavior). Two clamps is one more
   than can be tuned in one place, which is the whole reason the lint rule that
   flagged it exists. */

/* ============================================================
   TOUCH DRAG — assets/js/touch-drag.js carries a clone of the
   dragged element under the finger. These two rules are the only
   thing it needs from CSS, and they are here rather than inline
   so the look matches the SortableJS ghost (assets/js/dnd.js).
   ============================================================ */
html.touch-dragging { -webkit-user-select: none; user-select: none; }
/* The original stays in place and dims, so the row you lifted is still legible
   underneath — the same behaviour as the house .dnd-ghost. */
html.touch-dragging [draggable="true"]:not(.touch-drag-clone) { transition: opacity .12s ease; }
.touch-drag-clone { will-change: transform, left, top; }
