/* ============================================================================
   exotic — phone app shell · css/app/phone.css
   ----------------------------------------------------------------------------
   The app frame for phones, shared by every logged-in page. Three pieces,
   injected by js/app/phone.js:

     .ap-top     a minimal fixed top bar — just the mark (tap → account
                 drawer). It steps aside when you read (hides on scroll
                 down, returns on scroll up).
     .ap-dock    the navigation: one clean floating pill at the bottom —
                 Home · Explore · Activity. Active item carries the meridian
                 thread (the ramp as LIGHT, per MERIDIAN law).
     .ap-seek    Explore only — a round frosted search island beside the
                 dock; taps the page's ⌘K (#topSearch).
     .ap-drawer  account-related actions only, slid in from the left by the
                 logo: deposit / withdraw, light & theme, settings.

   Desktop is untouched — everything lives behind (max-width:640px), the same
   phone breakpoint lobby.css uses. Requires tokens.css + lobby.css first.

   ─────────────────────────────────────────────────────────
   ENTRANCE STORYBOARD (ms after load · matches docs/DESIGN.md)
     0ms   meridian draws across the top (../kit/motion.css owns it)
   120ms   top bar fades in
   180ms   dock rises 10px → 0, fades in
   ───────────────────────────────────────────────────────── */

@media (max-width:640px){

:root{ --ap-top-h:54px; --ap-dock-h:54px;
  /* the dock's float above the screen bottom: it rides INTO the safe area
     (the 34px inset already IS clearance — 10px on top of it left the pill
     hovering mid-air, device screenshot 2026-07-16). Browser tabs (env=0)
     keep a 12px float. */
  --ap-dock-b:max(12px, env(safe-area-inset-bottom,0px) - 6px); }

  /* iOS rubber-banding above y=0 must not drag the page out from under the
     fixed chrome — the shell owns the bounce (js/app/phone.js clamps scrollY
     to match, so the bar never hides mid-bounce) */
  html,body{ overscroll-behavior-y:none; }

  /* fast repeated taps are input, not a zoom gesture */
  button,a,.seg,[role="button"]{ touch-action:manipulation; }

  /* ---- the desktop chrome steps back ---- */
  .side{ display:none; }
  .lb-top{ display:none; }                /* search + money live in the app frame now */
  .lb-foot{ display:none; }               /* apps end with content, not a web footer;
                                             the dock already carries the nav */
  .lb-main{ margin-left:0;
    /* +4 puts the title's cap line ~26px under the mark — the same 26px the
       page uses between sections, so the chrome joins the rhythm */
    padding-top:calc(var(--ap-top-h) + 4px);
    padding-bottom:calc(var(--ap-dock-h) + var(--ap-dock-b) + 22px); }

  /* fold pages (detail + home): ONE bar. The mini-head never mounts — the
     hero's identity lives IN the top bar (.ap-id, JS-built), each piece
     fading in as its page twin hides. There is NO lit-edge pseudo-element
     and no meridian: the price line itself, squashed inside its svg with
     strokes held at full width (vector-effect), is the line that rests
     under the bar's hairline. One object end to end. */
  .ap-detail .meridian,.ap-home .meridian{ display:none; }
  .ap-detail .mini-head,.ap-home .mini-head{ display:none; }
  /* fold pages opt out of scroll anchoring: the pin swaps the chart for a
     spacer mid-scroll, and anchoring "helpfully" compensating for any
     residual sub-pixel shift re-fires scroll into the fold — feedback loop */
  .ap-detail,.ap-home{ overflow-anchor:none; }
  /* while its chart folds, the hero card rides above later cards (which
     otherwise paint over the pinned line) — still under the bar (85).
     animation:none — a lingering entrance fill holds an identity matrix on
     the card, making it a containing block that traps the chart's
     position:fixed pin (!important: .mo-on [data-mo] outranks one class) */
  .folding-host{ position:relative; z-index:83; animation:none !important; }
  /* and the pinned chart rides above its OWN card's siblings: the seg row
     passing under the bar painted OVER the line, strobing it during
     momentum — read as "snaps back and forth" (user recording + filmstrip
     frame 6, 2026-07-16). Fixed while folding, so z applies. */
  .fold-chart.folding{ z-index:6; }
  /* the identity cluster: the scroll COLLECTS the page into the bar — each
     piece fades in as its page twin slips under the bar (JS toggles
     id-logo when the heading row goes, id-px when the price row goes;
     then the chart itself folds into the line) */
  .ap-id{ display:flex; align-items:center; gap:7px; flex:1 1 auto; min-width:0;
    pointer-events:none; }
  .ap-id>*{ opacity:0; transform:translateY(5px);
    transition:opacity .22s ease, transform .22s var(--ease-emph); }
  .ap-top.id-logo .ap-id .logo,.ap-top.id-logo .ap-id .tile,.ap-top.id-logo .ap-id .nm,
  .ap-top.id-px .ap-id .px,.ap-top.id-chip .ap-id .chip{ opacity:1; transform:none; }
  .ap-id .logo,.ap-id .tile{ width:26px; height:26px; font-size:12px; flex:0 0 auto;
    display:grid; place-items:center; border-radius:8px; }
  .ap-id .tile{ background:var(--grad); color:var(--ink-on-grad); }
  .ap-id .nm{ font-weight:600; font-size:13.5px; min-width:0;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  /* left-packed, not spread: pushing the price right starved the name of
     width (SK H…) — packed, the free space sits before ☆/🔔 instead */
  .ap-id .px{ font-family:var(--font-num); font-variant-numeric:tabular-nums;
    font-size:13.5px; font-weight:500; flex:0 0 auto; }
  .ap-id .chip{ font-family:var(--font-num); font-size:10.5px; font-weight:600;
    border-radius:var(--rad-1); padding:2px 6px; white-space:nowrap; flex:0 0 auto; }
  .ap-id .px small{ font-size:11px; color:var(--ink-2); }

  /* ============================ top bar ============================
     At rest it isn't a bar at all — just the mark floating on the page.
     Chrome (frost + hairline) materializes only once content actually
     scrolls underneath it, so the page opens seamless. */
  .ap-top{ position:fixed; top:0; left:0; right:0; z-index:85;
    display:flex; align-items:center; gap:6px;
    height:calc(var(--ap-top-h) + env(safe-area-inset-top,0px));
    padding:env(safe-area-inset-top,0px) 12px 0 12px;
    background:transparent; border-bottom:1px solid transparent;
    transition:transform .32s var(--ease-emph), background-color .3s ease, border-color .3s ease;
    animation:apFade .3s both .12s; }
  .ap-top.scrolled{ background:color-mix(in oklab,var(--bg) 92%,transparent);
    -webkit-backdrop-filter:var(--mat-regular); backdrop-filter:var(--mat-regular);
    border-bottom-color:var(--hairline-2); }
  /* fold pages: the bar is a landing surface content dives under — at 92%
     frost the white seg disc and card labels ghosted through it (same
     lesson as the mini-head's 94%); content never reads above this */
  .ap-detail .ap-top.scrolled,.ap-home .ap-top.scrolled{
    background:color-mix(in oklab,var(--bg) 96%,transparent); }
  .ap-top.hide{ transform:translateY(-100%); }
  /* the meridian is chrome: when the bar steps aside to let you read, the
     thread retreats with it — a lone gradient line floating under the
     status bar read as a glitch (device screenshot 2026-07-16) */
  .meridian{ transition:opacity .32s ease; }
  body:has(.ap-top.hide) .meridian{ opacity:0; }
  /* (no status-bar frost when the bar hides — content runs full-height
     under the clock like native feeds; user call 2026-07-16) */
  /* P&L bars don't fold — once the chart has fully passed under the bar
     the brand line simply appears at the boundary (js/app/phone.js toggles
     .ap-line at p≥1), so depth ends in the same edge in every chart mode.
     Same ramp as the price line's lbStroke, same 2px weight. */
  .ap-top::after{ content:''; position:absolute; left:0; right:0; bottom:-2.1px;
    height:2.1px; pointer-events:none; opacity:0; transition:opacity .18s ease;
    background:linear-gradient(90deg,var(--g1),var(--g2),var(--g3),var(--g4),var(--g5),var(--g6)); }
  .ap-top.ap-line::after{ opacity:1; }

  /* -6px lands the mark's left edge exactly on the 14px content gutter —
     the mark and the title share one left axis */
  .ap-logo{ display:grid; place-items:center; width:44px; height:44px; margin-left:-6px;
    border:0; background:none; padding:0; cursor:pointer; border-radius:12px; flex:0 0 auto; }
  .ap-logo:active{ transform:scale(.94); }
  .ap-logo .markbox{ width:32px; height:32px; display:block; }

  /* drill-in pages (Asset, Thesis): a back chevron rides where the mark does */
  .ap-back{ display:grid; place-items:center; width:44px; height:44px; margin-left:-8px;
    border:0; background:none; padding:0; cursor:pointer; border-radius:12px;
    color:var(--ink); flex:0 0 auto; }
  .ap-back:active{ transform:scale(.92); color:var(--ink-2); }
  .ap-back svg{ width:23px; height:23px; stroke:currentColor; fill:none;
    stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }

  /* ============================ bottom pill ============================ */
  .ap-dock{ position:fixed; left:50%; z-index:88;
    bottom:var(--ap-dock-b);
    display:flex; align-items:stretch; gap:2px; padding:5px;
    background:color-mix(in oklab,var(--surface) 78%,transparent);
    -webkit-backdrop-filter:var(--mat-thick); backdrop-filter:var(--mat-thick);
    border:1px solid var(--hairline-2); border-radius:var(--rad-pill,999px);
    box-shadow:var(--shadow-pop);
    transform:translateX(var(--ap-dock-x,-50%));
    animation:apDockIn .45s var(--ease-emph) both .18s; }
  /* ICONS ONLY (user). The label survives as the link's aria-label, so the
     accessible name is unchanged — only the visible text goes. */
  .ap-dock a{ position:relative; display:grid; place-items:center;
    width:52px; height:48px; border-radius:var(--rad-pill,999px);
    text-decoration:none; color:var(--muted);
    transition:color .15s, background .15s, transform .15s var(--ease-out); }
  .ap-dock a > span{ display:none; }
  /* one press dialect across the shell — scale down snaps, release eases */
  .ap-logo,.ap-back,.apd-x{ transition:transform .15s var(--ease-out); }
  .ap-dock a:active{ transform:scale(.96); }
  .ap-dock a svg{ width:24px; height:24px; stroke:currentColor; fill:none;
    stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; }
  .ap-dock a.on{ color:var(--ink); background:color-mix(in oklab,var(--bg) 72%,transparent); }
  /* the meridian marks where you are — the same thread the side rail carried */
  .ap-dock a.on::after{ content:""; position:absolute; bottom:5px; left:50%;
    transform:translateX(-50%); width:14px; height:2px; border-radius:var(--rad-pill,999px);
    background:linear-gradient(90deg,var(--g2),var(--g4),var(--g5)); }

  /* Explore only: search IS the surface, so it rides beside the dock as a
     round frosted island. The dock cedes 31px so the pair reads as ONE
     centered group (dock ·· island) — symmetric at every width ≥320px. */
  /* THE PAIR IS ONE CENTRED GROUP, and these two numbers are arithmetic on the
     dock's exact width. Redo them if the item size or the tab count changes —
     I have now got them wrong twice by assuming the wrong count.
       tabs        = 3  (Home, Explore, Activity — search is the island)
       dock width  = 172  (measured, not derived — borders round up)
       island      = 60   (measured)
       gap         = 10
       group       = 172 + 10 + 60                 = 242  → half = 121
       dock offset = -121 from 50%; its own -50%   = -86  → -35px more
       island left = -121 + 172 + 10               = +61 from 50% */
  .ap-explore .ap-dock{ --ap-dock-x:calc(-50% - 35px); }
  .ap-seek{ position:fixed; z-index:88; left:calc(50% + 61px);
    bottom:var(--ap-dock-b);
    width:52px; height:52px; display:grid; place-items:center; padding:0;
    color:var(--ink); cursor:pointer;
    background:color-mix(in oklab,var(--surface) 78%,transparent);
    -webkit-backdrop-filter:var(--mat-thick); backdrop-filter:var(--mat-thick);
    border:1px solid var(--hairline-2); border-radius:var(--rad-pill,999px);
    box-shadow:var(--shadow-pop);
    transition:transform .15s var(--ease-out);
    animation:apSeekIn .45s var(--ease-emph) both .22s; }
  .ap-seek:active{ transform:scale(.94); }
  .ap-seek svg{ width:21px; height:21px; stroke:currentColor; fill:none;
    stroke-width:1.7; stroke-linecap:round; stroke-linejoin:round; }
  /* 320-class phones: the dock slims so the pair keeps real margins */
  @media (max-width:350px){ .ap-dock a{ min-width:64px; } }
  @keyframes apSeekIn{ from{ opacity:0; transform:translateY(10px); } }

  /* toasts rise above the bottom chrome — the dock pill (66px + 10px float)
     on tab pages, the action bar (60px) on detail pages; one clearance covers
     both, so a toast never lands under either */
  #toast{ bottom:calc(var(--ap-dock-h) + var(--ap-dock-b) + 14px); }

  /* ============================ account drawer ============================ */
  .ap-scrim{ position:fixed; inset:0; z-index:930; background:var(--scrim);
    opacity:0; pointer-events:none; transition:opacity .26s ease; }
  .ap-scrim.on{ opacity:1; pointer-events:auto; }

  .ap-drawer{ position:fixed; left:0; top:0; bottom:0; z-index:935;
    width:min(320px,86vw); display:flex; flex-direction:column; gap:16px;
    padding:calc(16px + env(safe-area-inset-top,0px)) 16px calc(16px + env(safe-area-inset-bottom,0px));
    background:var(--bg); border-right:1px solid var(--hairline-2); box-shadow:var(--shadow-modal);
    transform:translateX(calc(-100% - 24px));
    /* visibility rides the exit so the closed drawer's buttons can't take
       keyboard focus while invisible (same pattern as .tf) */
    visibility:hidden;
    transition:transform .34s var(--ease-emph), visibility 0s linear .34s; }
  .ap-drawer.on{ transform:none; visibility:visible;
    transition:transform .34s var(--ease-emph); }

  .apd-head{ display:flex; align-items:center; gap:11px; padding:4px 4px 14px;
    border-bottom:1px solid var(--hairline-2); }
  .apd-mark{ width:30px; height:30px; flex:0 0 auto; }
  .apd-id{ display:flex; flex-direction:column; gap:1px; min-width:0; }
  .apd-id b{ font-size:13.5px; font-weight:600; letter-spacing:-0.01em; color:var(--ink);
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  .apd-id span{ font-family:var(--font-num); font-size:10.5px; color:var(--muted); }
  .apd-x{ margin-left:auto; display:grid; place-items:center; width:44px; height:44px;
    border:0; background:none; padding:0; cursor:pointer; border-radius:10px;
    color:var(--muted); font-size:19px; line-height:1; }
  .apd-x:active{ color:var(--ink); transform:scale(.94); }

  /* money movement — the two actions that used to live in the top bar */
  .apd-money{ display:flex; gap:8px; }
  .apd-money button{ flex:1; padding:12px 10px; }

  .apd-nav{ display:flex; flex-direction:column; gap:2px; }
  .apd-nav .nav-it{ width:100%; text-align:left; background:none; border-color:transparent;
    justify-content:flex-start; }
  /* the rail hides labels until hover (it's a collapsed rail); the drawer is
     always expanded, so its labels are simply there */
  .apd-nav .nav-it .lb{ display:inline; }
  .apd-nav .nav-it .soon{ display:inline; margin-left:auto; }
  .apd-nav .nav-it:active{ background:var(--surface-2); }

  .apd-grow{ flex:1; }
  .apd-foot{ border-top:1px solid var(--hairline-2); padding-top:12px; }
  .apd-foot a{ font-size:12.5px; color:var(--muted); text-decoration:none; }
  .apd-foot a:active{ color:var(--ink); }

  /* ---- detail metrics: one thumb row — all six a swipe away, none hidden
     behind a fold. A fade lives ONLY on a side that still has content
     (JS toggles fade-l/fade-r): an exhausted edge shows its card clean,
     and snap positions leave a 24px peek of the previous card so the
     left edge cues the scroll the same way the right does. */
  .mets{ display:flex; overflow-x:auto; -webkit-overflow-scrolling:touch;
    scroll-snap-type:x proximity; scrollbar-width:none;
    scroll-padding-inline:24px; }
  .mets.fade-r{
    -webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 30px),transparent);
    mask-image:linear-gradient(90deg,#000 calc(100% - 30px),transparent); }
  .mets.fade-l{
    -webkit-mask-image:linear-gradient(90deg,transparent,#000 30px);
    mask-image:linear-gradient(90deg,transparent,#000 30px); }
  .mets.fade-l.fade-r{
    -webkit-mask-image:linear-gradient(90deg,transparent,#000 30px,#000 calc(100% - 30px),transparent);
    mask-image:linear-gradient(90deg,transparent,#000 30px,#000 calc(100% - 30px),transparent); }
  .mets::-webkit-scrollbar{ display:none; }
  /* two cards + a deliberate ~24px peek of the third — the scroll cue */
  .mets .met{ flex:0 0 calc((100vw - 76px) / 2); scroll-snap-align:start; }

  /* ---- primary buttons: the lit-horizon ink slab reads as a dead black
     block on phone glass. The phone primary keeps the law — the ramp only
     as LIGHT — by wearing the meridian as a 1.5px border around paper:
     surface fill, ink text, gradient hairline all the way round. ---- */
  .btn-grad,.sc-cta{ color:var(--ink);
    background:linear-gradient(var(--card),var(--card)) padding-box,var(--grad) border-box;
    border:1.5px solid transparent; box-shadow:var(--crisp); }
  .btn-grad::after,.sc-cta::after{ display:none; }
  .btn-grad:active,.sc-cta:active{ transform:scale(.97); }
  body.theme-dark .btn-grad,body.theme-dark .sc-cta{ color:var(--ink);
    background:linear-gradient(var(--card),var(--card)) padding-box,var(--grad) border-box; }

  /* ---- charts: the y-gutter gives its 46px back to the line — the
     crosshair/touch readout already names every price (the one engine,
     px-chart.js, skips the labels ≤640 to match; every chart wears
     .px-chart / .px-x, the Lobby's included — D22) ---- */
  .px-chart svg,.px-vol svg{ width:100%; }
  .px-chart .yl{ display:none; }
  .px-x{ margin-right:0; }

  /* ---- detail pages: page text must never read between the action-bar
     buttons — a fixed fade backs the bar (content < 87 < .tfbar at 88) ---- */
  .ap-botfade{ position:fixed; left:0; right:0; bottom:0; height:110px;
    z-index:87; pointer-events:none;
    background:linear-gradient(to top,var(--bg) 55%,transparent); }

  @keyframes apFade{ from{ opacity:0; } to{ opacity:1; } }
  @keyframes apDockIn{ from{ opacity:0; transform:translate(var(--ap-dock-x,-50%),10px); }
                       to{ opacity:1; transform:translate(var(--ap-dock-x,-50%),0); } }

  /* while the chart folds into the top line its HTML chips and labels fade
     out as a group (they no longer squash with the svg — text must never
     distort), and the dashed aux lines go with their labels so only the
     price line remains to become the bar's edge (.fold-chart = whichever
     chart the fold mounted: a detail hero or the Lobby's #chartWrap —
     both wear .px-chart since D22) */
  .fold-chart .ol-lb,.fold-chart .px-mk,.fold-chart .hl-lb,
  .fold-chart .yl,.fold-chart .oline,.fold-chart path[stroke-dasharray],
  .fold-chart .xdot,.fold-chart .xline,.fold-chart .tip,
  .fold-chart .grid{
    transition:opacity .18s ease; }
  /* a fast fade out, not an instant cut: with the pin correct the fold's
     entry is otherwise seamless, and a hard pop of every label/dash was
     the one discontinuity left (user, 2026-07-16). Mid-fold re-renders
     stay safe — a node REMOUNTED under .folding gets opacity:0 at first
     style resolution, and transitions never run on first render, so
     fresh chips still appear hidden instantly. Unfolding fades back at
     the base rule's .18s. */
  .fold-chart.folding .ol-lb,.fold-chart.folding .px-mk,
  .fold-chart.folding .hl-lb,.fold-chart.folding .yl,
  .fold-chart.folding .oline,.fold-chart.folding path[stroke-dasharray],
  .fold-chart.folding .xline,.fold-chart.folding .tip{ opacity:0; transition:opacity .12s ease; }
  /* !important: px-chart's restDot parks the live dot with INLINE opacity */
  .fold-chart.folding .xdot{ opacity:0 !important; }
  /* the grid sits outside .fg (it must not squash with the line — only the
     line moves, user call 2026-07-16): it DISSOLVES in place, keeping the
     .18s fade both ways instead of the instant hide above */
  .fold-chart.folding .grid{ opacity:0; }

  /* the layout HUD (drawer toggle) — device debugging readout, rides above
     every overlay incl. the trade flow (990) */
  .ap-hud{ position:fixed; left:10px; bottom:34%; z-index:9999; margin:0;
    padding:9px 11px; border-radius:10px; pointer-events:none;
    font:10.5px/1.55 ui-monospace,Menlo,monospace; white-space:pre;
    background:rgba(10,12,16,.78); color:#fff;
    -webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px); }

  /* metric cards: label + value only at glance speed — the definitions
     already live in the tap explainers */
  .met .s{ display:none; }

  /* chart marker chips, phone-compact: px-chart composes genuinely short
     labels at ≤640 (amounts dropped, pinned suffix abbreviated) — the cap
     is a last resort, wide enough that a PRICE is never the clipped token */
  .px-hero .ol-lb{ font-size:9.5px; max-width:172px; white-space:nowrap;
    overflow:hidden; text-overflow:ellipsis; }

  /* chrome that persists across pages must not re-enter on every navigation —
     js/app/phone.js stamps ap-warm after the first page of a session */
  .ap-warm .ap-top{ animation:none; }
  .ap-warm .ap-dock{ animation:none; }
  .ap-warm .ap-seek{ animation:none; }

  @media (prefers-reduced-motion:reduce){
    .ap-top,.ap-dock,.ap-seek,.ap-drawer,.ap-id,.ap-id>*{ animation:none !important; transition:none !important; }
    .ap-scrim{ transition:opacity .2s ease; }   /* the comprehension cue stays */
  }
  @media (prefers-reduced-transparency:reduce){
    .ap-dock,.ap-seek{ background:var(--surface); -webkit-backdrop-filter:none; backdrop-filter:none; }
    .ap-top.scrolled{ background:var(--bg); -webkit-backdrop-filter:none; backdrop-filter:none; }
  }
}

/* the shell is phone-only — if the window grows past the breakpoint after
   injection, none of it may linger */
@media (min-width:641px){
  .ap-top,.ap-dock,.ap-seek,.ap-scrim,.ap-drawer,.ap-botfade{ display:none !important; }
}
