/* The phone chrome. Loaded LAST, after touch.css, because it overrides sheets that load earlier.
 *
 * The rule this whole file follows: on a phone this is a MOBILE APP, not a desktop app viewed
 * small. That means the reference's own mobile grammar - one compact bar at the top with the
 * symbol and period, the actions at the thumb along the bottom, every dialog a bottom sheet with
 * the platform's spring, and the desktop's toolbar rows, menu bar and floating windows gone.
 * Everything still resolves through the theme tokens, so light and dark both hold.
 *
 * AND A NARROW PANEL IS NOT A PHONE.
 *
 * Every query here used to be width alone, and the width these frames report is the PANEL'S, not
 * the screen's. So a split screen on a desktop - the liquidation feed on the left, the chart on
 * the right - handed the chart a 481px frame and it became the phone app: the cluster-type
 * button, the cluster-text button, the Tick stepper and the settings door all set to
 * display:none, on the one pane where a footprint is being read. The report was "it won't let me
 * zoom in on the right side to see the cluster - I can only see the colours, not the numbers",
 * and that is exactly right: the two controls that decide whether the numbers can be drawn had
 * been taken away, and the phone's doors to them are in a bottom bar aimed at a thumb.
 *
 * `pointer: coarse` is what actually separates the two. touch.css has always been gated that way;
 * this file simply was not. A desktop panel keeps the desktop toolbar, which fitBar() already
 * compresses, and every control stays reachable.
 */

/* THE BREAKPOINT IS TWO CONDITIONS, AND THE SECOND ONE IS A PHONE LYING ON ITS SIDE.
 *
 * Width alone meant the whole phone layer switched off the moment anybody turned their phone
 * landscape: 844 x 390 is past 640, so the view bar, the tick stepper, the sheets and the thumb
 * bar all left and the desktop toolbar arrived on a 390-point-tall screen. Turning a chart
 * sideways to see more bars is the most obvious thing anyone does with one.
 *
 * The second condition is short AND not wide: a landscape phone is 844 x 390 and a touchscreen
 * laptop is 1920 x 1080, so 950 separates them without needing to know about either. mobile-boot
 * makes the same decision in JS off the screen's SHORTER side, which does not rotate - the two
 * have to agree, or the bar appears without the layout that makes room for it. */

/* Marked phone-only in markup, shown only under the same breakpoint everything else here uses. */
.phone-only { display: none; }

/* The phone's view bar exists in the markup on every screen and is drawn on none but a phone.
   `hidden` keeps it out of the layout until app.js has decided this IS one - see mobile-boot.js
   for why the decision is width AND a coarse pointer AND not inside the desktop shell. */
.mtabs[hidden] { display: none; }

@media (max-width: 640px) and (pointer: coarse),
       (max-height: 500px) and (max-width: 950px) and (pointer: coarse) {
  .phone-only { display: initial; }

  /* ONE NUMBER FOR EVERY CONTROL A THUMB PRESSES ON THIS SCREEN.
   *
   * Apple's floor is 44 and TradingView's own phone header sits at about 40; the audit reports
   * anything under 40 as a miss. It had drifted to five values - 32, 34, 36, 38 and 40 - across
   * the toolbar, the view bar and the stepper, which is both a row that does not line up and a
   * row where half the targets are smaller than the finger pressing them. Everything on the two
   * top bars derives from this, so it can only be wrong in one place. */
  :root { --m-ctl: 40px; }

  /* And the corner that goes with it. A phone control is forty pixels tall against a desktop's
     twenty-eight, and the themed four-pixel corner on a box that size reads as a rectangle with
     the edges knocked off rather than as a rounded field. One token, derived here for the same
     reason --m-ctl is: so a phone corner can only be wrong in one place, and so no phone rule
     ever has to spell a literal radius that an appearance cannot follow. */
  :root { --r-touch: 12px; }


  /* ------------------------------------------------------------ top chrome */

  /* THE TOOLBAR STANDS DOWN ON A PHONE. Everything it carried that a phone reaches for is on the
     thumb bar now - the instrument, the period and the tick are the same elements, moved - and
     everything else has a door in a sheet. Two bands of chrome above the chart is what "bulky"
     meant; this is the whole of the saving, 52 of 664 points. */
  body.bar-at-thumb > header.bar { display: none; }

  /* ONE row: the instrument, the period, the tick, the theme and the way in to everything else.
     The desktop header's second and third rows - cluster and text cyclers, the indicator chips,
     the date, replay - all have doors in a sheet. A toolbar that wraps to three rows IS the
     desktop tell. It no longer needs to scroll either; see the composition note below. */
  header.bar {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    gap: 6px;
    padding: 6px 6px;
  }
  header.bar::-webkit-scrollbar { display: none; }
  /* WHAT COMES OFF THE PHONE'S TOOLBAR, and what has now earned its way back on.
   *
   * The strip is one scrolling line and every control past the fourth pushes the next one off the
   * right edge mid-word, so the rule has to be: only the controls a chart is actually DRIVEN by
   * stay, and everything else gets a door in a sheet. What changed is which ones those are.
   *
   * TICK IS BACK, as a real minus / value / plus. It was hidden with the rest of the desktop
   * furniture and that was simply wrong: a footprint is READ through its row height. Too coarse
   * and there are four rows in a bar; too fine and the numbers will not fit and the chart stops
   * printing them. Anybody reading order flow changes it as often as they change the timeframe,
   * and on a phone there was no way to change it at all - not in the hub, not in the thumb bar,
   * only six rows down a settings sheet as a typed number with no stepper. That is the report
   * "there is nothing where they can change tick size to lower it and higher it", exactly.
   *
   * THEME IS BACK, as the sun. It was reachable only as the seventeenth tile of a hub you had to
   * scroll three screens to finish - which is "they can't change the theme, or don't know where".
   *
   * THE QUICK-TIMEFRAME ROW STANDS DOWN to pay for them. Three chips and a caret is 162 points of
   * a 390-point bar spent on one decision, and #tfBtn now carries the running period as its own
   * label and opens the phone period sheet, where every period is a thumb-sized chip. One
   * control, one tap, and a hundred points back for the tick.
   */
  #venueBtn,
  #frvpBtn,
  #clusterBtn,
  #textBtn,
  #indicatorsBtn,
  #settingsBtn,
  #replayTopBtn,
  #alertBtn,
  #undoBtn,
  #redoBtn,
  #snapBtn,
  #keysBtn,
  #dateInput,
  #nowBtn,
  #candleBtn,
  #drawBtn,
  /* THREE DOORS TO ONE SHEET ON ONE SCREEN. The action bar's ellipsis is the thumb-reachable one
     and the view bar's is the one that survives a view opening; a third on the toolbar, one row
     above the second and wearing the same glyph, is a control whose only distinguishing feature
     is which of two identical buttons you happened to press. Its forty points go to the
     instrument, which is the control that yields when a symbol is long. */
  #moreBtn,
  .conn,
  .tf-quick,
  .toggles,
  header.bar .div { display: none; }
  /* THE BAR IS COMPOSED, NOT SCROLLED.
   *
   * It was `overflow-x: auto` and it stayed that way after two controls came off, which is the
   * worst of both: a row that fits and still behaves like it might not, and a row that would
   * silently hide the next control anybody adds instead of showing that it no longer fits. The
   * five that are left are measured against the narrowest phone still shipping in volume:
   *
   *   360 - 16 padding = 344 available
   *   symbol + period 54 + tick 122 + theme 40 = 216 fixed, and three 6pt gaps, leaves the
   *   instrument 110 - which is every ticker this venue lists, spelled out.
   *
   * The instrument is the one that yields, because it is the only one of the four whose meaning
   * survives being shortened. */
  header.bar { overflow-x: hidden; }
  #symBtn {
    font-weight: var(--w-semi);
    font-size: var(--t-lg);
    height: var(--m-ctl);
    padding: 0 10px;
    border-radius: var(--r-ctl);
    /* The one that gives: 1000PEPEUSDT is fourteen characters and the four controls beside it
       are not negotiable. Ellipsis rather than a clip, because a word that ends mid-letter is
       the exact texture people call generated. */
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  #tfBtn.tf-caret {
    height: var(--m-ctl);
    /* It names the running period now, so it is a labelled control rather than a bare chevron. */
    gap: 2px;
    padding: 0 6px 0 9px;
    font-weight: var(--w-semi);
    font-size: var(--t-md);
    border-radius: var(--r-ctl);
  }

  /* ----------------------------------------------------------- the tick */

  /* Minus, value, plus - welded into one control the way the desktop stepper is, at thumb size.
     The value is the input the desktop uses, so typing 25 still works and still opens a numeric
     keypad; the two buttons step the LADDER, which is what makes them useful without typing.

     WHY THESE THREE GROUPS ARE WRITTEN UNDER :root[data-mobile] AND NOTHING ELSE HERE IS.

     An appearance theme sets its own control metric - theme-tv.css, which is the default look,
     pins .stepper, .sym-group and .tf-group to 26px at a specificity of (0,2,1). A media query
     adds none, so this file's own (0,1,0) rules lost to it however late they loaded, and the
     symbol button spent this whole build 34px tall inside a 26px box with `overflow: hidden`
     over it - clipped top and bottom on every phone, in the default theme.
     
     `:root[data-mobile]` is (0,2,1) and matches the attribute mobile-boot.js stamps before the
     first paint, so it wins on level ground and stays honest about WHEN it applies: on a device
     this app has decided is a phone, which is a stricter test than the media query alone. */
  :root[data-mobile] .stepper {
    display: inline-flex;
    flex: 0 0 auto;
    align-items: stretch;
    gap: 0;
    height: var(--m-ctl);
    border-radius: var(--r-ctl);
    overflow: hidden;
    background: var(--panel-2);
    border: 1px solid var(--line);
  }
  /* The two groups either side of it, at the same height, for the same reason. */
  :root[data-mobile] .sym-group,
  :root[data-mobile] .tf-group { height: var(--m-ctl); padding: 0; }
  /* The instrument is the one control here whose meaning survives being shortened, so it is the
     one that gives when the sum does not fit - 1000PEPEUSDT is fourteen characters and the four
     beside it are not negotiable. The group is `flex: 0 0 auto` on a desktop, which meant the
     button inside it could ellipsis all it liked and the box around it never yielded a point. */
  :root[data-mobile] .sym-group { flex: 0 1 auto; min-width: 3.5em; }
  /* The overflow arrows are for a bar that scrolls, and this one is composed to fit instead -
     see the note above. Left standing they appear the moment the sum is one point over and sit
     ON TOP of whichever control is passing underneath, which is worse than the overflow. */
  header.bar > .bar-scroll { display: none; }

  /* "Tick x" is four words of a bar that has room for none. The row height is what the control
     sets and the value is beside two arithmetic signs; nothing here is ambiguous without it. */
  .stepper .cap { display: none; }
  /* THE BAR IS TEXT, NOT BOXES. Three grey rounded rectangles behind three words - instrument,
     period, tick - is exactly the chrome the chart itself just lost: boxes that group nothing,
     on a strip that is already its own group. A phone bar reads like the platform bars people
     know: plain controls on the strip, hairline separators gone with the ground they sat on.
     Press states keep the quiet --sel ground, so the targets still answer the thumb. */
  :root[data-mobile] .bar .sym-group,
  :root[data-mobile] .bar .tf-group,
  :root[data-mobile] .bar .stepper { background: transparent; border-color: transparent; }
  :root[data-mobile] .stepper .ctl.tiny {
    width: var(--m-ctl);
    min-width: var(--m-ctl);
    height: 100%;
    min-height: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    color: var(--ink);
    /* Centred as a BOX, not on a baseline: the minus glyph and the plus glyph sit at different
       heights in every UI face, and lining them up by line-height is how one of them ends up a
       pixel low forever. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: var(--t-xl);
    font-weight: var(--w-medium);
    /* One glyph, so there is nothing for tracking to sit between - said rather than left blank,
       because a display size with no tracking declared is usually an oversight. */
    letter-spacing: var(--tr-none);
    line-height: 1;
    padding: 0;
  }
  :root[data-mobile] .stepper .ctl.tiny:active { background: var(--sel); }
  :root[data-mobile] .stepper .val {
    /* Forty, like the two buttons it sits between. It was 38 - two points short of the floor,
       for no reason but that 40+40+40 was three points over the budget at 360 - which meant the
       one control in the whole toolbar that failed the audit was the one this build added. The
       three points come out of the bar's padding instead; the instrument yields for the rest. */
    width: var(--m-ctl);
    min-width: var(--m-ctl);
    height: 100%;
    border: 0;
    border-radius: 0;
    background: transparent;
    color: var(--ink);
    text-align: center;
    /* 16px, because iOS Safari zooms the whole page into any focused field smaller than that -
       and a chart that zooms itself when you touch the tick box is the "it glitches" report. */
    font-size: var(--t-input-touch);
    font-weight: var(--w-medium);
    padding: 0;
  }
  /* The number spinners are a mouse affordance and they steal a third of a 40pt field. */
  .stepper .val::-webkit-outer-spin-button,
  .stepper .val::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
  .stepper .val { -moz-appearance: textfield; appearance: textfield; }
  /* The grid cannot always deliver the row that was asked for. The desktop says so with a
     tooltip, which a phone has no way to show, so the field says it in ink. */
  :root[data-mobile] .stepper .val.clamped { color: var(--sell-hi); }
  /* On a phone this field is a DOOR - readOnly, opening the row-size sheet - so it must not wear
     a text field's focus ring after being pressed. Nothing is being typed into it. */
  :root[data-mobile] .stepper .val:focus { background: transparent; outline: none; }

  #themeBtn { flex: 0 0 auto; }

  /* --------------------------------------------------------- the view bar */

  /* One row of what is open, and the "+" that opens more. See mobile-chrome.js for why a phone
     answers "chart beside the feed" in time rather than in space.

     It sits ABOVE the toolbar because the toolbar describes the ACTIVE tab - the symbol, the
     period and the tick belong to whichever of these rows is lit, and a control row that floats
     above the thing it controls is the one arrangement that never reads right. */
  .mtabs {
    display: flex;
    align-items: stretch;
    gap: 4px;
    height: calc(var(--m-ctl) + 6px);
    padding: 0 6px;
    background: var(--panel);
    border-bottom: 1px solid var(--line);
  }
  .mtabs-scroll {
    display: flex;
    align-items: stretch;
    gap: 4px;
    flex: 1 1 auto;
    min-width: 0;
    overflow-x: auto;
    scrollbar-width: none;
    /* A tab is a destination, so a flick lands ON one rather than between two. */
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
  }
  .mtabs-scroll::-webkit-scrollbar { display: none; }
  /* THE EDGES SAY THERE IS MORE. Six tabs is 637 points of content in a 290-point track, and a
     tab sliced flat by the edge of a row reads as a tab that was cut off rather than one that is
     scrolled. Eight points of fade on each end is the standard cue and costs nothing: the mask
     is on the scroller, so it travels with the content rather than sitting over it as a box that
     would eat a press. */
  .mtabs-scroll {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 8px, #000 calc(100% - 8px), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, #000 8px, #000 calc(100% - 8px), transparent 100%);
  }
  .mtab {
    flex: 0 0 auto;
    scroll-snap-align: start;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: var(--m-ctl);
    align-self: center;
    max-width: 46vw;
    padding: 0 10px;
    border: 0;
    border-radius: var(--r-ctl);
    background: transparent;
    color: var(--ink-dim);
    font: var(--w-medium) var(--t-md)/1 var(--ui);
    letter-spacing: var(--tr-ticker);
    cursor: pointer;
    /* The whole row is a scroller, so a press must not be read as the start of one. */
    touch-action: manipulation;
  }
  .mtab svg { flex: 0 0 auto; opacity: .75; }
  .mtab.on { background: var(--sel); color: var(--ink); }
  .mtab.on svg { opacity: 1; }
  .mtab:active { background: var(--panel-2); }
  /* The period chip, as the desktop strip carries it: two tabs on one instrument differ by
     exactly this, and reading it out of the middle of a label takes a beat a chip does not. */
  .mtab-tf {
    flex: 0 0 auto;
    color: var(--ink-faint);
    font-size: var(--t-xs);
    font-weight: var(--w-semi);
    letter-spacing: var(--tr-ticker);
  }
  .mtab.on .mtab-tf { color: var(--ink-dim); }
  .mtab-label {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* On the active tab only - six crosses in a 390pt row is six ways to lose a view you cannot
     see by a thumb's width. A real target, inset into the tab rather than hung off it. */
  .mtab-x {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    margin-right: -6px;
    border-radius: var(--r-surface);
    color: var(--ink-faint);
  }
  .mtab-x:active { background: var(--line-2); color: var(--ink); }
  .mtabs-btn {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--m-ctl);
    align-self: center;
    height: var(--m-ctl);
    border: 0;
    border-radius: var(--r-ctl);
    background: transparent;
    color: var(--ink-dim);
    cursor: pointer;
  }
  .mtabs-btn:active { background: var(--sel); }
  /* THE PAIR AT THE END OF THE ROW is one control with two halves - add a surface, or open the
     menu - welded the way the tick stepper is, with a hairline between them. Two loose identical
     icon buttons a thumb's width apart is two ways to press the wrong one. */
  .mtabs-add { border-radius: var(--r-ctl) 0 0 var(--r-ctl); }
  .mtabs-more { border-radius: 0 var(--r-ctl) var(--r-ctl) 0; box-shadow: inset 1px 0 0 var(--line); }
  /* It leads the column and never shrinks. `order` because body.panel is a flex column that
     places its children by number (see style.css) - the markup order alone would put it after
     the strip it replaces, which is fine, but leaving the number implicit is how a band ends up
     below the plot the day somebody adds another. */
  body.panel > .mtabs { order: 0; flex: 0 0 auto; }

  /* ------------------------------------------------------- the sheet grammar
   *
   * ONE bottom sheet, shared by the "+" menu, the hub and everything else on this device that
   * asks a question with a list of answers. See mobile-chrome.js for why a LIST beat the grid of
   * tiles the hub shipped as: twenty tiles at 135 points is three screens to reach Theme, and a
   * two-column grid is the one layout where looking for a word means reading in a zig-zag.
   *
   * The material matches the window sheets already here - glass, a grab bar, the platform's own
   * spring - because two kinds of sheet on one phone is two things to learn. */
  .msheet-back {
    position: fixed;
    inset: 0;
    z-index: 60;
    background: rgba(0, 0, 0, .38);
    animation: msheet-dim .2s ease;
  }
  @keyframes msheet-dim { from { opacity: 0; } to { opacity: 1; } }
  .msheet {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 61;
    display: flex;
    flex-direction: column;
    max-height: min(84%, 680px);
    border-radius: 18px 18px 0 0;
    border-top: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
    /* PLAIN, not glass - owner ask: "weird glass effect ... pls remove, make it plain".
       The sheet is an opaque surface; nothing behind it shows through. */
    background: var(--bg);
    box-shadow: 0 -12px 40px rgba(0, 0, 0, .38);
    animation: msheet-up .3s cubic-bezier(.32, .72, 0, 1);
  }
  @keyframes msheet-up { from { transform: translateY(100%); } to { transform: translateY(0); } }
  .msheet-grip {
    flex: 0 0 auto;
    width: 36px;
    height: 4px;
    margin: 7px auto 0;
    border-radius: 2px;
    background: var(--line-2);
  }
  .msheet-head {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 12px 8px 16px;
  }
  .msheet-title {
    margin: 0;
    font-size: var(--t-xl);
    font-weight: var(--w-semi);
    letter-spacing: var(--tr-tight);
    color: var(--ink);
  }
  .msheet-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border: 0;
    /* No disc behind the x: a circle drawn around a close glyph is furniture on furniture, and
       the grip above the sheet already says this surface floats. The 34px box stays - it is the
       touch target, not the picture. */
    border-radius: 0;
    background: transparent;
    color: var(--ink-dim);
    cursor: pointer;
  }
  .msheet-close:active { background: var(--sel); color: var(--ink); }
  .msheet-body {
    flex: 1 1 auto;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    /* The home indicator is a real 34pt strip on every phone shipped since 2017, and a row that
       ends under it is a row you cannot press. */
    padding-bottom: calc(10px + env(safe-area-inset-bottom));
  }
  .msheet-section {
    padding: 10px 16px 4px;
    color: var(--ink-faint);
    font-size: var(--t-xs);
    font-weight: var(--w-semi);
    letter-spacing: var(--tr-label);
  }
  .msheet-list { display: flex; flex-direction: column; }
  .msheet-row {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    min-height: 46px;
    padding: 0 16px;
    border: 0;
    border-radius: 0;
    background: transparent;
    color: var(--ink);
    font: var(--w-medium) var(--t-lg)/var(--lh-tight) var(--ui);
    text-align: left;
    cursor: pointer;
  }
  /* A hairline BETWEEN rows, not around them: the list is the surface. Boxes round every row is
     the look people mean by generated - it states that each row is a separate object when what
     is actually true is that they are one list. */
  .msheet-row + .msheet-row { box-shadow: inset 0 1px 0 var(--line); }
  .msheet-row:active { background: var(--sel); }
  .msheet-row svg { flex: 0 0 auto; color: var(--ink-dim); }
  .msheet-row.on svg { color: var(--ink); }
  .msheet-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* What it is set to, on the row that sets it - so the answer to "what theme am I on" is read
     rather than opened. */
  /* A row with no glyph starts where the glyphs' labels start, so a list of plain rows and a
     list of icon rows do not read as two different lists. */
  .msheet-row-plain { padding-left: 45px; }
  .msheet-value {
    flex: 0 0 auto;
    color: var(--ink-dim);
    font-size: var(--t-md);
    font-weight: var(--w-medium);
  }

  /* ---------------------------------------------------------- tool rail */

  /* Off-canvas until the Draw button asks for it, then an overlay panel with real targets.
     A 26px always-on gutter was unusable AND stole a tenth of a phone's width. */
  .rail { display: none; }
  /* ...so the boot skeleton must not draw one either, or the layout it promises is a layout the
     phone never lands on. Same reason its axis band narrows: the skeleton is only worth anything
     if the first real frame arrives on exactly the geometry it drew. */
  .boot-rail { display: none; }
  .boot-axis { flex-basis: 60px; }
  body.show-rail .rail {
    display: flex;
    position: fixed;
    left: 8px;
    top: 15%;
    bottom: auto;
    max-height: 70%;
    /* Wide enough for the 44px row the palette's buttons now sit in - the desktop column is 52
       and this panel used to be 46, which cropped the ground the buttons paint. */
    width: 52px;
    padding: 4px 0;
    background: var(--panel);
    border: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
    border-radius: var(--r-surface);
    box-shadow: 0 8px 30px rgba(0, 0, 0, .3);
    overflow-y: auto;
    z-index: 45;
  }
  body.show-rail .rail button { border-radius: var(--r-ctl); }

  /* --------------------------------------------------------- bottom bar */

  .mobilebar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    display: flex;
    align-items: stretch;
    justify-content: space-around;
    padding: 6px 4px calc(6px + env(safe-area-inset-bottom));
    /* PLAIN, not glass - the bar is an opaque strip, the way the reference's own bar reads. */
    background: var(--bg);
    border-top: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
  }
  .mobilebar[hidden] { display: none; }
  /* The reference's strip exactly: symbol and interval as text, then icon buttons, a hairline
     divider, undo and fullscreen at the end. No labels under icons - the strip is compact and
     the icons are the reference's own vocabulary. */
  /* ONE number for the bar's height, because two places need it and they had drifted.
   *
   * 8 + 40 + 8 is 56, and the stage below reserved 46 - so the last ten points of the chart lived
   * underneath the bar, which is exactly where the time axis is. Every phone screenshot had its
   * clock labels sliced through the middle. Deriving both from the same custom property is the
   * only version of this that cannot come apart again. */
  .mobilebar { --mb-pad: 8px; --mb-btn: 40px; }
  .mobilebar {
    gap: 2px;
    padding: var(--mb-pad) 0 calc(var(--mb-pad) + env(safe-area-inset-bottom));
    /* A ROW THAT SCROLLS, AND AN END THAT DOES NOT.
     *
     * The bar carries the instrument, the period, the tick and five verbs now - more than a
     * 390-point phone fits, and the reference's own answer is to scroll it. The navigation
     * cluster is the exception: undo and full screen are reached without looking, and a row
     * that can scroll can scroll them out of the world, so they sit against the right edge on
     * the bar's own glass with the rest passing underneath. */
    justify-content: flex-start;
    align-items: center;
  }
  .mb-scroll {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 1 1 auto;
    min-width: 0;
    padding: 0 4px 0 10px;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .mb-scroll::-webkit-scrollbar { display: none; }
  /* NO SNAPPING. A proximity snap looks tidy in a mock and lies about where the row is: the
     browser settles on the nearest control, which on a first paint parked the instrument half
     off the left edge - a bar that had quietly lost the control it exists for. A strip of
     controls is not a carousel. */
  .mb-scroll > * { flex: 0 0 auto; }
  /* THE WHEEL CHIPS OWN THEIR WHOLE TOUCH (owner 20b: "hold down on tick or time frame and I
     scroll down, it'll scroll left to right - that shouldn't be possible"; owner 20q, again,
     angrier). pan-y was not enough: a sideways drift is tested against each ancestor as the
     touch bubbles, and the ROW's own scroller still claimed it - the bar slid under the thumb.
     `none` ends the argument: a touch born on one of the three wheels is never a scroll for
     any element, the app's own door takes it whole, and the row itself stays scrollable from
     every other control and gap. */
  #symBtn, #tfBtn, .mb-scroll .stepper { touch-action: none; }
  /* THE ROW HAS TO BE ABLE TO CLEAR THE PINNED CLUSTER. The mb-end gradient means whatever
     scrolls past goes under glass - right, as long as "past" is voluntary. But the scrollable
     range used to end where the cluster begins, so the last icon parked permanently underneath
     the fade, half-visible and untouchable: the row looked truncated rather than scrollable.
     A right padding the width of the cluster lets the final icon scroll fully into the clear,
     and the mask below is the affordance the fade alone never was. */
  .mb-scroll {
    padding-right: 10px;
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-l, 0px));
    mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-l, 0px));
  }
  /* The left hairline of glass appears only once the row has moved: at rest the bar reads as a
     bar, mid-scroll it explains itself - there is more to the right, and this is where it went. */
  .mb-scroll.scrolled { --fade-l: 18px; }
  /* THE PINNED START, welded from INSIDE the scroller: the instrument and the period never
     slide - the two things that name the chart are reached without looking. `position: sticky`
     pins them to the left edge of the row while the verbs scroll beneath, and - the reason this
     replaced a pinned sibling - the end cluster is no longer squeezed off a 390-point screen by
     the start's width. The ground is opaque so content passing under never shows through. */
  .mb-start {
    position: sticky;
    left: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    /* THREE GLUED, HUGGING: pair, period and tick. No gap of the row's own - the groups carry
       just their internal padding, so the cluster reads as one instrument-and-size control, and
       the frame is as narrow as the chips themselves allow. */
    gap: 0;
    flex: 0 0 auto;
    padding: 0 6px 0 4px;
    background: var(--bg);
    box-shadow: 10px 0 12px -8px color-mix(in srgb, var(--bg) 90%, transparent);
  }
  /* THE END CLUSTER RIDES THE ROW: more, undo and full screen are reached by scrolling right,
     which is the behaviour he asked for by name. A hairline on its left says where the verbs
     stop and the navigation begins. */
  .mb-end {
    display: flex;
    align-items: center;
    gap: 2px;
    flex: 0 0 auto;
    padding: 0 6px 0 4px;
    margin-left: 4px;
    border-left: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
  }
  .mobilebar button {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 42px;
    height: 40px;
    padding: 0 8px;
    border: 0;
    border-radius: var(--r-surface);
    background: transparent;
    color: var(--ink);
    font: var(--w-semi) var(--t-lg) var(--ui);
    cursor: pointer;
  }
  /* The two TEXT buttons are the ones that give way. Symbols like 1000PEPEUSDT pushed undo and
     fullscreen off a 390pt screen entirely, because every button was flex: 0 0 auto with a 44px
     floor and the bar had no overflow. The icons stay fixed; the words truncate. */
  .mobilebar .mb-text {
    font-weight: var(--w-semi);
    letter-spacing: var(--tr-tight);
    flex: 0 1 auto;
    min-width: 0;
    max-width: 32vw;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .mobilebar button:active { background: color-mix(in srgb, var(--ink) 8%, transparent); }

  /* THE THREE THAT MOVED, wearing this row's face rather than the toolbar's.
   *
   * Same elements, same ids, same handlers - see the note in app.js. What changes is the seat:
   * the height this row uses, its corner, and a ground quiet enough that eight controls in a
   * line do not read as eight buttons.
   *
   * WRITTEN `:root[data-mobile]` FOR A REASON. The rules these have to beat are the old phone
   * toolbar's, and those are written that way too - `:root[data-mobile] .stepper .ctl.tiny` is
   * five specificity points and a plain `.mobilebar .stepper .ctl.tiny` is four, so the plain
   * version silently lost and the row kept a stepper this block had already deleted. Anything
   * here that contradicts the toolbar block above must out-rank it, not merely follow it. */
  :root[data-mobile] .mobilebar .sym-group,
  :root[data-mobile] .mobilebar .tf-group,
  :root[data-mobile] .mobilebar .stepper {
    flex: 0 0 auto;
    width: auto;
    height: 40px;
    align-items: stretch;
    border-radius: var(--r-touch);
    background: color-mix(in srgb, var(--ink) 6%, transparent);
    overflow: hidden;
  }
  :root[data-mobile] .mobilebar .sym-group .ctl,
  :root[data-mobile] .mobilebar .tf-group .ctl,
  :root[data-mobile] .mobilebar .stepper .ctl {
    height: 40px;
    min-height: 40px;
    border: 0;
    border-radius: 0;
    background: transparent;
    font: var(--w-semi) var(--t-lg) var(--ui);
  }
  /* The venue rides inside the instrument on the desktop; on a phone it is the fact you are
     least likely to be checking and the first thing worth the room. It is on the symbol sheet. */
  :root[data-mobile] .mobilebar #venueBtn { display: none; }
  :root[data-mobile] .mobilebar #symBtn {
    flex: 0 0 auto;
    padding: 0 12px;
    max-width: 34vw;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  :root[data-mobile] .mobilebar .tf-group .tf-quick { display: none; }
  /* NO CARET on the period chip: the drum is the affordance, and the arrow read as a second
     control the owner never asked for - "remove the arrow next to the timeframe selector". */
  :root[data-mobile] .mobilebar #tfBtn svg { display: none; }
  :root[data-mobile] .mobilebar #tfBtn { min-width: 52px; justify-content: center; padding: 0 10px; }

  /* THE TICK IS ONE CHIP, NOT A STEPPER, on a phone.
   *
   * The minus and plus are seventy points of a 390-point row for one step each, and the ladder
   * they step through is already a sheet: every legal rung, at thumb size, one tap away - which
   * is the better control here anyway, because most numbers between two rungs are not legal (see
   * the readOnly note in app.js). Losing them buys the four verbs their place on the row. The
   * word stays: a bare "100" beside a period is a number with no unit. */
  :root[data-mobile] .mobilebar .stepper .ctl.tiny { display: none; }
  :root[data-mobile] .mobilebar .stepper .cap {
    display: inline-flex;
    align-items: center;
    padding: 0 0 0 10px;
    color: var(--ink-dim);
    font: var(--w-med) var(--t-md) var(--ui);
  }
  :root[data-mobile] .mobilebar .stepper .cap .lbl { display: none; }
  :root[data-mobile] .mobilebar .stepper .val {
    width: 48px;
    height: 40px;
    border: 0;
    background: transparent;
    text-align: center;
    font: var(--w-semi) var(--t-lg) var(--ui);
    color: var(--ink);
  }

  /* the stage keeps clear of the bar: 8 + 40 + 8, the same arithmetic the bar itself uses */
  .stage { padding-bottom: calc(56px + env(safe-area-inset-bottom)); }
  /* ---------------------------------------------------------- bar replay

     ITS CONTENTS WERE 610 POINTS WIDE ON A 390-POINT PHONE, and the consequence was not a
     clipped control - it was the whole application shrinking. Five transport buttons, a
     separator, a 64pt speed picker, an 80pt scrubber, a 150pt clock and a stop button sum past
     the screen, so the LAYOUT VIEWPORT grew to 610 to hold them and the browser scaled the page
     down to fit: every band, every glyph, every cell, at 64% - and the thumb bar, `position:
     fixed` against a viewport that was now 1,321 points tall, rendered four hundred points below
     the bottom of the screen where nothing could reach it. That is one CSS row overflowing and
     the entire phone build reporting as broken.

     Two rows. The scrubber and the clock lead, because a replay is driven by where you are in
     it; the transport follows underneath at thumb size. */
  #replayBar .rb {
    flex-wrap: wrap;
    justify-content: center;
    height: auto;
    row-gap: 2px;
    padding: 4px 8px calc(4px + env(safe-area-inset-bottom));
  }
  /* The scrubber's flex-basis is what FORCES the break: at 55% of the row it plus the clock
     leave less than a transport button's width, so the first one wraps and the two of them have
     the line to themselves. A wrapping flex row has no other way to say "break here" without an
     element to put the break on, and these children are built in JS. */
  .rb-scrub { order: -2; flex: 1 1 55%; min-width: 0; }
  .rb-pos {
    order: -1;
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .rb-btn { min-width: var(--m-ctl); height: 36px; }
  .fld-pick.rb-speed { height: 36px; }
  /* The separator either side of the scrubber has nothing to separate once they are on their own
     line, and a hairline hanging at the end of a row reads as a control that failed to draw. */
  #replayBar .rb-sep { display: none; }

  /* THE TRANSPORT IS THE THUMB BAR while a replay is running. Draw, chart style, indicators,
     undo and full screen are all secondary to play/step/stop for as long as you are watching a
     day print again - and the action bar is `position: fixed` at bottom: 0, so left standing it
     covers the transport's own second row. The menu is still one tap away on the view bar, which
     is drawn on every surface. The stage gives back the room it was reserving for it. */
  body.replay-on .mobilebar { display: none; }
  body.replay-on .stage { padding-bottom: 0; }

  /* ------------------------------------------------------------- sheets */

  /* Every window is a bottom sheet: full width, rounded top, a grab bar, the content scrolling
     inside. The curve is the platform's own sheet spring - the one number that makes a surface
     read as iOS rather than as a div that moved. Wins over the native chrome's transition:none
     because two classes out-rank an attribute-and-star. */
  .win.sheet {
    left: 0 !important;
    right: 0 !important;
    bottom: 0 !important;
    top: auto !important;
    width: 100% !important;
    /* IT IS AS TALL AS WHAT IS IN IT, up to a ceiling.
     *
     * This was a flat `height: min(82%, 640px)`. Every sheet in the application therefore opened
     * at the same height whatever it held, and most of them hold three rows: the instrument
     * picker came up as three lines of text and four hundred points of empty ground, the row-size
     * ladder the same, the alert dialog the same. A surface that is always the same size
     * regardless of its contents is the single most reliable sign that nobody looked at it, and
     * it is most of what "AI generated" means when someone says it about a layout.
     *
     * `height: auto` with a `max-height` ceiling: short sheets shrink to their rows, long ones
     * stop at 88% and scroll inside. The body is what scrolls, which is why it gets the min-height
     * of zero - without it a flex child refuses to shrink below its content and the ceiling does
     * nothing. */
    height: auto !important;
    max-height: min(88%, 680px);
    border: 0;
    border-top: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
    border-radius: 18px 18px 0 0 !important;
    /* PLAIN, not glass - owner ask. An opaque sheet; the chart behind it stays behind it. */
    background: var(--bg);
    box-shadow: 0 -12px 40px rgba(0, 0, 0, .35);
    animation: sheet-up .32s cubic-bezier(.32, .72, 0, 1);
  }
  .win.sheet { display: flex; flex-direction: column; }
  .win.sheet .win-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }
  .win.sheet .win-head, .win.sheet .win-foot { flex: 0 0 auto; }
  .win.sheet .win-body, .win.sheet .win-foot { background: transparent; }
  @keyframes sheet-up {
    from { transform: translateY(100%); }
    to { transform: translateY(0); }
  }
  .win.sheet .win-head {
    position: relative;
    height: 44px;
    align-items: center;
    justify-content: center;
    background: transparent;
    border-radius: 16px 16px 0 0;
  }
  /* the grab bar, centred above the title, as every iOS sheet wears it */
  .win.sheet .win-head::before {
    content: '';
    position: absolute;
    top: 6px;
    left: 50%;
    transform: translateX(-50%);
    width: 36px;
    height: 4px;
    border-radius: 2px;
    background: var(--line-2);
  }
  .win.sheet .win-title {
    padding-top: 8px;
    font-size: var(--t-lg);
    font-weight: var(--w-semi);
    text-align: center;
  }
  /* collapse and maximise are desktop verbs; a sheet has one verb, and it is the close button */
  .win.sheet .win-btn { display: none; }
  .win.sheet .win-close {
    display: inline-flex;
    position: absolute;
    right: 8px;
    top: 8px;
    width: 34px;
    height: 34px;
    border-radius: 17px;
    background: var(--panel-2);
    font-size: var(--t-lg);
  }
  .win.sheet .win-grip { display: none; }
  .win.sheet .win-foot { padding-bottom: calc(8px + env(safe-area-inset-bottom)); }

  /* sheets are stacked navigation, not tiled windows - dim what is underneath */
  .win.sheet::backdrop { background: rgba(0, 0, 0, .3); }

  /* ------------------------------------------------- period sheet layout */

  /* The three columns share the width evenly and the options move below them; a 720px-wide
     desktop layout inside a 390px sheet was three unreadable slivers. */
  .pp-body { flex-direction: column; overflow-y: auto; }
  .pp-cols { flex: 0 0 auto; }
  .pp-col { width: 0; flex: 1 1 0; }
  .pp-list { max-height: 240px; }
  .pp-opts { overflow: visible; }

  /* ----------------------------------------------------- sheet anatomy v2 */

  /* The reference sheet's anatomy, from its own screenshots: a LEFT-aligned title in large bold
     type under the grab bar, the close button a grey circle at the right, content padded 16px,
     and the platform type stack - on iOS "Segoe UI" would render as a lie anyway. */
  .win.sheet { --ui: -apple-system, BlinkMacSystemFont, "Segoe UI Variable Text", "Segoe UI", system-ui, sans-serif; }
  .win.sheet .win-head {
    height: 58px;
    justify-content: flex-start;
    padding: 14px 56px 0 16px;
  }
  .win.sheet .win-title {
    padding-top: 6px;
    font-size: var(--t-2xl);
    font-weight: var(--w-semi);
    letter-spacing: var(--tr-tight);
    text-align: left;
  }
  .win.sheet .win-body { padding: 4px 0 0; }

  .sheet-dim {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, .35);
    animation: dim-in .25s ease;
  }
  @keyframes dim-in { from { opacity: 0; } to { opacity: 1; } }

  /* -------------------------------------- indicators menu, on a phone */

  /* Big rounded search, generous rows with the name over the note, hairline separators and no
     boxes - the list IS the surface. */
  .im-searchwrap { margin: 6px 16px 12px; }
  /* The magnifier is absolutely positioned INSIDE the field, so the field's left padding has to
     clear it. The phone rule reset the padding to a flat 14px and the placeholder came out
     underneath the glyph - two pieces of type in the same square, which is what the last build's
     search box actually was. */
  .im-search {
    margin: 0;
    height: 44px;
    border-radius: var(--r-touch);
    border: 0;
    background: var(--panel-2);
    font-size: var(--t-input-touch);
    padding: 0 14px 0 40px;
  }
  /* The field also carries the .search class, and the tv appearance's
     :root[data-appearance="tv"] .search reset outranks a bare .im-search - so the phone fill
     has to be lifted above it, not restated. */
  :root[data-mobile] .win .im-search { background: var(--panel-2); }
  .im-searchwrap > svg { left: 14px; }
  /* THE CATEGORY CHIPS WERE ONE CHIP.
   *
   * .im-cat is `width: 100%` because on a desktop it is a row in a 156px left rail. Turning the
   * rail into a horizontal strip here changed the direction and not the width, so the first chip
   * took the whole strip and the other seven queued off the right edge - which is why every
   * phone screenshot of this window shows a full-width "All" and half of "Classic". A chip
   * sizes to its own word. */
  .im-side {
    display: flex;
    flex: 0 0 auto;
    flex-direction: row;
    gap: 6px;
    overflow-x: auto;
    scrollbar-width: none;
    padding: 0 16px 12px;
    border: 0;
  }
  .im-side::-webkit-scrollbar { display: none; }
  /* 21a: the strip scrolls but nothing said so - "Lev" ended in a hard clip at the screen edge.
     A fade on both edges names the scroll the way every native chip row does. */
  .im-side {
    -webkit-mask-image: linear-gradient(to right, transparent, #000 20px, #000 calc(100% - 24px), transparent);
    mask-image: linear-gradient(to right, transparent, #000 20px, #000 calc(100% - 24px), transparent);
  }
  .im-cathead { display: none; }
  .im-cat {
    flex: 0 0 auto;
    width: auto;
    height: var(--m-ctl);
    padding: 0 14px;
    border: 0;
    border-radius: 20px;
    background: var(--panel-2);
    color: var(--ink-dim);
    font-size: var(--t-md);
    font-weight: var(--w-semi);
  }
  /* The icon is a second thing to read in a control whose whole content is one word, and eight
     of them in a row that has to scroll is eight words of room spent on decoration. */
  .im-cat svg { display: none; }
  .im-cat.on { background: var(--sel); color: var(--ink); }
  .im-split { flex-direction: column; }
  .im-list { padding: 0; }

  /* The heading over "what is already on" - see indicators-menu.js. */
  .im-group {
    padding: 12px 16px 6px;
    color: var(--ink-faint);
    font-size: var(--t-xs);
    font-weight: var(--w-semi);
    letter-spacing: var(--tr-label);
  }

  .im-row {
    min-height: 58px;
    padding: 8px 16px;
    border: 0;
    border-bottom: 1px solid var(--line);
    border-radius: 0;
    align-items: center;
  }
  .im-row:last-child { border-bottom: 0; }
  .im-name { font-size: var(--t-lg); font-weight: var(--w-semi); }
  /* THE REFERENCE'S INDICATORS SHEET IS A LIST OF NAMES, full stop. The right-edge mono code and
     the boxed gear are desktop furniture - on a phone they are two more objects in every row.
     The checkmark moves to the far right, which is where the eye looks for on/off. */
  .im-abbrev { display: none; }
  .im-tick { order: 1; width: auto; flex: 0 0 auto; }
  .im-gear {
    opacity: 1;
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 18px;
    background: transparent;
  }

  /* ------------------------------------------------------- theme, on a phone */

  /* IT WAS THE DESKTOP PANEL, SIDEWAYS, IN A 390-POINT SHEET: four preset buttons on one line,
     then two scrolling columns side by side - Typography on the left and twenty-one theme packs
     on the right, each column 170 points wide with 33-point rows inside a bordered box inside a
     scroller inside a sheet. Three nested scrolls to change the theme, and this is the surface
     people said they could not find or use.
     
     Stacked, at row height, with each pack showing its own colours. */
  .th-root { gap: 4px; padding: 0; }
  .th-quick {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    padding: 4px 16px 10px;
  }
  .th-preset { height: 44px; font-size: var(--t-lg); border-radius: var(--r-surface); }
  /* One column, in the order the questions are asked: the theme first, because that is what the
     sun on the toolbar was pressed for, and the type set after. */
  .th-split { flex-direction: column; gap: 0; overflow-y: auto; }
  .th-col { flex: 0 0 auto; }
  .th-col:first-child { order: 2; }
  .th-head {
    padding: 14px 16px 6px;
    color: var(--ink-faint);
    font-size: var(--t-xs);
    font-weight: var(--w-semi);
    letter-spacing: var(--tr-label);
    text-transform: uppercase;
  }
  /* The bordered inner box and its own scrollbar go: the sheet already scrolls, and a list that
     scrolls inside a surface that scrolls is the single most reliable way to trap a thumb. */
  .th-list {
    border: 0;
    background: transparent;
    padding: 0;
    gap: 0;
    overflow: visible;
    min-height: 0;
  }
  .th-item {
    min-height: 52px;
    padding: 8px 16px;
    border-radius: 0;
    font-size: var(--t-lg);
  }
  .th-item + .th-item { box-shadow: inset 0 1px 0 var(--line); }
  .th-item-name { font-size: var(--t-lg); }
  .th-item-blurb { font-size: var(--t-md); }
  .th-swatch { width: 44px; height: 26px; }
  .th-foot { padding: 8px 16px calc(8px + env(safe-area-inset-bottom)); }

  /* ------------------------------------------------ settings rows on phone */

  /* ------------------------------------- a settings window's page list, on a phone

     THE NAV IS A COLUMN, AND A COLUMN COSTS THE WHOLE SCREEN HERE. Every indicator's own window
     is a 176-point page list beside its content: on a 390-point sheet that leaves 214 for the
     content, so Auto-Markers - nine pages of it - printed its sentences four words to a line
     beside a rail of nine one-word rows. It is the clearest example in the build of a desktop
     layout put on a phone rather than a phone layout.

     Across the top instead, as a strip of chips that scrolls sideways: the same nine pages, one
     tap each, and the content gets all 390. It is the same shape the indicator menu's categories
     wear and the same shape the wire's do, so the third list a reader meets is one they already
     know how to drive. */
  .set-split, .ind-split { flex-direction: column; min-height: 0; }
  .set-nav {
    flex: 0 0 auto;
    flex-direction: row;
    gap: 6px;
    padding: 8px 16px;
    border-right: 0;
    border-bottom: 1px solid var(--line);
    background: transparent;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    /* The strip is the thing that scrolls sideways; the page under it must not. */
    scroll-snap-type: x proximity;
  }
  .set-nav::-webkit-scrollbar { display: none; }
  .set-navitem {
    flex: 0 0 auto;
    scroll-snap-align: start;
    height: 34px;
    padding: 0 14px;
    border-radius: 17px;
    background: var(--panel-2);
    font-size: var(--t-md);
    font-weight: var(--w-semi);
  }
  .set-navitem.on { background: var(--sel); color: var(--ink); }
  /* The chevron and the star that mark a group in the desktop tree have no column to sit in
     here, and a caret on a chip points at nothing. */
  .set-navitem > svg:first-child { display: none; }
  .set-pane, .ind-split .set-pane { padding: 0; min-width: 0; }
  .fld {
    min-height: 52px;
    padding: 6px 16px;
    border-bottom: 1px solid var(--line);
    margin: 0;
  }
  .fld:last-child { border-bottom: 0; }
  .fld-label { font-size: var(--t-lg); }
  .fld-help { font-size: var(--t-md); }
  .set-foot { padding: 10px 16px calc(10px + env(safe-area-inset-bottom)); }

  /* -------------------------------------------------- period sheet rows */

  .pp-head {
    text-align: left;
    padding-left: 4px;
    font-size: var(--t-md);
    font-weight: var(--w-semi);
    letter-spacing: var(--tr-label);
    text-transform: uppercase;
    color: var(--ink-faint);
  }
  .pp-item { height: 38px; border-radius: var(--r-ctl); font-size: var(--t-lg); }
  .pp-olabel { font-size: var(--t-lg); }
  .pp-orow { min-height: 44px; font-size: var(--t-lg); }

  /* ---------------------------------------------------- targets, not slivers
   *
   * touch.css floors every control at 30 points for a coarse pointer, which is the right number
   * for a dense desktop panel being used with a finger and the wrong one for the five controls a
   * phone chart is actually driven by. Apple's floor is 44 and TradingView's own phone header
   * sits at about 40; these are the buttons a thumb hits fifty times a session, so they get it.
   * Nothing else grows - a phone that made every control 44 points would fit four of them. */
  header.bar { min-height: calc(var(--m-ctl) + 12px); }
  header.bar .ctl { min-height: var(--m-ctl); }
  header.bar .ctl.tf-caret { min-width: 54px; justify-content: center; }
  header.bar .ctl.icon { min-width: var(--m-ctl); justify-content: center; }
  .hub-close { width: 40px; height: 40px; }

  /* ------------------------------------------------ the object tree, and the drawing bar

     A PHONE HAS NO RIGHT-CLICK, so the object tree is its only route to a drawing's colour, its
     width and its delete - which makes it a primary surface here and not the side panel it is on
     a desktop. At 226 points docked to the left of a 390-point stage it left the chart 164, with
     the drawing palette floating on top of it and the one row it was opened to show reading
     "to 77329..." from behind a tool rail.

     Full width, over the stage. And the palette stands down with it: two overlays claiming the
     same corner is the shape of "I opened something and something else was in the way". */
  .objpanel {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    /* NOT `bottom: 0`. An absolutely positioned child resolves against its container's PADDING
       box, and the stage's padding-bottom is the room the action bar stands in - so bottom: 0 put
       the panel's own footer, which is the Delete all button, underneath the thumb bar. */
    bottom: calc(56px + env(safe-area-inset-bottom));
    z-index: 30;
    width: auto;
    border-right: 0;
  }
  body.show-obj .rail { display: none; }
  .ot-row { min-height: 48px; }
  .ot-head { min-height: var(--m-ctl); padding: 0 8px 0 16px; }
  .ot-head .ot-btn { min-width: var(--m-ctl); min-height: var(--m-ctl); }
  .ot-title { font-size: var(--t-lg); font-weight: var(--w-semi); }
  .ot-row { padding: 0 8px 0 16px; }
  .ot-foot .btn, .objpanel .btn { min-height: var(--m-ctl); }

  /* The floating drawing palette must fit the screen it floats over. Its left edge is clamped in
     JS; this is the other half - a bar wider than the phone cannot be positioned into it. */
  .dw-bar { max-width: calc(100vw - 8px); overflow-x: auto; scrollbar-width: none; }
  .dw-bar::-webkit-scrollbar { display: none; }
  .dw-btn { min-height: 34px; }

  /* ------------------------------------------------------------- immersive

     What the action bar's full-screen verb actually does here. Browser fullscreen on a phone
     hides the address bar and nothing else, and on iPhone the API does not exist at all - so the
     control was doing almost nothing on Android and literally nothing on iOS. The view bar and
     the toolbar stand down and the chart takes their 86 points, which is about a fifth more
     chart on an 844-point screen.

     The action bar STAYS. It is what you press to come back, and a mode you can enter and not
     leave is not a mode - it is a trap. */
  body.immersive > .mtabs,
  body.immersive > header.bar { display: none; }

  /* The floating "Now" chip is gone - owner's call, it sat in the way. "Jump to now" lives on
     in the hub, which is where a verb you need once in a while belongs. */

  /* --------------------------------------------- context menus, as sheets

     Every picker in this application - cluster type, cluster text, candle type, the axis menu,
     the tab menu, the ladder's own settings - is one call to showMenuAt, which places a rectangle
     at a point. That is a mouse's grammar; see the note in menu.js. On a phone the same entries
     wear the same material as every other sheet here, with rows a thumb can hit.

     `!important` on the position, because showMenuAt writes left/top as inline styles for the
     desktop case and an inline style outranks any selector. The alternative is a second code
     path in the menu builder, and one class is cheaper than that. */
  .ctxmenu.sheet {
    position: fixed !important;
    left: 0 !important;
    right: 0 !important;
    top: auto !important;
    bottom: 0 !important;
    z-index: 61;
    width: 100% !important;
    min-width: 0 !important;
    max-width: none;
    max-height: min(72%, 560px);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding: 8px 0 calc(8px + env(safe-area-inset-bottom));
    border: 0;
    border-top: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
    border-radius: 18px 18px 0 0;
    background: var(--bg);
    box-shadow: 0 -12px 40px rgba(0, 0, 0, .38);
    animation: msheet-up .28s cubic-bezier(.32, .72, 0, 1);
  }
  .ctx-back {
    position: fixed;
    inset: 0;
    z-index: 60;
    background: rgba(0, 0, 0, .38);
    animation: msheet-dim .2s ease;
  }
  .ctxmenu.sheet .ctx-item {
    min-height: var(--m-ctl);
    padding: 0 16px;
    border-radius: 0;
    font-size: var(--t-lg);
  }
  .ctxmenu.sheet .ctx-head {
    padding: 10px 16px 6px;
    font-size: var(--t-xs);
    font-weight: var(--w-semi);
    letter-spacing: var(--tr-label);
    text-transform: uppercase;
  }
  .ctxmenu.sheet .ctx-sep { margin: 6px 16px; }
  .ctxmenu.sheet .ctx-label { font-size: var(--t-lg); }
  /* The arrow points at a submenu, and showMenuAt has no submenus to open - every row that
     carries one here is a row that is disabled and says why. A chevron promising a screen that
     never arrives is worse than the grey text on its own. */
  .ctxmenu.sheet .ctx-arrow { display: none; }

  /* ------------------------------------------------ every target, at one size

     touch.css floors a control at 30 points for a coarse pointer, which is right for a dense
     desktop panel being driven with a finger and wrong for a phone. This is the phone's own
     floor, applied to every surface an audit found under it - the window sheets, the view-choice
     page, the replay bar, the instrument picker, the settings rows, the news wire, the feed and
     the watchlist. One number, --m-ctl, so a row of mixed controls lines up as well as fits.

     TWO KINDS OF THING ARE DELIBERATELY LEFT SMALLER, and it is worth saying which so the next
     reader does not "fix" them:

       - a price pill riding inside a headline (.nw-pill). It is a word in a sentence that also
         happens to be pressable. At 40 points it becomes a button the headline is arranged
         around, and the wire stops being a wire. It gets 30 and a wider press area.
       - the favourite star on a picker row (.ip-star). Same argument, and its row is 44.

     Both are secondary to a row that is itself a target, so a miss lands on something sensible
     rather than on nothing. */

  .win-btn, .win.sheet .win-close { width: var(--m-ctl); height: var(--m-ctl); border-radius: 20px; }
  .msheet-close { width: var(--m-ctl); height: var(--m-ctl); border-radius: 20px; }

  /* The view-choice page: its Back and its close were 33 and 28. */
  .newtab-close { width: var(--m-ctl); height: var(--m-ctl); }
  /* BACK IS THE CHEVRON, and on a phone that is all it is.
   *
   * The word cost ninety-two points of a 390-point header - a quarter of the row - to say what
   * the arrow says, on a screen where the tab strip one row above is the way back anyway. It was
   * being paid for by the strip beside it: the wire's five categories need 216 points and had
   * 202, so "World" was cut in half at rest with nothing to say it could be scrolled to. Every
   * view's header gets the room back, because they all wear this same button. */
  .newtab-back {
    min-height: var(--m-ctl);
    min-width: var(--m-ctl);
    padding: 0 8px;
    justify-content: center;
  }
  .newtab-back > span:not(.ic) { display: none; }

  /* The replay transport. Five buttons at 28x24 is the control set you use while watching a bar
     print, which is exactly when you are not looking at your thumb. */
  .rb-btn { min-width: var(--m-ctl); height: var(--m-ctl); }

  .im-gear { width: var(--m-ctl); height: var(--m-ctl); border-radius: 20px; }

  /* A CHIP THAT FILTERS A LIST IS A PRIMARY CONTROL.
   *
   * The venue pills on the instrument picker, the asset-class tabs beside them, the category
   * strip on the wire, the indicator categories and the profile board's own row are how each of
   * those surfaces is actually driven - they are the first thing pressed on the way to anything
   * else. They had drifted to 32, 34 and 36 across five files, all of them under the floor and
   * none of them agreeing. They sit in a horizontal scroller, so the whole cost of fixing this is
   * six points of height on one row. */
  /* HEIGHT AND WIDTH. A tab sizes to its own word, and "FX" is two characters - so the asset
     class that is hardest to hit is the one with the shortest name. Same for "All" on the wire
     and "5" on the profile board: a chip at the right height and 26 points wide is still a miss,
     and it is always the shortest label, which is always the one you reach for. */
  .ip-pill, .ip-tab, .im-cat { min-height: var(--m-ctl); }
  .ip-tab, .im-cat { min-width: var(--m-ctl); }
  .nw-chip { min-width: 34px; }
  /* The ladder strip's own ellipsis, which is one glyph wide. */
  .ladder-bar .ctl { min-width: 34px; }
  /* The colour swatch: touch.css gives it 40 x 34, which is the desktop's dense metric grown a
     little rather than this device's. */
  .cf-swatch { height: var(--m-ctl); }
  /* The wire's categories live INSIDE a 46-point header rather than in a strip of their own, so
     at forty they fill it edge to edge and stop reading as chips at all - the lit one becomes a
     tab. Thirty-four keeps the pill and still clears the floor by more than the rows beneath it,
     which are the thing this view is actually scrolled and pressed. */
  .nw-chip { min-height: 34px; }
  /* Inside a 44-point row, and secondary to it: a miss lands on the row, which opens the
     instrument the star belongs to. */
  .ip-star { width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center; }

  /* Settings rows. touch.css gives them 36 and a 24pt checkbox; a checkbox is the smallest
     control in the application and it is also the most common one in here. */
  .fld-input, input.txt, select.fld-input { min-height: var(--m-ctl); }
  /* .fld-pick ALONE, not only where it also carries .fld-input. The alert dialog's own selects
     and the replay bar's speed picker are the same control without the settings-row class, and
     they were the two that stayed at 30. */
  .fld-pick { min-height: var(--m-ctl); }
  /* Any committing button in any window - the footers each name their own and three did not. */
  .win .btn, .msheet .btn { min-height: var(--m-ctl); }
  /* The object tree's per-row verbs: show, lock, delete, at twenty points square. */
  .ot-btn { min-width: 32px; min-height: 32px; }
  /* A CHECKBOX ROW IS TOGGLED BY ITS ROW. The box itself stays 28 - a 40-point native checkbox
     is a slab, and the thing that has to be 40 is the TARGET, not the glyph. settings-dialog.js
     makes the whole 52-point row press it on a phone, which is both a bigger target than any
     checkbox and the behaviour every phone settings list already has. */
  input[type="checkbox"].fld-input { width: 28px; height: 28px; min-height: 28px; }
  .fld:has(input[type="checkbox"].fld-input) { cursor: pointer; }
  .set-foot .btn, .th-foot .btn, .win-foot .btn { min-height: var(--m-ctl); }

  /* The news wire's own chrome - the source and search buttons; its chips are above.
     NOT the padding: news.css narrows these deliberately below 420 so that all five categories
     fit the strip, and re-widening them here pushed World off the end. Height is this file's
     business; width is that rule's. */
  .ctl.nw-tool { min-width: var(--m-ctl); min-height: var(--m-ctl); }
  /* The pill stays a word in a headline; the press area grows around it instead. */
  .nw-pill { padding: 5px 8px; }

  /* The liquidation feed and the watchlist headers. */
  .ctl.fl-fbtn, .fl-step { min-height: var(--m-ctl); }
  .wl-listname, .wl-tool { min-height: 34px; min-width: 34px; }

  /* ---------------------------------------------------------- the alert dialog

     A LABEL COLUMN AND A FIELD COLUMN is a desktop form. At 390 points the labels took 104 and
     the fields got 240, so "Fires when price passes the level either way" wrapped to two lines
     beside a one-word label and the whole dialog read as a table that had been squeezed. Stacked:
     the label above its field, full width, which is how every phone form is built.

     And the notification checkboxes sit in a 16-point grid column - they carry no class of their
     own, so the settings floor never reached them and they were the browser's 13-point default.
     Raising the box without the column meant the box grew INTO its own label: "Notify in this
     browser" printed underneath a tick. The column and the box are one number now. */
  .al-input { min-height: 44px; font-size: var(--t-input-touch); }
  .al-row { grid-template-columns: 1fr; gap: 4px; }
  .al-label { line-height: var(--lh-ui); font-size: var(--t-md); }
  .al-chan { grid-template-columns: 26px 1fr; }
  .al-chan input[type="checkbox"] { width: 26px; height: 26px; margin: 0; }
  .al-chan-label { font-size: var(--t-lg); }
  .al-msg { min-height: 72px; }

  /* The liquidation map's own strip: period, market and the camera. Twenty points tall, on the
     view a phone reaches for when it wants to know where the stops are. */
  /* THE PLATE IS OPAQUE HERE. It is 62% black over the canvas, which on a desktop is a 26-point
     strip and reads as glass. At thumb size it is forty, so it now covers the topmost row of the
     map's own price labels - and a label showing through a translucent plate, cut off where the
     plate ends, reads exactly like text that failed to render. A control strip standing on a
     field of data should hide what is under it or not be there. */
  /* THE STRIP SCROLLS RATHER THAN CLIPPING. Measured at 360: the period chips take 206, the Hits
     latch 34 and the camera 34, which leaves the readout twenty-one points - and the readout is
     `overflow: hidden`, so "C 77,622  -183.00 (-0.24%)" was laid out to 467 and simply painted
     nowhere. The change is the one fact on that band that is not printed anywhere else on the
     map, so it must not be the thing that silently disappears. Too much for the width is a
     scroller; it is only a bug when the width lies about it. */

  /* EVERY VIEW'S OWN HEADER. The wire, the feed, the map, the book and the watchlist each build
     their chrome out of plain .ctl buttons, which touch.css floors at 30 - the desktop number.
     One rule rather than five, and the ladder's own strip still sets its 34 after this. */
  body.view-page .ctl,
  .wl-listname, .wl-tool { min-height: 34px; }

  /* The ladder's own strip: the reach stepper and the Book|Tape switch, at thumb size. */
  .ladder-bar { min-height: var(--m-ctl); padding: 4px 8px; }
  .ladder-step, .ladder-seg { height: 36px; border-radius: var(--r-ctl); }
  .ladder-step .ctl, .ladder-segbtn { height: 34px; min-height: 34px; padding: 0 14px; font-size: var(--t-md); }
  .ladder-bar .ctl { min-height: 34px; }

  /* Two points, and they are the two that made the tick buttons miss: the group is 40 and its
     children lose the group's own hairline top and bottom. */
  :root[data-mobile] .stepper { height: calc(var(--m-ctl) + 2px); }
  .mtab-x { width: 32px; height: 32px; border-radius: 16px; }

  /* ONE TITLE PER SHEET. The feedback form carries its own heading because on a desktop it sits
     under a 24-point window title bar that is chrome rather than a headline. A sheet's head IS a
     headline, so the pair printed "Send feedback" and then "Feedback" directly beneath it. */
  .fb-title { display: none; }

  /* --------------------------------------------- the gestures card, on a phone

     The desktop card is a three-column reference sheet sized so the whole thing is taken in at
     once. One column here, rows at reading size. */
  .keys-cols { display: flex; flex-direction: column; gap: 0; }
  .keys-group { min-width: 0; padding: 0; border: 0; background: transparent; border-radius: 0; }
  .keys-group h3 {
    padding: 14px 16px 6px;
    font-size: var(--t-xs);
    letter-spacing: var(--tr-label);
  }
  .keys-row {
    display: flex;
    align-items: center;
    gap: 12px;
    min-height: 44px;
    padding: 4px 16px;
    box-shadow: inset 0 1px 0 var(--line);
  }
  .keys-combo { flex: 0 0 auto; min-width: 96px; }
  .keys-label { flex: 1 1 auto; font-size: var(--t-md); }
  .keys-row kbd { font-size: var(--t-md); }

  /* ------------------------------------------------------------ tabstrip */

  /* THE DESKTOP TAB STRIP IS GONE FROM THE PHONE, because .mtabs is the same record drawn for a
   * thumb. Two strips at once would be two answers to "which of these am I looking at".
   *
   * It used to show only at two tabs or more, which meant the phone's one piece of navigation
   * appeared the moment you no longer needed to be told it existed and was absent every time you
   * did. And what it drew when it did appear was the desktop tab: an 11px close cross on every
   * one of them, a drag handle for a grid this device has no way to show, and a rename on
   * double-click - a gesture a touch screen spends on zoom.
   *
   * The viewport controls that shared the strip - reset scale, auto-scale, lock, zoom - were
   * 13px targets riding along with it. They are in the hub, at hub size. */
  body.panel > .tabstrip { display: none; }

  /* THE THUMB BAR BELONGS TO THE CHART. In a view - news, the liquidation feed, the book, the
     watchlist - every one of its actions (draw, undo, symbol, period, fullscreen) is about a
     chart that is not on screen, and the bar sat on top of the view's last row. The view carries
     its own header with Back and its own filters, and the tab strip is showing whenever a view
     is open, so nothing is lost by standing this down. */
  body.view-page .mobilebar { display: none; }
  body.panel .tabs { border-top: 0; border-bottom: 1px solid var(--line); }
  .tab { border-radius: var(--r-ctl) var(--r-ctl) 0 0; }
}

/* ------------------------------------------------- the liquidation feed, on a phone

   TWO LINES PER PRINT, because six columns do not fit in 390 points and the build that tried
   printed three of them broken.

   The narrow layout squeezed eight tracks into one line: marker, coin, phrase, notional, price,
   wallet, time. Measured at 390 there are 340 points for them once the padding and the six gaps
   are taken out, and the fixed tracks alone want 298 - so the phrase got 44 points and printed
   "Short so", the price got what was left and printed "$73,099....", and the timestamp fell off
   the rows that carried a long address. Every one of those is a FACT the row exists to state,
   and two of them were being cut mid-character.

   Splitting the sentence from its metadata fixes all three at once and costs density: about
   seventeen prints on screen instead of twenty-eight. That is the right trade for a feed whose
   whole job is to say what was liquidated, how big, at what price and by whom - a row you have
   to guess at is not denser, it is shorter.

   The line above is the sentence, in the row's own colour: what, which way, how much. The line
   below is where and when, quiet: the price, the wallet, the clock. It is also 44 points tall,
   which is the first time a row you can tap through to a wallet has been a target. */
@media (max-width: 640px) and (pointer: coarse),
       (max-height: 500px) and (max-width: 950px) and (pointer: coarse) {
  .fl-row {
    grid-template-columns: 18px minmax(0, auto) minmax(0, 1fr) auto;
    grid-template-rows: auto auto;
    align-items: center;
    column-gap: 7px;
    row-gap: 1px;
    padding: 6px 12px;
  }
  /* The asset's mark spans both lines of the row, so the sentence and its metadata sit against
     one left edge rather than two. */
  .fl-mark { grid-area: 1 / 1 / 3 / 2; align-self: center; }
  .fl-coin { grid-area: 1 / 2 / 2 / 3; }
  .fl-what { grid-area: 1 / 3 / 2 / 4; }
  .fl-size { grid-area: 1 / 4 / 2 / 5; }
  .fl-price { grid-area: 2 / 2 / 3 / 3; justify-self: start; }
  .fl-who { grid-area: 2 / 3 / 3 / 4; justify-self: start; text-align: left; }
  .fl-time { grid-area: 2 / 4 / 3 / 5; }

  /* The whole phrase is back: there is a line for it now, so nothing has to be guessed from
     "Short so". "Liquidated Short:" is the reference channel's own wording. */
  .fl-what-full { display: inline; }
  .fl-what-abbr { display: none; }
  /* EXCEPT WHERE THE ROW CARRIES A CHIP. An imminent row is "Imminent Short Liquidation:" plus
     "0.4% away", and the chip is the half that matters - it is the whole reason the row is on
     screen before anything has happened. The phrase is what gives, and it has a short form that
     says the same thing: "Short soon". Without this the cell clipped its own chip to "0.4% aw",
     which is a number ending mid-word on the one row that exists to state it. */
  .fl-row.imminent .fl-what-full { display: none; }
  .fl-row.imminent .fl-what-abbr { display: inline; }
  .fl-away, .fl-pnl { flex: 0 0 auto; }

  /* The second line is metadata, so it reads as metadata. */
  .fl-price, .fl-who, .fl-time { font-size: var(--t-sm); color: var(--ink-faint); }
  .fl-price { overflow: visible; }
  .fl-row.hot .fl-price, .fl-row.whale .fl-price { color: var(--ink-dim); }

  /* AND THE CLOCK IS ALWAYS PRINTED HERE.
   *
   * The desktop hides the stamp on a row whose minute matches the one above it - a cascade is
   * twenty rows stamped 19:15 and a column of twenty identical numbers is a fact stated once -
   * and gives it back on HOVER. There is no hover on a phone, so that row's time was simply not
   * reachable: a fact the feed holds, printed nowhere, with no gesture that reveals it. Quiet
   * rather than absent. */
  .fl-row.same-min .fl-time { opacity: 0.45; }

  /* THE FEED'S FILTER IS A SHEET ON A PHONE, AND EVERY CONTROL IN IT IS A TARGET.
   *
   * Measured on an iPhone (audit:mobile, which now opens each view's own settings surface): the
   * size segments were 41x22, the event switches 143x28, the market chips 39x24 and the "All"
   * link 13x17. Twenty-four controls under the floor, on the one surface in this view you have
   * to use - which is the whole of "I don't like the selectors or the settings menu... make it
   * cleaner and more user friendly on mobile".
   *
   * It was also a 320-point popover hanging off a button in the corner, over the feed it
   * filters. A phone has one place for a panel like this and every application agrees about it:
   * the bottom, full width, with room to scroll. */
  .fl-panel {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    width: auto;
    max-height: 78vh;
    z-index: 60;
    gap: 16px;
    padding: 14px 14px calc(16px + env(safe-area-inset-bottom));
    border-radius: var(--r-touch) var(--r-touch) 0 0;
    border-bottom: 0;
    box-shadow: 0 -18px 40px -12px rgba(0, 0, 0, .55);
  }
  /* The scale is one control, so it grows as one: seven segments across 366 points is 52 each,
     which is a thumb's width and reads as a ruler rather than seven small buttons. */
  .fl-seg { height: var(--m-ctl); }
  .fl-segbtn { font-size: var(--t-md); }
  /* A kind is a row with a count. At 44 it is a row you can hit while walking. */
  .fl-kind { height: 44px; font-size: var(--t-md); }
  .fl-kinds { gap: 8px; }
  .fl-find { height: var(--m-ctl); font-size: var(--t-input-touch); }
  /* Chips ride in a scroller, so height is the only cost - the secondary tier, 34. */
  .fl-chip { min-height: 34px; padding: 0 12px; }
  .fl-chips { gap: 8px; }
  /* "All" is a verb, not a caption: it clears the market filter, and it was thirteen points
     wide. */
  .fl-plink { min-height: var(--m-ctl); min-width: var(--m-ctl); justify-content: flex-end; }
  .fl-plabel { font-size: var(--t-sm); }

  /* THE SIZE SCALE'S SEGMENTS ARE FORTY, NOT THIRTY-EIGHT. The track is 40 and it carries a
     hairline, so its members came out two short of the floor - which is the kind of miss that is
     invisible to read and certain to be measured. */
  .fl-seg { height: calc(var(--m-ctl) + 2px); }

  /* ------------------------------------------------ the wire's own settings

     Measured with the feed's: the source switches were 34x18 - less than half the floor, twenty
     of them in a list - the segments 24 tall, and the links 17. A panel of controls nobody can
     hit is the same fault as one nobody styled, and it is the other half of "I don't like the
     selectors or the settings menu". */
  .nw-switch { width: 46px; height: 28px; }
  .nw-knob { width: 22px; height: 22px; }
  .nw-switch.on .nw-knob { transform: translateX(18px); }
  /* The row forwards to the switch (see news-view.js), so the target is the whole line. Forty
     four, which is what the rest of this application's list rows stand at. */
  .nw-srcrow { padding: 9px 2px; min-height: 44px; }
  .nw-seg { height: calc(var(--m-ctl) + 2px); }
  /* The SEGMENT ITSELF, not its track: the track has no height of its own and the members were
     carrying 24 from the desktop's chip metrics, so raising the frame moved nothing. */
  .nw-segbtn { min-height: var(--m-ctl); padding: 0 14px; }
  /* "Show more" ends a list of headlines and was nineteen points tall. */
  .nw-more { min-height: var(--m-ctl); padding: 0 12px; }
  /* A link that clears a filter or opens the rest of a list is a control: forty, like the rest. */
  .nw-plink, .nw-srcmore {
    min-height: var(--m-ctl);
    /* And wide enough to be aimed at: "All" is three characters and was a 28-point target. */
    min-width: var(--m-ctl);
    display: inline-flex;
    align-items: center;
    justify-content: center;
  }
}

/* The Liquidation Map view on a phone: the canvas IS the product; the wallet board and the
   pill row would each cost a third of the height, so the board folds away and the search
   stays. The drill-in still names every wallet - one tap deeper. */
@media (max-width: 640px) and (pointer: coarse),
       (max-height: 500px) and (max-width: 950px) and (pointer: coarse) {
}

/* ------------------------------------------------------ a phone on its side

   390 points of height with 98 of top chrome and 56 at the thumb is forty per cent of the screen
   spent on furniture, on the orientation people turn to specifically because they want more
   chart. Nothing is taken away - the view bar, the toolbar and the action bar are all still there
   and still driveable - they are drawn at the smaller of the two thumb sizes, which is the one
   TradingView's own phone header uses. 118 points instead of 154.

   Height, not orientation: a folding phone half-open is short in both directions and wants the
   same thing. */
@media (max-height: 500px) and (max-width: 950px) and (pointer: coarse) {
  :root { --m-ctl: 34px; }
  header.bar { padding: 3px 8px; }
  .mtabs { padding: 0 6px; }
  .mobilebar { --mb-pad: 4px; --mb-btn: 34px; }
  .mobilebar button { height: 34px; }
  .stage { padding-bottom: calc(42px + env(safe-area-inset-bottom)); }
  .objpanel { bottom: calc(42px + env(safe-area-inset-bottom)); }
  /* A SHEET COMES IN FROM THE SIDE HERE, NOT FROM THE BOTTOM.
   *
   * A bottom sheet works because it is a shape rising out of the edge nearest the thumb into a
   * screen that is mostly taller than it. Lying down, that screen is 390 points tall: a sheet
   * with any content in it takes nearly all of them, which leaves no chart to see it against and
   * about twenty points of backdrop to tap to get rid of it. That is a modal dialog with a
   * hairline of escape at the top, and it is exactly the shape people mean by "I open something
   * and then I am stuck in it".
   *
   * From the right instead: the sheet is as tall as the screen, the chart keeps its left half,
   * and the half of the screen that is not the sheet is somewhere to tap. It is also the side a
   * right thumb is already on when a phone is held sideways. */
  .msheet, .ctxmenu.sheet {
    left: auto !important;
    right: 0 !important;
    top: 0 !important;
    bottom: 0 !important;
    width: min(420px, 55%) !important;
    max-width: 55%;
    max-height: none;
    border-radius: 18px 0 0 18px;
    border-top: 0;
    border-left: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
    box-shadow: -12px 0 40px rgba(0, 0, 0, .38);
    animation: msheet-in .28s cubic-bezier(.32, .72, 0, 1);
  }
  @keyframes msheet-in { from { transform: translateX(100%); } to { transform: translateX(0); } }
  /* The grab bar says "this goes DOWN", and this one does not. */
  .msheet-grip { display: none; }
  .win.sheet {
    left: auto !important;
    right: 0 !important;
    top: 0 !important;
    bottom: 0 !important;
    width: min(460px, 62%) !important;
    height: 100% !important;
    border-radius: 18px 0 0 18px !important;
  }
  .win.sheet .win-head::before { display: none; }
}

/* ------------------------------------------------------------------ wheel picker */

/* The iOS-style wheel that replaced the phone period grid and the phone instrument sheet. It
   is opened only from phone-gated code paths (PHONE in app.js), and the material is the same
   glass the sheets wear - one surface, one vocabulary - but it hangs in the CENTRE of the
   screen with a low dim, because the point of a wheel is that the chart stays visible behind
   the decision. The mask fades the top and bottom of the drum into the panel; the cursor
   hairlines bracket the row that IS the selection. */
@media (max-width: 640px) and (pointer: coarse),
       (max-height: 500px) and (max-width: 950px) and (pointer: coarse) {
  .wp-back {
    position: fixed;
    inset: 0;
    z-index: 70;
    display: flex;
    align-items: center;
    justify-content: center;
    /* The reference picker dims LESS than a sheet: the chart stays an instrument you can still
       read around the panel, not scenery behind a curtain. 21a: .22 was too thin - rung type
       lost to the cluster grid behind it and the whole thing read as broken. .32 keeps the
       chart legible around the panel while the panel itself owns its ground. */
    background: rgba(0, 0, 0, .32);
    animation: wp-dim .18s ease;
  }
  @keyframes wp-dim { from { opacity: 0; } to { opacity: 1; } }
  /* TWO COLORS, the owner's words: plain WHITE text on a LOW-OPACITY BLACK panel. No border, no
     shadow, no boxes, no accents - and no blur either (the glass removal stands): a black veil,
     so the chart reads through the wheel while it is up. The owner's first cut sat at .22; 21a
     nudged it to .52 after the mobile polish pass - at .22 the rungs sank into the live chart
     behind them ("looks more AI and bugs and flaws than pc"). Still no blur, still one colour. */
  .wp-panel {
    width: min(320px, calc(100vw - 24px));
    border-radius: 16px;
    border: 0;
    box-shadow: none;
    background: rgba(0, 0, 0, .52);
    padding: 6px;
    animation: wp-pop .24s cubic-bezier(.32, .72, 0, 1);
  }
  .wp-back.wp-out { opacity: 0; transition: opacity .22s var(--ease); }
  .wp-back.wp-out .wp-panel { transform: scale(.96); transition: transform .22s var(--ease); }
  @keyframes wp-pop { from { opacity: 0; transform: scale(.92); } to { opacity: 1; transform: scale(1); } }
  /* 20i, THE DRUM IS THE OS SCROLLER: overflow + scroll-snap mandatory - the phone's own
     momentum, detents and refresh rate - and the cylinder is painted on top from scroll
     events. The frame wraps the scroller so the centre marker never scrolls and never fades. */
  .wp-frame { position: relative; }
  .wp-view {
    position: relative;
    height: 238px;          /* five rows - tighter window, the barrel fills the panel */
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
    scroll-snap-type: y mandatory;
    touch-action: pan-y;
    -webkit-overflow-scrolling: touch;  /* iOS momentum - the panel is an animated ancestor */
    cursor: grab;
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(to bottom, transparent, #000 24%, #000 76%, transparent);
    mask-image: linear-gradient(to bottom, transparent, #000 24%, #000 76%, transparent);
  }
  .wp-view::-webkit-scrollbar { display: none; }
  /* The band is the strip now: every rung in flow, with (238 - 44) / 2 = 97px of padding so
     the first and last can reach the centre. KEEP THE 97 in step with PAD in wheel-picker.js. */
  .wp-band { position: relative; padding: 97px 0; }
  .wp-item {
    position: relative;
    height: 44px;
    scroll-snap-align: center;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    font-size: 18px;
    font-weight: var(--w-med);
    color: #fff;
    /* 21a, INK THAT SURVIVES THE CHART: the veil stays at the owner's ~22% - the chart reads
       through the wheel - but white type over bright cluster cells and price labels collided
       with them, which read as broken. A soft dark cushion under the ink separates it without
       a box, a border or a deeper veil - the two-colour spec stands. */
    text-shadow: 0 1px 3px rgba(0, 0, 0, .55), 0 0 16px rgba(0, 0, 0, .35);
        backface-visibility: hidden;
      }
  /* The centre row: same white, more of it - heavier ink, one step larger. Neighbours are the
     same white at the drum's fade, so the wheel is white-on-black and nothing else. */
  .wp-item.on { font-size: 22px; font-weight: var(--w-semi); }
  /* The pair's logo, drum-sized: the picker's own mark (.ip-mark in windows.css), scaled up to
     sit beside the drum's 18-22px ink instead of the picker's 13px list text. */
  .wp-item .ip-mark { width: 24px; height: 24px; }
  .wp-item .ip-logo { width: 24px; height: 24px; }
  /* Invisible centre marker - the drum's geometry answer for scripts, no drawn lines: the
     reference picker does not rule lines through its value. */
  .wp-cursor {
    position: absolute;
    left: 8px;
    right: 8px;
    top: 50%;
    height: 44px;
    transform: translateY(-50%);
    pointer-events: none;
    /* THE OWNER TOOK THE BAND'S HAIRLINES (19y: "two gray horizontal lines in the middle,
       above and below it - I don't want that thing"). What stays is nothing: the selection is
       named by the type alone - bigger, bolder, centred - the way the panel already read. */
  }
  /* ONLY THE WHEEL (owner's ask): the panel carries no text but the drum's own rows - no title,
     no segment, no link. Nothing to style but the drum. */
  /* NO LINES AROUND THE CHIPS (owner's ask): the lower bar itself is the only tab - the symbol,
     period and tick controls sit on it as bare text, no boxes, no outlines. */
  :root[data-mobile] .mobilebar #symBtn,
  :root[data-mobile] .mobilebar #tfBtn,
  :root[data-mobile] .mobilebar .sym-group,
  :root[data-mobile] .mobilebar .tf-group,
  :root[data-mobile] .mobilebar .stepper,
  :root[data-mobile] .mobilebar .stepper button,
  :root[data-mobile] .mobilebar .stepper input {
    border: 0;
    background: transparent;
    box-shadow: none;
  }
  /* THE EMPTY WATCHLIST'S ONE RUNG: a sentence, not a symbol - dim, small, unwheel-like. */
  :root[data-mobile] .wp-item.wp-hint { color: var(--ink-dim); font-size: 15px; font-weight: 500; letter-spacing: 0; }
}

  /* THE WATCHLIST DRUM'S DOOR back to the full catalogue. A quiet pill under the rungs - the
     footer is part of the picker panel, so it fades with the wheel and never fights the chart. */
  .wp-foot-row { display: flex; justify-content: center; padding: 10px 0 4px; }
  .wp-browse {
    border: 0;
    background: color-mix(in srgb, var(--ink) 9%, transparent);
    color: var(--ink-dim);
    font: var(--w-med) 13px/1 var(--ui);
    padding: 9px 16px;
    border-radius: 999px;
    cursor: pointer;
  }
  .wp-browse:active { background: color-mix(in srgb, var(--ink) 16%, transparent); }

/* THE DOCK - the second bottom row, under the bar it sits on: watchlist, chart, menu, side by
   side, each an icon with its name in small white text beneath. White plain, like the wheel's
   words. The row above moves up the dock's height; the safe-area inset moves into the dock, so
   exactly one row owns it. */
@media (max-width: 640px) and (pointer: coarse),
       (max-height: 500px) and (max-width: 950px) and (pointer: coarse) {
  :root { --dock-h: 56px; }
  .dock {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    display: flex;
    align-items: stretch;
    justify-content: space-around;
    height: calc(var(--dock-h) + env(safe-area-inset-bottom));
    padding: 6px 8px calc(6px + env(safe-area-inset-bottom));
    background: var(--bg);
    border-top: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
  }
  .dock[hidden] { display: none; }
  .dock-btn {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    border: 0;
    border-radius: var(--r-surface);
    background: transparent;
    color: var(--ink);
    cursor: pointer;
    opacity: .55;   /* the door you are NOT behind stands down to a quiet half */
  }
  .dock-btn.on { opacity: 1; }
  .dock-btn svg { display: block; }
  .dock-label {
    font: var(--w-med) 10px/1 var(--ui);
    letter-spacing: var(--tr-tight);
    color: var(--ink);
  }
  .dock-btn:active { background: color-mix(in srgb, var(--ink) 8%, transparent); }

  /* THE ROW ABOVE clears the dock; the stage clears BOTH. */
  .mobilebar {
    bottom: calc(var(--dock-h) + env(safe-area-inset-bottom));
    padding-bottom: var(--mb-pad);
  }
  .stage { padding-bottom: calc(56px + var(--dock-h) + env(safe-area-inset-bottom)); }
  .objpanel { bottom: calc(42px + var(--dock-h) + env(safe-area-inset-bottom)); }
  /* A VIEW PAGE: the chart's bar stands down and the dock IS the bottom chrome. */
  body.view-page .stage { padding-bottom: calc(var(--dock-h) + env(safe-area-inset-bottom)); }
  /* Replay and immersive stay full-bleed; the dock stands down with the bar. */
  body.replay-on .dock,
  body.immersive .dock { display: none; }
}

  /* THE PHONE WATCHLIST (wl-phone): one clean list - mark, symbol + contract name on the left,
     price and its green/red 24-hour move on the right, one row per saved pair. The desktop
     panel keeps its own sheet in watchlist.css; nothing here reaches it. */
  /* .wl-phone .wl-head geometry now comes from the shared .panel-head base. */
  :root[data-mobile] .wlp-title { font-size: 17px; font-weight: 700; color: var(--ink); }
  :root[data-mobile] .wl-phone .wlp-body { flex: 1 1 auto; overflow-y: auto; padding: 6px 0 12px; }
  :root[data-mobile] .wlp-row { position: relative; overflow: hidden; touch-action: pan-y; width: 100%; background: none; border: 0; border-bottom: 1px solid color-mix(in srgb, var(--ink) 6%, transparent); font: inherit; text-align: left; cursor: pointer; }
  :root[data-mobile] .wlp-row .wl-in { position: relative; display: flex; align-items: center; gap: 12px; width: 100%; padding: 12px var(--mx, 16px); background: var(--bg); transition: transform 300ms cubic-bezier(0.32, 0.72, 0, 1); }
  :root[data-mobile] .wlp-row.swiping .wl-in { transition: none; will-change: transform; }
  :root[data-mobile] .wlp-row:not(.swiping):not(.swiped):active .wl-in { background: color-mix(in srgb, var(--ink) 6%, transparent); }
  /* THE PLATE ONLY EXISTS UNDER THE ROW. It is absolutely positioned, and an absolutely
     positioned element paints ABOVE a static sibling - which is exactly what the inner was, so
     the red plate showed through every row BEFORE any swipe (his report: "it says red big icons
     delete" by default). `position: relative` on the inner puts it back on top; the plate then
     appears only when the swipe pulls the row off it. Icon only - a red disc with the bin - no
     Delete word; the swipe itself is the sentence. */
  /* THE DELETE IS A SQUARE SLAB, iOS Clock style: the revealed 72px is flat red, full row
     height, the white bin centred in it - no floating disc, no rounding, the same plain
     red rectangle a swiped alarm reveals. The whole 72px stays the hit target. */
  :root[data-mobile] .wlp-row .wl-del { position: absolute; top: 0; right: 0; bottom: 0; width: 72px; display: flex; align-items: center; justify-content: center; background: var(--sell-hi, #f23645); color: #fff; border: 0; cursor: pointer; font: inherit; -webkit-tap-highlight-color: transparent; visibility: hidden; }
  /* Belt over the braces: even if the row's inner arithmetic leaves a sliver of the plate
     uncovered at rest, a hidden plate cannot peek. It exists visually only while the finger
     drags the row across it, and stays while the row rests open. */
  :root[data-mobile] .wlp-row.swiping .wl-del,
  :root[data-mobile] .wlp-row.swiped .wl-del { visibility: visible; }
  /* THE BIN RIDES A DISC, not a slab: a 52px red circle centred in the 72px reveal - the whole
     72px stays the hit target, but the paint is a floating disc over the row's ground, the way
     a clean list does it. The icon sits above the disc's paint layer. */
  :root[data-mobile] .wlp-row.swiping .wl-in { box-shadow: none; }
  :root[data-mobile] .wlp-row .wl-del:active { filter: brightness(1.12); }
  :root[data-mobile] .wlp-row.dragging { position: relative; z-index: 2; background: var(--panel, #14151d); box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45); }
  /* THE GRIP IS GONE (19w): no icon, no right-edge aim - a HOLD anywhere on the row reorders
     it (wireDrag in watchlist-view.js). The row keeps touch-action:pan-y for its everyday
     scroll; the armed drag claims the gesture with a non-passive touchmove veto. */
  :root[data-mobile] .wlp-mark { flex: 0 0 auto; position: relative; width: 40px; height: 40px; border-radius: 50%; overflow: visible; display: grid; place-items: center; }
  :root[data-mobile] .wlp-mark.art .wlp-ini { display: none; }
  /* The art is rounded ITSELF, not by clipping the mark: overflow:hidden here used to shear the
     alert badge clean off - the badge lives on this same box and must be able to leave it. */
  :root[data-mobile] .wlp-ini { width: 40px; height: 40px; border-radius: 50%; display: grid; place-items: center; font-size: 13px; font-weight: 700; color: var(--ink-dim, #8a8f98); background: var(--panel-2, #1a1c26); border: 1px solid color-mix(in srgb, var(--ink) 9%, transparent); }
  :root[data-mobile] .wlp-logo { width: 40px; height: 40px; object-fit: cover; border-radius: 50%; }
  /* THE ALERT BADGE: its own little circle, pinned over the avatar's top-right corner and hanging
     off it - white face, black clock, ringed in the row's ground so it reads as a separate disc,
     never as a mark printed on the logo. Only when an alert stands. */
  :root[data-mobile] .wlp-badge { position: absolute; top: -3px; right: -3px; width: 17px; height: 17px; border-radius: 50%; background: #fff; border: 2px solid var(--bg, #0d0e14); box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35); display: grid; place-items: center; color: #0e0f13; z-index: 2; }
  :root[data-mobile] .wlp-badge svg { width: 9px; height: 9px; stroke-width: 2.75; }
  :root[data-mobile] .wlp-id { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
  :root[data-mobile] .wlp-sym { font-size: 16px; font-weight: var(--w-semi, 600); color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  :root[data-mobile] .wlp-name { font-size: 12.5px; color: var(--ink-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  :root[data-mobile] .wlp-nums { flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end; gap: 1px; }
  :root[data-mobile] .wlp-price { font-size: 15px; font-weight: var(--w-semi, 600); color: var(--ink); font-variant-numeric: tabular-nums; padding: 0 4px; margin: 0 -4px; border-radius: 6px; }
  :root[data-mobile] .wlp-chg { font-size: 12px; font-variant-numeric: tabular-nums; color: var(--ink-dim); }
  :root[data-mobile] .wlp-chg.up { color: var(--buy-hi, #35c46f); }
  :root[data-mobile] .wlp-chg.dn { color: var(--sell-hi, #e85449); }
  /* The grip and its rules are gone with the drag - a row is a tap, edge to edge. */
  :root[data-mobile] .wlp-empty { padding: 30px 12px 8px; text-align: center; color: var(--ink-dim); font-size: 13px; }
  :root[data-mobile] .wlp-add { display: block; margin: 10px auto 0; padding: 8px 16px; border-radius: 10px; border: 1px solid color-mix(in srgb, var(--ink) 18%, transparent); background: none; color: var(--ink); font-size: 13px; }
  :root[data-mobile] .wlp-foot { flex: 0 0 auto; padding: 3px var(--mx, 16px) calc(4px + env(safe-area-inset-bottom)); font-size: 11px; color: var(--ink-faint); text-align: center; }
  :root[data-mobile] .wlp-foot.stale { color: var(--sell-hi, #e85449); }
  /* The head's + door, and the dock's lit door wearing a dot. */
  :root[data-mobile] .wlp-addbtn { margin-left: auto; width: 32px; height: 32px; display: grid; place-items: center; background: none; border: 0; color: var(--ink); cursor: pointer; }
  :root[data-mobile] .wlp-addbtn:active { background: color-mix(in srgb, var(--ink) 8%, transparent); border-radius: 6px; }
    
  /* THE MOTION CONTRACT: state changes ease over 160ms, nothing animates on load. */
  :root[data-mobile] .wlp-row, :root[data-mobile] .dock-btn, :root[data-mobile] .mtab,
  :root[data-mobile] .wlp-addbtn, :root[data-mobile] .wlp-add {
    transition: background-color 160ms ease-out, color 160ms ease-out, border-color 160ms ease-out;
  }

  /* THE FOOT IS SILENT UNTIL IT IS NOT: a watchlist that is working needs no caption; the one
     time it has something to say is when the feed stops. */
  :root[data-mobile] .wlp-foot:empty { display: none; }

  /* THE PHONE INSTRUMENT PICKER (ipp): search-first sheet - the pill is the header, text tabs,
     contextual chips, 44px result rows with the query highlighted and a +/check to add. */
  :root[data-mobile] .ipp-root { display: flex; flex-direction: column; height: 100%; min-height: 0; background: var(--bg); }
  :root[data-mobile] .ipp-head { flex: 0 0 auto; display: flex; align-items: center; gap: 12px; padding: 12px var(--mx, 16px) 10px; background: var(--panel); }
  :root[data-mobile] .ipp-search { flex: 1 1 auto; display: flex; align-items: center; gap: 8px; height: 36px; padding: 0 12px; border-radius: 18px; background: var(--panel-2); color: var(--ink-faint); }
  :root[data-mobile] .ipp-input { flex: 1 1 auto; min-width: 0; border: 0; background: none; color: var(--ink); font: inherit; font-size: 15px; outline: none; }
  :root[data-mobile] .ipp-close { flex: 0 0 auto; border: 0; background: none; color: var(--ink-dim); font: inherit; font-size: 15px; cursor: pointer; padding: 4px 0 4px 4px; }
  /* THE TABS FIT THE FRAME, AND THEY ARE TALL ENOUGH TO PRESS. Six labels at 15px with a 20px
     gap overran a 390pt screen and became a scroll lane nobody read to the end (his report: the
     tab does not fit); 14px with a 16px gap lands the whole set inside the margins. The old
     4px pad made the tap target the text's own ~27px - a section that takes two taps is broken,
     and the 700-weight active state was the "thick text" look he flagged. Resting at the
     medium weight, active alone bold, is the iOS pattern. */
  :root[data-mobile] .ipp-tabs { flex: 0 0 auto; display: flex; gap: 16px; padding: 6px var(--mx, 16px); overflow-x: auto; scrollbar-width: none; background: var(--panel); }
  :root[data-mobile] .ipp-tabs::-webkit-scrollbar { display: none; }
  :root[data-mobile] .ipp-tab { flex: 0 0 auto; display: inline-flex; align-items: center; min-height: 36px; border: 0; background: none; padding: 4px 0; font: inherit; font-size: 14px; font-weight: var(--w-med, 500); color: var(--ink-dim); cursor: pointer; }
  :root[data-mobile] .ipp-tab.on { color: var(--ink); font-weight: var(--w-semi, 600); }
  :root[data-mobile] .ipp-chips { flex: 0 0 auto; display: flex; gap: 8px; padding: 8px var(--mx, 16px) 10px; background: var(--panel); }
  :root[data-mobile] .ipp-chip { display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 12px; border: 0; border-radius: 16px; background: var(--panel-2); color: var(--ink-dim); font: inherit; font-size: 13px; font-weight: var(--w-med, 500); cursor: pointer; }
  :root[data-mobile] .ipp-list { flex: 1 1 auto; overflow-y: auto; padding: 4px 0 12px; }
  /* SEVEN THOUSAND ROWS ARE NOT ALL PAINTED. content-visibility skips the layout and paint of
     everything off screen - the intrinsic size keeps the scrollbar honest at the row pitch - so
     a flick down the All tab scrolls at the pace of a screenful instead of a wall, and a row's
     logo, which only starts loading when the row renders, arrives with the row instead of
     chasing it from three screens back. */
  :root[data-mobile] .iprow { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 44px; padding: 6px var(--mx, 16px); background: none; border: 0; border-bottom: 1px solid color-mix(in srgb, var(--ink) 6%, transparent); font: inherit; text-align: left; cursor: pointer; content-visibility: auto; contain-intrinsic-size: auto 45px; }
  :root[data-mobile] .iprow:active { background: color-mix(in srgb, var(--ink) 6%, transparent); }
  :root[data-mobile] .iprow .ip-mark { width: 32px; height: 32px; }
  :root[data-mobile] .iprow .ip-initials { width: 32px; height: 32px; font-size: 11px; }
  :root[data-mobile] .iprow .ip-logo { width: 32px; height: 32px; }
  :root[data-mobile] .iprow-id { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
  :root[data-mobile] .iprow-sym { font-size: 15px; font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  :root[data-mobile] .ipp-hl { color: var(--ds-accent); }
  :root[data-mobile] .iprow-desc { font-size: 12px; color: var(--ink-dim); }
  :root[data-mobile] .iprow-num { flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-end; gap: 1px; }
  :root[data-mobile] .iprow-venue { font-size: 13px; font-weight: var(--w-med, 500); color: var(--ink); }
  :root[data-mobile] .iprow-class { font-size: 11px; color: var(--ink-faint); }
  :root[data-mobile] .iprow-act { flex: 0 0 auto; width: 28px; height: 28px; display: grid; place-items: center; color: var(--ink-faint); cursor: pointer; }
  :root[data-mobile] .iprow-act.on { color: var(--ds-accent); }
  :root[data-mobile] .ipp-empty { padding: 32px 16px; text-align: center; color: var(--ink-dim); font-size: 13px; }
  /* THE FILTER SHEET: bottom, rounded top, centred title, Close top-right, check on the live
     option. Flat rows, no cards - the global row pattern at 44px. */
  :root[data-mobile] .ips-back { position: fixed; inset: 0; z-index: 2147483000; background: rgba(0, 0, 0, 0.5); }
  :root[data-mobile] .ips-sheet { position: fixed; left: 0; right: 0; bottom: 0; z-index: 2147483001; max-height: 72vh; overflow-y: auto; background: var(--panel-2); border-radius: var(--ds-radius-md, 10px) var(--ds-radius-md, 10px) 0 0; padding: 0 0 calc(8px + env(safe-area-inset-bottom)); }
  :root[data-mobile] .ips-shead { position: sticky; top: 0; display: flex; align-items: center; justify-content: center; min-height: 44px; padding: 8px 12px; background: var(--panel-2); }
  :root[data-mobile] .ips-stitle { font-size: 15px; font-weight: 600; color: var(--ink); }
  :root[data-mobile] .ips-shead .ipp-close { position: absolute; right: 12px; }
  :root[data-mobile] .ips-row { display: flex; align-items: center; justify-content: space-between; width: 100%; min-height: 44px; padding: 0 16px; border: 0; background: none; font: inherit; font-size: 15px; color: var(--ink); text-align: left; cursor: pointer; }
  :root[data-mobile] .ips-row.on svg { color: var(--ds-accent); }
  :root[data-mobile] .ips-row:active { background: color-mix(in srgb, var(--ink) 6%, transparent); }

/* THE ONE MARGIN + THE ONE SECTION GAP (final polish pass). --mx is the single horizontal inset
   every section on a phone screen lands on - heads, tab rows, filter chips, list row content,
   the foot. --gap-sec is the section-to-section gap, deliberately a step above the row pitch so
   groups read as groups. Sheets use the same tokens; the picker's search -> filter -> list
   hierarchy spaces 24 / 14 / 6. */
:root[data-mobile] { --mx: 16px; --gap-sec: 24px; }
:root[data-mobile] .panel-head { padding-left: var(--mx); padding-right: var(--mx); }
:root[data-mobile] .lstate { padding: 40px var(--mx) 40px; }
:root[data-mobile] .lstate-sk-row { padding: 6px var(--mx); }

/* NEWS ON A PHONE: the filter strip owns its own row.
 * The category chips sat in the same flex line as the tools, so on a 390-point sheet the
 * "All / Crypto / ..." strip and the gear row fought for width and the loser overran the
 * hairlines above the list - the button overextending over the lines, as he put it. The strip
 * now scrolls horizontally inside its own lane and can never push anything out of the frame. */
:root[data-mobile] .nw-cats {
  flex: 1 1 auto;
  flex-wrap: nowrap;
  overflow-x: auto;
  max-width: 100%;
  scrollbar-width: none;
}
:root[data-mobile] .nw-cats::-webkit-scrollbar { display: none; }
:root[data-mobile] .nw-chip { flex: 0 0 auto; }
/* The asset pill wears its face at a size a thumb can read: two more points than the inline
   12px attribute, on its own rounded tile like the picker marks. */
:root[data-mobile] .nw-pill-logo { width: 15px; height: 15px; border-radius: 4px; }
:root[data-mobile] .nw-pill { gap: 5px; }

/* ================================================================== SESSION 17 */

/* ONE CLOSE PER TAB. A fresh tab drew its own X on the page (newtab-close) and every view head
   drew a back chevron (newtab-back) - both sat a few points under the close the tab bar already
   owns, and two X-looking glyphs on one screen read as two ways to lose the tab (his report,
   twice: the chooser and the Web directory). The tab bar's X is the only close; the dock and the
   view bar are the navigation, so neither of these ever carried a job the bar cannot do. The
   newtab WIZARD's back (instrument steps) is not inside a panel-head and stays. */
:root[data-mobile] .newtab-close { display: none !important; }
:root[data-mobile] .panel-head .newtab-back { display: none !important; }

/* THE PICKER SHEET IS A BOX, not a page that happens to be capped. The bare sheet had no
   resolved height, so ipp-root's height:100% resolved against the CONTENT - a 433,000px-tall
   root on the All tab: the head sat off-screen above the fold (his "little strip at the top"),
   the list never scrolled inside its own lane, and the sheet's 1px border read as a line across
   the menu - and with the square head corners sitting inside the rounded sheet the top geometry
   read as dirty. Two anchors give every child a real height to flex inside; NO border on any
   side and overflow hidden so the corners clip to the radius; one flat ground colour. */
:root[data-mobile] .win.sheet.win-bare {
  top: 6vh !important;
  bottom: 0 !important;
  height: auto !important;
  max-height: none !important;
  border: 0 !important;
  overflow: hidden;
  background: var(--panel);
  border-radius: 18px 18px 0 0 !important;
}
/* THE TAP ROUND IS NOT OURS. iOS paints a grey disc behind every touched button - visible
   exactly on the small, precise targets like a close cross, which is the "weird round thing"
   on the close icons. Native apps do not have it; neither do we. */
:root[data-mobile] button,
:root[data-mobile] [role='button'],
:root[data-mobile] label {
  -webkit-tap-highlight-color: transparent;
}
/* The hidden switch-checkbox behind an armed close icon. Clip + opacity, NEVER display:none
   and NEVER appearance:none - either one kills the iOS haptic (see lib/haptics.js). */
:root[data-mobile] .haptic-switch {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  opacity: 0;
  pointer-events: none;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  overflow: hidden;
  white-space: nowrap;
}
/* The chooser's rows are taps, not links: a 48pt row is the app's own minimum-control rule,
   and his thumb kept missing the smaller ones. */
:root[data-mobile] .newtab-mrow { min-height: 48px; }
/* The cut line under a capped list - quiet, one row, says how to reach what was cut. */
:root[data-mobile] .iprow-hint {
  padding: 14px 16px 18px;
  font-size: 12px;
  color: var(--ink-dim);
  text-align: center;
}
:root[data-mobile] .win.sheet.win-bare .win-body { min-height: 0; }
:root[data-mobile] .ipp-root { background: var(--panel); }
:root[data-mobile] .ipp-head { background: var(--panel); }
:root[data-mobile] .ipp-tabs { background: var(--panel); }
:root[data-mobile] .ipp-list { min-height: 0; }

/* THE TICK SAYS IT IN THE DIGITS. The old flash painted a colour square BEHIND the price on
   every change - a box blinking at glance rate, which is exactly the thing he flagged. The
   replacement keeps the direction and loses the box: the number itself tints, then eases back
   to ink, so a fast list reads as colour moving through digits, the way a tape does.
   20f, THE COLOUR HOLDS (owner: the numbers should run "green or red ... like real time" and
   be white only when the market is quiet). The old 1s fade spent its whole life returning to
   ink, so on a phone a tick was a blink of faint colour. Now the direction colour sits at full
   strength for the first 60% and then releases - and because every new tick restarts the
   animation, a trending pair STAYS green or red while it moves and only a quiet one settles
   back to white. That is the exchange-app behaviour the ask names. */
@keyframes wlpTickUp {
  0% { color: var(--buy-hi, #35c46f); transform: translateY(2px); }
  12% { transform: translateY(0); }
  60% { color: var(--buy-hi, #35c46f); }
  100% { color: var(--ink); }
}
@keyframes wlpTickDn {
  0% { color: var(--sell-hi, #e85449); transform: translateY(-2px); }
  12% { transform: translateY(0); }
  60% { color: var(--sell-hi, #e85449); }
  100% { color: var(--ink); }
}
:root[data-mobile] .wlp-price { transition: color 300ms ease-out; }
:root[data-mobile] .wlp-price.flash-up { animation: wlpTickUp 1900ms cubic-bezier(0.22, 0.61, 0.36, 1); }
:root[data-mobile] .wlp-price.flash-dn { animation: wlpTickDn 1900ms cubic-bezier(0.22, 0.61, 0.36, 1); }

/* NEWS CATEGORIES AS TEXT TABS. The pills were boxes fighting the headlines for attention; the
   system's own tab grammar - dim text at rest, white and heavier when chosen - is what the
   reference's news strip does and what he means by clean. Same lane, same scroll, no boxes. */
:root[data-mobile] .nw-cats { gap: 20px; }
:root[data-mobile] .nw-chip {
  background: none;
  box-shadow: none;
  border-radius: 0;
  min-height: 36px;
  padding: 4px 0;
  font-weight: var(--w-med, 500);
  color: var(--ink-dim);
}
:root[data-mobile] .nw-chip.on { color: var(--ink); font-weight: var(--w-semi, 600); }

/* THE LIQUIDATION COLUMN IS A BOTTOM SHEET ON A PHONE. The dock is a 240pt column beside the
   chart - on a 390pt screen that is the whole screen, and the chart it exists to annotate
   vanished behind its own summary (his report: totals, top longs, top shorts, and no chart).
   Out of the flex flow and pinned to the foot at under half the height, it summaries OVER the
   chart instead of instead of it, and the canvas keeps the full width - it was measured at open
   with the column gone, because the CSS pulls it out of the flow before that measure runs. */
:root[data-mobile] .liqpanel {
  position: absolute !important;
  top: auto !important;
  bottom: 0 !important;
  left: 0 !important;
  right: 0 !important;
  width: 100% !important;
  height: 42vh !important;
  max-height: 42vh !important;
  border-radius: 14px 14px 0 0 !important;
  box-shadow: 0 -10px 34px rgba(0, 0, 0, 0.45);
}
:root[data-mobile] .liqpanel-grip { display: none; }
:root[data-mobile] .liqpanel-body {
  height: calc(42vh - 8px) !important;
  max-height: calc(42vh - 8px) !important;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}



/* THE FPBS SHEET - the indicator's own settings, in the app's sheet grammar. A head with back
   and close, groups of 48pt rows, one control per row. Same family as the picker sheet: flat
   panel ground, hairline separators, a stepper for numbers, the shared swatch for colours. */
:root[data-mobile] .fpb-root {
  height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--panel);
}
:root[data-mobile] .fpb-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 48px;
  padding: 0 8px 0 6px;
  box-shadow: inset 0 -1px 0 var(--line);
}
:root[data-mobile] .fpb-back,
:root[data-mobile] .fpb-x {
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--ink);
  background: none;
  border: 0;
  padding: 0;
  -webkit-tap-highlight-color: transparent;
}
:root[data-mobile] .fpb-title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
:root[data-mobile] .fpb-main {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding-bottom: max(16px, env(safe-area-inset-bottom));
}
:root[data-mobile] .fpb-group {
  padding: 18px 16px 6px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-dim);
}
:root[data-mobile] .fpb-page .fpb-row {
  width: 100%;
  min-height: 48px;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 16px;
  background: none;
  border: 0;
  box-shadow: inset 0 1px 0 var(--line);
  text-align: left;
  color: var(--ink);
  font-size: 15px;
  -webkit-tap-highlight-color: transparent;
}
:root[data-mobile] .fpb-page .fpb-row:active { background: var(--sel); }
:root[data-mobile] .fpb-row-label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
:root[data-mobile] .fpb-row-val { flex: 0 1 auto; font-size: 14px; color: var(--ink-dim); }
:root[data-mobile] .fpb-row-val.dim { opacity: 0.55; }
:root[data-mobile] .fpb-row > svg { flex: 0 0 auto; color: var(--ink-dim); }
:root[data-mobile] .fpb-row-tog {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  color: transparent;
}
:root[data-mobile] .fpb-row-tog.on { color: var(--ink); }
:root[data-mobile] .fpb-row.off .fpb-row-label,
:root[data-mobile] .fpb-slot.off .fpb-row-label { opacity: 0.45; }
:root[data-mobile] .fpb-row.danger .fpb-row-label,
:root[data-mobile] .fpb-row.danger > svg { color: var(--sell, #e5484d); }
:root[data-mobile] .fpb-row-color { flex: 0 0 auto; display: inline-flex; align-items: center; }
:root[data-mobile] .fpb-stepper {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 2px;
}
:root[data-mobile] .fpb-step {
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 1px solid var(--line);
  border-radius: 9px;
  color: var(--ink);
  -webkit-tap-highlight-color: transparent;
}
:root[data-mobile] .fpb-step:active { background: var(--sel); }
:root[data-mobile] .fpb-step-val {
  min-width: 52px;
  text-align: center;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  color: var(--ink-dim);
}
:root[data-mobile] .fpb-slot-grip { flex: 0 0 auto; display: inline-flex; color: var(--ink-dim); opacity: 0.6; }
:root[data-mobile] .fpb-slot { touch-action: pan-y; }
:root[data-mobile] .fpb-slot.dragging { opacity: 0.45; }
:root[data-mobile] .fpb-slot.drop-before { box-shadow: inset 0 2px 0 var(--ink); }

/* THE ANALYSIS HUB - the phone menu as a TradingView-style bottom sheet. True black, a grab
   bar, a 28px title, a circular close, and flat #1C1C1E card grids (2- and 3-wide) with
   uppercase group labels. Flat everywhere: the ONE gradient is the CTA's border. No shadows,
   no glass - the dim under the sheet is the only depth. */
:root[data-mobile] .ahub-back {
  position: fixed;
  inset: 0;
  z-index: 2147483000;
  background: rgba(0, 0, 0, 0.5);
  transition: opacity 240ms ease;
}
:root[data-mobile] .ahub-back.closing { opacity: 0; }
:root[data-mobile] .ahub-sheet {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  background: #0a0a0a;
  border-radius: 16px 16px 0 0;
  transform: translateY(100%);
  touch-action: none;
}
:root[data-mobile] .ahub-grab { padding: 8px 0 2px; display: grid; place-items: center; touch-action: none; cursor: grab; }
:root[data-mobile] .ahub-grab::before { content: ''; width: 36px; height: 4px; border-radius: 2px; background: rgba(255, 255, 255, 0.28); }
:root[data-mobile] .ahub-head { display: flex; align-items: center; justify-content: space-between; padding: 6px 16px 2px; touch-action: none; cursor: grab; }
:root[data-mobile] .ahub-title { margin: 0; font: 700 22px/1.2 var(--ui, system-ui); color: #fff; letter-spacing: -0.3px; }
:root[data-mobile] .ahub-x { width: 30px; height: 30px; border-radius: 50%; background: #2c2c2e; display: grid; place-items: center; color: #fff; cursor: pointer; }
:root[data-mobile] .ahub-body { overflow-y: auto; padding: 10px 16px calc(16px + env(safe-area-inset-bottom, 0px)); overscroll-behavior: contain; }
:root[data-mobile] .ahub-cta {
  width: 100%;
  border: 0;
  border-radius: 16px;
  padding: 1.5px;
  background: linear-gradient(100deg, #e34fd0, #7a5cff 55%, #3b82f6);
  background-clip: padding-box;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  padding: 15px;
  color: #fff;
  cursor: pointer;
  position: relative;
}
:root[data-mobile] .ahub-cta::before { content: ''; position: absolute; inset: 1.5px; border-radius: 14.5px; background: #1c1c1e; z-index: 0; }
:root[data-mobile] .ahub-cta > * { position: relative; z-index: 1; }
:root[data-mobile] .ahub-cta-ic { display: inline-flex; }
:root[data-mobile] .ahub-cta-ic svg + svg { margin-left: -10px; }
:root[data-mobile] .ahub-cta-label { font: 600 15px/1 var(--ui, system-ui); }
:root[data-mobile] .ahub-div { height: 1px; background: rgba(255, 255, 255, 0.08); margin: 14px 0; }
:root[data-mobile] .ahub-sect { font: 600 12px/1 var(--ui, system-ui); letter-spacing: 1.2px; text-transform: uppercase; color: #8e8e93; margin: 2px 2px 8px; }
:root[data-mobile] .ahub-grid { display: grid; gap: 8px; margin-bottom: 4px; }
:root[data-mobile] .ahub-grid.c2 { grid-template-columns: repeat(2, 1fr); }
:root[data-mobile] .ahub-grid.c3 { grid-template-columns: repeat(3, 1fr); }
:root[data-mobile] .ahub-card {
  border: 0;
  border-radius: 16px;
  background: #1c1c1e;
  padding: 20px 6px 14px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  color: #fff;
  cursor: pointer;
}
:root[data-mobile] .ahub-card:active { background: #2c2c2e; }
:root[data-mobile] .ahub-card-label { font: 500 12px/1.25 var(--ui, system-ui); color: #fff; text-align: center; }
:root[data-mobile] .ahub-card.off { opacity: 0.45; pointer-events: none; }

  /* THE WHEEL AT REST, BACK IN THE TAB (19s): the chip's own rect is the drum's whole window -
     overflow clips it, so nothing rises above the lower tab (that rise was the 19o complaint).
     The chip's own label stands down while the drum shows; the stepper's +/- stay live. */
  :root[data-mobile] .wp-rest {
    position: fixed;
    overflow: hidden;
    perspective: 220px;
    pointer-events: none;
    color: #fff;
    z-index: 44;
  }
  :root[data-mobile] .wp-rest span {
    position: absolute;
    left: 0;
    right: 0;
    height: 12px;
    line-height: 12px;
    font-size: 11px;
    font-weight: var(--w-med);
    text-align: center;
    color: #fff;
    text-shadow: 0 1px 2px rgba(0, 0, 0, .6);   /* 21a: same cushion as the big wheel's rungs */
        backface-visibility: hidden;
  }
  :root[data-mobile] .wp-rest span.c { font-size: 13px; font-weight: var(--w-semi); }
  :root[data-mobile] #tfBtn.wp-rest-host { color: transparent; }
  :root[data-mobile] .wp-rest-host.stepper .val { visibility: hidden; }
  /* the pair chip joins the drum carriers (19y): its name stands down while its drum shows,
     exactly as the period and tick chips' labels do */
  :root[data-mobile] #symBtn.wp-rest-host, :root[data-mobile] #symBtn.wp-rest-host * { color: transparent !important; }
  /* ============ 19x: THE CHROME FOLLOWS THE THEME ============ */

  /* THE ANALYSIS HUB WEARS THE THEME (owner: "when I changed theme, the menu didn't change").
     Every colour here was a hardcoded dark - the sheet stayed true black under a white page.
     The vars are the ones every other surface reads, with the shipped blacks kept as fallbacks
     so a first paint without them still looks like the sheet that shipped. */
  :root[data-mobile] .ahub-sheet { background: var(--bg, #0a0a0a); }
  :root[data-mobile] .ahub-title { color: var(--ink, #fff); }
  :root[data-mobile] .ahub-x { background: var(--panel-3, #2c2c2e); color: var(--ink, #fff); }
  :root[data-mobile] .ahub-grab::before { background: color-mix(in srgb, var(--ink, #fff) 28%, transparent); }
  :root[data-mobile] .ahub-cta::before { background: var(--panel-2, #1c1c1e); }
  :root[data-mobile] .ahub-div { background: color-mix(in srgb, var(--ink, #fff) 8%, transparent); }
  :root[data-mobile] .ahub-sect { color: var(--ink-faint, #8e8e93); }
  :root[data-mobile] .ahub-card { background: var(--panel-2, #1c1c1e); color: var(--ink, #fff); }
  :root[data-mobile] .ahub-card:active { background: var(--panel-3, #2c2c2e); }
  :root[data-mobile] .ahub-card-label { color: var(--ink, #fff); }
  /* The CTA's plate follows the panels; its text follows the ink, so it reads on a light plate
     under the gradient border the same way white read on the dark one. */
  :root[data-mobile] .ahub-cta { color: var(--ink, #fff); }
  :root[data-mobile] .ahub-cta-label { color: var(--ink, #fff); }

  /* THE INDICATOR SETTINGS SHEETS, to the instruments menu's standard (owner: "they feel a bit
     cheap"). The bones were right - themed ground, one row per field - and three details were
     off: the page chips were two mud-greys with nothing saying which one was live; the help
     box was a bordered rectangle, a box for the sake of a box; and the close button washed out
     on a light ground. */
  :root[data-mobile] .win.sheet .set-navitem {
    height: 32px;
    padding: 0 14px;
    border-radius: 16px;
    background: transparent;
    border: 1px solid color-mix(in srgb, var(--ink) 16%, transparent);
    color: var(--ink-dim);
  }
  :root[data-mobile] .win.sheet .set-navitem.on {
    background: var(--ink);
    border-color: var(--ink);
    color: var(--bg);
  }
  :root[data-mobile] .win.sheet .set-info {
    margin: 10px 16px;
    padding: 12px 14px;
    border: 0;
    border-radius: 12px;
    background: color-mix(in srgb, var(--ink) 5%, transparent);
    color: var(--ink-dim);
    white-space: pre-line;
  }
  :root[data-mobile] .win.sheet .win-close {
    color: var(--ink);
    border: 1px solid color-mix(in srgb, var(--ink) 12%, transparent);
  }
  :root[data-mobile] .win.sheet .set-foot {
    border-top: 1px solid color-mix(in srgb, var(--ink) 8%, transparent);
  }

  /* 20j, REORDER BY LONG PRESS: the lifted tab rides the finger - no transition - while the
     others slot aside animated; the lift itself reads as picked-up: scale, shadow, full ink. */
  .mtabs-scroll .mtab { transition: transform .18s var(--ease); touch-action: pan-y; /* horizontal drags come here for the reorder, vertical still scrolls */
  -webkit-user-select: none; user-select: none; }
  /* no touch-action pan: the OS scroll machine was cancelling the drag with pointercancel
     the moment the finger moved - the lift armed and died in the same gesture */
  .mtab.mtab-drag {
    transition: none;
    position: relative;
    z-index: 3;
    opacity: 1;
    box-shadow: 0 10px 24px rgba(0, 0, 0, .55);
  }

/* ===========================================================================
   20y - THE iOS PASS. Three ideas, no glass (the owner removed the blur once:
   "weird glass effect ... pls remove, make it plain" - it stays plain).

   1. HAIRLINES, NOT BORDERS. Half the "noise" on the phone is chrome drawing
      its separators at a full-strength grey that competes with the data. On
      the phone every --line/--line-2 consumer drops to an alpha of the ink:
      present enough to separate, quiet enough to read past. Canvas data
      (candles, ladder, tape) is painted from JS constants and untouched.
   2. EVERY PRESS ANSWERS. An iOS surface is known by its press state: the
      control dims AND contracts slightly while the finger is down, and eases
      back over 120ms. One shared rule for every pressable on the phone.
   3. SOFT CORNERS ON FLOATING SURFACES. Menus and popovers pick up the sheet's
      own radius language (13-16px, iOS's menu corner) with a hairline edge,
      so nothing floating on the chart carries a square desktop corner.
   =========================================================================== */
@media (max-width: 640px) and (pointer: coarse),
       (max-height: 500px) and (max-width: 950px) and (pointer: coarse) {
  /* data-mobile beats the appearance themes: applyChromeTheme writes --line/--line-2 as INLINE
     styles on <html>, and an inline declaration beats every ordinary selector. A stylesheet
     !important is the one thing that outranks it (the inline write is not important). */
  :root[data-mobile] {
    --line: color-mix(in srgb, var(--ink) 10%, transparent) !important;
    --line-2: color-mix(in srgb, var(--ink) 16%, transparent) !important;
  }

  .ctl,
  .mtab,
  .msheet-row,
  .dock > *,
  .ctxmenu button,
  .nw-panel button,
  .iprow {
    transition: background-color .12s ease, color .12s ease,
      border-color .12s ease, transform .12s ease, opacity .12s ease;
  }
  .ctl:active,
  .mtab:active,
  .msheet-row:active,
  .dock > *:active,
  .ctxmenu button:active,
  .iprow:active {
    transform: scale(.97);
  }
  /* A sheet row pressed flat is iOS's own highlight; the scale would wobble a
     full-width row's edges, so rows dim without contracting. */
  .msheet-row:active { transform: none; background: var(--panel-2); }

  .ctxmenu,
  .nw-panel,
  .msheet {
    border-radius: 14px;
    border: 1px solid color-mix(in srgb, var(--ink) 10%, transparent);
  }
  .msheet { border-radius: 16px 16px 0 0; }
  .ctxmenu { overflow: hidden; }
  .ctxmenu button { min-height: 44px; }

  /* The grabber reads at a glance; make it feel like a handle. */
  .msheet-grip { width: 40px; height: 5px; border-radius: 999px; background: color-mix(in srgb, var(--ink) 26%, transparent); }

  /* Titles carry the sheet; iOS sheet titles are a semibold step up from body. */
  .msheet-title { font-size: 16px; letter-spacing: -.01em; }

  /* NEWS SETTINGS ROWS NEVER CLIP. The right-aligned row ran a wide segmented control past the
     panel's edge - "Price only" was entirely off-screen, Time and Rows hugged the border. iOS
     stacks it: the label stays, the control takes the full row width on the next line (the
     narrow toggle rows don't wrap at all - they fit). */
  .nw-prow { flex-wrap: wrap; row-gap: 8px; }
  .nw-prow > .nw-seg { flex: 1 1 100%; }
  .nw-prow > .nw-seg .nw-segbtn { flex: 1 1 auto; }

  /* ONE ACCENT, NOT A RAINBOW. The Trade card's pink-purple-blue gradient ring read as promo
     chrome on an instrument - the one element on the phone that looked bought rather than built.
     A single quiet accent ring says "this one matters" the way the rest of the palette does. */
  :root[data-mobile] .ahub-cta {
    background: color-mix(in srgb, #3b82f6 55%, transparent);
  }
}
