/* The window frame: tool rail, chart, trades dock, watchlist, side rail.
 *
 * Loaded after app.css and never edits it. Everything here is the arrangement
 * of the panels and the chrome around them - the panels' own insides (the
 * ticket, the trade tables, the report, every dialog) are still styled where
 * they always were. That split is the point: this file can be thrown away and
 * the application still works, looking as it did before.
 *
 * Right-to-left is left alone deliberately. `main` is a flex row, so in
 * Persian the browser mirrors the whole arrangement by itself - tools on the
 * right, watchlist on the left - which is what a Persian reader expects. The
 * chart keeps `direction: ltr` from app.css because time runs left to right in
 * every language.
 */

:root {
  /* How much of the page is the window's own title bar. Zero in a browser,
     where the browser draws one; see `#titlebar` below. Every height that is
     worked out from the viewport subtracts it, so there is one number to
     change and nothing to keep in step by hand. */
  --titlebar:  0px;

  --rail:      44px;   /* the drawing tools */
  --sidebar:   300px;  /* watchlist + ticket */
  --iconrail:  38px;   /* the strip that shows and hides them */
  --dock:      210px;  /* the trades panel under the chart */
  --watch:     240px;  /* the watchlist above the ticket */
  --grip:      5px;    /* what you drag */
}

/* ---------- light mode ---------- */
/* The whole interface reads its colours from these variables, so a light theme
   is just a second set of them. The per-chart colour overrides (theme.js) still
   sit on top of whichever mode is chosen.

   On the root element, not on <body>. A custom property is inherited *down*,
   so a palette declared on the body reaches every panel and never reaches
   <html> - and the chart reads its colours off <html>, once a frame, through
   getComputedStyle(document.documentElement). Declared on the body, light mode
   turned the whole interface light around a chart that stayed black. */

:root.light {
  /* What the BROWSER paints for us, and this is the light half of it. See
     the note beside the dark one in app.css. Declared INSIDE the palette
     rather than in a rule of its own: a second `:root.light {` above this one
     shadowed it for anything reading the first block it finds, and one of
     those things is the check that the title bar is the same grey as the
     panel under it. */
  color-scheme: light;

  /* The panels are near-white; the chart is the surface that steps down.
   *
   * Third arrangement. The first had a white ground with grey panels on it, so
   * the chart - the largest area on screen - sat at full brightness. The second
   * softened the ground and left the panels white, which put every surface
   * within 1.07 of its neighbour: nothing had an edge and the whole interface
   * read as one sheet of paper.
   *
   * The obvious third move, darkening the panels, was tried and the numbers
   * refused it. Every ink colour here was measured against a near-white
   * ground; at 1.16 the green, the red, the blue and the accent all drop under
   * 4.5 to one at once. So the panels stay where they are and the *chart*
   * comes down a step - it is the one large area with almost no small text on
   * it, it is the thing that was too bright, and a fifth of a stop is enough
   * to give every panel an edge before a line is drawn.
   *
   * Solved for rather than chosen: hue and saturation held, lightness moved
   * until the ratio came out. The chart sits 1.20 under the panels.
   */
  /* The depth tokens, turned over for a pale ground. `--lift` and `--top` are
     defined in the dark theme as white over the surface; on paper white over
     near-white is nothing at all, and `--panel-2` - which is *darker* here -
     would have shaded the top of every card instead of lighting it. */
  /* Weak, and finished early. Measured from the painted pixels: at .85 fading
     to 62% of the height, a 34px row was still being lightened in the middle
     and came out at 1.07 against the sheet behind it - under the 1.15 this
     theme was solved to. The highlight belongs on the top edge only; the depth
     on paper comes from the shadow under the card, not from bleaching it. */
  --sheen: inset 0 1px 0 rgba(255,255,255,.9), 0 1px 2px rgba(30,40,60,.07);
  --top: linear-gradient(180deg, rgba(255,255,255,.34), transparent 38%);
  /* A field on paper is a shade UNDER the card, not over it - the opposite
     turn from the dark theme, and the reason `--field` is translucent black
     here. Same idea either way: one step off whatever is underneath. */
  --field:       rgba(0,0,0,.045);
  --field-hover: rgba(0,0,0,.075);
  --caret: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20'%3E%3Crect x='.5' y='.5' width='19' height='19' rx='6' fill='%23d8dadd'/%3E%3Cpath d='M6.6 8.6 10 12l3.4-3.4' fill='none' stroke='%234a4f57' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --lift: linear-gradient(180deg, rgba(255,255,255,.30), transparent 40%);
  /* A sunk track. On paper the shadow that hollows something out is a
     grey a shade under the ground, not the black the dark theme uses. */
  --well: inset 0 1px 2px rgba(30,40,60,.14);
  /* A third of the dark theme's, and green dropped entirely. Measured: at 7%
     the wash took the sheet from #ecf1f7 to #e7eef6, which moved the paper
     *toward* the fields sitting on it - 1.169 down to 1.135, under the floor.
     A ground on paper has to be nearly nothing. */
  --ground: radial-gradient(90% 65% at 15% -10%, rgba(255,255,255,.6), transparent 58%),
            radial-gradient(70% 55% at 92% 108%,
              color-mix(in srgb, var(--accent) 4%, transparent), transparent 60%);

  /* GitHub's Primer, quoted from its published token file, with the steps
     opened until this interface's bars hold.

     As published, Primer's white panel and its own muted grey are 1.06 apart
     where this asks 1.15 - and Radix's slate, the family the Linear look is
     built on, measures 1.11 at the same place. Neither is careless: their
     hierarchy is carried by borders and spacing where ours is carried by one
     surface being lighter than the next. So the colours are theirs and the
     spacing is ours.

     The panels are white, which is what gives that family its clean look and
     what this theme did not have: it was a blue-grey throughout, and that
     blue is exactly what stopped it looking like the thing it was copying. */
  /* Taken down with the panels, so the chart keeps the edge it had.
     Darkening the panels alone softened the chart-to-panel step from 1.275 to
     1.157 - still over the floor, but visibly less held. The ground could not
     move before because the candles on it were only 3.6 to one; now that they
     have come down too they are at 4.25 here, so there is room. Back to
     1.260, which is where it was.

     Then taken up again, deliberately, to #e0e2e3. Six values between here and
     the panels were measured on the running program and this is the lightest
     one that still clears every floor: 1.18 against the panels, and it *lifts*
     the two numbers that were sitting on their minimums - the grid line from
     1.404 to 1.500 and the time labels from 4.553 to 4.863. The step to the
     panels is smaller than it was; the marks on the chart are easier to read
     than they were. */
  --bg:        #e0e2e3;
  /* And inside the chart pane the two change places: the canvas takes the
     light one and the strip of prices beside it takes the ground. The chart
     reads its own names first, so this is said to the chart alone and no panel
     in the rest of the window moves. A colour somebody picks for either is
     written on the root by the theme and still wins over both of these. */
  --chart-bg:    #f3f4f4;
  --chart-panel: #e0e2e3;
  /* Off paper-white, and that is the whole intent.
   *
   * The panels were #ffffff and there are a great many of them: a quarter of
   * the drawn area sat within a whisker of white, and the two largest regions
   * on the screen - the trades panel and the indicator sheet - were almost
   * entirely it, with a handful of glyphs in the middle. That reads as
   * unfinished rather than as calm.
   *
   * The note that used to stand here said this had been tried and the numbers
   * refused it, because six inks drop under 4.5 to one the moment the panel
   * moves. The arithmetic was right and the conclusion was wrong: an ink that
   * falls under the bar can be walked back down to it, which is what this
   * palette already does in four other places. Every one of the six was
   * re-solved by a walk that holds its hue exactly - see
   * `design/light-panel-solve.mjs`, which prints the whole ladder.
   *
   * What it costs: the chart-to-panel edge goes 1.275 to 1.157, still over
   * the floor. What it buys, besides the white: the candles on the chart
   * ground go from 3.60 to 4.60, because the two market colours came down
   * with everything else. */
  --panel:     #f3f4f4;
  /* Solved, not nudged: a real step under the panel now that the panel has
     moved. 1.168, where it was 1.164 against white. It sits within a hair of
     the chart ground, which is fine - they are never next to each other. */
  --panel-2:   #e1e3e4;
  --panel-3:   #ccced1;   /* the third step, matching the dark theme's */
  /* A divider at 1.64 against the panel beside it. */
  /* A divider has two sides and was only ever measured against one.
     Against a white panel it reads 1.65 and passed every check; against
     the grey ground - which is the side the top bar, the rail, the dock
     and every form field on a sheet actually meet it on - it read 1.29
     against a floor of 1.40. Sixteen edges in the running program.
     Eight points deeper puts the weak side at 1.40 and the strong side
     at 1.79. */
  /* Deeper again with the ground: on the darker ground the old value read
     1.289 against a floor of 1.40. */
  --line:      #b7babd;
  /* Follows the panel down. It was lighter than the panel it divides, which
     was invisible on white and would have been a pale line on a grey. */
  --line-soft: #eceeef;

  /* Measured against both the card and the ground, not chosen by eye. The
     three greys come out at 14.4, 5.8 and 4.2 to one on the ground; the old
     --faint sat at 3.9, which is under the line for text that small. */
  --text:      #212324;
  /* Primer holds two greys for writing and this holds three, so the middle
     one is solved on their own neutral rather than invented: midway in
     contrast between the ink above it and the muted one below. */
  --dim:       #3b3d3f;
  /* Darkened a shade with the chart. This one labels the price and time axes,
     which are small text sitting on the chart rather than on a panel, and on
     the new ground the old value came out at 3.7. */
  --faint:     #5d6064;

  /* Deeper than the dark theme's, because the same hue on white is a paler
     contrast: the old green measured 2.9 to one against the ground, which is
     not a readable figure for a profit somebody has to trust. These are 4.5,
     4.5 and 4.3. */
  /* The dark theme's hues, at the luminance these already had, so every
     figure above still describes them: 4.60, 4.58 and 4.60 where they were
     4.57, 4.59 and 4.57. */
  --up:        #00734d;
  /* The dark theme's hues, at the luminance these already had, so every
     figure this palette was solved for is the number it was. */
  --down:      #b13e22;
  /* The same separation as the dark palette: this is a buy, not the accent.
     The dark theme's green taken deep enough to be read on paper - `#26a67b`
     measures 2.71 against this panel, which is a colour somebody can see and
     not one they can read, and this is 5.35 against the panel and 4.58
     against the ground. White on it is 6.08, better than the 5.3 the gold it
     replaces managed, so the ink stays white here. */
  /* Not moved to the candle hue: see `--buy` in `app.css`. A buy is
     green, and at 174 it is a teal. */
  --buy:       #0f7050;
  --sell:      #b13e22;
  /* The same split as the dark theme, and the same fault was here too:
     25% of lightness against 49%. White reads on both. */
  --buy-solid:  #0f7050;
  --sell-solid: #64291b;
  --on-buy:    #ffffff;
  --on-sell:   #ffffff;
  /* The dark theme's cyan, taken deep enough to hold on paper. Solved for
     the same contrast the violet it replaces had against this ground rather
     than picked by eye, so nothing that was legible stops being so: 3.97
     against the ground where the violet was 3.96, 4.64 against a panel where
     it was 4.63, and white on it 5.27 where it was 5.26. */
  --accent:    #2667a7;
  --accent-2:  #25598d;
  /* The same fault the dark theme had, and found by the same measurement: as
     a glyph on this theme's ground the accent comes out at 4.11. Seven points
     deeper rather than lighter, because here the ground is the pale one. */
  /* Deeper again with the ground: on #d9dbdd the old value read 4.165. */
  --accent-ink: #2162a2;
  /* Both market colours needed deepening here, and both by about the
     same amount: on --panel-2 the red measured 3.94 and the green 3.96,
     against a floor of 4.5. The floating figure - the one that says how
     much you are up or down - is the writing this was found on.

     The green could not be moved the way the red was. Taking the same
     amount off each channel keeps a colour recognisable, but #008659 has
     no red left to take, so the walk clipped on its first step and
     reported the green unreachable. Scaling all three by one fraction
     keeps the hue exactly and never clips: both are their own hue to the
     tenth of a degree. */
  --up-ink:    #00744d;
  --down-ink:  #b13e22;
  --ink:       #ffffff;
  /* The gold, taken deep enough to be read on paper. The dark theme's
     `#d4a03c` measures 2.1 to one against a near-white panel, which is a
     colour somebody can see and not a colour somebody can read; this one is
     4.6. Hue held, lightness moved, the same way the green and red were
     solved for this theme. */
  /* The mark and the writing part company in this theme. As a filled
     star or a news flag the gold is a shape and 4.13 against the chart
     is fine for one; as the session's NAME on the chart it is ten-pixel
     writing and 4.13 is under the floor. Seven points deeper clears it
     at 4.57 without moving the hue at all. */
  --gold:      #836911;
  /* Likewise: 4.192 on the new ground, against a floor of 4.5. */
  --gold-ink:  #775d05;
  /* The crypto market's colour, same hue on paper as on the screen and
     carried down to hold as writing: 5.42 : 1 on the card. */
  --cat-crypto: #15626f;
  --news:      #836911;

  /* The three that had no light values at all until now, because they were
     written into the stylesheet as dark literals. */
  --hover:       #eceeef;
  --line-strong: #b4b7bb;
  --sel:         #bfcedc;   /* same luminance as the violet one it replaces */

  /* Lighter grounds need much softer shadows than the dark theme's. */
  --elev-1: 0 1px 2px rgba(30,40,60,.06);
  --elev-2: 0 12px 30px -14px rgba(30,40,60,.18), 0 2px 6px -3px rgba(30,40,60,.10);
  --ring:   color-mix(in srgb, var(--accent) 55%, transparent);
}
/* The light theme's scroll bars come from the same tokens as the dark one's
   now - see the note there. Nothing to override. */

/* ---------- the window's own title bar ---------- */
/* Only in our own window. Windows draws no frame around it any more, so this
   strip is the whole of the top of the window: the name, somewhere to take
   hold of it, and the three buttons.

   Sized like Windows' own, on purpose. 32 tall, buttons 46 wide, the close one
   turning red — a person should not have to look for controls they have used
   every day for years, and matching them exactly is cheaper than teaching
   anybody anything. What is ours is the colour, which follows the theme like
   everything else here, and the fact that it is the same surface as the bar of
   chart controls directly underneath rather than a different program's. */
:root.native { --titlebar: 32px; }

#titlebar { display: none; }

:root.native #titlebar {
  display: flex;
  align-items: stretch;
  height: var(--titlebar);
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  /* Above everything, and this is not decoration. Several screens are
     full-window overlays — the language question, the sign-in, every dialog —
     and any one of them covering this strip is a window that cannot be moved
     or closed while it is on screen. The language question does exactly that,
     and it is the first thing a new customer sees. The highest z-index
     anywhere else in the interface is 200. */
  position: relative;
  z-index: 300;
  /* Left to right in both languages. See the note in index.html: the close
     button lives in the top-right corner because that is where Windows keeps
     it, and a mirrored one would be a trap for the hand rather than a courtesy
     to the reader. */
  direction: ltr;
  user-select: none;
}

#tbdrag {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 10px;
  /* Every pixel of this is the handle. `-webkit-app-region` does nothing here
     — that is a Chrome-app-window feature and this is not one — so the drag is
     started from JavaScript and handed to Windows. See web/titlebar.js. */
  cursor: default;
}

.tbmark { width: 15px; height: 15px; flex: 0 0 auto; border-radius: 3px; }

/* Where the three things in this bar sit, measured rather than judged.
 * `design/titlebar-centre.mjs` photographs the strip at 5x and reads the
 * numbers below off the PAINT. None of this can be got from the source, and
 * two goes at it from the metrics alone were both wrong.
 *
 * All three BOXES are centred and always were - the mark's centre and both
 * text boxes' centres land within 0.01px of each other. What is not centred
 * is the paint inside them, and the miss is different for each because the
 * glyphs are different. Measured as the centre of the ink MASS, which is what
 * an eye levels against - the extreme edges are no good, because one letter's
 * descender counts as much there as the whole body of the word:
 *
 *     from the bar's centre line       mark    Rahyab    رهیاب     1.1.67
 *     the element offset by -1px      -0.05     -2.08     -0.15     -1.29
 *                             0       -0.05     -1.08     +0.85     -0.29
 *                            +1       -0.05     -0.08     +1.85     +0.71
 *
 * So the name wants +1 in English and -1 in Persian - a two-pixel spread,
 * entirely because Latin hangs one letter below the line and Persian hangs
 * most of them - and the version wants 0 in both, being the same digits
 * either way. The mark is already there and is not moved: it is the one
 * thing whose box IS its paint.
 *
 * The name and the version are nudged separately, which would be wrong for
 * two words in a sentence and is right here: they are 12px and 10.5px, in
 * different faces and different colours, with a gap between them. Nobody
 * reads them as sharing a baseline; they read each one against the mark. An
 * earlier version tied them together and one of the two was always out.
 */
.tbname { position: relative; top: 1px; }
body.fa .tbname { top: -1px; }

.tbname {
  font-size: 12px;
  color: var(--dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The version, beside the name and quieter than it. Latin figures, so it is
   its own left-to-right island and does not re-order against a Persian
   interface. */
.tbver {
  font: 10.5px var(--mono);
  color: var(--faint);
  direction: ltr; unicode-bidi: isolate;
  white-space: nowrap; flex: 0 0 auto;
}
.tbver:empty { display: none; }

/* Above the strips at the rim, and that is a decision rather than an accident.
   The top strip is four pixels tall and the corner one is eight square, so
   left underneath them the top edge of all three buttons — and the whole
   top-right corner of Close — would be a resize handle instead. Closing is
   the more common action and the more annoying one to miss, so the buttons
   win. Resizing from the very top-right corner is what that costs; the right
   edge and the top edge are both still there, either side of it. */
#tbbtns { display: flex; flex: 0 0 auto; position: relative; z-index: 500; }

#tbbtns button {
  width: 46px;
  border: 0;
  background: transparent;
  color: var(--text);
  display: grid;
  place-items: center;
  padding: 0;
  border-radius: 0;
  transition: background .12s ease, color .12s ease;
}

#tbbtns svg {
  width: 10px; height: 10px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1;
  /* At 10 px a stroke that lands between two pixels is a grey smudge rather
     than a line. Squared ends sit on the pixel grid. */
  stroke-linecap: square;
}

#tbbtns button:hover { background: color-mix(in srgb, var(--text) 12%, transparent); }
#tbbtns button:active { background: color-mix(in srgb, var(--text) 20%, transparent); }

/* The one button that is not grey on hover, in every version of Windows since
   8. Red here too, and white on it in both themes rather than var(--text),
   because the ground is the same red either way. */
#tbclose:hover { background: #c42b1c; color: #fff; }
#tbclose:active { background: #b8291b; color: #fff; }

/* Which of the two glyphs the middle button shows. A single square while the
   window can still be made bigger, two stacked once it is maximized. The class
   is put on by the window itself, not by the click, because Win+Up, a
   double-click and the snap grid all maximize without asking the page. */
#tbmax .tbstack, :root.zoomed #tbmax .tbsquare { display: none; }
:root.zoomed #tbmax .tbstack { display: block; }

/* The eight strips at the rim that the window is resized by. Invisible, and
   thin: six pixels is about what Windows itself gives a window's border, and
   four at the top so that the corner does not eat into the close button.

   Gone while the window is maximized, because a maximized window has no edges
   to pull — leaving them there would put a resize cursor over the chart. */
#tbgrips { display: none; }
:root.native #tbgrips { display: block; }
:root.native.zoomed #tbgrips { display: none; }

#tbgrips i { position: fixed; z-index: 400; }

#tbgrips [data-edge="3"] { top: 0; left: 8px; right: 8px; height: 4px;
                           cursor: ns-resize; }
#tbgrips [data-edge="6"] { bottom: 0; left: 8px; right: 8px; height: 6px;
                           cursor: ns-resize; }
#tbgrips [data-edge="1"] { left: 0; top: 8px; bottom: 8px; width: 6px;
                           cursor: ew-resize; }
#tbgrips [data-edge="2"] { right: 0; top: 8px; bottom: 8px; width: 6px;
                           cursor: ew-resize; }
#tbgrips [data-edge="4"] { top: 0; left: 0; width: 8px; height: 8px;
                           cursor: nwse-resize; }
#tbgrips [data-edge="5"] { top: 0; right: 0; width: 8px; height: 8px;
                           cursor: nesw-resize; }
#tbgrips [data-edge="7"] { bottom: 0; left: 0; width: 8px; height: 8px;
                           cursor: nesw-resize; }
#tbgrips [data-edge="8"] { bottom: 0; right: 0; width: 8px; height: 8px;
                           cursor: nwse-resize; }

/* ---------- the frame ---------- */

main {
  display: flex;
  height: calc(100% - var(--topbar) - var(--titlebar));
  min-height: 0;
  overflow: hidden;
}
/* Below 600 the order column moves under the chart and the page scrolls down
   to it (app.css, «A window narrower than the chart's columns»). The rule
   above loads later and beat that one, so nothing scrolled and the ticket's
   fields and Buy and Sell were out of reach (the round-3 area-13 adversary,
   F2). */
@media (max-width: 600px) {
  main { overflow-y: auto; }
}

#center {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column;
  min-height: 0;
}

/* A flex row of the chart and — when it is open — the second-timeframe pane
   beside it. The tool strip is no longer in here (it is its own column now),
   but the row layout has to stay: the context pane is a flex sibling that
   takes 38% off the right, and the refactor that made this `block` quietly
   left that pane nowhere to go, so "show a higher timeframe alongside" opened
   onto nothing. Back to a row; the canvas takes what the context pane does
   not. */
/* ---- the composition pass ----
 *
 * A: one spacing scale, B: three type roles, D: four surfaces. Written
 * together because they are one decision - what this screen is composed
 * of - and separating them would leave three rules nobody can see the
 * reason for.
 */

/* A - the side column on the scale. */
#paneTrade .panel { padding: var(--s3) var(--s4); }
#paneTrade .field { margin-bottom: var(--s2); }
.acctstats, .accrows { gap: var(--s2); }
/* Six below and seven inside rather than eight and eight: the promise made
   when the watchlist and the ticket started taking turns (shell.js) is seven
   rows at 1366x700 with the ticket closed, and on the scale's eights the
   seventh card was cut at its middle - 28 of heading and seven cards of 76
   in 532 pixels of list. On 72 they fit with room. Measured by
   design/qa-rest.mjs (QA 15.1.8). */
#paneWatch .wrow { margin: 0 var(--s2) 6px; padding: 7px var(--s3); }
.quote { gap: var(--s2); margin-block: var(--s3) var(--s2); }
.buttons { gap: var(--s2); }
.seg.small { margin-bottom: var(--s2); }

/* B - the figure you act on, the row you read, the caption you glance
   at. Everything used to be between 11 and 12.5, which is one voice
   for three jobs. */
.quote .price {
  font-size: 21px; font-weight: 600; letter-spacing: -.2px;
  font-variant-numeric: tabular-nums;
}
.quote .side { font-size: 10px; letter-spacing: .8px; opacity: .85; }
#paneTrade .field > span:first-child,
.accrows span, .arow span:first-child { font-size: 11.5px; color: var(--faint); }
.accrows b, .arow b, .astat > b {
  font-size: 13.5px; font-variant-numeric: tabular-nums;
}
.buttons button { font-size: 14px; font-weight: 600; letter-spacing: .2px; }
.rcap, .secline { font-size: 10px; letter-spacing: 1.1px; }

/* D - a list lies flat on the ground, the tool it feeds is raised, the
   subject is sunk into the ground, and a table is neither. */
#paneWatch { background: var(--bg); box-shadow: none; }
#paneTrade { background: var(--panel); box-shadow: inset 0 1px 0 var(--line-soft); }
#dock { background: var(--bg); box-shadow: inset 0 1px 0 var(--line); }
#dockHead { background: transparent; }

#chartWrap {
  flex: 1 1 0; min-height: 0;
  display: flex; flex-direction: column;
  position: relative;
}
/* The canvas and the higher-timeframe pane, side by side. This is the row
   `#chartWrap` used to be; it became an inner element when the region got a
   header, because a header and a side-by-side pair cannot be the same flex
   container. Positioned, so every overlay on the chart measures from the top
   of the chart rather than from the top of the header. */
#chartRow {
  flex: 1 1 0; min-height: 0;
  display: flex;
  position: relative;
}
#chart { flex: 1 1 0; min-width: 0; height: 100%; }

/* The tools used to sit inside the chart area, so this was pushed clear of
   them. The chart now starts at its own edge. */
#hud { left: 12px; }

/* ---------- what a menu row is made of ----------
 *
 * A command, and the figure that command carries: closing this trade realises
 * that much, half of it is that many lots, reversing it makes that. The
 * figures sit on the trailing edge in the mono face so the column of them
 * lines up and can be read down.
 */
.topmenu .mlab { min-width: 0; overflow: hidden; text-overflow: ellipsis;
                 white-space: nowrap; }
.topmenu .mhint {
  /* At the far end of its track, so the figures read as a column. */
  justify-self: end; text-align: end;
  font-family: var(--mono); font-size: 11px; color: var(--faint);
  direction: ltr; unicode-bidi: isolate;
}
.topmenu button:hover .mhint { color: var(--dim); }
.topmenu button.up .mi, .topmenu button.up .mhint { color: var(--up-ink); }
.topmenu button.down .mi, .topmenu button.down .mhint { color: var(--down-ink); }

/* Which trade the menu is about. Not a button: there is nothing to press
   here, it is the thing the buttons under it are about. */
.topmenu .mhead {
  display: flex; align-items: baseline; gap: 10px;
  padding: 8px 12px 7px; margin-bottom: 2px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--panel-2);
}
.topmenu .mhead b { font-size: 12px; font-weight: 600; }
.topmenu .mhead .mnote {
  margin-inline-start: auto;
  font-family: var(--mono); font-size: 12px; color: var(--dim);
  direction: ltr; unicode-bidi: isolate;
}
.topmenu .mhead .mnote.up { color: var(--up-ink); }
.topmenu .mhead .mnote.down { color: var(--down-ink); }

/* ---------- what the chart's menu opens with ----------
 *
 * The price under the pointer. A right-click on a chart is about a price
 * whether or not anything of yours happens to be near it, and the menu used
 * to have a subject only in the twelve pixels around a trade's own line -
 * which is to say almost never.
 */
.topmenu .mtop {
  padding: 9px 12px 8px; margin: -5px -5px 3px;
  border-bottom: 1px solid var(--line-soft);
  background: var(--top), var(--panel-2);
  border-radius: 8px 8px 0 0;
}
.topmenu .mtopline { display: flex; align-items: baseline; gap: 8px; }
.topmenu .mtop b {
  font-family: var(--mono); font-size: 15px; font-weight: 600;
  letter-spacing: .3px; direction: ltr; unicode-bidi: isolate;
}
.topmenu .mtop .mpts {
  margin-inline-start: auto;
  font-family: var(--mono); font-size: 11px; color: var(--faint);
  direction: ltr; unicode-bidi: isolate;
}
.topmenu .mtop .mpts.up { color: var(--up-ink); }
.topmenu .mtop .mpts.down { color: var(--down-ink); }
.topmenu .mtop .msub {
  display: block; margin-top: 2px; font-size: 10.5px; color: var(--faint);
}

/* A section label. Written lower-case and upper-cased here, because Persian
   has no upper case and `text-transform` leaves it alone. */
.topmenu .mcap {
  padding: 8px 12px 3px;
  font-size: 9.5px; font-weight: 700; letter-spacing: 1px;
  text-transform: uppercase; color: var(--faint);
}

/* One sentence where a section would otherwise be missing. */
.topmenu .msaid {
  display: flex; align-items: center; gap: 7px;
  padding: 8px 12px 6px; font-size: 11.5px; color: var(--faint);
}
.topmenu .msaid::before {
  content: ''; width: 5px; height: 5px; border-radius: 50%;
  background: var(--line); flex: 0 0 5px;
}

/* The trade the pointer is on, as the thing it is. */
.topmenu .mcard {
  margin: 0 8px 5px; padding: 7px 9px; border-radius: 8px;
  background: var(--panel-2); border: 1px solid var(--line-soft);
}
.topmenu .mcardtop { display: flex; align-items: baseline; gap: 7px; }
.topmenu .mcard .mside {
  font-size: 9.5px; font-weight: 700; letter-spacing: .6px;
  padding: 1px 6px; border-radius: 4px;
}
.topmenu .mcard .mside.buy { background: var(--buy-solid); color: var(--on-buy); }
.topmenu .mcard .mside.sell { background: var(--sell-solid); color: var(--on-sell); }
.topmenu .mcard .mat {
  font-size: 11px; color: var(--dim);
  direction: ltr; unicode-bidi: isolate;
}
.topmenu .mcard .mgap { flex: 1; }
.topmenu .mcard b {
  font-size: 12px; direction: ltr; unicode-bidi: isolate;
}
.topmenu .mcard b.up { color: var(--up-ink); }
.topmenu .mcard b.down { color: var(--down-ink); }
/* Where the price is between the stop and the target. `inset-inline-start`
   rather than `left`, so the bar runs from the reading edge in both
   languages - unlike the watchlist's history track, this one has no dates
   under it and nothing about it says which way time goes. */
.topmenu .mbar {
  position: relative; height: 4px; margin-top: 7px;
  border-radius: 2px; background: var(--panel-3); box-shadow: var(--well);
}
.topmenu .mbar i {
  position: absolute; top: 0; bottom: 0; border-radius: 2px; opacity: .6;
}
.topmenu .mbar i.up { background: var(--up); }
.topmenu .mbar i.down { background: var(--down); }
.topmenu .mbar u {
  position: absolute; top: -2px; bottom: -2px; width: 2px;
  border-radius: 1px; background: var(--text);
}
.topmenu .mcard .mnone {
  margin-top: 5px; font-size: 10px; color: var(--faint);
}

/* A row of blocks: the two or three things somebody presses here. Written as
   buttons so they are reachable by keyboard like every other command. */
.topmenu .mrow { display: flex; gap: 6px; padding: 2px 8px 6px; }
.topmenu .mrow .mblock {
  flex: 1; display: block; margin: 0; padding: 7px 4px;
  border: 1px solid var(--line-soft); border-radius: 7px;
  background: var(--panel-2); color: var(--text);
  font: inherit; font-size: 11.5px; text-align: center; cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  box-shadow: var(--sheen);
  transition: border-color .12s, background .12s;
}
.topmenu .mrow .mblock.up { color: var(--up-ink); }
.topmenu .mrow .mblock.down { color: var(--down-ink); }
.topmenu .mrow .mblock.buy { color: var(--buy); }
.topmenu .mrow .mblock.sell { color: var(--sell); }
.topmenu .mrow .mblock:hover {
  background: var(--panel-3); border-color: var(--line);
  box-shadow: var(--sheen);
}
.topmenu .mrow .mblock span {
  display: block; margin-top: 2px; font-size: 10px; color: var(--faint);
  direction: ltr; unicode-bidi: isolate;
}

/* A shelf is as tall as what is on it.
 *
 * `.sheet.wide` sets `height`, not `max-height`, so this window stood at 74vh
 * whether it held three rows or none - three rows used 274px of 696 and the
 * rest was black. Only this one is changed: the journal and the laboratory
 * fill a fixed sheet on purpose. */
/* ---------- the challenge, as an invitation ----------
 *
 * What is being sent is a stretch of a market, so the window opens with that
 * stretch: its own candles, at the timeframe it was traded on. Under it, the
 * three figures as tiles, and the conditions as chips that can be seen to be
 * off. The five label-and-value rows and four empty boxes this replaces could
 * not say what they were - an empty box never manages to say "optional".
 */
/* The one thing on the panel before the thing exists - a challenge, a blind
   pass. A well of its own rather than a button loose under a paragraph,
   because it is the moment the thing begins and it should look like a step
   rather than like one more control. The sentence under it is where the
   button says what it is about to take, or why it cannot yet. */
.startstep {
  display: flex; flex-direction: column; align-items: flex-start; gap: 8px;
  padding: 16px; border-radius: 12px;
  background: var(--panel-2); box-shadow: var(--well);
}
/* The one thing to press sits in the middle of its card. The sentence under it
   is left where it is: a paragraph is read from one end, and centring it would
   cost more than the button gains. */
.startstep > button { align-self: center; }
.startstep .sub { margin: 0; max-width: 62ch; }
/* Off, it says why rather than looking broken: the sentence under it is the
   answer, so the button only has to stop being pressable. */
.startstep button[disabled] { opacity: .55; cursor: default; box-shadow: none; }

.chalhero {
  position: relative; height: 78px; border-radius: 10px; overflow: hidden;
  background: var(--bg); border: 1px solid var(--line-soft);
}
.chalhero canvas { display: block; width: 100%; height: 100%; }
.chalhero .chsym, .chalhero .chwhen {
  position: absolute; font-size: 10px; letter-spacing: .4px;
  direction: ltr; unicode-bidi: isolate; pointer-events: none;
}
.chalhero .chsym { inset-inline-start: 9px; top: 7px; color: var(--dim); }
.chalhero .chwhen { inset-inline-end: 9px; bottom: 6px; color: var(--faint); }

.chaltiles { display: flex; gap: 7px; }
.chaltiles .ctile {
  flex: 1; min-width: 0; padding: 7px 9px; border-radius: 9px;
  background: var(--top), var(--panel-2); box-shadow: var(--sheen);
}
.chaltiles .ctile span {
  display: block; font-size: 9px; letter-spacing: .4px;
  text-transform: uppercase; color: var(--faint);
}
.chaltiles .ctile b {
  display: block; font-size: 13px; margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* A condition, as a chip. Off it shows a `+` where its figure will go, which
   is the one thing the empty box it replaces never managed to say. */
#challenge .chalchips { display: flex; flex-wrap: wrap; gap: 6px; }
#challenge .chalchips .cchip {
  /* `flex-direction` said out loud: the rule this is beating sets `column`
     on every label in this window, and a property left unmentioned is one
     that has not been beaten. */
  display: inline-flex; flex-direction: row; align-items: center; gap: 6px;
  flex: 0 0 auto; width: auto;
  padding: 5px 10px; border-radius: 999px; cursor: text;
  background: var(--panel-2); border: 1px solid var(--line-soft);
  font-size: 11.5px; color: var(--faint);
  transition: border-color .12s, background .12s, color .12s;
}
#challenge .chalchips .cchip:hover { border-color: var(--line); }
#challenge .chalchips .cchip:focus-within { border-color: var(--accent); }
#challenge .chalchips .cchip.on {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  border-color: var(--accent); color: var(--text);
}
#challenge .chalchips .cchip .cl { white-space: nowrap; }
/* Sized to its own content so an unset chip is as short as its `+`. The
   spinner arrows go: they are a second control inside a chip, and on a chip
   this small they are most of it. */
#challenge .chalchips .cchip input {
  /* The shadow and the radius said out loud. The window's own field styling
     puts `--sheen` on every input here, and a one-pixel inset highlight with
     a corner radius still draws a box even with no border and no ground. */
  width: 3.2em; min-width: 0; padding: 0; border: 0; background: none;
  box-shadow: none; border-radius: 0;
  font: inherit; font-family: var(--mono); font-size: 11px;
  color: var(--accent); text-align: end;
  direction: ltr; unicode-bidi: isolate;
}
#challenge .chalchips .cchip input::placeholder { color: var(--faint); opacity: 1; }
#challenge .chalchips .cchip input:placeholder-shown {
  width: 1.1em; text-align: center;
}
#challenge .chalchips .cchip input:focus { outline: none; }
#challenge .chalchips .cchip input::-webkit-outer-spin-button,
#challenge .chalchips .cchip input::-webkit-inner-spin-button {
  -webkit-appearance: none; margin: 0;
}
#challenge .chalchips .cchip input { -moz-appearance: textfield; appearance: textfield; }
/* The unit only means anything once there is a figure beside it. */
#challenge .chalchips .cchip .cu { font-size: 10px; color: var(--faint); }
#challenge .chalchips .cchip:not(.on):not(:focus-within) .cu { display: none; }

#challenge .sheet { height: auto; max-height: 74vh; }
/* And an empty shelf is a sentence, not a hall. */
#challenge .pad { min-height: 0; }

/* ---------- a challenge's conditions ----------
 *
 * The unit sits inside the field, the way it does on the account settings: a
 * box asking for a bare number leaves somebody guessing whether 2 means two
 * lots or two hundred, and every one of these rules has carried its unit in
 * the data since it was written.
 */
.rule .rulebox {
  display: flex; align-items: stretch; margin-top: 5px;
  background: var(--bg); border: 1px solid var(--line-soft);
  border-radius: 9px; overflow: hidden;
}
/* A number field is wrapped by `numfield.js` into a `.numfield` span with a
   spinner in it, so the child of this box is that wrapper and not the input.
   Styling the input alone left the wrapper at its content width, with the
   unit stranded in the middle of the row and empty box beside it. */
#challenge .rule .rulebox .numfield { flex: 1 1 auto; min-width: 0; }
/* Said at the weight it takes to be heard: `#challenge input` carries an id
   and gives every input in that window its own background, border, radius and
   padding - which inside this box drew a second box around the number. */
#challenge .rule .rulebox input {
  flex: 1 1 auto; width: 100%; min-width: 0;
  background: none; border: 0; border-radius: 0; padding: 9px 11px;
  color: var(--text); font: 13px var(--mono);
}
#challenge .rule .rulebox input:focus { outline: none; border: 0; }
.rule .rulebox:focus-within { border-color: var(--accent); }
.rule .rulebox .u {
  display: grid; place-content: center; padding: 0 10px;
  background: var(--panel-2); border-inline-start: 1px solid var(--line-soft);
  font-size: 11px; color: var(--faint); white-space: nowrap;
}

/* What the other person will be held to, once there is a code to send. */
.chalfacing {
  margin: 10px 0 2px; padding: 10px 12px;
  border-radius: 12px; border: 1px solid var(--line-soft);
  background: var(--top), var(--panel-2); box-shadow: var(--sheen);
}
.chalfacing.hidden { display: none; }
.chalfacing ul { margin: 6px 0 0; padding-inline-start: 16px; }
.chalfacing li { font-size: 12px; color: var(--dim); line-height: 1.9; }

/* ---------- the account, as instruments ----------
 *
 * Two figures answer "how am I doing" and six are detail. Those two get a
 * ring; the rest stay the list they were. Same shape as the readings in
 * Trade Analysis, so the two windows are recognisably one program.
 */
.acctdials { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; padding: 2px 0 8px; }

/* ---- the invariant ----
 *
 * At every height the live price and the two buttons are on the screen without
 * scrolling. Everything between them may scroll; those two may not leave.
 *
 * This is here rather than in the ladder below because it is not a matter of
 * size. Measured at 1280 by 900 - an ordinary window - the column wanted 807
 * pixels, had 585, and put Buy and Sell sixty eight pixels below the bottom
 * edge. The point of the program was behind a scrollbar at a size nobody
 * would call short, and no amount of hiding rows fixes that in general: a
 * long symbol, a crypto ticket with two more fields, a window nobody tested.
 * Pinning the two ends is the only version of the rule that cannot be broken
 * by content.
 *
 * They stick inside `#paneTrade`, which is the box that scrolls. Each needs
 * its own ground, because sticky means the fields travel underneath them.
 *
 * Sticky alone was not enough, and measuring said so: sticky never lifts a
 * thing INTO view, it only holds one that has been scrolled to. With the whole
 * column as a single scroller the price simply started below the fold on a
 * short window and stayed there. So the column is a flex stack of two boxes
 * that scroll separately - the account gives way first, and the ticket always
 * gets placed - and the price and the buttons pin to the ends of the ticket,
 * which is now a scrollport that starts at the top of what is visible. */
#paneTrade > .sidetop { flex: 0 0 auto; }
/* The account does not give way by being squeezed, which was the first try
   and looked like a fault: the picture showed "Margin / f" with the bottom
   half of the row shaved off by the scroll edge. A row is legible or it is
   absent, never a strip of itself. So the box keeps its own height and the
   LADDER is the only thing that shortens it. */
#paneTrade > .panel.account { flex: 0 0 auto; }
/* The ticket is the box that is always placed, and 168 is not a round number:
   it is the price at 77, the buttons at 54 and one field row at 37. Below
   that the two ends would meet in the middle and cover each other, so this is
   the height at which the invariant stops being expressible - and the account
   is squeezed to nothing before the ticket is asked to go under it. */
#paneTrade > .panel.ticket { flex: 1 1 auto; min-height: 168px; }
/* Sticky was the first attempt and the picture refused it: the fields went on
   scrolling UNDER the buttons, so the row above them was cut through the
   middle and half a word of "Stop loss" hung below. Sticky puts a thing over
   its neighbours; what was wanted was a thing BESIDE them. So the ticket is
   three boxes - a head that does not move, a middle that scrolls, and a foot
   that does not move - and nothing can ever be underneath the buttons. */
.panel.ticket { display: flex; flex-direction: column; overflow: hidden; }
.panel.ticket { container-type: inline-size; container-name: ticket; }
.panel.ticket > .quote { flex: 0 0 auto; }
/* The price you buy at, whole, in the narrowest column.
 *
 * Between 641 and 1100 CSS pixels the column is 224 or 260 wide, and the row
 * of three - Sell, the spread, Buy - needed 239 of the 222 the panel has: the
 * Buy tile ran 16 pixels past the edge and «1.17539» was drawn «1.1753»; an
 * index's eight characters lost two or three (the area-13 adversary, F1).
 * A smaller figure alone would have made the one number that matters harder
 * to read. So the row changes shape instead: the two prices share the width
 * between them and the spread, which is read far less often, takes a line of
 * its own beneath them. Measured in design/area13-page.mjs from 641 to 1920. */
@container ticket (max-width: 250px) {
  .panel.ticket > .quote { display: grid; grid-template-columns: 1fr 1fr; row-gap: 4px; }
  .panel.ticket > .quote > .side { min-width: 0; padding-inline: 2px; }
  .panel.ticket > .quote > .sell { grid-column: 1; grid-row: 1; }
  .panel.ticket > .quote > .buy { grid-column: 2; grid-row: 1; }
  .panel.ticket > .quote > .spreadbox {
    grid-column: 1 / -1; grid-row: 2; flex-direction: row; gap: 6px; min-width: 0;
  }
}
.panel.ticket > .ticketmid {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; scrollbar-width: thin;
  /* The room the scrollbar takes, given back as the gutter it sits in. */
  padding-inline-end: 2px;
}
/* --- placing an order without scrolling for it -------------------------
 *
 * Measured before any of this was written (`design/ticket-height.mjs`): the
 * middle of the ticket holds 231px and was given 150 at a 768px window, 154
 * at 900 and 220 at 1040. So it did not scroll on small screens - it scrolled
 * on ALL of them, and the last row was cut through the middle every time,
 * which is the one thing the note above about the account block forbids.
 *
 * The 81px came from three places and none of them is a control:
 *
 *   the foot    the six keyboard hints were laid out over three fat rows.
 *               Same six hints, same words, one tight line.  -41px
 *   the middle  `.seg` carried a 9px bottom margin and `.field` an 8px one,
 *               inside a column that is already spaced. Density only.  -23px
 *   the top     the head row and the account block gave back padding, and
 *               kept every row they had.  -7px
 *
 * That reached 10px short, and 10px short is still a half-drawn row. The last
 * of it came from putting the two segmented pickers on one line - see the
 * note in index.html. Measured after: 198 of 198 at 768, 202 of 202 at 900.
 * Nothing is hidden and nothing is smaller than it was to read.
 *
 * All of it is scoped to `#paneTrade`: `.seg` and `.field` dress a dozen
 * other windows and none of them has this problem. */
/* Left on one line on purpose: `tests/test_web.mjs` pins this exact rule to
   keep the keys OUT of the scrolling middle, and the density below is a
   separate concern that has no business rewriting it. */
.panel.ticket > .keys { flex: 0 0 auto; }
#paneTrade .keys { padding-top: 6px; margin-top: 6px; }
#paneTrade .keylist { gap: 3px 7px; }
#paneTrade .keyrow { font-size: 10px; gap: 4px; }
#paneTrade .keys kbd {
  padding: 2px 4px; min-width: 15px; font-size: 9px; border-radius: 4px;
}
#paneTrade .ticketmid .seg { margin-bottom: 5px; }
#paneTrade .ticketmid .field { margin-bottom: 4px; }
#paneTrade > .sidetop { padding-block: 0; }
#paneTrade > .sidetop .sidehead { min-height: 32px; }
#paneTrade > .panel.account { padding-top: 6px; padding-bottom: 6px; }
/* How the order is placed and how its size is counted, side by side. The
   ratio is the content's: three words against two.
 *
 * `body:not(.crypto)` is load-bearing, not decoration. The crypto dress hides
 * both pickers with `body.crypto .fx-only { display: none }`, which weighs
 * (0,2,0) - and `#paneTrade .segpair` weighs (1,1,0) and would BEAT it,
 * leaving an empty flex box in the crypto ticket that nothing could explain.
 * Written as a guard the rule simply is not there in crypto. */
body:not(.crypto) #paneTrade .segpair {
  display: flex; gap: 6px; align-items: stretch;
}
#paneTrade .segpair > #orderKind { flex: 1.45 1 0; margin-bottom: 5px; }
#paneTrade .segpair > #sizeMode  { flex: 1 1 0; margin-bottom: 5px; }
.panel.ticket > .buttons {
  flex: 0 0 auto;
  padding-top: var(--s2);
  margin-top: var(--s2);
  box-shadow: 0 -1px 0 var(--line-soft);
}

.adial {
  display: grid; grid-template-columns: 1fr 38px; align-items: center; gap: 7px;
  padding: 10px 9px; border-radius: 14px;
  background: var(--top), var(--panel-2);
  border: 1px solid var(--line-soft); box-shadow: var(--sheen);
  min-width: 0;
}
.adial .adtext { min-width: 0; }
.adial .rcap { display: block; margin-bottom: 2px; }
/* Every figure in this block is its own left-to-right island. Without it the
   `-` on a losing percentage is drawn at the far end of the number, which is
   the fault this application has now had to fix in three separate places. */
.adial b, .adnote, .acctbar b {
  direction: ltr; unicode-bidi: isolate;
}
.adial b {
  display: block; font-family: var(--mono); font-size: 14px; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis;
  /* It had the ellipsis and not the thing that makes one possible, so ten
     thousand broke across two lines at the comma - "10,000." above "00" -
     and the dial grew a line taller to hold the half of a number nobody can
     read. A figure is one line or it is trimmed with a mark saying so. */
  white-space: nowrap;
}
.adnote { display: block; font-size: 9.5px; color: var(--faint); margin-top: 1px;
          white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.adnote:empty { display: none; }
/* Drawn at 44 and shown at 38: the ring is a shape and loses nothing by it,
   and the ten pixels go to the figure beside it, which was being trimmed to
   "10,00..." on a 350 pixel column - a balance with its thousands cut off.
   The viewBox does the scaling, so the stroke stays true. */
.adring { flex: 0 0 auto; width: 38px; height: 38px; }
.adtrack { fill: none; stroke: var(--line); stroke-width: 5; }
/* The arc's length is set from the code, in the same units the circumference
   is measured in - see `ringAt`. */
/* Square cap, not round: with a round one a reading of almost nothing is
   drawn as a dot, which reads as a fault rather than as "almost nothing". */
.adarc {
  fill: none; stroke: var(--accent); stroke-width: 5; stroke-linecap: butt;
  transition: stroke-dasharray .25s ease, stroke .2s ease;
}

/* Which way the open trades are running. The mark leaves the middle in the
   direction the money is going, so a glance says the sign before the eye
   reaches the figure. */
.acctbar {
  display: grid; grid-template-columns: auto 1fr auto; align-items: center;
  gap: 9px; padding: 8px 11px; margin-bottom: 8px; border-radius: 12px;
  background: var(--panel-2); border: 1px solid var(--line-soft);
  box-shadow: var(--sheen);
}
.acctbar b { font-family: var(--mono); font-size: 12px; }
.abtrack {
  position: relative; height: 7px; border-radius: 5px;
  background: color-mix(in srgb, var(--panel-3) 80%, transparent);
  box-shadow: inset 0 1px 2px rgba(0,0,0,.38);
}
/* The middle, so nothing has to be read to know which side of it the bar is
   on. */
.abtrack::before {
  content: ""; position: absolute; top: -1px; bottom: -1px; left: 50%;
  width: 1px; background: color-mix(in srgb, var(--text) 20%, transparent);
}
.abtrack i {
  position: absolute; top: 0; bottom: 0; border-radius: 5px;
  left: 50%; width: 0; background: var(--up);
  transition: left .25s ease, width .25s ease, background-color .2s ease;
}

/* ---------- what a region is, said at the top of it ----------
 *
 * Every region on this screen is a card, and until now not one of them said
 * what it was: the chart had no header at all and the trades panel opened
 * straight into its tabs. One shape, so a header is recognisable as a header
 * wherever it appears - a slightly raised strip, a hairline under it, and a
 * small capital label naming the region.
 */
.regionhead {
  display: flex; align-items: center; gap: var(--s2);
  flex: 0 0 auto; padding: var(--s2) var(--s3);
  background: var(--panel-2);
  border-bottom: 1px solid var(--line-soft);
}

/* C - the chart is the subject and stops introducing itself.
 *
 * Its header said CHART, the symbol and the timeframe. The top bar says the
 * symbol and the timeframe forty pixels above it, so two thirds of that row
 * was a second copy - and the chart paid for it out of the height it is drawn
 * in, which is about forty pixels of price.
 *
 * The session is the one thing on that row that is nowhere else, so it does
 * not go with the others: it moves onto the chart's own corner, where what it
 * describes is. */
/* The width of the price scale the canvas paints down its own right edge.
   It is `RIGHT_AXIS` in `web/chart.js` and the two have to agree: a canvas has
   no writing direction, so that scale is at the same place in both languages
   while everything laid out around it moves. `design/sesstag-place.mjs` reads
   the number out of chart.js and fails if this stops matching. */
:root {
  --price-axis: 62px;
  /* The second chart is `flex: 0 0 38%` of the ROW plus a 1px border, and the
     header's `38%` resolves against the WRAP. The two land a constant 10px
     apart at every width measured (1100, 1280, 1500, 1920), so it is named
     rather than folded into the number above - and `sesstag-place.mjs` fails
     the moment it stops being 10, which is the whole point of naming it. */
  --context-slip: 10px;
}

#chartHead {
  padding: 0; background: none; border-bottom: 0; min-height: 0;
  position: absolute; inset-block-start: var(--s2); inset-inline: 0;
  z-index: 4; pointer-events: none;
}
/* The header ends where the price scale begins, not where the region does.
   Without this the session tag sat ON the scale - measured at every width we
   ship to, in both languages, in every session: 40 placements out of 40, over
   by 50px. It read as a Persian fault only because «لندن/نیویورک» is the
   longest of the five labels and lands on the numbers rather than beside
   them. */
#chartHead { inset-inline-end: var(--price-axis); }
#chartHead .rcap, #chartHead #chartSym, #chartHead #chartWhat { display: none; }
/* With the second chart open, the far end of the region is the second chart,
   and the session tag was landing on its bar and its timeframe box. The same
   expression the second chart is sized by - see `#context` in app.css - puts
   the header's edge exactly where that chart begins. Both languages: the tag
   sits at the end of the line, whichever end that is. */
#chartWrap:has(#context:not(.hidden)) #chartHead {
  /* Plus the scale, for the same reason as the rule above: the main chart
     still paints its own price column at its right edge, and stopping at the
     second chart's edge stopped one column too late. */
  inset-inline-end: calc(max(38%, 260px) + var(--price-axis) + var(--context-slip));
}
#chartHead .sesstag { margin-inline-end: var(--s3); }
.rcap {
  font-size: 9.5px; font-weight: 700; letter-spacing: 1.2px;
  text-transform: uppercase; color: var(--faint);
  flex: 0 0 auto;
}
.regionhead .grow { flex: 1; }
.regionhead .mono { font-family: var(--mono); font-size: 12px; font-weight: 600; }
.rsub { font-family: var(--mono); font-size: 11px; color: var(--faint);
        overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Which session the replay is standing in. Gold, because gold marks and blue
   acts - and this is a thing to notice, not a thing to press. */
.sesstag {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 10px; color: var(--gold-ink); flex: 0 0 auto;
}
.sesstag::before {
  content: ""; width: 6px; height: 6px; border-radius: 3px;
  background: var(--gold);
}
.sesstag:empty { display: none; }

/* The trades panel wears the same strip, with its tabs sitting in it. */
#dockHead { background: var(--panel-2); border-bottom-color: var(--line-soft); }
#dockHead .rcap { padding-inline: 10px 4px; }

/* And so does the side column, at both of its headings. */
.sidehead {
  background: var(--panel-2);
  border-bottom: 1px solid var(--line-soft);
}

/* ---------- drawing tools ---------- */

.tools {
  flex: 0 0 var(--rail);
  width: var(--rail);
  display: flex; flex-direction: column; align-items: center;
  gap: 3px;
  padding: 8px 0;
  background: var(--panel);
  /* Logical, not physical: in Persian the whole row mirrors, and a
     `border-right` would then draw on the outside edge instead of the one
     facing the chart. Both physical sides are cleared first - app.css still
     sets border-right, and in Persian that lands opposite the logical one, so
     the rail ends up outlined on both sides. */
  border-left: 0; border-right: 0;
  border-inline-end: 1px solid var(--line);
  overflow-y: auto;
  scrollbar-width: none;
}
.tools::-webkit-scrollbar { width: 0; }

.tools button {
  width: 32px; height: 30px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: 7px;
  color: var(--dim); cursor: pointer;
  font-size: 15px; line-height: 1;
  transition: background .12s, color .12s;
}
.tools button:hover { color: var(--text); background: var(--panel-2); }
.tools button.active { background: var(--accent); color: #fff; }
.tools button svg { width: 17px; height: 17px; display: block; }
.tools button svg * { vector-effect: non-scaling-stroke; }

/* A group's button is two things: the tool it is wearing, and the way to the
   rest of them. */
.tools .grp[data-group] { position: relative; }
.tools .grp .gface {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%;
}
.tools .grp .gcaret {
  position: absolute; inset-inline-end: 3px; bottom: 3px;
  width: 0; height: 0; padding: 4px; margin: -4px;
  /* Its own hit area, a little larger than the mark it draws, because four
     pixels of triangle is not something a hand aims at. */
  cursor: pointer;
}
.tools .grp .gcaret::after {
  content: ''; position: absolute; inset-inline-end: 0; bottom: 0;
  border-inline-start: 4px solid transparent;
  border-bottom: 4px solid currentColor;
  opacity: .5;
}
.tools .grp:hover .gcaret::after { opacity: 1; }
.tools .grp.active .gcaret::after { opacity: .85; }
/* The cursor and the favourites are not groups with a list behind them. */
.tools .grp.cur .gcaret, .tools .grp.fav .gcaret { display: none; }

/* Drawn by shell.js between the shapes and the bin, so "clear everything" is
   not one slip away from "draw a rectangle". */
.tools .sep {
  width: 20px; height: 1px; background: var(--line);
  margin: 5px 0; flex: 0 0 1px;
}
.tools .toolsep { width: 22px; height: 1px; background: var(--line); margin: 4px 0; flex: 0 0 1px; }
.tools .chartset { font-size: 15px; color: var(--dim); }
.tools .chartset:hover { color: var(--accent); }

/* The grouped tool buttons and their flyout. */
.tools .grp { font-size: 15px; }
.tools .grp.active { background: var(--accent); color: #fff; }

#toolFlyout {
  position: absolute; z-index: var(--layer-float);
  min-width: 190px;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 10px; padding: 5px;
  box-shadow: 0 10px 30px rgba(0,0,0,.5);
  display: flex; flex-direction: column; gap: 1px;
}
#toolFlyout button {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 10px; border: 0; border-radius: 6px;
  background: none; color: var(--text); cursor: pointer;
  font: inherit; font-size: 13px; text-align: start; white-space: nowrap;
}
#toolFlyout button:hover { background: var(--panel-2); }
#toolFlyout .i {
  width: 18px; text-align: center; color: var(--dim); font-size: 14px;
}
#toolFlyout .nm { flex: 1; }
#toolFlyout .star {
  color: var(--faint); font-size: 13px; padding: 0 2px; opacity: 0;
  transition: opacity .1s, color .1s;
}
#toolFlyout button:hover .star { opacity: 1; }
#toolFlyout .star.on { color: var(--gold-ink); opacity: 1; }
#toolFlyout .star:hover { color: var(--gold-ink); }
#toolFlyout .flyempty { padding: 10px 12px; color: var(--faint); font-size: 12px; max-width: 180px; }
/* Gold, like every other star in the program. The accent means "this is
   the one you are on", which a shelf of kept tools is not. */
.tools .grp.fav { color: var(--gold-ink); }

/* ---------- trades dock ---------- */

#dockGrip {
  flex: 0 0 var(--grip); height: var(--grip);
  background: var(--bg); cursor: ns-resize;
  border-top: 1px solid var(--line);
}
/* The unit switch, beside the word it switches. */
.field .unittog {
  margin-inline-start: 5px; padding: 0 4px; border: 0; border-radius: 5px;
  background: none; color: var(--faint); font-size: 11px; line-height: 1.4;
  cursor: pointer;
}
.field .unittog:hover { color: var(--text); background: var(--hover); }

/* ---- the moment a trade is opened ----
 *
 * Filmed before it was written: twenty elements changed in one frame, sixty
 * eight milliseconds after the press, and the button never moved. Twenty
 * simultaneous changes is the same as none.
 *
 * The press first, because it is the only part that can answer before any
 * data exists. No transition on the way down - a press that eases in is a
 * press that has not landed yet. */
.buttons button:active {
  transform: translateY(1px);
  filter: brightness(1.14);
  transition: none;
}

/* Then the consequence, named. The row that just arrived is lit for three
   quarters of a second: long enough to find, short enough that it is gone
   before it becomes something to look at. */
@keyframes justOpened {
  0%   { box-shadow: inset 0 0 0 9999px color-mix(in srgb, var(--accent) 26%, transparent); }
  100% { box-shadow: inset 0 0 0 9999px transparent; }
}
.postab tr.fresh td { animation: justOpened .75s ease-out 1; }

/* And the count, which is a state rather than a number. */
@keyframes countTicked {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.28); }
  100% { transform: scale(1); }
}
.tab .cnt.ticked { animation: countTicked .42s ease-out 1; display: inline-block; }

/* Somebody who has told their computer that movement makes them ill has not
   asked for a lit row. */
@media (prefers-reduced-motion: reduce) {
  .postab tr.fresh td, .tab .cnt.ticked { animation: none; }
  .buttons button:active { transform: none; }
}

#dockGrip:hover { background: var(--accent); }

/* The foot of the trades panel: what can be done to the positions listed above
   it. It used to sit in the order ticket, three buttons of equal weight a
   thumb-width under the one control that commits money - and it sat there
   whether or not there was anything to close. Pushed to the trailing edge so
   it does not read as a heading for the table. */
.closers {
  display: flex; gap: 8px; justify-content: flex-end; flex: 0 0 auto;
  padding: 8px 14px 10px; border-top: 1px solid var(--line-soft);
}
.closers.hidden { display: none; }

#dock {
  flex: 0 0 var(--dock);
  min-height: 0;
  display: flex; flex-direction: column;
  background: var(--panel);
  border-top: 1px solid var(--line);
}

#dockHead {
  display: flex; align-items: center;
  border-bottom: 1px solid var(--line);
  padding-inline-end: 6px;
}
#dockHead .tabs { border-bottom: 0; }
/* A narrow dock wraps its tabs onto a second row rather than pushing the row
   past its own edge: from about 790 wide down to 641 «Fill the window», «Hide
   the trades panel» and then the Report tab were off the dock's far side in
   Persian (the round-2 area-13 adversary, F9). The two buttons never shrink. */
#dockHead .tabs { flex: 0 1 auto; min-width: 0; flex-wrap: wrap; }
#dockHead > button { flex: 0 0 auto; }
/* The caption goes first: it only names what the tabs already say, and with
   it the English head took three rows at 641. */
#dock { container-type: inline-size; container-name: dockbox; }
@container dockbox (max-width: 450px) { #dockHead .rcap { display: none; } }

/* A little life in the trades tabs: a rounded highlight behind the active one
   instead of a bare underline, and the count as a pill. */
#dockHead .tab {
  flex: 0 0 auto; padding: 8px 14px; margin: 4px 2px; border-radius: 8px;
  border-bottom: 0; transition: background .12s, color .12s;
  display: inline-flex; align-items: center; gap: 6px;
}
#dockHead .tab:hover { color: var(--text); background: var(--panel-2); }
#dockHead .tab.active { background: color-mix(in srgb, var(--accent) 16%, var(--panel)); color: var(--text); }
#dockHead .tab span {
  min-width: 17px; height: 17px; padding: 0 5px; border-radius: 9px;
  background: var(--panel-2); color: var(--dim);
  /* The interface face, not the mono one: at ten pixels inside a circle,
     Cascadia's dotted zero reads as a smudge. Tabular figures so the
     pill does not change width between 8 and 11. */
  font: 600 10px var(--ui); font-variant-numeric: tabular-nums;
  border: 1px solid var(--line);
  display: inline-flex; align-items: center; justify-content: center;
}
#dockHead .tab.active span { background: var(--accent); color: #fff;
                            border-color: var(--accent); }
#dockBody { flex: 1 1 0; min-height: 0; overflow: auto; }
#dockBody .tabbody { height: 100%; }
/* So the report inside can ask how wide this panel is. */
#dockBody { container-type: inline-size; container-name: dock; }
/* The report does its own scrolling, in the half that needs it. */
#tabStats { overflow: hidden; }

/* Collapsed to just its tab strip. The tabs stay visible on purpose: a panel
   that vanishes completely is one the user has to remember exists. */
body.dock-shut #dock { flex-basis: auto; }
body.dock-shut #dockBody { display: none; }
/* The account row deliberately stays when the panel is rolled up. It is one
   line, and before it moved here it floated over the chart and was always
   there — losing sight of the balance because the trades were put away would
   be a step backwards. */
body.dock-shut #dockGrip { display: none; }
body.dock-shut #dockToggle { transform: rotate(180deg); }

.ghost {
  background: none; border: 0; color: var(--dim);
  cursor: pointer; font: inherit; font-size: 13px;
  padding: 4px 8px; border-radius: 5px;
}
.ghost:hover { color: var(--text); background: var(--panel-2); }

/* ---------- the side column ---------- */

#side {
  width: var(--sidebar); flex: 0 0 var(--sidebar);
  display: flex; flex-direction: column;
  min-height: 0;
  border-inline-start: 1px solid var(--line);
  border-inline-end: 0;
  background: var(--panel);
  overflow: hidden;
}

/* The watchlist gives way before the ticket does.
 *
 * It used to be `flex: 0 0 var(--watch)` - a height it kept whatever else
 * needed room - and the ticket below it took what was left. On a short window
 * what was left was not enough. Measured at 1366x700: the ticket's scrolling
 * middle held 329 pixels of rows inside 46, so the Volume stepper had scrolled
 * under the Sell button pinned to the foot, and pressing its arrow hit
 * `#btnSell` instead. Asked at that point, the browser said so. Nothing looked
 * broken and nothing was disabled; the panel's controls simply did nothing,
 * which is exactly how it was reported.
 *
 * The step ladder further down still sets the height by viewport, and this
 * lets it give way continuously between those steps, down to a floor where it
 * is still a list rather than a stub. The empty-state rules on `#watchList`
 * already drop the message's parts as it shortens, so it stays legible all
 * the way down. */
#paneWatch {
  flex: 0 1 var(--watch);
  min-height: 104px;
  display: flex; flex-direction: column;
  border-bottom: 1px solid var(--line);
}

/* The column is a stack of two boxes that scroll separately rather than one
   long scroller. See "the invariant" above: the account gives way first, the
   ticket is always placed, and the price and buttons pin to the ticket's
   ends. `overflow: hidden` here is what makes the two inner scrollers the
   only ones - with a scroller out here as well the ticket would be pushed
   below the fold before it ever ran out of room. */
#paneTrade {
  flex: 1 1 0; min-height: 0;
  display: flex; flex-direction: column;
  overflow: hidden;
}
#paneTrade .panel:last-child { border-bottom: 0; }

/* What the column is, and how to fold it away: both stay where they are.
   `top: -1px` rather than 0 so the hairline underneath meets the panel edge
   without a seam of ground showing through it while something scrolls past. */
.sidetop {
  position: sticky; top: -1px; z-index: 3;
  background: var(--panel);
  border-bottom: 1px solid var(--line-soft);
  padding-top: 1px;
}
/* The switches carried the gap that separated them from the figures below.
   Inside a block that is now flush against those figures, that gap belongs to
   the block.

   The same eight on every side. It was eight on three of them and nothing on
   top: the strip ran edge to edge - measured at 0 before and 0 after inside
   `.sidetop` - so the two buttons touched the panel's frame, and then, with
   the sides fixed, they still sat hard against the underside of the heading's
   rule. Measured at 0 above against 8 left, 8 right and 8 below, which is
   what makes it read as stuck rather than tight: a control spaced on three
   edges and flush on the fourth looks like a mistake, because it is one.

   The number is not chosen, it is the one already here. Both themes and both
   languages; a margin is not a colour. `body.tradeshut` still takes the
   bottom back off, which is right - with the panel folded away there is
   nothing underneath for it to be spaced from. */
.sidetop #tradeMode { margin: 8px; }

#watchGrip {
  flex: 0 0 var(--grip); height: var(--grip);
  cursor: ns-resize; background: var(--bg);
}
#watchGrip:hover { background: var(--accent); }

.sidehead {
  display: flex; align-items: center; gap: 6px;
  /* Even top and bottom now that there is a strip behind it; it used to be
     lighter underneath because there was nothing to sit inside. */
  padding: 9px 14px;
  font-size: 12px; letter-spacing: .3px;
}
.sidehead b { font-weight: 600; }
.sidehead .spacer { flex: 1; }

/* Six above as well as below. It was nothing above: the field sat directly on
   the underside of the heading's rule while carrying six underneath and ten
   down each side, and a control spaced on three edges and flush on the fourth
   reads as stuck to the ceiling.

   Six rather than ten, because the ten is the column's inset and the six is
   this field's own vertical rhythm - a field is tighter above and below than
   it is at the sides. Matching the ten would have made it even on four edges
   and wrong on two. */
#watchFilter {
  margin: 6px 10px; padding: 5px 8px;
  background: var(--panel-2); color: var(--text);
  border: 1px solid var(--line); border-radius: 6px;
  font: inherit; font-size: 12px;
}
#watchFilter:focus { outline: none; border-color: var(--accent); }
/* Raised on paper, sunk in the dark, and that is not a taste.
 *
 * A field is a surface one step under the thing it sits on, and `--panel-2`
 * is that step - under a panel. This one sits on the watchlist, which lies
 * flat on the ground on purpose. In the dark theme `--panel-2` is LIGHTER
 * than the ground, so a step still exists; in the light theme it is darker
 * than the panel and lands within a hair of the ground, and the field came
 * out at 1.078 against what it sits on where every other field on screen is
 * 1.168. Under the ground there is nothing, so here it rises: 1.260.
 *
 * The theme has to be named because the two palettes stack their surfaces in
 * opposite directions. That is the whole reason one value cannot serve both. */
:root.light #watchFilter { background: var(--panel); }

#watchList {
  flex: 1 1 0; min-height: 0; overflow-y: auto;
  scrollbar-width: thin;
}

/* Category accents — one hue per market, used by the group dots and row dots. */
[data-cat="forex"]  { --cat: #4c8dff; }
[data-cat="metal"]  { --cat: #e0b03a; }
[data-cat="index"]  { --cat: #c65ad0; }
/* Crypto had green, and gave it up when the owner ruled that green means a
   buy and nothing else. It was moved onto `var(--gold)` - and the gold is
   four degrees of hue from the gold metals already wore, so from that day the
   two groups were one colour. Reported from a real copy: «رنگ رمزارز با فلزات
   یکی هست، قدیم هر دسته رنگ جداگانه داشت».

   The move was deliberate and the collision was not: the guard below it only
   ever asked whether a group wore GREEN, never whether two groups wore the
   same thing as each other. That is now measured, in both themes, by
   `design/watchlist-hues.mjs` and by a check in the suite.

   Cyan at hue 188, which is the widest gap left: 143 degrees from the gold,
   32 from the forex blue, and clear of the green a buy owns - the green
   channel is not above the blue at all here, which is the test that rule is
   written as. Two values because `--cat` is read as well as seen: it colours
   the ticker on the row you are looking at, so it is 13px writing on the
   card. 5.39 : 1 dark and 5.42 : 1 light, where one literal for both themes
   would have had to fail one of them. */
[data-cat="crypto"] { --cat: var(--cat-crypto); }
[data-cat="energy"] { --cat: #e0663a; }
[data-cat="other"]  { --cat: #8a93a5; }

.wgroup {
  display: flex; align-items: center; gap: 7px;
  padding: 10px 10px 4px;
  font-size: 10px; letter-spacing: 1px; text-transform: uppercase;
  color: var(--dim);
}
.wgroup .gdot { width: 6px; height: 6px; border-radius: 50%; background: var(--cat); flex: 0 0 6px; }

/* A card rather than a line of text.
 *
 * Four things are being asked of every entry - which market, which
 * instrument, how much history, is it mine - and as a row they were four
 * columns fighting for a 200px width, with the coverage figure hidden on
 * hover because the star needed the same corner. As a card each gets its own
 * place: the market is the colour of the light, the ticker and the years lead,
 * the description sits under them, and the years are drawn again along the
 * bottom against every other instrument's.
 */
.wrow {
  position: relative; overflow: hidden;
  display: grid; grid-template-columns: auto 1fr auto auto;
  align-items: center; gap: 3px 8px;
  margin: 0 8px 6px; padding: 8px 10px;
  border: 1px solid color-mix(in srgb, var(--cat) 26%, var(--line-soft));
  border-radius: 12px;
  background: var(--top), var(--panel-2);
  box-shadow: var(--sheen);
  /* The card's own colour, so the halo and the foot below can be drawn in
     `currentColor` and every one of them is its market's. */
  color: var(--cat);
  cursor: pointer;
  transition: border-color .12s, box-shadow .12s, background .12s;
}

/* The market's colour, as an edge.
 *
 * It used to be a blurred halo under one corner and a glowing foot along the
 * bottom, and between them they made a *list row* the loudest thing on the
 * screen - louder than the two buttons that commit money. A row in a list is
 * not an announcement. The colour still says which market this is; it says it
 * in three pixels down the leading edge instead of in a glow. */
.wrow::before {
  content: ''; position: absolute; pointer-events: none;
  inset-block: 0; inset-inline-start: 0; width: 3px;
  background: currentColor; opacity: .75;
}

.wrow .cdot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--cat);
  box-shadow: 0 0 7px var(--cat);
  align-self: center; flex: 0 0 7px;
}
.wrow:hover { border-color: color-mix(in srgb, var(--cat) 55%, var(--line-soft)); }

/* The one you are on: the light comes all the way up. */
/* The one you are looking at. It used to be a wash of the market's colour
   across the whole card, a border in that colour, and a coloured shadow cast
   under it - which, with the halo above, made the current row the brightest
   object on the screen. It is the same shape as the others now, one surface
   brighter, with its edge at full strength. Being current is a small fact and
   it is said quietly. */
.wrow.on {
  background: var(--panel-3);
  border-color: var(--line);
  box-shadow: var(--sheen);
}
.wrow.on::before { opacity: 1; }

/* Everything written on the card takes its own colour: the card's is the
   market's, and letting a ticker inherit that would tint the writing. */
.wrow .sym {
  position: relative;
  font-weight: 600; font-size: 13px; letter-spacing: .2px; color: var(--text);
}
.wrow.on .sym { color: var(--cat); }
.wrow .cov {
  position: relative;
  font-size: 10px; color: var(--faint); font-family: var(--mono);
  white-space: nowrap;
}
.wrow .nm {
  position: relative;
  grid-column: 1 / -1;
  font-size: 10.5px; color: var(--dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* The coverage track: the years of this instrument inside the years of the
   whole catalogue, so a short history looks short. */
.wrow .wspan {
  position: relative; grid-column: 1 / -1;
  height: 5px; margin-top: 5px; border-radius: 3px;
  background: var(--panel-3);
  box-shadow: var(--well);
}
.wrow .wspan i {
  position: absolute; top: 0; bottom: 0; border-radius: 3px;
  background: var(--cat); opacity: .9; box-shadow: 0 0 8px var(--cat);
}

/* Not downloaded: still listed, because "it is not here" and "it does not
   exist" are different answers and the card is where that question is asked.
   Its light is turned most of the way down instead of off, so the market
   colour still reads while the card plainly is not one of yours. */
.wrow.empty .sym { color: var(--dim); font-weight: 500; }
.wrow.empty .cov { color: var(--accent); }
.wrow.empty::before { opacity: .3; }
.wrow.empty .cdot { opacity: .45; box-shadow: none; }
.wrow.empty .wspan i { opacity: .3; box-shadow: none; }

.wnothing { padding: 14px 10px; color: var(--faint); font-size: 12px; line-height: 1.6; }

/* The star sits on the title line and stays there. It used to be an absolute
   button in the corner the coverage figure also wanted, so the figure was
   hidden on hover and permanently on anything starred - which is to say the
   history was hidden on every card the customer actually keeps. */
.wpin {
  position: relative;
  width: 18px; height: 18px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: 4px;
  color: var(--faint); font-size: 13px; cursor: pointer;
  opacity: .45; transition: opacity .1s, color .1s;
}
.wrow:hover .wpin { opacity: 1; }
/* Gold, like every other star in the program. The accent means "this is the
   one you are on", which is what the card itself already says. */
.wpin.on { opacity: 1; color: var(--gold-ink); text-shadow: 0 0 8px var(--news); }
.wpin:hover { background: var(--panel); color: var(--text); }
.wpin.on:hover { color: var(--gold-ink); }

/* On paper the light comes down.
 *
 * A glow is light leaving a surface, and it reads as light only against
 * something darker than itself. On a near-white panel the same halo is a
 * smudge and the same 10px bloom under the foot is a stain, so the halo goes
 * to a tint, the blooms go out, and the border carries the market colour
 * instead - which on paper is the stronger signal anyway.
 */
/* The edge carries in the light theme too; there is no halo left to dim. */
:root.light .wrow::before { opacity: .85; }
:root.light .wrow.on { box-shadow: var(--sheen); }
:root.light .wrow .cdot { box-shadow: none; }
:root.light .wrow .wspan i { box-shadow: none; }
:root.light .wpin.on { text-shadow: none; }

/* The blind pass hides which instrument this is. A list with it highlighted
   would hand the answer straight back. */
body.blind #paneWatch { display: none; }
body.blind #rail [data-pane="watch"] { opacity: .35; pointer-events: none; }

/* ---------- the strip that shows and hides them ---------- */

#rail {
  flex: 0 0 var(--iconrail);
  display: flex; flex-direction: column; align-items: center;
  gap: 4px; padding: 8px 0;
  background: var(--panel);
  border-inline-start: 1px solid var(--line);
}
#rail button {
  width: 28px; height: 28px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: 6px;
  color: var(--faint); cursor: pointer; font-size: 13px;
}
#rail button:hover { color: var(--text); background: var(--panel-2); }
#rail button.on { color: var(--text); background: var(--panel-2); }

body.no-tools .tools { display: none; }
body.no-watch #paneWatch,
body.no-watch #watchGrip { display: none; }
body.no-trade #paneTrade { display: none; }

/* The order panel folded shut.
 *
 * Everything below the two switches goes, the switches themselves stay, and
 * the column stops claiming the height it no longer needs - so the watchlist
 * above grows into it and the switches sit at the foot of the sidebar. The
 * one rule this obeys: a control that closes something has to still be there
 * to open it again. Emptying the column completely is the rail's job, and
 * `no-trade` above still does exactly that. */
body.tradeshut #paneTrade {
  flex: 0 0 auto; overflow: visible;
}
/* They are one block now, so this hides everything but that block. Named the
   two of them as direct children of the pane before, which stopped being true
   the moment they were wrapped. */
body.tradeshut #paneTrade > *:not(.sidetop) { display: none; }
body.tradeshut #paneTrade #tradeMode { margin-bottom: 0; }
body.tradeshut .sidetop { border-bottom: 0; }
/* Either way of putting the order panel away, and there are two.
   The chevron on its own heading FOLDS it - `tradeshut` - and the rail down
   the side HIDES it - `no-trade`. Both leave the same dead column, and the
   watchlist takes it either way.

   Naming only the fold here was a fix that worked in the one place it was
   tested. Reported straight back: closing the panel from the rail left the
   watchlist at 240 pixels of a 932 pixel column with six hundred of nothing
   under it. `no-trade` is the plainer of the two cases, too - the pane is
   `display: none`, so there is not even a heading left to sit under.

   This was the other way round once, and the reason was good: the fold used
   to be half of a two-button track in the MIDDLE of the column, so letting
   the watchlist grow slid that track to the foot of the sidebar and the
   button just pressed was no longer under the pointer that pressed it. The
   fold is a chevron on the panel's own heading now; the heading moves once,
   to the foot, and stays there. */
body.tradeshut #paneWatch,
body.no-trade #paneWatch { flex: 1 1 auto; }

body.no-watch.no-trade #side { display: none; }

/* And the same when the ticket is folded rather than removed.
 *
 * `tradeshut` leaves the two switches on screen so the control that closed the
 * panel is still there to open it - which is right while the watchlist is
 * above them. With the watchlist hidden too there is nothing left in that
 * column but a switch, and a whole sidebar's width for it. So the column goes
 * and the chart takes the room.
 *
 * The rule that a control stays reachable still holds: both come back from the
 * rail down the side, and the rail's order-ticket button unfolds the panel
 * rather than only un-hiding it - see `showPane` in shell.js. Without that the
 * button would toggle a pane that is already on and look broken. */
body.no-watch.tradeshut #side { display: none; }
body.no-dock #dock,
body.no-dock #dockGrip { display: none; }

/* ---------- the trades panel, filling the window ----------
 *
 * For writing on a trade. 210 pixels is enough to read a row in and not
 * enough to write in, and writing is what the journal is for.
 *
 * The chart is hidden rather than squashed: a canvas told to be two pixels
 * tall spends the whole time redrawing itself at a size nobody is looking at.
 * Not remembered on purpose — this is a thing you do for a minute and undo,
 * and starting up with no chart would look like a program that had broken. */
body.dock-full #dock { flex: 1 1 auto; }
body.dock-full #chartWrap,
body.dock-full #chart,
body.dock-full #dockGrip { display: none; }
#dockFull.on { color: var(--accent); }

/* ---------- on-chart indicator legend ---------- */
#indLegend {
  /* Clear of the reading rather than sharing its space. The reading is one
     line at rest and three with the pointer over a bar - symbol, date, then
     open/high/low/close - so a legend parked at the one-line height sat under
     the other two every time somebody moved the mouse. Below the tallest it
     gets; and lower again when the buy/sell strip has the top of the chart. */
  position: absolute; top: 72px; inset-inline-start: 12px; z-index: 4;
  transition: top .12s ease;
  display: flex; flex-direction: column; gap: 2px;
  pointer-events: none;
}
body.chart-strip #indLegend { top: 118px; }

/* ---------- the chart's own overlays, in Persian ----------
 *
 * Nothing over the chart moves any more, and this note is the record of how
 * that was arrived at twice.
 *
 * The readout and the indicator list were both pinned to the top left, and
 * both were moved to the right on the argument that Persian reading starts
 * there, so a thing that is read belongs where the eye lands. The legend came
 * back first, on the better argument: it is not a paragraph, it is a key to
 * the lines on the chart, and the chart is the working surface that this whole
 * section says does not mirror.
 *
 * The readout is the same case and stayed on the right until it was reported
 * as a fault in exactly those words - a set of figures landing at the top
 * right of the chart. It is a row of Latin figures, `O … H … L … C …`, over
 * that same working surface, and it now sits where the English has it. So the
 * rule that moved it is gone rather than rewritten, and so is the one that
 * had to keep it clear of the second chart, which only existed because it was
 * parked at the far end of the region.
 *
 * The order ticket was never one of them - see the note below. */
/* Not the ticket. The other two overlays are read and belong where the
   reading starts; this one is two buttons a hand goes to, it already holds
   SELL on the left of BUY, and on the right it lands against the order panel
   where those same two buttons already are. It stays on the left. */
/* The legend is NOT one of them either, and used to be. It was moved to the
   right on the argument that it is read, and reading starts there - but it is
   not a paragraph, it is a key to the lines on the chart, and the chart is the
   working surface that this whole section says does not mirror. Asked for
   plainly: put it where the English has it. So it keeps the left, the rows
   inside it keep their left-to-right order, and nothing about it changes with
   the language. */
/* What it is set to, beside what it is. Its own element because the name has
   to be a whole text node on its own to be found in the dictionary, and
   because a list of numbers reads left to right in either language. */
#indLegend .lprm {
  font-family: var(--mono); font-size: 10px; color: var(--faint);
  direction: ltr; unicode-bidi: isolate;
}
/* The row itself turns, not just the block it sits in. Moved to the right of
   the chart it still ran squares, name, settings from the left, so in Persian
   the eye landed on the last thing in the row. The numbers keep their own
   direction through `.lprm` above. */
#indLegend .legrow {
  display: flex; align-items: center; gap: 6px;
  pointer-events: auto;
  font-size: 12px; color: var(--text);
  background: rgba(0,0,0,.25); border-radius: 5px; padding: 1px 4px;
  width: fit-content;
}
#indLegend .dot { width: 9px; height: 9px; border-radius: 2px; flex: 0 0 9px; }
#indLegend .lbl { text-shadow: 0 1px 2px #000; }
#indLegend .gear, #indLegend .x {
  border: 0; background: none; cursor: pointer; color: var(--dim);
  font-size: 12px; padding: 0 2px; opacity: 0; transition: opacity .1s;
}
#indLegend .legrow:hover .gear, #indLegend .legrow:hover .x { opacity: 1; }
#indLegend .gear:hover { color: var(--text); }
#indLegend .x:hover { color: var(--sell); }
body.blind #indLegend { display: none; }

/* ---------- full-screen chart ---------- */
/* Inside the plot, not on the axis. At twelve pixels it stood on top of
   the price labels, which are the one thing on that edge somebody reads. */
#btnFull {
  /* 62 is the price axis exactly - `RIGHT_AXIS` in chart.js - so at 62 the
     button's edge landed on the axis boundary with nothing between them,
     measured as 0px. The 12 is the inset the chart's other overlay uses,
     so the button now clears the axis by the same margin the legend keeps
     from the opposite edge. */
  position: absolute; inset-inline-end: 74px; bottom: 34px; z-index: 5;
  width: 30px; height: 30px; border-radius: 7px;
  background: var(--panel); border: 1px solid var(--line);
  color: var(--dim); cursor: pointer; font-size: 15px;
  display: flex; align-items: center; justify-content: center;
  opacity: .55; transition: opacity .12s;
}
#btnFull:hover { opacity: 1; color: var(--text); border-color: var(--accent); }
body.chart-full #side,
body.chart-full #rail,
body.chart-full #dock,
body.chart-full #dockGrip { display: none; }

/* The Journal and Trade Analysis sheets are a fixed height, and their content —
   every section, or the six analysis categories and their charts — is taller
   than that. Let the body scroll inside the sheet, or half of it is off the
   bottom with no way to reach it (which read as "where did the options go" and
   "it hangs"). */
/* Wide enough for the nine-column trade table and the two-up analysis cards,
   so their content fits instead of blowing the panel out sideways (which both
   put a stray horizontal scrollbar on it AND thrashed layout badly enough to
   feel like a hang once there were trades to lay out). */
/* The journal carries a curve, five figures, the trades, the trade being
   read and four cards under them. 900 was the width of a table. */
#journal .sheet { width: 1180px; max-width: 96vw; height: 88vh; }
#analysis .sheet { width: 900px; max-width: 95vw; height: 82vh; }
#journal .pad, #analysis .pad { flex: 1 1 auto; min-height: 0;
  overflow-y: auto; overflow-x: hidden; flex-wrap: nowrap;
  /* Reserve the scrollbar's width always. Without it, showing a section (e.g.
     turning the Calendar on) can push content past the fold, add the vertical
     scrollbar, which narrows the content box, which reflows the whole column —
     and a grid sized in `1fr` recomputes every cell — potentially oscillating.
     A stable gutter removes that width change, so no reflow storm. */
  scrollbar-gutter: stable; }
/* THE fix for the "messed-up layout / stray horizontal scroll / hang once there
   were trades": a `.pad` is `flex-wrap: wrap`, and once these panels got a
   constrained height (for vertical scroll) a tall column started wrapping its
   sections into SIDE-BY-SIDE columns instead of stacking them — throwing the
   body hundreds of px to the right and blowing the width out. A column must not
   wrap. */
/* Setting overflow-y turns overflow-x to auto by default, so a wide element (the
   nine-column trade table) put a horizontal scrollbar on the whole panel. The
   panel scrolls only vertically now; the table scrolls inside its own frame, and
   every card is held to the panel width. */
#journal .pad.col > *, #analysis .pad.col > * { max-width: 100%; min-width: 0; }
.jcard, .jcard .jbody { min-width: 0; }
.jrtablewrap { max-width: 100%; }

/* ---------- chart appearance settings ---------- */
/* Wider than the default sheet so the two-column colour grid and the account
   fields both have room; the colour pickers were clipped at 360px. */
#settings .sheet, #chartSettings .sheet { width: 560px; max-width: 94vw; }
#settings .pad, #chartSettings .pad { overflow-y: auto; }
#settings #chartColours, #chartSettings #chartColours,
#settings .addrow, #chartSettings .addrow,
#settings .wide, #chartSettings .wide,
#settings .secline, #chartSettings .secline,
#chartSettings .checklist,
#chartSettings .shadelist, #chartSettings p.sub,
#settings hr, #chartSettings hr { grid-column: 1 / -1; }

/* ---------- chart settings, with the chart in it ----------
 *
 * The window was one strip of 951 pixels in a 669-pixel sheet, so a third of
 * it was always below the fold and nothing in it was grouped. The preview at
 * the top is the point of the rebuild: a window about how something looks
 * should show the thing, and then "daily separators" is a line you can see
 * rather than a phrase you have to picture.
 */
/* Wider than it was, and the reason is the shape rather than the taste: this
   window is a preview with five tabs under it now, and both the tab bar and
   the row of candle tiles need room a 520 pixel column does not have. */
#chartSettings .sheet { width: 720px; }
.cspreview {
  position: relative; height: 104px;
  background: var(--bg); border-bottom: 1px solid var(--line-soft);
}
.cspreview canvas { display: block; width: 100%; height: 100%; }
.cspreview .cslabel {
  position: absolute; inset-inline-start: 11px; top: 8px;
  font-size: 9px; font-weight: 700; letter-spacing: 1.1px;
  text-transform: uppercase; color: var(--dim); pointer-events: none;
}

/* The pad is a plain column now: the two-column grid it used to be is what
   put a full-width rule beside a half-width one. */
#chartSettings .pad.cspad { display: flex; flex-direction: column; gap: 0; }
#chartSettings .cspad .secline { margin: 15px 0 7px; }
#chartSettings .cspad p.sub { margin: 7px 0 0; }

.csline { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

/* Two states, one control. Two full-width buttons said they were two separate
   things you could press, which is not what choosing a theme is. */
#chartSettings .seg {
  display: inline-flex; padding: 2px; border-radius: 9px;
  background: var(--panel-2); box-shadow: var(--sheen); flex: 0 0 auto;
  margin-bottom: 0;
}
#chartSettings .seg button {
  padding: 5px 16px; border: 0; border-radius: 7px; background: none;
  color: var(--faint); font: inherit; font-size: 11.5px; cursor: pointer;
  transition: background .12s, color .12s;
}
#chartSettings .seg button:hover { color: var(--dim); }
#chartSettings .seg button.on {
  background: var(--panel-3); color: var(--text); box-shadow: var(--sheen);
}

.csbright { flex: 1 1 200px; display: flex; align-items: center; gap: 9px; }
.csbright .lbl { font-size: 11px; color: var(--faint); flex: 0 0 auto; }
.csbright input[type=range] { flex: 1; min-width: 0; }
.csbright .sub { flex: 0 0 auto; font-size: 10px; }

/* The swatches and the button that resets them in one wrapping row, so the
   button follows the last swatch instead of starting a line of its own. */
.csrow { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.csrow #chartColours { display: contents; }
/* The reset beside the five swatches it puts back.
 *
 * It used to be a dashed outline in the faint grey, which read as a gap in
 * the row rather than as a control - the one thing on that line that DOES
 * something looked the least like it could. It is an ordinary chip now, the
 * same shape as every other button in the program, with the sign for undo on
 * it so the row says what it does without a fifth word.
 *
 * The arrow is drawn rather than typed. The character for it renders in the
 * system font at the system's own weight, which is a second icon language in
 * a row of nothing but colour.
 */
#chartSettings .chip.resetfive {
  display: inline-flex; align-items: center; gap: 7px;
  background: var(--top), var(--panel-2);
  border: 1px solid var(--line-soft); color: var(--text);
  box-shadow: var(--sheen);
}
#chartSettings .chip.resetfive:hover {
  border-color: var(--line-strong); background: var(--top), var(--hover);
}
#chartSettings .chip.resetfive svg { flex: 0 0 auto; opacity: .8; }

/* And the row it stands in, made level.
 *
 * The swatch grid carries `margin: 8px 0 0`, which is right when it is
 * stacked under a heading and wrong when it is a flex item beside a button:
 * what gets centred is the box WITH its margin, so the swatches sat four
 * pixels below the middle while the button sat exactly on it. The button was
 * never the thing that was crooked.
 *
 * Same fault as the theme switch's bottom margin one section up: a spacing
 * decision that is correct where it was written and travels badly. */
#chartSettings .csrow .colgrid { margin-top: 0; }

/* A switch that looks switched. The tick boxes it replaces were a square that
   said nothing until it was read. */
.cstoggles { display: grid; grid-template-columns: 1fr 1fr; gap: 2px 16px; }
/* The switch was written for the row of them in chart settings and the rules
   were scoped to that row. The first one put anywhere else - the candle
   window's hollow tick - came out as the browser's own blue box glued to its
   label, which is what an unstyled checkbox is. So a second row was named in
   every selector, and the comment here said the look belongs to the control
   rather than to the row it sits in while the rules went on saying otherwise.
   The third place - the order panel's "On the chart" - is where that stopped
   being tidy. It is `.tg` now, wherever `.tg` is. */
.tg {
  display: flex; align-items: center; gap: 9px;
  padding: 6px 2px; font-size: 12px; color: var(--dim); cursor: pointer;
}
.tg input {
  position: absolute; opacity: 0; pointer-events: none;
}
.tg i {
  position: relative; width: 30px; height: 17px; flex: 0 0 30px;
  border-radius: 999px; background: var(--panel-3); box-shadow: var(--sheen);
  transition: background .14s;
}
.tg i::after {
  content: ''; position: absolute; top: 2px; inset-inline-start: 2px;
  width: 13px; height: 13px; border-radius: 50%; background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .5);
  transition: inset-inline-start .14s;
}
.tg:hover i { background: var(--line-strong); }
.tg:has(input:checked) { color: var(--text); }
.tg:has(input:checked) i { background: var(--accent); }
.tg:has(input:checked) i::after { inset-inline-start: 15px; }
.tg:has(input:focus-visible) i {
  outline: 2px solid var(--ring); outline-offset: 2px;
}

.csfield { display: flex; flex-direction: column; gap: 4px; flex: 1 1 auto; }
.csfield.narrow { flex: 0 0 auto; }
.csfield .lbl { font-size: 11px; color: var(--faint); }

/* While a timeframe's bars are on their way. On a small store this is over
   before it is seen; on a big one it is the difference between waiting and
   pressing the button again, which is what put two requests in the air. */
#topbar .chip.busy { opacity: .6; }
#topbar .chip.busy .caret { visibility: hidden; }
#topbar .chip.busy::after {
  content: ''; width: 9px; height: 9px; margin-inline-start: 2px;
  border: 1.5px solid var(--line-strong); border-top-color: var(--accent);
  border-radius: 50%; animation: chipspin .7s linear infinite;
}
@keyframes chipspin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  #topbar .chip.busy::after { animation: none; }
}

/* The selected chip in a chip group (dark/light mode). */
.chip.on { border-color: var(--accent); color: var(--accent); }
#topbar .iconbtn { display: inline-flex; align-items: center; justify-content: center; padding: 6px 8px; }
#topbar .iconbtn svg { display: block; }
#topbar .iconbtn.on { border-color: var(--accent); color: var(--accent); }
#themeMode { display: flex; gap: 6px; }
#themeMode .chip { flex: 1; }
.pickrow .open {
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 6px;
  color: var(--text); cursor: pointer; padding: 4px 12px; font-size: 13px;
  margin-inline-end: 6px;
}
.pickrow .open:hover { border-color: var(--accent); }

/* ---------- a colour, with its own halo ----------
 *
 * The old control was a 34x24 rectangle at the end of a label: too small to
 * judge a colour by, and too small to aim at. Each swatch now sits in a frame
 * of its own with a soft glow of its own colour beneath it. The frame is what
 * is pressed, so the target is bigger than the colour; and the frame is what
 * lights up, so a chosen dark colour is still visibly chosen.
 *
 * `--glow` is the swatch's own colour and is set where the swatch is made.
 */
/* ---- painting the candles ----
 *
 * The two sides side by side, because a bullish and a bearish candle are read
 * against each other and nobody chooses one without looking at the other.
 * Under a narrow window they stack rather than squeezing to half a swatch. */
.candleshow {
  border-radius: 12px; overflow: hidden; background: var(--bg);
  box-shadow: var(--well); height: 132px; flex: 0 0 auto;
}
.candleshow canvas { display: block; width: 100%; height: 100%; }
.candlesides { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 18px; }
@media (max-width: 620px) { .candlesides { grid-template-columns: 1fr; } }
.candleside .colgrid { grid-template-columns: repeat(3, 1fr); }
.candleside .secline { margin-top: 4px; }
/* Following the body is the state a part is born in, so the way back to it is
   the quiet control: it only lights up once there is something to undo. */
.candleside .linkback {
  display: block; margin: 3px auto 0; padding: 0 5px; border: 0; line-height: 1.5;
  background: none; color: var(--faint); font-size: 12px; border-radius: 5px;
  cursor: pointer; visibility: hidden;
}
.candleside .linkback.on { visibility: visible; }
.candleside .linkback:hover { color: var(--text); background: var(--hover); }

.colgrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 15px 11px;
           margin: 10px 0 4px; }
.colcell { display: block; text-align: center; cursor: pointer; }
.colcell .frame {
  display: block; padding: 6px; border-radius: 18px;
  border: 1px solid transparent; transition: background .12s, border-color .12s;
}
.colcell:hover .frame { background: var(--panel-2); }
.colcell:focus-within .frame { background: var(--panel-2); border-color: var(--accent); }
.colcell .swatch {
  display: block; width: 100%; height: 42px; padding: 0; border: 0;
  border-radius: 13px; background: var(--glow); cursor: pointer;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.15),
              0 6px 18px -8px var(--glow, transparent);
}
:root.light .colcell .swatch {
  box-shadow: inset 0 0 0 1px rgba(20,26,38,.16),
              0 6px 18px -8px var(--glow, transparent);
}

/* In chart settings the same cells are a wrapping row of chips: the swatch
   beside its name, sized to itself. See the note above about why the grid
   shape stopped fitting when the window became one column. */
#chartSettings .colgrid {
  display: flex; flex-wrap: wrap; gap: 7px; margin: 8px 0 0;
}
#chartSettings .colcell {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 4px 9px 4px 5px; border-radius: 9px;
  background: var(--panel-2); border: 1px solid var(--line-soft);
  font-size: 11px; color: var(--dim); text-align: start;
}
#chartSettings .colcell:hover { border-color: var(--line); color: var(--text); }
#chartSettings .colcell .frame { padding: 0; border-radius: 6px; }
#chartSettings .colcell:hover .frame,
#chartSettings .colcell:focus-within .frame { background: none; }
#chartSettings .colcell:focus-within { border-color: var(--accent); }
#chartSettings .colcell .swatch {
  width: 20px; height: 20px; border-radius: 6px;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .15),
              0 4px 12px -5px var(--glow, transparent);
}
:root.light #chartSettings .colcell .swatch {
  box-shadow: inset 0 0 0 1px rgba(20, 26, 38, .16),
              0 4px 12px -5px var(--glow, transparent);
}
#chartSettings .colcell .cap { font-size: 11px; letter-spacing: 0; }
.colcell .cap { display: block; font-size: 11px; color: var(--dim); margin-top: 7px;
                line-height: 1.35; }

/* The same frame, at the size a row can hold. */
.swframe { display: inline-block; padding: 4px; border-radius: 12px;
           border: 1px solid transparent; }
.swframe:hover { background: var(--panel-2); }
.shaderow .swatch {
  display: block; width: 30px; height: 22px; padding: 0; border: 0;
  border-radius: 7px; background: var(--glow); cursor: pointer;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.15),
              0 4px 12px -6px var(--glow, transparent);
}
#setTz { width: 100%; }
/* The brightness slider and the word beside it. The word is what makes the
   control readable without a number nobody can interpret: 50 means nothing,
   "as built" does. */
.toneline { display: flex; align-items: center; gap: 10px; width: 100%; }
.toneline input[type=range] { flex: 1; }
.toneline .sub { flex: 0 0 auto; min-width: 68px; text-align: end; }

/* ---------- sliders ----------
   The browser's own slider paints its track across the whole element and then
   travels the handle inside it, so at one end a stretch of bare track is left
   over that the handle can never reach. Measured at "brightest": the track ran
   from x=2 to x=446 and the handle stopped at x=13. Eleven pixels, and it
   reads exactly like a control that has stopped early - it was reported four
   times as one.

   So both pieces are ours. `appearance: none` lets the handle travel edge to
   edge, and the fill stops under the handle's centre rather than at the edge
   of the element: `--at` is the fraction along, set by `paintSlider` on every
   move, and `--at-x` turns it into the position of that centre. Which side of
   it is filled depends on the direction the control is laid out in, and that
   is a computed style rather than an attribute here, so the class is put on by
   the same function. */
/* Named three ways because a range input takes the panel's ordinary field
   styling now. It never used to: with the browser's own appearance, Chromium
   throws away `border`, `background` and `padding` on a range, so the frame
   `.pad.grid2 input` and `.stepper input` put on every field was invisible
   here - and appeared the moment the appearance became ours. This is the same
   weight as those two and comes after them. */
input[type=range],
.pad input[type=range],
.stepper input[type=range] {
  --at-x: calc(var(--at, 0.5) * (100% - 16px) + 8px);
  width: auto;
  -webkit-appearance: none;
  appearance: none;
  height: 20px;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
input[type=range]::-webkit-slider-runnable-track,
.stepper input[type=range]::-webkit-slider-runnable-track {
  height: 6px;
  border-radius: 999px;
  background: linear-gradient(to right,
    var(--accent) 0 var(--at-x), var(--line-soft) var(--at-x) 100%);
}
input[type=range].rtl::-webkit-slider-runnable-track,
.stepper input[type=range].rtl::-webkit-slider-runnable-track {
  background: linear-gradient(to right,
    var(--line-soft) 0 var(--at-x), var(--accent) var(--at-x) 100%);
}
input[type=range]::-webkit-slider-thumb,
.stepper input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  margin-top: -5px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 2px var(--panel);
}
input[type=range]:hover::-webkit-slider-thumb { background: var(--accent-2, var(--accent)); }
input[type=range]:focus-visible { outline: none; }
input[type=range]:focus-visible::-webkit-slider-thumb {
  box-shadow: 0 0 0 2px var(--panel), 0 0 0 4px var(--accent);
}
input[type=range]:disabled { cursor: default; opacity: 0.5; }


/* ============================================================ two windows
 *
 * The jump-to-date and the indicator style panel, built as three columns
 * under one header rather than a stack of rows. Both had everything they
 * needed and neither was worth looking at.
 *
 */
/* ---------- jump to date ---------- */
#jump .sheet.jump { width: 760px; max-width: 95vw; }
.jumpbody {
  display: grid; grid-template-columns: 212px 1fr 198px;
  gap: 0; align-items: stretch;
}
.jumpbody .jcol { padding: 16px 18px; min-width: 0; }
.jumpbody .jcol + .jcol { border-inline-start: 1px solid var(--line-soft); }

.jfield { display: flex; flex-direction: column; gap: 5px; margin-bottom: 12px; }
.jfield > span {
  font-size: 10px; letter-spacing: .7px; text-transform: uppercase;
  color: var(--faint);
}
.jfield input { font-size: 14px; }
.jnote {
  display: flex; gap: 8px; align-items: flex-start;
  margin: 16px 0 0; padding: 10px 11px;
  background: var(--panel-2); border-radius: 11px;
  font-size: 11.5px; color: var(--dim); line-height: 1.7;
}
.jnote > span:last-child { min-width: 0; }
/* A date is one word. Let it break at its own hyphens and the halves come
   back in the order the paragraph is going, not the order the date is. */
.jnote b { white-space: nowrap; font-weight: 500; color: var(--text); }
.jnote .jtick {
  flex: 0 0 auto; margin-top: 1px;
  width: 17px; height: 17px; display: grid; place-content: center;
  border-radius: 6px; background: var(--panel-2); color: var(--accent);
}

.calhead {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 10px;
}
/* «Days in UTC» on a line of its own under the month. Beside the two
   arrows it broke in two in Persian, with the earlier-month arrow over
   «UTC» (the round-4 area-13 review, F15). */
.calhead { flex-wrap: wrap; row-gap: 2px; }
/* Under the month it names, so centred like it: `body.fa .sub` would put it
   at the start. */
.calhead > .sub.jutc { flex: 0 0 100%; text-align: center; }
.calhead b {
  font-size: 12px; letter-spacing: 1.2px; text-transform: uppercase;
  color: var(--text);
}
/* The month and the year, said rather than walked to. Drawn as text with a
   quiet frame under the pointer: they sit between two arrows in a small
   calendar, and two full fields there would read as a form rather than as a
   heading. */
.calpick { display: flex; align-items: center; gap: 4px; }
.calpick select {
  appearance: none; -webkit-appearance: none;
  /* Said out of the shared field on purpose. This one is a HEADING that can
     be spoken to, not a box on a form: no ground, no border until the pointer
     is on it, and as tall as the words rather than 34. */
  height: auto; font-weight: 600;
  padding: 3px 18px 3px 7px; border: 1px solid transparent; border-radius: 7px;
  box-shadow: none;
  color: var(--text); font: inherit; font-size: 12px; font-weight: 600;
  letter-spacing: .4px; cursor: pointer; text-align: center;
  /* A caret, because the two of them read as the heading they replaced - which
     is the point, and also the risk: somebody who has spent a month pressing
     the arrow will go on pressing it unless the heading says it can be
     spoken to. */
  background: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23888f9c' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 5px center;
  background-size: 9px 6px;
}
.calpick select:hover { border-color: var(--line-soft);
  background-color: var(--hover); }
.calpick select:focus-visible { outline: 2px solid var(--ring); outline-offset: 1px; }
/* The heading is a phrase - "December 2024" - and a phrase runs in its own
   direction. The window around it stays left to right: a grid of days is a
   table, not a sentence. */
body.fa .calpick { direction: rtl; }
body.fa .calpick select {
  padding: 3px 7px 3px 18px; background-position: left 5px center;
}
.calarrow:disabled { opacity: .3; cursor: default; }
.calarrow:disabled:hover { background: none; color: var(--dim); }
.calarrow {
  width: 26px; height: 26px; display: grid; place-content: center;
  background: none; border: 1px solid transparent; border-radius: 8px;
  color: var(--dim); font-size: 17px; line-height: 1; cursor: pointer;
}
.calarrow:hover { background: var(--hover); color: var(--text); }
.caldow, .calgrid {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px;
}
.caldow > span {
  text-align: center; font-size: 9.5px; letter-spacing: .6px;
  text-transform: uppercase; color: var(--faint); padding-bottom: 4px;
}
.calgrid button {
  aspect-ratio: 1; min-height: 26px;
  display: grid; place-content: center;
  background: none; border: 1px solid transparent; border-radius: 9px;
  color: var(--text); font: 12.5px var(--mono); cursor: pointer;
}
.calgrid button.pad { color: var(--faint); }
.calgrid button:disabled { color: var(--line-strong); cursor: default; }
.calgrid button:hover:not(:disabled):not(.on) { background: var(--hover); }
.calgrid button.on {
  background: var(--accent); border-color: var(--accent); color: var(--ink);
  font-weight: 600;
}
/* Where the replay is standing, so the day you came from is findable. */
.calgrid button.here:not(.on) { border-color: var(--line-strong); }

/* ---------- the dial ----------
 * A ring is a day. The arcs on it are the four sessions, taken from the same
 * table the chart shades them with, so the two always agree. */
.jclock { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.dialwrap { width: 162px; height: 162px; position: relative; touch-action: none; }
.dialwrap svg { display: block; cursor: grab; }
.dialwrap svg:active { cursor: grabbing; }
.dialface {
  position: absolute; inset: 0; display: grid; place-content: center;
  pointer-events: none; text-align: center;
}
.dialface b { font: 22px var(--mono); letter-spacing: .5px; color: var(--text); }
.dialface span { display: block; font-size: 9.5px; color: var(--faint); letter-spacing: .8px; }
.dialsay {
  display: flex; align-items: center; justify-content: center;
  gap: 5px 8px; flex-wrap: wrap;
  font-size: 11.5px; color: var(--dim); min-height: 18px; text-align: center;
}
.dialsay i { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }

.jfoot {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  padding: 13px 18px; border-top: 1px solid var(--line-soft);
  position: relative;
}
/* Pinned to the far end rather than pushed there. It used to claim the whole
   line's slack with an automatic margin, which is what held the two buttons
   against the near edge - taking it out of the row is what lets them sit in
   the middle, and the hint stays exactly where it was. */
.jtip {
  position: absolute; inset-inline-end: 18px;
  font-size: 11px; color: var(--faint);
}
.jtip kbd {
  font: 11px var(--mono); background: var(--panel-2);
  border: 1px solid var(--line); border-radius: 5px;
  padding: 1px 5px; margin: 0 2px; color: var(--dim);
}

/* ---------- the indicator panel ---------- */
#indSetup .sheet.indset { width: 860px; max-width: 96vw; height: 78vh; }
.isbody {
  flex: 1 1 auto; min-height: 0;
  display: grid; grid-template-columns: 186px 1fr 232px;
}
.isbody > * { min-width: 0; min-height: 0; overflow-y: auto; }
.isnav { padding: 14px; border-inline-end: 1px solid var(--line-soft); }
.ismain { padding: 14px; display: flex; flex-direction: column; gap: 12px; }
.isside {
  padding: 14px; border-inline-start: 1px solid var(--line-soft);
  display: flex; flex-direction: column; gap: 12px;
}

.isnav .istabs {
  display: flex; gap: 3px; padding: 3px; margin-bottom: 14px;
  background: var(--panel-2); border-radius: 10px;
}
.isnav .istab {
  flex: 1; padding: 6px 0; border: 0; border-radius: 8px;
  background: none; color: var(--dim); font-size: 12px; cursor: pointer;
}
.isnav .istab.on { background: var(--panel); color: var(--text); box-shadow: var(--elev-1); }

.ispieces { display: flex; flex-direction: column; gap: 4px; }
.ispiece {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 10px; border-radius: 10px;
  background: none; border: 1px solid transparent;
  color: var(--dim); font-size: 12.5px; cursor: pointer; text-align: start;
}
.ispiece:hover { background: var(--hover); color: var(--text); }
.ispiece.on { background: var(--sel); border-color: var(--line-strong); color: var(--text); }
.ispiece svg { flex: 0 0 auto; }
.ispiece span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* A card per thing the indicator draws. */
.iscard {
  background: var(--lift), var(--panel-2);
  border: 1px solid var(--line-soft);
  border-radius: 14px; padding: 12px 13px;
  box-shadow: var(--sheen);
}
.iscard > .cardhead {
  display: flex; align-items: center; gap: 9px; margin-bottom: 11px;
}
.iscard > .cardhead b { font-size: 13px; }
.iscard .isrow2 {
  display: grid; grid-template-columns: 74px 1fr; gap: 9px;
  align-items: center; margin-top: 9px;
}
.iscard .isrow2 > span:first-child { font-size: 11.5px; color: var(--dim); }
/* Five and five, rather than however many happen to fit: ten colours wrapping
   on their own put the chosen one alone on a second line, which reads as a
   different control from the row above it. */
.iscolours { display: grid; grid-template-columns: repeat(5, 24px); gap: 5px; }
.iscolours .sw {
  position: relative;
  width: 24px; height: 24px; border-radius: 7px; padding: 0;
  border: 1px solid var(--line); background: none; cursor: pointer;
  transition: box-shadow .14s ease, border-color .14s ease;
}
/* Inset rather than sized: the swatch was a grid with `place-content: center`,
   and a child asking for 100% of a track sized to its own content gets none
   of it. Ten swatches with no colour in them. */
.iscolours .sw i { position: absolute; inset: 2px; border-radius: 5px; background: var(--glow); }
.iscolours .sw.on {
  border-color: var(--glow);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--glow) 28%, transparent);
}
.iscolours .sw.on::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  width: 10px; height: 10px; background: var(--ink);
  clip-path: polygon(14% 44%, 0 60%, 39% 100%, 100% 18%, 84% 4%, 39% 68%);
}

.isseg { display: flex; gap: 4px; }
.isseg button {
  flex: 1; height: 28px; display: grid; place-content: center;
  background: var(--bg); border: 1px solid var(--line); border-radius: 8px;
  color: var(--dim); cursor: pointer; padding: 0;
}
.isseg button.on { border-color: var(--accent); color: var(--text); }
.isseg button i { display: block; width: 26px; background: currentColor; border-radius: 1px; }
.isseg svg { display: block; }

.isside .sidehead {
  display: flex; align-items: center; gap: 7px;
  font-size: 11px; letter-spacing: .7px; text-transform: uppercase;
  color: var(--faint);
}
.ispreview {
  background: var(--bg); border: 1px solid var(--line-soft);
  border-radius: 12px; padding: 8px; overflow: hidden;
}
.ispreview svg { display: block; width: 100%; height: auto; }
.islegend { display: flex; flex-direction: column; gap: 8px; }
.islegend .lrow {
  display: grid; grid-template-columns: 1fr 46px auto; gap: 8px; align-items: center;
  font-size: 11.5px; color: var(--dim);
}
.islegend .lrow .nm {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text);
}
.islegend .lrow .val { font: 11.5px var(--mono); color: var(--dim); text-align: end; }
.istipcard {
  background: var(--panel-2); border: 1px solid var(--line-soft);
  border-radius: 12px; padding: 11px 12px;
  font-size: 11.5px; color: var(--dim); line-height: 1.7;
}
.istipcard b { display: block; color: var(--text); font-size: 12px; margin-bottom: 3px; }
.isgap { flex: 1; }

/* ---------- this one window, read right to left ----------
 *
 * `direction` on the sheet is all it takes: the three columns swap ends, the
 * two tabs swap, the piece list puts its swatch on the right of the name, and
 * the footer's buttons mirror - every one of those already asks the writing
 * direction rather than naming a side.
 */
/* The window's own mark and the line under its name.
 *
 * The mock-up this follows leads with both, and they do different jobs: the
 * mark makes the window recognisable at a glance among a stack of others, and
 * the line under the name answers "what does this window do" without anybody
 * having to work it out from the controls. */
#indSetup > .sheet > header { align-items: flex-start; }
#indSetup .wicon svg { display: block; }
#indSetup .wtitle span {
  display: block; margin-top: 2px; font-size: 11.5px; color: var(--faint);
  font-weight: 400;
}

/* A key rather than three lines of text. The dot carries the line's own
   colour, which is the one thing a reader is matching against the chart. */
.islegend .lrow { display: flex; align-items: center; gap: 8px; }
.islegend .ldot {
  width: 8px; height: 8px; border-radius: 50%; flex: 0 0 8px;
  box-shadow: 0 0 6px -1px currentColor;
}
.islegend .nm { flex: 1 1 auto; min-width: 0; }

/* The tip, in the gold this interface already uses for "worth a second look",
   with a lamp on it. Without either it was a fourth paragraph in a window that
   already has three, and it read as one. */
.istipcard {
  position: relative; padding-inline-start: 30px;
  border: 1px solid color-mix(in srgb, var(--news) 26%, transparent);
  background: color-mix(in srgb, var(--news) 7%, var(--panel-2));
}
.istipcard .tipmark {
  position: absolute; inset-inline-start: 10px; top: 11px;
  color: var(--gold-ink); display: block;
}
.istipcard .tipmark svg { display: block; }
.istipcard b { color: var(--gold-ink); }


/* And the things inside it that are not Persian and must not turn round: a
   figure in the legend, and anything typed into. Isolated rather than
   reversed, so a minus sign stays on the correct end of its number. */
body.fa #indSetup .islegend .lrow .val,
body.fa #indSetup input[type="number"],
body.fa #indSetup input[type="text"] { direction: ltr; unicode-bidi: isolate; }
/* The drawn preview is a picture of a chart, and a chart never turns round.
   The rows of colours and thicknesses used to be held here with it; they are
   not pictures, they are choices in an order, and in a Persian panel the first
   of them belongs on the right. */
body.fa #indSetup .ispreview { direction: ltr; }

/* The legend row is name, then swatch, then figure. Turned round, the figure
   would sit against the name and the swatch would float in the middle of the
   row; this keeps the reading order and puts the figure at the far end. */
body.fa #indSetup .islegend .lrow { grid-template-columns: 1fr 46px auto; }

/* A label and its control sit on one line. In Persian the label belongs on
   the right of that line, which `text-align: start` already gives once the
   sheet has turned - but the field itself keeps its own direction, so the
   pair is written back here rather than left to chance. */
body.fa #indSetup .iscard .isrow2 > span:first-child { text-align: start; }

/* ============================================================ the account
 *
 * Seven numbers in a grid is a form. What it is is the account somebody
 * practises against, and each figure is a different kind of thing - money, a
 * ratio, a distance, a share. Each gets a box with the mark of what it is at
 * one end and the unit it is counted in at the other, and the unit is the part
 * that was missing: `10,000` said nothing about whether that was dollars,
 * tether or bitcoin, and the account can now be any of them.
 */
#settings .pad.grid2 { gap: 14px 12px; }
.setfield { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.setfield > .lbl {
  font-size: 10px; letter-spacing: .7px; text-transform: uppercase;
  color: var(--faint);
}
.setfield > .box {
  display: flex; align-items: stretch; gap: 0; min-width: 0;
  background: var(--bg); border: 1px solid var(--line); border-radius: 11px;
  transition: border-color .14s ease;
}
.setfield > .box:focus-within { border-color: var(--accent); }
.setfield > .box > .ico {
  flex: 0 0 auto; width: 38px;
  display: grid; place-content: center;
  color: var(--accent); border-inline-end: 1px solid var(--line-soft);
  background: linear-gradient(150deg,
    color-mix(in srgb, var(--accent) 13%, transparent), transparent);
}
/* The box draws the field; the input inside it draws none.
   A DESCENDANT, because `numfield.js` wraps every number in a `.numfield`
   with its arrows and the input is then a GRANDCHILD - the same trap the note
   at `#settings .setfield > .box > .numfield > input` below is about. Written
   as a direct child, this missed exactly the fields the arrows are on, and
   they came out as a bordered box inside a bordered box the moment the shared
   field stopped being something every window opted into. */
.setfield > .box input {
  flex: 1 1 auto; min-width: 0; height: auto; box-shadow: none;
  background: none; border: 0; border-radius: 0;
  padding: 10px 12px; color: var(--text); font: 14px var(--mono);
  /* Against the unit, not against the far wall. A balance of 1 sitting a hand's
     width from the ETH that says what it is reads as two unrelated things. */
  text-align: end;
}
/* Figures sit against their unit; WORDS start where reading starts. The one
   text box on this field (the run's name, «EURUSD 2017-10-01») took the
   figures' `end` and came out flush right in the English window, reading as
   a right-to-left box (the owner, 2026-09-14). */
.setfield > .box input[type="text"] { text-align: start; }
.setfield > .box input:focus { outline: none; box-shadow: none; }
.setfield > .box > .unit {
  flex: 0 0 auto; display: grid; place-content: center;
  padding: 0 12px; color: var(--faint); font: 12px var(--mono);
}
/* ...which the line above only half did. `text-align: end` puts the digits at
   one end of a box that is 159 to 179 pixels wide, and the unit sits at the
   other: measured, «۱۲» and «پوینت» a whole field apart, saying nothing to
   each other. The box no longer stretches the input at all - it is as wide as
   the figures it holds - and the free space is pushed out past the unit, so
   the two are neighbours in both languages. */
/* `numfield.js` wraps every number in a `.numfield` with its arrows, so the
   input is a GRANDCHILD of the box and a `> input` rule here matches nothing
   at all - which is how the first go at this changed the picture not one
   pixel while reading as if it had. */
#settings .setfield > .box > .numfield { flex: 0 1 auto; width: auto; }
#settings .setfield > .box > .numfield > input {
  width: 8ch; min-width: 0; padding-inline-end: 2px;
}
#settings .setfield > .box > .unit {
  margin-inline-end: auto; padding-inline: 4px 12px;
}

/* A figure no account could be made from: a balance of nothing, leverage of
   zero, a stop-out that is not a percentage. The box is marked because the
   sentence explaining it sits at the foot of a long window, next to Apply,
   which is where the decision is. Only ever one at a time - the window names
   the first, and the next once that one is fixed. The stepper wraps some of
   these inputs and not others, so both shapes are asked for. */
#settings .setfield > .box:has(> input.bad),
#settings .setfield > .box:has(> .numfield > input.bad) {
  border-color: var(--sell);
}
#setWhatApply.bad { color: var(--sell); }
/* The sentence beside Apply. It shares the footer with the buttons, so it
   wraps rather than pushing them out of the window. */
.atfoot > .whyno {
  color: var(--sell); font-size: 12.5px; line-height: 1.45;
  min-width: 0;
  /* `start`, not `end`.
   *
   * `end` was chosen to keep the sentence against the buttons, and it did
   * that by aligning the TEXT rather than by sizing the box: the box took
   * every spare pixel in the footer and the words sat at the far end of it.
   * Which end depends on the language, and in Persian `end` is the LEFT
   * edge - so a sentence long enough to take three lines began each of them
   * at the wrong side, ragged down the right where the eye starts. Measured
   * in the running window: three lines, all flush at 10px from the left, and
   * gaps of 26.9, 24.1 and 125.7px on the right.
   *
   * A sentence begins where its language begins, in both languages. And the
   * box no longer takes the spare room - `.isgap` already does that - so the
   * sentence sits against the buttons because it is next to them, which is
   * what «against the buttons» should have meant in the first place. */
  flex: 0 1 auto; text-align: start;
  padding-inline: 10px;
}
.atfoot > .whyno:empty { display: none; }
/* The buttons keep their size and their one line; the sentence takes what is
   left. Without this the sentence squeezed them until their own words wrapped. */
.atfoot > .chip, .atfoot > .primary { flex: 0 0 auto; white-space: nowrap; }
/* A button that cannot be pressed has to look like one. It kept the full
   accent while being refused, so the only sign was that nothing happened. */
.atfoot > .primary:disabled {
  opacity: .45; cursor: not-allowed; filter: saturate(.6);
}
/* The time field has no unit to sit beside, so it keeps the whole box. */
#settings .setfield > .box > input[type="time"] { flex: 1 1 auto; width: auto; }
/* The settings grid centres what it calls `wide`, which is right for a note
   and wrong for a field: the label belongs over the left edge of its own box,
   like every other label on this window. */
#settings .setfield { text-align: start; align-items: stretch; }
/* ---------- what the balance is counted in ----------
   Four tiles, each one whole: the code, and under it what it means. */
/* Its own row, laid out on its own. Left inside the flex column the rest of
   this window uses, the grid was squeezed to 150px and its four columns came
   out 31px each with the tiles overflowing them - measured, not guessed. */
#rowCcy { display: block; width: 100%; grid-column: 1 / -1; justify-self: stretch; }
#rowCcy > .lbl { display: block; margin-bottom: 7px; }
.ccypick { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px;
           width: 100%; }
.ccypick .ccy {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 9px 6px 8px; border-radius: 12px; cursor: pointer;
  background: var(--top), var(--panel-2);
  border: 1px solid var(--line-soft); box-shadow: var(--sheen);
  color: var(--dim); font: inherit;
  transition: border-color .14s ease, box-shadow .14s ease, transform .06s ease;
}
.ccypick .ccy b {
  font: 600 14px var(--mono); letter-spacing: .4px; color: var(--text);
  /* A currency code is Latin whichever language the window is in. */
  direction: ltr; unicode-bidi: isolate;
}
.ccypick .ccy span {
  font-size: 10.5px; line-height: 1.3; color: var(--faint); text-align: center;
}
.ccypick .ccy:hover {
  border-color: color-mix(in srgb, var(--accent) 45%, var(--line));
  box-shadow: var(--sheen), 0 6px 16px -10px var(--accent);
}
.ccypick .ccy:active { transform: translateY(1px); }
.ccypick .ccy.on {
  border-color: var(--accent); color: var(--text);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent) 26%, transparent),
    color-mix(in srgb, var(--accent) 8%, transparent));
  box-shadow: var(--sheen), 0 8px 20px -12px var(--accent);
}
.ccypick .ccy.on b { color: #fff; }
.ccypick .ccy.on span { color: color-mix(in srgb, #fff 78%, transparent); }
@media (max-width: 520px) { .ccypick { grid-template-columns: repeat(2, 1fr); } }

/* The things worth knowing, marked as such rather than left as grey text at
   the foot of a form. */
.setnote {
  grid-column: 1 / -1; margin: 0;
  display: flex; gap: 9px; align-items: flex-start;
  background: var(--panel-2); border: 1px solid var(--line-soft);
  border-radius: 11px; padding: 10px 12px;
  font-size: 11.5px; color: var(--dim); line-height: 1.65;
}
.setnote:empty { display: none; }
.setnote::before {
  content: "i"; flex: 0 0 auto;
  width: 17px; height: 17px; margin-top: 1px;
  display: grid; place-content: center;
  border-radius: 50%; background: var(--sel); color: var(--accent);
  font: 600 11px var(--ui); font-style: italic;
}
#settings .actions { grid-column: 1 / -1; display: flex; justify-content: flex-end; }

/* ============================================================ the shelves
 *
 * The instrument list already grouped itself and drew each group's name as a
 * line of text. Three and a half thousand instruments live behind it, so the
 * groups are cards that say how many they hold and fold away.
 */
.pickshelf {
  background: var(--lift), var(--panel-2);
  border: 1px solid var(--line-soft);
  border-radius: 14px; margin-bottom: 10px; overflow: hidden;
  box-shadow: var(--sheen);
}
.pickhead {
  width: 100%; display: flex; align-items: center; gap: 10px;
  padding: 11px 13px; background: none; border: 0; cursor: pointer;
  color: var(--text); text-align: start;
}
.pickhead:hover { background: var(--hover); }
.pickhead .folder {
  flex: 0 0 auto; width: 26px; height: 26px;
  display: grid; place-content: center; border-radius: 8px;
  background: linear-gradient(150deg,
    color-mix(in srgb, var(--accent) 24%, transparent), var(--sel));
  color: var(--accent); box-shadow: var(--sheen);
}
.pickhead b { font-size: 13px; }
.pickhead .count {
  margin-inline-start: auto; font-size: 11px; color: var(--faint);
}
.pickhead .fold {
  flex: 0 0 auto; width: 7px; height: 7px;
  border: 1.6px solid var(--faint); border-left: 0; border-top: 0;
  transform: rotate(-135deg); margin-top: 3px;
  transition: transform .14s ease;
}
.pickshelf.shut .pickhead .fold { transform: rotate(45deg); margin-top: -3px; }
.pickshelf.shut .pickcols,
.pickshelf.shut .pickrow,
.pickshelf.shut .more { display: none; }

/* What the columns are, said once per shelf. */
.pickcols {
  display: grid; align-items: center;
  grid-template-columns: 26px 92px 1fr auto auto; gap: 10px;
  padding: 6px 13px; border-top: 1px solid var(--line-soft);
  font-size: 9.5px; letter-spacing: .6px; text-transform: uppercase;
  color: var(--faint);
}
.pickshelf .pickrow {
  display: grid; align-items: center;
  grid-template-columns: 26px 92px 1fr auto auto; gap: 10px;
  padding: 8px 13px; border-top: 1px solid var(--line-soft);
  cursor: pointer;
}
.pickshelf .pickrow:hover { background: var(--hover); }
.pickshelf .pickrow.on { background: var(--sel); }
.pickshelf .pickrow .sym { font: 13px var(--mono); color: var(--text); }
.pickshelf .pickrow .nm {
  color: var(--dim); font-size: 12px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pickshelf .pickrow .cov { color: var(--faint); font: 11px var(--mono); white-space: nowrap; }
.pickshelf .pickacts { display: flex; gap: 6px; }
.pickshelf .more { padding: 8px 13px; font-size: 11px; color: var(--faint); }

/* ============================================================ on the chart
 *
 * The shelf listing what is already on the chart drew a line each: a dot, the
 * name with its numbers run together after it, and two buttons. An indicator
 * is a thing with settings, a colour, a place in the favourites and a state,
 * and the state had nowhere to live at all - switching one off meant removing
 * it, which throws away everything you had set on it.
 */
.indactive { display: flex; flex-direction: column; gap: 10px; }
.indcard {
  background: var(--lift), var(--panel-2);
  border: 1px solid var(--line-soft);
  border-radius: 14px; padding: 11px 12px;
  box-shadow: var(--sheen);
  transition: opacity .14s ease, border-color .14s ease;
}
.indcard:hover { border-color: var(--line); }
.indcard.off { opacity: .55; }
.indcard .cardtop { display: flex; align-items: center; gap: 10px; }
.indcard .cardtop .nm { font-size: 13px; flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.indcard .cardtop .star {
  background: none; border: 0; cursor: pointer; font-size: 15px;
  color: var(--faint); padding: 0 2px;
}
.indcard .cardtop .star.on { color: var(--gold-ink); }
.indcard .cardfoot {
  display: flex; align-items: center; gap: 6px; margin-top: 10px;
  flex-wrap: wrap;
}
.indcard .params { display: flex; gap: 5px; flex-wrap: wrap; flex: 1; min-width: 0; }
.indcard .param {
  display: inline-flex; align-items: baseline; gap: 5px;
  background: var(--bg); border: 1px solid var(--line-soft);
  border-radius: 8px; padding: 3px 8px;
  font-size: 10.5px; color: var(--faint);
}
.indcard .param b { font: 12px var(--mono); color: var(--text); }
.indcard .act {
  width: 28px; height: 28px; flex: 0 0 auto;
  display: grid; place-content: center;
  background: var(--bg); border: 1px solid var(--line-soft);
  border-radius: 9px; color: var(--dim); cursor: pointer;
}
.indcard .act:hover { color: var(--text); border-color: var(--line); }
.indcard .act.danger:hover { color: var(--down-ink); border-color: var(--down); }

/* The switch. Its own, because `.sw` is built out of three elements and this
   is one button that has to sit in a row of them. */
.sw2 {
  width: 34px; height: 20px; flex: 0 0 auto; padding: 0;
  border: 0; border-radius: 20px; cursor: pointer;
  background: var(--line-strong);
  transition: background .15s ease;
  position: relative;
}
.sw2.on { background: var(--accent); }
.sw2 i {
  position: absolute; top: 3px; inset-inline-start: 3px;
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--ink); transition: inset-inline-start .15s ease;
}
.sw2.on i { inset-inline-start: 17px; }

.indempty {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 34px 20px; text-align: center;
  border: 1px dashed var(--line); border-radius: 14px;
  color: var(--dim);
}
.indempty .mark {
  width: 44px; height: 44px; display: grid; place-content: center;
  border-radius: 13px; color: var(--accent);
  background: linear-gradient(150deg,
    color-mix(in srgb, var(--accent) 26%, transparent), var(--sel));
  box-shadow: var(--sheen), var(--glow-soft);
  margin-bottom: 4px;
}
.indempty b { color: var(--text); font-size: 13px; }
.indempty .sub { font-size: 11.5px; }

/* ================================================== the pass over every window
 *
 * Eighteen windows share one skeleton, so this is where all eighteen are made
 * of the same thing. Each rule below is deliberately about *material* and not
 * about layout: a window's composition is its own business and is done one at
 * a time.
 */

/* The ground. Painted on the sheet itself rather than on the body, so a window
   whose body is a grid or a scroller does not have to be restructured to carry
   it, and anything inside with a background of its own simply covers it. */
.sheet { background: var(--ground), var(--panel); }

/* ---- the things that hold rows ----
   These were flat rectangles with a hairline round them. They keep their
   shape; they gain an edge that catches light. */
.grp, .choice, .statgrid, .sessrow, .customrow, .mirrorrow, .indrow,
.jcard, .pickrow, .shelfrow, .chalrow, .prow {
  background: var(--top), var(--panel-2);
  border-color: var(--line-soft);
  box-shadow: var(--sheen);
}

/* ---- the things you press ----
   A chip is a surface too. The hover was a flat swap of two greys; now the
   surface lifts and the edge warms toward the accent, which is the difference
   between a button that responds and a button that changes colour. */
.chip, .act, .btn, .mini {
  background: var(--top), var(--panel-2);
  border-color: var(--line-soft);
  box-shadow: var(--sheen);
  transition: box-shadow .15s ease, border-color .15s ease, transform .06s ease;
}
.chip:hover, .act:hover, .btn:hover, .mini:hover {
  border-color: color-mix(in srgb, var(--accent) 40%, var(--line));
  box-shadow: var(--sheen), 0 6px 16px -10px var(--accent);
}
.chip:active, .act:active, .btn:active, .mini:active { transform: translateY(1px); }
/* A button wearing both names is the thing you press, not a chip.
 *
 * `.chip` is repainted just above, in this file, which loads after the
 * one `.primary` is defined in - so at one class each the chip won and
 * the button took the chip's pale surface while keeping primary's white
 * writing. Measured at 1.16 to one in the light theme. Two classes here,
 * so this outranks both and the question cannot come up again. */
.chip.primary {
  background: linear-gradient(180deg, var(--accent), var(--accent-2));
  color: var(--ink);
  border-color: transparent;
}
.chip.primary:hover { background: linear-gradient(180deg,
  var(--accent), var(--accent-2)); filter: brightness(1.08); }

/* One that is already on is lit rather than merely filled.
 *
 * Not a segmented control, though it used to catch those too, and quietly:
 * this sheet loads after `app.css` and matches at the same weight, so
 * `.seg button.on` lost the `var(--sel)` it was given there and got this
 * instead. That is the wrong dress for it twice over. A chip carries a
 * border, and this rule says most of what it has to say through
 * `border-color` - but `.seg button` has `border: 0`, so that half landed
 * nowhere. What was left was a wash of accent at 22% fading to 6%, which
 * against the track a segmented control sits in measures 1.25 at its top edge
 * and 1.06 at its bottom. Reported as "chosen and not chosen look the same",
 * and they nearly did.
 *
 * So the segmented controls are out of it and keep their own spelling, which
 * is `--sel` at 1.61 - the same "chosen" as a picked instrument or a live tab.
 * Every one of them changes, not only the pair that was noticed:
 * `#tradeMode`, `#orderKind`, the two size pickers, the account tabs. */
.chip.on, .act.on, .btn.on, .chip.active {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent) 22%, transparent),
    color-mix(in srgb, var(--accent) 6%, transparent));
  border-color: color-mix(in srgb, var(--accent) 42%, transparent);
  box-shadow: var(--sheen), 0 8px 20px -12px var(--accent);
}

/* ---- the things you type in ----
   And a focus ring that is visible, which several of these did not have. */
/* The shape a field has when nothing has said otherwise.
 *
 * The rule below gives every field a background and a border COLOUR and stops
 * there, leaving the border's style and width, the corner, the padding and the
 * ink to the user agent - which draws its 2px inset bevel with near-black text,
 * on a dark panel. Wherever a window dressed its own fields nobody saw it.
 * Where none did, that bevel is what has been shipping: the indicator's
 * settings, the percentage of balance on the order ticket, the folder box in
 * the journal.
 *
 * `:where()` because this is a floor and not a decision. It has no specificity
 * at all, so it beats the user agent - any author rule does - and loses to
 * every rule in this program, which all carry some weight. Written as an
 * ordinary rule it flattened twenty-four fields that had already chosen: the
 * order ticket's square corners, the jump window's larger ones, the lab's
 * smaller ones. */
/* This is where the shared field used to be described, in two halves: a
 * `:where()` floor with no specificity that lost to everything, and a real
 * rule underneath it that set the ground. Between them they were one of the
 * seven descriptions `design/select-review.mjs` found, and the floor is what
 * made seven possible - a rule that loses to everything invites everything to
 * write its own.
 *
 * Both have gone to `A field` in app.css, which is one rule that wins, and
 * the windows that had written their own no longer do. What is left here is
 * the focus ring, which was always shared and always right. */
/* The things you press. A field is not in this list any more: it draws its
   own ring in `A field` in app.css, and an outline on top of that ring was
   two marks for one state. A checkbox, a range and a file button are still
   fields with no ring of their own, so they stay. */
input[type=checkbox]:focus-visible, input[type=range]:focus-visible,
input[type=file]:focus-visible, input[type=radio]:focus-visible,
.chip:focus-visible, .act:focus-visible,
.btn:focus-visible, .mini:focus-visible, .primary:focus-visible,
.x:focus-visible, .ank:focus-visible, .anask:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 1px;
}

/* ---- the bar you drag ----
   The default is a wide pale slab that belongs to the browser rather than to
   this. */
.sheet ::-webkit-scrollbar { width: 10px; height: 10px; }
.sheet ::-webkit-scrollbar-track { background: transparent; }
.sheet ::-webkit-scrollbar-thumb {
  border-radius: 999px; border: 3px solid transparent; background-clip: content-box;
  background-color: color-mix(in srgb, var(--text) 18%, transparent);
}
.sheet ::-webkit-scrollbar-thumb:hover {
  background-color: color-mix(in srgb, var(--text) 30%, transparent);
}

/* ============================================================== the parts kit
 *
 * The four shapes the reading in Trade Analysis is built from, given names any
 * window can use. They were written inside that one window; every window after
 * it would have had them written again, slightly differently, which is how an
 * interface stops looking like one thing.
 */

/* A figure with a ring showing how far along it is. */
.dial, .andial {
  position: relative; border-radius: 14px; padding: 11px 14px; overflow: hidden;
  border: 1px solid var(--line-soft);
  background: var(--lift), color-mix(in srgb, var(--panel-2) 78%, transparent);
  box-shadow: var(--sheen);
  display: grid; grid-template-columns: 1fr 50px; align-items: center; gap: 10px;
  --anring: color-mix(in srgb, var(--text) 9%, transparent);
}

/* A key that runs something, as against a card that shows something. */
.askkey {
  position: relative; border-radius: 14px; padding: 10px 13px; overflow: hidden;
  display: grid; grid-template-columns: 28px 1fr; align-items: center; gap: 11px;
  text-align: start; cursor: pointer; font: inherit; color: var(--text);
  border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent) 8%, transparent),
    color-mix(in srgb, var(--accent) 2%, transparent));
}

/* A row of bars that can run either way from the middle. */
.dbar { display: grid; grid-template-columns: 36px 1fr 54px;
        align-items: center; gap: 9px; font-size: 11px; }
.dbar .track { height: 7px; border-radius: 5px; position: relative;
  background: color-mix(in srgb, var(--panel-3) 70%, transparent);
  box-shadow: inset 0 1px 2px rgba(0,0,0,.38); }
.dbar .track i { position: absolute; top: 0; bottom: 0; border-radius: 5px;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.26); }
.dbar .track i.g { background: linear-gradient(180deg,
    color-mix(in srgb, var(--up) 72%, #fff), var(--up));
  box-shadow: 0 0 12px -2px var(--up), inset 0 1px 0 rgba(255,255,255,.3); }
.dbar .track i.r { background: linear-gradient(180deg,
    color-mix(in srgb, var(--down) 72%, #fff), var(--down));
  box-shadow: 0 0 12px -2px var(--down), inset 0 1px 0 rgba(255,255,255,.3); }
.dbar .track i.b { background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent) 72%, #fff), var(--accent));
  box-shadow: 0 0 12px -2px var(--accent), inset 0 1px 0 rgba(255,255,255,.3); }

/* A grid of squares whose light follows the number in them. */
.heatgrid { display: grid; gap: 3px; }
.heatgrid i { aspect-ratio: 1; border-radius: 3px;
  background: color-mix(in srgb, var(--panel-3) 60%, transparent); }

/* =================================================== trade analysis
 *
 * It opened on twenty cards in a column and left the reader to work out which
 * of them mattered. All twenty are still here, behind the rail on the left,
 * but the window now leads with the one sentence they add up to and with the
 * shape of the person who took the trades.
 *
 * Three colours only, and they mean the same thing everywhere: green is what
 * was made, red is what was lost, and the accent is the instrument itself.
 */
#analysis > .sheet > .pad.an {
  display: grid; grid-template-columns: 58px 1fr;
  /* One row, and it may not be taller than the window. Left at `auto` the row
     grows to fit the whole report, the deck inside it never becomes shorter
     than its own content, and so the deck never scrolls - the report simply
     runs off the bottom of a window that hides its overflow. */
  grid-template-rows: minmax(0, 1fr);
  padding: 0; gap: 0; overflow: hidden; min-height: 0;
  /* `.pad` is a flex row that centres its children, and that `align-items`
     carries into the grid: centred items take their content height instead of
     the row's, so the deck came out 4245px tall inside a 638px row and never
     scrolled. Measured, not guessed - it looked exactly like a missing
     `min-height: 0`. */
  align-items: stretch;
  position: relative;   /* the ground below is painted against this */
}
/* the ground the whole window sits on */
/* Grey, not a nebula. Three coloured washes used to sit behind this window;
   against Photoshop's neutral greys they read as a blue stain rather than as
   depth, so what is left is a single fall of light with no hue in it. */
#analysis > .sheet > .pad.an::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background: radial-gradient(70% 58% at 20% -12%, rgba(255,255,255,.045),
                              transparent 62%);
}
#analysis > .sheet > .pad.an > * { position: relative; z-index: 1; }

.anrail {
  border-inline-end: 1px solid var(--line-soft);
  background: color-mix(in srgb, var(--panel) 55%, transparent);
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 12px 0; overflow-y: auto;
}
.ank {
  width: 40px; height: 40px; border-radius: 12px; flex: 0 0 auto;
  display: grid; place-items: center; font-size: 15px; cursor: pointer;
  color: var(--faint); border: 1px solid transparent; background: none;
  position: relative;
}
.ank:hover { color: var(--text); background: var(--hover); }
.ank.on {
  color: var(--text);
  border-color: color-mix(in srgb, var(--accent) 40%, transparent);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent) 22%, transparent),
    color-mix(in srgb, var(--accent) 6%, transparent));
  box-shadow: 0 8px 22px -12px var(--accent), var(--sheen);
}
/* the lit edge that says which one you are in */
.ank.on::before {
  content: ""; position: absolute; inset-inline-start: -10px; top: 10px; bottom: 10px;
  width: 2px; border-radius: 2px; background: var(--accent);
  box-shadow: 0 0 9px var(--accent);
}

/* Asked of the deck, not of the browser. A `@media` query here measures the
   whole browser window, which in this application is the screen - so at a 900px
   application window every rule below stayed off and the sentence was laid out
   in a column 126px wide. */
.andeck { overflow-y: auto; padding: 13px 16px 16px; min-height: 0; min-width: 0;
          container-type: inline-size; container-name: andeck; }
.andeck > .addrow { margin-bottom: 12px; }
/* It picks one of two things. Stretched across the window it read as the most
   important control on the screen, which it is not. */
.andeck > .addrow select { flex: 0 1 280px; }
/* The tag menu names one of the customer's own tags: as wide as the longest
   of them, no wider, so the line that says what is being read and the
   picture button stay on the same row. A long tag is cut with an ellipsis
   rather than pushing them off it. */
.andeck > .addrow #anTag { flex: 0 1 auto; width: auto; max-width: 10em;
  text-overflow: ellipsis; }
/* The line that says what is being read gives way first: it shrinks and
   ends in an ellipsis (the whole of it on hover) before the picture button
   is pushed to a second row by a long tag (the adversary: a 24-character
   tag wrapped it at every width). */
.andeck > .addrow #anScopeSay { flex: 0 1 auto; min-width: 0; overflow: hidden;
  white-space: nowrap; text-overflow: ellipsis; }
/* One row, always: the general rule lets an .addrow wrap, and wrapping is
   what put the picture button on a line of its own. */
.andeck > .addrow { flex-wrap: nowrap; }

/* ---- the verdict, and the shape ---- */
.antop { display: grid; grid-template-columns: 1fr 372px; gap: 12px; }
.anverdict {
  position: relative; border-radius: 17px; overflow: hidden;
  border: 1px solid var(--line);
  background: var(--wash), linear-gradient(180deg,
    color-mix(in srgb, var(--panel-2) 72%, transparent),
    color-mix(in srgb, var(--bg) 78%, transparent));
  box-shadow: var(--sheen), 0 22px 54px -34px rgba(0,0,0,.9);
  display: grid; grid-template-columns: 1fr 300px;
}
/* The sentence sits in the middle of its half rather than at the top of it:
   the row is as tall as the shape beside it, which is taller than three lines
   of writing. */
.anvtext { padding: 16px 20px 18px; min-width: 0;
           display: flex; flex-direction: column; justify-content: center; }
.aneyebrow {
  font-size: 9.5px; letter-spacing: 1.4px; text-transform: uppercase;
  color: var(--faint);
}
.anvtext h2 {
  font-size: 25px; line-height: 1.3; font-weight: 500; letter-spacing: -.3px;
  margin: 8px 0 0; color: var(--text); text-wrap: balance;
}
/* The part of the sentence that is the finding, rather than the grammar. */
.anvtext h2 em { font-style: normal; color: var(--accent);
                 text-shadow: 0 0 24px color-mix(in srgb, var(--accent) 55%, transparent); }
.anvtext p { margin-top: 9px; color: var(--dim); font-size: 12.5px;
             max-width: 52ch; line-height: 1.6; }
.anchips { display: flex; gap: 7px; margin-top: 12px; flex-wrap: wrap; }
.anchip {
  font-size: 11px; padding: 5px 11px; border-radius: 999px; color: var(--dim);
  border: 1px solid var(--line); background: color-mix(in srgb, var(--panel-3) 55%, transparent);
}
.anchip.hot { color: var(--gold-ink); background: color-mix(in srgb, var(--news) 10%, transparent);
              border-color: color-mix(in srgb, var(--news) 42%, transparent); }
.anvcurve { position: relative; }
.anvcurve svg { position: absolute; inset: 0; width: 100%; height: 100%; }

.anradar {
  position: relative; border-radius: 17px; overflow: hidden; min-height: 264px;
  border: 1px solid var(--line-soft);
  background: var(--lift), color-mix(in srgb, var(--panel-2) 70%, transparent);
  box-shadow: var(--sheen);
  /* The line underneath is in the flow rather than pinned to the bottom: the
     lowest label of the six sits close to the edge of the drawing, and pinned
     it landed on top of that label at every height. */
  display: flex; flex-direction: column; justify-content: center;
  padding: 32px 12px 12px;
}
.anrt { position: absolute; top: 13px; inset-inline: 15px; display: flex;
        align-items: center; gap: 8px; }
.anrt b { font-size: 11.5px; font-weight: 600; }
.anrt span { margin-inline-start: auto; font-size: 9.5px; letter-spacing: 1px;
             text-transform: uppercase; color: var(--faint); }
.anradarsvg { width: 100%; height: 226px; display: block; }
.anrsay { text-align: center; font-size: 10.5px; color: var(--dim); margin-top: 3px; }
/* Named once, used by every part of the drawing, so the ring and the edge of
   the shape can never drift apart. */
.anradar, .andials { --anring: color-mix(in srgb, var(--text) 9%, transparent);
                     --anedge: color-mix(in srgb, var(--accent) 72%, var(--text));
                     --anlit: var(--text); }

/* ---- the four dials ---- */
.andials { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px;
           margin-top: 12px; }
.andial {
  position: relative; border-radius: 14px; padding: 11px 14px; overflow: hidden;
  border: 1px solid var(--line-soft);
  background: var(--lift), color-mix(in srgb, var(--panel-2) 78%, transparent);
  box-shadow: var(--sheen);
  display: grid; grid-template-columns: 1fr 50px; align-items: center; gap: 10px;
}
.andial .k { font-size: 9px; letter-spacing: 1.2px; text-transform: uppercase;
             color: var(--faint); }
.andial .v { font: 600 23px/1.1 var(--mono); margin-top: 4px; letter-spacing: -.5px;
             color: currentColor; }
.andial .n { font-size: 10.5px; color: var(--faint); margin-top: 2px; }
.andial .halo {
  position: absolute; inset: auto auto -44px -28px; width: 116px; height: 86px;
  border-radius: 50%; filter: blur(26px); opacity: .35;
  background: currentColor; pointer-events: none;
}
.andial::after { content: ""; position: absolute; inset-inline: 0; bottom: 0; height: 2px;
                 background: linear-gradient(90deg, transparent, currentColor);
                 box-shadow: 0 0 10px currentColor; }
.andial.g { color: var(--up-ink); } .andial.r { color: var(--down-ink); }
.andial.b { color: var(--accent); } .andial.p { color: var(--gold-ink); }
/* The label and the note are read, not decorated, so they keep the text colour
   rather than the dial's. */
.andial .k, .andial .n { color: var(--faint); }

/* ---- the four questions ----
 *
 * Not another section of the report: things the reader asks, each of which
 * runs when it is pressed. So they are shaped like keys - no filled panel, a
 * lit mark instead - and each says underneath what pressing it will do.
 */
.anasks { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px;
          margin-top: 12px; }
.anask {
  position: relative; border-radius: 14px; padding: 10px 13px; overflow: hidden;
  display: grid; grid-template-columns: 28px 1fr; align-items: center; gap: 11px;
  text-align: start; cursor: pointer; font: inherit; color: var(--text);
  border: 1px solid color-mix(in srgb, var(--accent) 22%, transparent);
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--accent) 22%, transparent),
    color-mix(in srgb, var(--accent) 9%, transparent));
}
.anask:hover { border-color: color-mix(in srgb, var(--accent) 45%, transparent);
               background: linear-gradient(180deg,
                 color-mix(in srgb, var(--accent) 15%, transparent),
                 color-mix(in srgb, var(--accent) 4%, transparent)); }
.anask .mark {
  width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center;
  font-size: 13px;
  background: radial-gradient(circle at 50% 30%,
    color-mix(in srgb, var(--accent) 55%, transparent),
    color-mix(in srgb, var(--accent) 14%, transparent));
  box-shadow: 0 0 16px -3px var(--accent), inset 0 1px 0 rgba(255,255,255,.28);
}
.anask .say { min-width: 0; }
.anask b { display: block; font-size: 12px; font-weight: 600; }
.anask .say span { display: block; font-size: 10.5px; color: var(--faint);
                   overflow: hidden; text-overflow: ellipsis; }

/* A narrow window cannot hold four of anything beside a radar. The shape goes
   under the sentence rather than beside it, and the dials and the keys pair
   up - measured against the smallest window this one opens at, 900px, where
   four dials came out 195px wide and every note in them wrapped to three
   lines. */
@container andeck (max-width: 1060px) {
  .antop { grid-template-columns: 1fr; }
  .anradar { min-height: 250px; }
  .andials, .anasks { grid-template-columns: repeat(2, 1fr); }
}
@container andeck (max-width: 620px) {
  .anverdict { grid-template-columns: 1fr; }
  .anvcurve { height: 120px; }
  .anvcurve svg { position: static; }
  .andials, .anasks { grid-template-columns: 1fr; }
  /* too narrow for the four in one row: wrap rather than overflow */
  .andeck > .addrow { flex-wrap: wrap; }
}

/* ---- the sections under them ----
 *
 * The cards below the reading were flat panels with a hairline round them, and
 * next to the head of the window they read as a different program. They keep
 * their shape and their content; they gain the same light. */
#analysis .ancard {
  background: var(--lift), color-mix(in srgb, var(--panel-2) 72%, transparent);
  border-color: var(--line-soft); border-radius: 14px;
  box-shadow: var(--sheen);
}
#analysis .atwo > div {
  background: var(--lift), color-mix(in srgb, var(--panel-3) 55%, transparent);
  border-color: var(--line-soft); border-radius: 10px; box-shadow: var(--sheen);
}
#analysis .abw > div {
  background: var(--lift), color-mix(in srgb, var(--panel-3) 45%, transparent);
  border-color: var(--line-soft); border-radius: 10px; box-shadow: var(--sheen);
}
/* A square that made money glows a little; one that lost glows the other
   colour. The opacity is set per square by the code that knows how big the
   day was, so the light follows the money. */
#analysis .heatrow .hc { border-radius: 3px; }
#analysis .heatrow .hc.up { box-shadow: 0 0 7px -2px var(--up); }
#analysis .heatrow .hc.down { box-shadow: 0 0 7px -2px var(--down); }
#analysis .sechead { color: var(--faint); }
#analysis .stats .k, #analysis .stats .v { border-bottom-color: var(--line-soft); }

/* A figure is written left to right whichever way the sentence around it runs.
   Isolated rather than reversed: the sentence keeps its direction and the
   number keeps its minus sign on the correct end. */
body.fa #analysis .andial .v,
body.fa #analysis .anchip .mono,
body.fa #analysis .stats .v { direction: ltr; unicode-bidi: isolate; }

/* ---- the sections under them ---- */
.ansec { display: block; scroll-margin-top: 8px; }

/* How far the new version has come. */
.upbar {
  height: 8px; border-radius: 5px; margin-top: 12px; overflow: hidden;
  background: color-mix(in srgb, var(--panel-3) 75%, transparent);
  box-shadow: inset 0 1px 3px rgba(0,0,0,.45);
}
.upbar i {
  display: block; height: 100%; width: 0; border-radius: 5px;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--accent) 55%, transparent), var(--accent));
  box-shadow: 0 0 14px -2px var(--accent), inset 0 1px 0 rgba(255,255,255,.35);
  transition: width .25s ease;
}
#upSay { margin-top: 8px; }
#analysis .sechead { margin-top: 20px; }
#anSec-figures .sechead:first-child { margin-top: 18px; }
#anOut:not(:empty) { margin-top: 12px; }

/* ============================================================ the journal
 *
 * It was four cards in a column - filter, trades, calendar, diary - each with
 * a switch. Everything was in it and it said nothing: you opened it and read a
 * table.
 *
 * Now it is one composition. The session's own shape across the top with every
 * trade standing on it, the five figures, the filters, then the trades beside
 * the one being read, and at the foot what it all means. The detail has its
 * own column because it used to be a row that expanded, and opening a trade
 * moved everything under it.
 */
/* The window is the thing that has a height, and everything inside divides
   it up. `.pad` scrolls for every other window, which here would let the whole
   composition grow past the sheet and hang out of the bottom - measured, 481
   elements were outside it. */
#journal > .sheet > .pad {
  padding: 13px 16px 16px; overflow: hidden;
  display: flex; flex-direction: column; min-height: 0;
}
/* `#jrBody` sits between the pad and the composition, with a height of its
   own choosing, so a `flex: 1` inside it divided nothing. Measured: the whole
   composition hung 1,468 pixels below the window. */
/* And it scrolls when the window is too short to hold the composition. The
   curve, the figures, the filters and the four cards at the foot are all
   fixed heights, so on a short screen they ate the lot and the trades were
   given nothing - measured, the table came out 2 pixels tall with its rows
   hanging a thousand pixels below the window. */
#journal #jrBody { flex: 1; min-height: 0; display: flex; overflow-y: auto; }
#journal .jr { flex: 1; min-height: 0; display: flex; flex-direction: column;
               gap: 11px; }
/* `.pad.col` sets `flex-shrink: 0` on its children so a stacked body cannot
   squash itself; this one is the body and has to. */
#journal > .sheet > .pad.col > #jrBody { flex-shrink: 1; }

.jrhero {
  position: relative; border-radius: 16px; overflow: hidden; flex: 0 0 auto;
  border: 1px solid var(--line-soft); box-shadow: var(--sheen);
  background: radial-gradient(90% 130% at 20% -30%,
    color-mix(in srgb, var(--accent) 14%, transparent), transparent 62%), var(--bg);
}
.jrhero svg { display: block; }
.jrdot { cursor: pointer; }
.jrheroname {
  position: absolute; inset: 11px auto auto 15px;
  font-size: 9.5px; letter-spacing: .7px; text-transform: uppercase;
  color: var(--faint);
}
/* What the curve is drawn from, said to somebody rather than labelled: it is
   read on its own up there, away from the picker it belongs to. */
.jrheroscope { position: absolute; inset: 10px 15px auto auto;
               font-size: 11px; color: var(--dim); }
[dir="rtl"] .jrheroname { inset: 11px 15px auto auto; }
[dir="rtl"] .jrheroscope { inset: 10px auto auto 15px; }

.jcard2 {
  background: var(--lift), var(--panel-2); border: 1px solid var(--line-soft);
  border-radius: 14px; padding: 11px 13px; box-shadow: var(--sheen);
  min-width: 0;
}
.jlbl { font-size: 9.5px; letter-spacing: .7px; text-transform: uppercase;
        color: var(--faint); margin-bottom: 9px; }

.jrfigs { display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px;
          flex: 0 0 auto; }
.jrfig { padding: 9px 13px; }
.jrfig .jlbl { margin-bottom: 2px; }
.jrfig b { font: 18px var(--mono); display: block; }
.jrfig i { font-style: normal; font-size: 10px; color: var(--faint); }
/* A figure worth looking at says so. Three losses in a row is a fact about a
   person rather than about a strategy. */
.jrfig.flag { border-color: color-mix(in srgb, var(--down) 40%, transparent); }

.jrfilters { flex: 0 0 auto; }
.jrmain { flex: 1; min-height: 0; display: grid;
          grid-template-columns: 1fr 320px; gap: 12px; }
/* A grid item's `min-height` is `auto`, which means "as tall as my contents"
   - so a table inside one grows past the row rather than scrolling in it. */
.jrmain > * { min-height: 0; }
/* The trades are what the window is for: they get a floor, and anything that
   does not fit under it scrolls rather than squeezing them out. */
.jrmain { min-height: 268px; }
/* The card has the inset, not the things inside it.
 *
 * The line over the table carried its own `padding: 2px`, so its text began
 * one pixel under the frame and three in from it - measured, in a card with a
 * 14px corner radius, so the words sat inside the curve. Putting the gap on
 * the card instead fixes both edges at once and needs no left or right named
 * anywhere, which is the trap this program keeps falling into: a padding
 * written as `left` is right in English and wrong in Persian. */
.jrlist { min-height: 0; overflow: hidden; display: flex; flex-direction: column;
          background: var(--lift), var(--panel-2);
          border: 1px solid var(--line-soft); border-radius: 14px;
          padding: 12px; box-shadow: var(--sheen); }
.jrdetail { min-height: 0; overflow-y: auto; padding-inline-end: 4px; }
.jrfoot { flex: 0 0 auto; display: grid;
          grid-template-columns: 1fr 1fr 232px 1fr; gap: 11px; }
/* None of the four may grow tall enough to push the trades off the screen. */
.jrfoot > .jcard2 { max-height: 232px; overflow-y: auto; }
/* Except the month, which is the one card whose whole point is being taken in
   at a glance. A calendar you scroll is a calendar you read twice.
   
   Six rows of square days plus its two headings come to 263, and no amount of
   squeezing gets that under 232: the chrome is already 53 of it, so the cells
   would have to fall to about 20px and a day cannot carry a figure at that
   size. So this card is as tall as a month needs and the trades table gives up
   the difference - the table is the thing built to scroll, and this is not. */
.jrfoot > .jcard2:has(.cal) { max-height: none; }

/* The head of the table stays while the trades go past it. */
#journal .jrtablewrap { flex: 1; min-height: 0; overflow-y: auto; }
#journal .jrtab thead th { position: sticky; top: 0; z-index: 1;
                           background: var(--panel-2); }

/* The trade being read is a column now, not a row that expanded, so its own
   two-column layout has to fold. */
.jrdetail .jdet { display: block; }
.jrdetail .jdet-l, .jrdetail .jdet-r { min-width: 0; }
.jrdetail .jdet-r { margin-top: 11px; }
.jrdetail .jshot img { width: 100%; height: auto; border-radius: 10px; }
.jrdetail .kv { display: flex; justify-content: space-between; gap: 10px;
                font-size: 11.5px; padding: 5px 0;
                border-bottom: 1px solid var(--line-soft); }
.jrdetail .kv:last-of-type { border-bottom: 0; }
.jrdetail .kv b { color: var(--dim); font-weight: 400; }

/* A sentence, with the colour of what it is. */
.jsay { display: flex; gap: 9px; margin-bottom: 9px; font-size: 11.5px;
        line-height: 1.55; color: var(--dim); }
.jsay i { flex: 0 0 auto; width: 6px; height: 6px; border-radius: 50%;
          margin-top: 6px; background: var(--faint); }
.jsay.up i { background: var(--up); box-shadow: 0 0 8px -1px var(--up); }
.jsay.down i { background: var(--down); box-shadow: 0 0 8px -1px var(--down); }
.jsay b { color: var(--text); font-weight: 600; }

.jtagrow { display: flex; align-items: center; gap: 10px; margin-bottom: 9px; }
.jtagrow .tag { min-width: 74px; text-align: center; }
.jtagtrack { flex: 1; height: 8px; border-radius: 5px; background: var(--panel-3);
             box-shadow: inset 0 1px 2px rgba(0,0,0,.3); min-width: 0; }
.jtagtrack i { display: block; height: 100%; border-radius: 5px;
               box-shadow: inset 0 1px 0 rgba(255,255,255,.25); }
.jtagtrack i.up { background: linear-gradient(180deg,
  color-mix(in srgb, var(--up) 72%, #fff), var(--up)); }
.jtagtrack i.down { background: linear-gradient(180deg,
  color-mix(in srgb, var(--down) 72%, #fff), var(--down)); }
.jtagrow .mono { min-width: 58px; text-align: end; font-size: 12px; }

/* A day is a small piece of glass rather than a filled square: how much it
   made decides how deep the colour is and how far the light spills, so the
   strongest day is findable from across the room instead of one shade darker. */
#journal .jcal { display: grid; grid-template-columns: repeat(7, 1fr); gap: 7px; }
#journal .jcal .jday {
  aspect-ratio: 1; border-radius: 7px;
  background: linear-gradient(160deg, rgba(255,255,255,.035), transparent 70%),
              var(--panel-3);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.05);
}
#journal .jcal .jday.up, #journal .jcal .jday.down {
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.2),
    inset 0 0 0 1px color-mix(in srgb, currentColor 36%, transparent),
    0 3px 9px -3px color-mix(in srgb, currentColor 60%, transparent);
}
#journal .jcal .jday.up {
  color: var(--up-ink);
  background: linear-gradient(158deg,
    color-mix(in srgb, var(--up) 62%, transparent),
    color-mix(in srgb, var(--up) 34%, transparent));
}
#journal .jcal .jday.down {
  color: var(--down-ink);
  background: linear-gradient(158deg,
    color-mix(in srgb, var(--down) 62%, transparent),
    color-mix(in srgb, var(--down) 34%, transparent));
}

/* ---------- tick boxes ----------
   `accent-color` only says what colour to fill a box the browser has already
   decided how to draw, and an unchecked one it draws as a white square with a
   grey edge whatever the theme is doing. Against a pale panel that is
   invisible; against a dimmed one it is a bright hole. Photographed in the
   light theme at its lowest setting: eight white squares agreeing with
   nothing around them.

   Both of the colours the box is made of are toned ones, so it dims with the
   panel it sits on. The accent and the ink it is checked in are not - a tick
   is a signal, and somebody dimming the screen has not asked for a different
   one. The switches under `.sw` keep their own hidden input; the rule for
   those comes after this one on purpose. */
input[type=checkbox] {
  -webkit-appearance: none;
  appearance: none;
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  margin: 0;
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: 5px;
  background: var(--panel-2);
  cursor: pointer;
  display: inline-grid;
  place-content: center;
  transition: background .12s ease, border-color .12s ease;
}
/* The tick itself, cut out of a square rather than drawn as a glyph: a font
   is not guaranteed to have one that sits right at 9 pixels. */
input[type=checkbox]::before {
  content: "";
  width: 10px;
  height: 10px;
  background: var(--ink);
  transform: scale(0);
  transition: transform .12s ease;
  clip-path: polygon(14% 44%, 0 60%, 39% 100%, 100% 18%, 84% 4%, 39% 68%);
}
input[type=checkbox]:checked {
  background: var(--accent);
  border-color: var(--accent);
}
input[type=checkbox]:checked::before { transform: scale(1); }
input[type=checkbox]:hover { border-color: var(--accent); }
input[type=checkbox]:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--panel), 0 0 0 4px var(--accent);
}
input[type=checkbox]:disabled { opacity: 0.5; cursor: default; }

/* ---------- per-indicator colour + width ---------- */
.indrow .indstyle { display: inline-flex; align-items: center; gap: 8px; margin-inline-start: auto; }
.indrow .indstyle .swatches { display: inline-flex; gap: 2px; }
.indrow .indstyle .sw {
  width: 14px; height: 14px; padding: 0; border-radius: 3px;
  border: 2px solid transparent; cursor: pointer;
}
.indrow .indstyle .sw.on { border-color: var(--text); }
.indrow .indstyle .widths { display: inline-flex; gap: 2px; }
.indrow .indstyle .wd {
  width: 22px; height: 20px; display: inline-flex; align-items: center;
  justify-content: center; background: var(--panel-2);
  border: 1px solid var(--line); border-radius: 4px; color: var(--dim); cursor: pointer;
}
.indrow .indstyle .wd i { display: block; width: 12px; background: currentColor; border-radius: 2px; }
.indrow .indstyle .wd.on { border-color: var(--accent); color: var(--text); }

/* ---------- the drawing style bar ---------- */

/* The words of a note, on the style bar beside its colours. */
#dsText {
  width: 150px; background: var(--bg); border: 1px solid var(--line);
  border-radius: 6px; padding: 4px 8px; color: var(--text);
  font: inherit; font-size: 12px;
}
#dsText:focus { outline: none; border-color: var(--accent); }

/* The switches at the foot of the rail. Dimmer than the tools when off,
   because they are a state and not something you are about to do. */
#tools .sw { opacity: .55; }
/* The drawn icons take the button's colour, so one rule dims them all.
   The flyout shows the same ones, so it is not scoped to the rail. */
.ic { width: 16px; height: 16px; display: block; margin: 0 auto; }
/* The eye is struck through only while the drawings are actually hidden -
   the icon then says what the state is, not what the button does. */
.ic .slash { opacity: 0; }
#tools .sw.on .ic .slash { opacity: 1; }
#tools .sw:hover { opacity: 1; }
#tools .sw.on {
  opacity: 1; background: var(--sel);
  box-shadow: inset 2px 0 0 var(--accent);
}

#drawStyle {
  /* Beside the thing it is about, and it moves with it. `app.js` sets `top`
     and `left` from the selected shape's own box every time the chart is
     drawn, which is every frame of a drag.

     It used to be pinned to the top middle of the chart, which is where the
     buy/sell strip is - so the controls for a rectangle down in the corner
     appeared at the far end of the window, and stayed there while the
     rectangle was dragged away from them. Reported as «why do these settings
     come up at the top? they should come up on the drawing and move with
     it». */
  position: absolute; top: 10px; left: 10px;
  z-index: var(--layer-float);
  display: flex; align-items: center; gap: 6px;
  padding: 5px 8px;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 10px; box-shadow: 0 6px 24px rgba(0,0,0,.45);
}
/* In Persian the bar reads the other way, like everything else. It only
   started to matter when a note's words went on it: a text box at the far
   left of a right-to-left row is the one thing on it that looks wrong.
 *
 * Turned by direction rather than by `flex-direction: row-reverse`, which was
 * here first and only turned the bar's GROUPS: the colours, the widths and the
 * dashes each still began on the left inside a bar that reads from the right,
 * and tabbing along it crossed it backwards. */
body.fa #drawStyle { direction: rtl; }
#drawStyle .sep { width: 1px; height: 20px; background: var(--line); }

/* ---- the two openers, and what they open --------------------------------
 *
 * Measured before it was drawn: the old bar was 713 x 38, 18.8 to one, and
 * covered three quarters of the chart's width from the middle of the picture.
 * Folded like this it is about 150 wide - the colour it is set to, one word
 * for the line, and the bin with its word beside it, which stays a word
 * because a bare glyph was once reported as «the delete option is not clear at
 * all». `design/drawstyle-four.html` holds the four it was chosen from.
 */
#drawStyle .dsdot {
  width: 24px; height: 24px; flex: 0 0 24px; padding: 3px;
  border: 1px solid var(--line); border-radius: 7px;
  background: var(--panel-2); cursor: pointer;
}
#drawStyle .dsdot i {
  display: block; width: 100%; height: 100%; border-radius: 4px;
  background: currentColor;
}
#drawStyle .dspill {
  height: 24px; padding: 0 9px; border: 1px solid var(--line);
  border-radius: 7px; background: var(--panel-2); color: var(--dim);
  font: inherit; font-size: 11px; cursor: pointer; white-space: nowrap;
}
#drawStyle .dsdot:hover, #drawStyle .dspill:hover { border-color: var(--line-strong); }
#drawStyle .dsdot.on, #drawStyle .dspill.on {
  border-color: var(--accent); color: var(--text);
}

/* What they open: a small card under the bar. Under, not beside - the bar is
   already at the drawing and a card that opened sideways would be the long
   strip again by another name. */
#drawStyle .dspop {
  position: absolute; top: calc(100% + 6px); inset-inline-start: 0;
  z-index: 1;
  display: flex; flex-direction: column; gap: 6px;
  padding: 8px; background: var(--panel);
  border: 1px solid var(--line); border-radius: 9px;
  box-shadow: 0 6px 24px rgba(0,0,0,.45);
}
/* Above instead, when the bar is low enough that the card would fall off the
   bottom of the chart. `app.js` decides and sets the class. */
#drawStyle.popup .dspop { top: auto; bottom: calc(100% + 6px); }
#drawStyle .dspop.hidden { display: none; }
/* Ten colours in two rows of five: a rectangle, which is what was asked for,
   and the same ten that were in the strip. */
#drawStyle .dspop .swatches {
  display: grid; grid-template-columns: repeat(5, 22px); gap: 4px;
}
/* The ruler is a reading, and its colour and thickness are the chart's, not
   the drawing's. Offering the three rows anyway would put a control in front
   of somebody that changes nothing when pressed. The separator after each
   goes with it, or the bar ends in a stack of bare lines. */
/* The two openers now, rather than the three rows they replaced - and the
   separator that follows them, or the bar ends in a bare line. */
#drawStyle.readout #dsColourBtn,
#drawStyle.readout #dsLineBtn,
#drawStyle.readout #dsLineBtn + .sep,
#drawStyle.readout .dspop {
  display: none;
}

#drawStyle .swatches { display: flex; gap: 1px; }
/* The same halo, at toolbar size. The frame is the button, so what is pressed
   is 26 across rather than 18, and the chosen one lights its frame instead of
   growing a border that changes the colour's apparent size. */
#drawStyle .sw {
  width: 26px; height: 26px; padding: 4px; border-radius: 10px;
  border: 1px solid transparent; background: var(--glow); cursor: pointer;
}
#drawStyle .sw i {
  display: block; width: 100%; height: 100%; border-radius: 6px; font-style: normal;
  background: var(--glow); box-shadow: inset 0 0 0 1px rgba(255,255,255,.16),
                                       0 3px 9px -4px var(--glow);
}
#drawStyle .sw:hover { background: var(--panel-2); }
#drawStyle .sw.on { background: var(--panel-2); border-color: var(--accent); }

#drawStyle .widths, #drawStyle .dashes { display: flex; gap: 3px; }
#drawStyle .wd, #drawStyle .dh {
  min-width: 26px; height: 24px; display: flex; align-items: center;
  justify-content: center; gap: 0;
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 5px; color: var(--dim); cursor: pointer;
  font-size: 11px; padding: 0 6px;
}
#drawStyle .wd i { display: block; width: 16px; background: currentColor; border-radius: 2px; }
#drawStyle .wd.on, #drawStyle .dh.on { border-color: var(--accent); color: var(--text); }
#drawStyle .wd:hover, #drawStyle .dh:hover { color: var(--text); }

#drawStyle .del {
  display: inline-flex; align-items: center; gap: 5px;
  height: 24px; padding: 0 9px; background: var(--panel-2);
  border: 1px solid var(--line); border-radius: 5px;
  /* Not `--dim`. This is the one control on the bar that cannot be undone by
     pressing it again, and it was drawn quieter than the colour swatches
     beside it. It is the sell colour at rest now, which is what every other
     irreversible thing in this program is drawn in. */
  color: var(--sell); cursor: pointer;
  font: 500 12px var(--ui); white-space: nowrap;
}
#drawStyle .del svg { flex: 0 0 auto; }
#drawStyle .del:hover {
  border-color: var(--sell); background: color-mix(in srgb, var(--sell) 14%, var(--panel-2));
}

/* ---------- sessions and storage ----------
 *
 * Nothing here any more, on purpose.
 *
 * There used to be a window called `#session` — sessions and storage — whose
 * rows were an icon, two lines of text and two ICON buttons, and this is where
 * it was dressed. That window is gone: `#session`, `.sicon` and `.sinfo` match
 * nothing in the program today.
 *
 * What outlived it was one unscoped rule:
 *
 *     .sessrow button { width: 30px; height: 30px; … }
 *
 * `.sessrow` is also the class of a row in «Saved runs», which is a different
 * window whose buttons carry WORDS. So every one of them was forced into a
 * 30-pixel square with «Open it» and «Delete» hanging out of it, which is what
 * was photographed and reported as «این دکمه های open it و delete زشتن خیلی».
 * Nothing was shrinking it and no long name was to blame; the width was simply
 * imposed by a rule written for another window's icons.
 *
 * Read from the engine rather than found by eye — `CSS.getMatchedStylesForNode`
 * lists both `.sessrow button` rules at once, which reading two stylesheets
 * separately never showed. `design/sessrow-now.mjs` is that measurement kept.
 *
 * The rest of the block was already dead, or re-declared further down by the
 * block that dresses «Saved runs». If a second window of saved things is ever
 * built, give it its own class — not `.sessrow` again. */

/* Each titled block below the sessions reads as its own quiet card. */
#session .pad.col > .rule { border: 0; border-top: 1px solid var(--line); margin: 6px 0; }
#session .pad.col > p.sub > b { color: var(--text); font-size: 13px; }

/* ---------- account as a side drawer ---------- */
/* The account/subscription panel slides in from the edge and runs the full
   height, rather than floating in the middle — it is the showcase, so it gets
   room to breathe. The inline-end side means it comes from the right in
   English and the left in Persian, each the natural "drawer" side. */
#welcome { justify-content: flex-end; }
/* The drawer comes from the same side in both languages. */
#welcome .sheet {
  width: 400px; max-width: 92vw; height: 100%; max-height: 100%;
  border-radius: 0; border-top: 0; border-bottom: 0;
}
#welcome .pad.col { gap: 14px; }

/* A sign-in form in a full-height drawer.
 *
 * The drawer runs the whole window on purpose - signed in, it carries the
 * profile, the subscription, the purchase and the account details, and it
 * needs every pixel. Signed OUT it carries a title, two fields and a button,
 * and measured on a 1800 pixel window that left about twelve hundred pixels
 * of nothing under them, with the language chooser stranded at the bottom.
 *
 * The height is not the fault and must not be touched. What was wrong is
 * WHERE the emptiness went: the rule above the language chooser already
 * carries `margin-top: auto`, so every spare pixel in the column piled up in
 * that one place and the form sat on the ceiling.
 *
 * `justify-content: center` was the first attempt and it changed nothing,
 * which is the useful part: there was no free space left for it to centre
 * anything in, because that one auto margin had already claimed all of it. A
 * second auto margin does work, because two of them share the space.
 *
 * Only while signed out. The moment there is an account to show, the drawer
 * is full and centring it would push the top of a long list off the screen -
 * so the rule asks `#acctIn` whether it is hidden rather than being switched
 * on from the code, and it cannot get out of step with what is on screen. */
#welcome .pad.col:has(> #acctIn.hidden) > .wchead {
  margin-block-start: auto;
}
/* No rule is needed for the language chooser: `#welcome .pad > .rule` already
   carries `margin-top: auto`, and that is the other half of the pair. Two
   auto margins in a flex column share the free space between them, so the
   form ends up centred and the chooser stays exactly where it was. */
/* The drawer runs the full height and its contents — profile, subscription,
   purchase, all the account details — are taller than most screens. Let the
   body scroll inside the drawer instead of being clipped by it.
   flex-wrap:nowrap is essential: a base `.pad` is flex-wrap:wrap, and a column
   flex box with a fixed height + wrap re-flows the overflow into a SECOND
   side-by-side column that overflow-x:hidden then clips off-screen — so half
   the signed-in profile silently disappeared. Force a single column that
   scrolls vertically instead. */
#welcome .pad { flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden;
  flex-wrap: nowrap; }
#welcome .pad.col > * { max-width: 100%; min-width: 0; }

/* The name of the thing, once, small. It was a mark the size of a card with
   the word under it, which is a splash screen rather than a header - and it
   sat above the person whose panel this is. */
/* ---------- the name of the thing, at the top of the way in ----------
   A badge over the name, in the middle: this is the first window somebody
   meets the application in, and a badge at one edge with the name at the
   other is not an introduction. The only definition of these three now. */
.wchead {
  display: flex; flex-direction: column; align-items: center; gap: 11px;
  padding: 4px 0 16px;
}
.wchead .wcmark {
  width: 52px; height: 52px; flex: 0 0 auto; margin: 0;
  display: grid; place-content: center; border-radius: 16px;
  background: linear-gradient(150deg, var(--accent), var(--accent-2));
  color: var(--ink);
  box-shadow: var(--sheen), var(--glow);
}
/* The mark is drawn at 15 in the markup, which is the size it was when it sat
   in a 26px badge. */
.wchead .wcmark svg { width: 28px; height: 28px; }
.wchead .wctitle {
  margin: 0; text-align: center;
  font-size: 21px; font-weight: 700; letter-spacing: .3px; color: var(--text);
}

/* ---------- what the account is worth, in two figures ----------
   The panel had a name, a ring and eight closed drawers. These two are what
   somebody opens it to see, and neither was on it. */
.acctstats { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 12px 0 4px; }
.astat {
  background: var(--lift), var(--panel-2);
  border: 1px solid var(--line-soft);
  border-radius: 12px; padding: 10px 12px;
  box-shadow: var(--sheen);
  display: flex; flex-direction: column; gap: 3px; min-width: 0;
}
.astat > span {
  font-size: 10px; letter-spacing: .6px; text-transform: uppercase; color: var(--faint);
}
.astat > b {
  font: 15px var(--mono); color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* What the subscription buys, rather than only when it stops. */
.subhas { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 5px; }
.subhas li {
  display: flex; gap: 7px; align-items: flex-start;
  font-size: 11.5px; color: var(--dim); line-height: 1.5;
}
.subhas li::before {
  content: ""; flex: 0 0 auto; margin-top: 3px;
  width: 11px; height: 11px; background: var(--up);
  clip-path: polygon(14% 44%, 0 60%, 39% 100%, 100% 18%, 84% 4%, 39% 68%);
}

/* The licence and the version, moved here from a window of unrelated things.
   Both are about this account and this copy. */
.acctfoot {
  margin-top: 12px; padding-top: 12px;
  border-top: 1px solid var(--line-soft);
  display: flex; flex-direction: column; gap: 8px;
}
.acctfoot .footrow {
  display: flex; align-items: center; gap: 9px;
  font-size: 11.5px; color: var(--faint);
}
.acctfoot .footrow > span:first-child { flex: 0 0 auto; }
.acctfoot .footrow b {
  color: var(--text); font-weight: 500; margin-inline-start: auto;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.acctfoot .footrow .chip { flex: 0 0 auto; margin-inline-start: 0; }
.acctfoot .setnote { margin: 0; }

/* ---------- the settings, as places to go ----------
   Two to a row while they are shut and the full width once one is open, so
   the panel reads as a set of tiles rather than as a column of closed
   drawers. The one that is open is the only one that needs the room. */
.accttiles { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 10px; }
.accttiles > details {
  margin: 0; min-width: 0;
  background: var(--lift), var(--panel-2);
  border: 1px solid var(--line-soft);
  border-radius: 12px; overflow: hidden;
  box-shadow: var(--sheen);
  transition: border-color .14s ease;
}
.accttiles > details[open] { grid-column: 1 / -1; border-color: var(--line); }
/* An odd number of boxes would leave the last one alone with a hole beside it,
   which reads as a box that failed to draw rather than as the end of a list.
   Widening it is the same shape an opened box already takes, so the row ends
   on something the eye has seen before. */
.accttiles > details:last-child:nth-child(odd) { grid-column: 1 / -1; }
.accttiles > details:hover { border-color: var(--line); }
/* The summary fills the tile.
 *
 * The tiles sit two to a row and a grid row is as tall as its tallest cell, so
 * when one heading wraps to a second line the tile beside it is stretched to
 * match - but its summary was not, and a summary that is 39 tall in a tile
 * that is 58 puts its words at the top with the spare room underneath. The
 * `align-items: center` below was already doing its job perfectly, inside a
 * box that ended before the tile did, which is why this looks like a centring
 * fault and is not one. */
.accttiles > details { display: flex; flex-direction: column; }
.accttiles > details > summary {
  flex: 1;
  list-style: none; cursor: pointer;
  padding: 11px 12px; font-size: 12px; color: var(--text);
  display: flex; align-items: center; gap: 8px; line-height: 1.4;
}
.accttiles > details > summary::-webkit-details-marker { display: none; }
/* `.quiet` puts a "＋" in front of every summary in the program. These have a
   chevron of their own at the other end, and two markers on one row is one
   marker too many. */
.accttiles > details > summary::before { content: none; }
.accttiles > details > summary::after {
  content: ""; margin-inline-start: auto; flex: 0 0 auto;
  width: 7px; height: 7px; border: 1.6px solid var(--faint);
  border-left: 0; border-top: 0; transform: rotate(-45deg);
  transition: transform .14s ease;
}
.accttiles > details[open] > summary::after { transform: rotate(45deg); }
.accttiles > details > *:not(summary) { padding: 0 12px 12px; }
/* The sections carry fields of their own, and a field in a half-width tile
   has nowhere to be; they only ever show while the tile is full width. */
.accttiles > details .field input,
.accttiles > details .addrow input { min-width: 0; }

/* Language chooser, now living in the profile drawer rather than in the
   trading-account panel where it never belonged. */
/* A question with its answers under it, in the middle. The label used to sit
   at one end of a row with the two choices at the other. */
.langrow { display: flex; flex-direction: column; align-items: center; gap: 9px; }
.langpick { display: flex; gap: 8px; }
.langpick .chip { min-width: 92px; justify-content: center; }

/* Trading-account settings: the action sits to the right, on its own line. */
#settings .actions { grid-column: 1 / -1; display: flex; justify-content: flex-end; }

.subcard {
  display: flex; align-items: center; gap: 16px;
  background: var(--wash), var(--lift), var(--panel-2);
  border: 1px solid var(--line);
  border-radius: 14px; padding: 16px;
  box-shadow: var(--sheen);
}
.subring { position: relative; width: 96px; height: 96px; flex: 0 0 96px; }
.subring svg { width: 96px; height: 96px; transform: rotate(-90deg); }
.subring .track { fill: none; stroke: var(--line); stroke-width: 8; }
.subring .arc { fill: none; stroke-width: 8; stroke-linecap: round; }
.subring.ok .arc { stroke: var(--up); }
/* Gold, not the accent. This ring goes amber when a licence is nearly up, and
   in the accent it was the same blue as a chosen button - the one state on
   this card that wants somebody to do something looked like every ordinary
   highlight in the application. */
.subring.warn .arc { stroke: var(--gold); }
.subring .submid {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center;
}
.subring .subnum { font-size: 24px; font-weight: 700; color: var(--text); }
.subring .subsub { font-size: 10px; color: var(--dim); }
.subinfo { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.subinfo b { font-size: 14px; }
.subinfo .warn { color: var(--gold-ink); font-size: 12px; }

/* ---------- MetaTrader-style open-positions table ---------- */
.postab { width: 100%; border-collapse: collapse; font-size: 12px; }
.postab th {
  text-align: start; font-weight: 500; color: var(--faint); font-size: 10.5px;
  padding: 5px 8px; border-bottom: 1px solid var(--line); white-space: nowrap;
  position: sticky; top: 0; background: var(--panel);
}
.postab td { padding: 6px 8px; border-bottom: 1px solid var(--line); white-space: nowrap; }
.postab tr:hover td { background: var(--panel-2); }
.postab .mono { font-family: var(--mono); }
.postab .sub2 { color: var(--faint); font-size: 10px; }
/* The side of a trade as a badge, the way the closed-trade cards below
   already show it. It was coloured text in a cell here and a pill on the
   cards, which made one thing look like two - and a pill also gives the
   volume beside it something to sit in, so `BUY 0.10` reads as one fact
   rather than as a word and a number that happen to be adjacent. */
.postab .sidepill {
  display: inline-block; padding: 2px 8px; border-radius: 999px;
  font-size: 11px; white-space: nowrap;
}
/* The direction of a trade reads `--buy` / `--sell`, not `--up` / `--down`.
 *
 * Those two pairs mean different things and the table was using the wrong
 * one: `--up` and `--down` are money - the profit column, the equity ring,
 * the floating bar - while `--buy` and `--sell` are which way a trade was
 * taken. Reading direction in the money colours put two greens on one row
 * meaning two different things, so a losing BUY wore a green badge.
 *
 * The chart's position lines and the closed-trade cards already read these
 * tokens, so all three places are now one decision: whatever `--buy` is set
 * to in the palette is what a buy looks like everywhere. */
.postab .sidepill.buy { background: color-mix(in srgb, var(--buy) 16%, transparent); }
.postab .sidepill.sell { background: color-mix(in srgb, var(--sell) 16%, transparent); }
.postab .buy { color: var(--buy); font-weight: 600; }
.postab .sell { color: var(--sell); font-weight: 600; }
.postab .pl.up { color: var(--up-ink); }
.postab .pl.down { color: var(--down-ink); }
.postab .acts { display: flex; gap: 3px; }
.postab .act {
  width: 22px; height: 22px; border: 1px solid var(--line); border-radius: 5px;
  background: var(--panel-2); color: var(--dim); cursor: pointer; font-size: 12px;
  display: flex; align-items: center; justify-content: center;
}
.postab .act:hover { color: var(--text); }
.postab .act.kill:hover { border-color: var(--sell); color: var(--sell); }
#tabOpen { overflow-x: auto; }

/* ---------- Journal: the record ---------- */
/* Only the gap down to the table. The two side gaps and the one above are the
   card's now, so there is no side to get the wrong way round. */
.jrsum { font-size: 13px; padding: 0 0 10px; color: var(--dim); }
.jrsum b { font-size: 14px; }
.jrsum .up { color: var(--up-ink); } .jrsum .down { color: var(--down-ink); }
.jrtablewrap { overflow-x: auto; border: 1px solid var(--line); border-radius: 10px; }
.jrtab { width: 100%; border-collapse: collapse; font-size: 12px; }
.jrtab th {
  text-align: center; font-weight: 500; color: var(--faint); font-size: 10.5px;
  padding: 7px 9px; border-bottom: 1px solid var(--line); white-space: nowrap;
  position: sticky; top: 0; background: var(--panel-2);
}
.jrtab td { padding: 7px 9px; text-align: center; white-space: nowrap;
  border-bottom: 1px solid var(--line-soft); }
.jrtab tbody tr:hover td { background: var(--panel-2); }
.jrtab .mono { font-family: var(--mono); }
.jrtab .sub2 { color: var(--faint); font-size: 10px; }
.jrtab .sym { font-weight: 600; }
.jrtab .b { color: var(--up-ink); font-weight: 600; }
.jrtab .s { color: var(--down-ink); font-weight: 600; }
.jrtab .pl.up, .jrtab .up { color: var(--up-ink); }
.jrtab .pl.down, .jrtab .down { color: var(--down-ink); }
.jrtab .tag { display: inline-block;
  background: color-mix(in srgb, var(--accent) 16%, var(--panel));
  color: color-mix(in srgb, var(--accent) 78%, var(--text));
  border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--line));
  border-radius: 20px; padding: 1px 8px; font-size: 10.5px; }

/* ---------- Journal: sections, toggles, detail, calendar ---------- */
.jcard { background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  margin-bottom: 12px; overflow: hidden; }
.jcard > header { display: flex; align-items: center; gap: 9px; padding: 9px 13px;
  border-bottom: 1px solid var(--line); background: var(--panel-2); }
.jcard > header .jico { width: 20px; text-align: center; }
.jcard > header b { font-size: 13px; }
.jcard > header .sub { font-size: 11px; color: var(--dim); font-weight: 400; }
.jcard > header .sp { flex: 1; }
.jcard .jbody { padding: 12px 13px; }
.jcard.off .jbody { display: none; }
.jcard.off { opacity: .6; }

/* The one-time journal-setup rows: an icon, a title+description, a switch. */
.jsrow { display: flex; align-items: center; gap: 12px; padding: 11px 12px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--panel-2);
  margin-bottom: 8px; }
.jsrow .jsico { width: 22px; text-align: center; font-size: 16px; flex: 0 0 auto; }
.jsrow .jstext { display: flex; flex-direction: column; gap: 1px; flex: 1; min-width: 0; }
.jsrow .jstext b { font-size: 13px; }
.jsrow .jstext .sub { font-size: 11px; color: var(--dim); }
.jsrow.locked { opacity: .75; }
.jsrow .sw { flex: 0 0 auto; }

/* The screenshots folder, in that same window and in that same list.
 *
 * It was a 170-pixel box with 260 pixels of nothing beside it, sitting under a
 * loose line of text among five full-width bordered rows. Measured: each row
 * is 430 wide with a 15-pixel gutter on either side, and the box was 170. So
 * it is given the rows' own box and its input fills it, and the label above it
 * belongs to the same card rather than floating over it. */
#jrSetup label.wide {
  display: block; padding: 11px 12px; margin: 4px 0 8px;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--panel-2); font-size: 13px;
}
#jrSetup label.wide input { display: block; width: 100%; margin-top: 9px; }

/* toggle switch */
.sw { position: relative; width: 36px; height: 21px; flex: 0 0 36px; cursor: pointer; display: inline-block; }
/* Nothing at all: the switch is the track and the knob beside it. The
   border comes from the shared tick-box rule above and would otherwise
   leave a 2x2 remainder - invisible behind the opacity, but it is not
   meant to be there. */
.sw input { opacity: 0; width: 0; height: 0; border: 0; position: absolute; }
.sw .track { position: absolute; inset: 0; background: var(--line-strong); border-radius: 20px; transition: .15s; }
.sw .knob { position: absolute; top: 3px; inset-inline-start: 3px; width: 15px; height: 15px;
  border-radius: 50%; background: var(--text); transition: .15s; }
.sw input:checked + .track { background: var(--up); }
.sw input:checked + .track + .knob { inset-inline-start: 18px; background: var(--ink); }

.jfilters { display: flex; flex-wrap: wrap; gap: 8px; }
.jfilters input { flex: 1; min-width: 140px; }

.jrow { cursor: pointer; }
.jrow.open > td { background: var(--panel-2); }
.jdetail > td { padding: 0 !important; background: var(--panel-2); }
.jdet { display: grid; grid-template-columns: 1.1fr 1fr; gap: 14px; padding: 12px 14px; }
@media (max-width: 720px) { .jdet { grid-template-columns: 1fr; } }
.jshots { display: flex; gap: 8px; }
.jshots figure { flex: 1; margin: 0; min-width: 0; }
.jshots img { width: 100%; border: 1px solid var(--line); border-radius: 8px; display: block; cursor: zoom-in; }
.jshots figcaption { text-align: center; font-size: 10px; color: var(--dim); margin-top: 3px; }
.jshots img.shotopen { cursor: zoom-in; }
.jshotacts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.jshotacts .chip.mini { font-size: 11px; }
.jheat { margin-top: 10px; }
.jheatlab { display: flex; justify-content: space-between; font-size: 11px; color: var(--dim); }
.jbar { height: 9px; border-radius: 5px; background: var(--panel-2); position: relative; overflow: hidden;
  margin-top: 4px; display: flex; }
.jbar .jfill { height: 100%; }
.jbar .jfill.down { background: var(--down); }
.jbar .jfill.up { background: var(--up); opacity: .55; margin-inline-start: auto; }
.jdet .kv { display: flex; justify-content: space-between; padding: 5px 0;
  border-bottom: 1px dashed var(--line); font-size: 12.5px; }
.jdet .kv b { color: var(--dim); font-weight: 500; }
/* The theme's own dim ink: a fixed #c4cdda read 1.23:1 on the light theme,
   the Jump window's «History runs from» (the round-2 area-13 adversary, F3). */
.jnote { background: var(--bg); border: 1px solid var(--line); border-radius: 8px;
  padding: 8px 11px; color: var(--dim); font-size: 12.5px; margin-top: 8px; }

.calhead { font-size: 12px; color: var(--dim); margin-bottom: 8px; text-align: center; }
/* Fixed column width instead of 1fr, and layout containment, so the calendar
   grid's sizing never depends on (nor invalidates) the width of the scrolling
   panel around it — a self-contained block the browser lays out once. */
.cal { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 4px;
  contain: layout style; }
.cal .cwd { text-align: center; font-size: 9.5px; color: var(--faint); padding-bottom: 1px; }
/* Square, and the month fits in the card without scrolling.
 *
 * The cell asked for `min-height: 46px` in a column 25 wide, so every day was
 * a rectangle nearly twice as tall as it was wide and the month ran 342 tall
 * in a 232 card - a calendar you scroll is a calendar you cannot take in.
 * `aspect-ratio` ties the height to whatever width the column gives, so the
 * shape holds at any card size, and six rows of it come to 110 less than
 * before: the whole month lands inside the card with room over.
 *
 * The two lines inside are sized to the cell rather than the other way round,
 * which is why they are small: at this size the widest figure a day can carry
 * is about five characters. */
.cal .cday { background: var(--panel-2); border: 1px solid var(--line); border-radius: 6px;
  aspect-ratio: 1; min-height: 0; padding: 1px; text-align: center;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  line-height: 1.05; overflow: hidden; }
.cal .cday.empty { background: transparent; border: 0; box-shadow: none; }
.cal .cday .cn { font-size: 9.5px; color: var(--faint); }
.cal .cday .cp { font-size: 9px; font-weight: 700; margin-top: 1px;
                 white-space: nowrap; letter-spacing: -.2px; }
.cal .cday .cp.up { color: var(--up-ink); } .cal .cday .cp.down { color: var(--down-ink); }
/* Glass, not a panel with a coloured edge. `--deep` is how big the day was
   against the biggest day of the month, and it drives all three: the fill, the
   light on the top edge, and how far the glow spills. */
.cal .cday.win, .cal .cday.loss {
  border-color: transparent;
  background:
    linear-gradient(158deg,
      color-mix(in srgb, currentColor calc(30% + var(--deep, 0) * 46%), transparent),
      color-mix(in srgb, currentColor calc(14% + var(--deep, 0) * 30%), transparent)),
    var(--panel-2);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255, calc(.08 + var(--deep, 0) * .18)),
    inset 0 0 0 1px color-mix(in srgb, currentColor calc(20% + var(--deep, 0) * 28%), transparent),
    0 3px calc(4px + var(--deep, 0) * 9px) -3px
      color-mix(in srgb, currentColor calc(30% + var(--deep, 0) * 45%), transparent);
}
.cal .cday.win { color: var(--up-ink); }
.cal .cday.loss { color: var(--down-ink); }
/* The figures on it keep their own colour rather than the cell's. */
.cal .cday .cn { color: color-mix(in srgb, var(--text) 62%, transparent); }

.jdiary { display: flex; flex-direction: column; gap: 8px; }
.jdiary input[type=date] { align-self: flex-start; }
.jdiary textarea { width: 100%; font-size: 12.5px; resize: vertical; }

/* ---------- Trade Analysis: insight cards ---------- */
.angrid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 6px; }
@media (max-width: 640px) { .angrid { grid-template-columns: 1fr; } }
/* The key-and-value list under a section head. It had no rule at all, so the
   two divs of each pair simply stacked - in English as well; this is not a
   right-to-left problem, it was just never given a shape. */
.stats { display: grid; grid-template-columns: 1fr auto; gap: 0 14px; }
.stats .k, .stats .v { padding: 5px 0; border-bottom: 1px dashed var(--line); }
.stats .k { color: var(--dim); font-size: 12.5px; }
.stats .v { font: 12.5px var(--mono); font-variant-numeric: tabular-nums; text-align: end; }

.ancard { background: var(--panel); border: 1px solid var(--line); border-radius: 11px; padding: 12px 13px; }
.ancard h3 { margin: 0 0 8px; font-size: 13px; display: flex; align-items: center; gap: 8px; }
.ancard h3 .anico { width: 18px; text-align: center; }
/* The eight pixels here never applied. `.pad p { margin: 0 }` in the other
   stylesheet is one class and one element against this rule's one class, so
   it won, and the note ended up sitting a pixel *over* the drawing above it.
   Naming the card as well is what makes the rule stick, and it is measured
   from the writing rather than the box: a line box is taller than its
   letters, so the gap that gets specified is never the gap that is seen. */
.annote { color: var(--dim); font-size: 11px; line-height: 1.5; }
/* Sixteen, which is fifteen pixels of air above the writing. Ten looked
   right against a stat box and still cramped under a curve, because a
   drawing's ink reaches wherever its line happens to end: the gap the
   stylesheet gives is to the box, and the gap the eye sees is to the
   nearest mark inside it. Compared at 10, 13 and 16 on the real cards. */
.ancard .annote { margin: 16px 0 0; }

/* «This figure is standing on too few trades.»
 *
 * Deliberately NOT a warning colour. It is not a fault and nothing is wrong
 * with the record; it is the figure saying how much it knows, and painting it
 * red would teach people to close the window rather than to read the line.
 * Faint, small, and directly under the number it is about - a caution three
 * cards away from its figure is a caution nobody connects to anything. */
/* «You are here»: the row the open trade has already reached.
 *
 * A tint and a rule on the leading edge rather than a colour on the figures -
 * the gain and loss colours in that row are carrying their own meaning and
 * must not be argued with. `border-inline-start` on the first cell, so the
 * rule lands on the right edge in Persian and the left in English without a
 * second rule to maintain. */
.heattable tr.here td { background: var(--hover); }
.heattable tr.here td:first-child {
  border-inline-start: 3px solid var(--accent); font-weight: 600;
}
/* The line that names the open trade, directly above the table it points
   into: a caption three cards away is a caption nobody connects. */
.ancard .hnow {
  margin: 0 0 10px; font-size: 11.5px; line-height: 1.6; color: var(--dim);
}
.ancard .hnow.none { color: var(--faint); }

/* A row that is shown but must not be quoted: too few trades behind it.
   Faded rather than hidden, and by opacity rather than by colour, so the
   gain/loss colour of the figure inside it survives the fading. */
.heattable tr.wisp { opacity: 0.5; }
/* The dots need room the two averages above them do not. */
.amaemfe { margin-top: 12px; }

.anthin {
  margin: 10px 0 0; color: var(--faint); font-size: 10.5px; line-height: 1.5;
  padding-inline-start: 9px; border-inline-start: 2px solid var(--line-strong);
}

/* «Lately, against before»: three rows of two figures with an arrow between,
   so the eye reads left-to-right as «this was, this is» in either language -
   the arrow is turned with the page, the figures never are. */
.ahalves { display: flex; flex-direction: column; gap: 7px; }
.ahrow {
  display: grid; grid-template-columns: 1fr auto auto auto; align-items: baseline;
  gap: 8px; font-size: 12px;
}
.ahrow > span { color: var(--dim); }
.ahrow b {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: 13.5px; direction: ltr; unicode-bidi: isolate; text-align: end;
  min-width: 5.5ch;
}
.ahrow b.dim { color: var(--faint); font-weight: 400; }
.ahrow i { color: var(--faint); font-style: normal; }
body.fa .ahrow i { transform: scaleX(-1); display: inline-block; }

.abars { display: flex; align-items: stretch; gap: 3px; height: 66px; }
.abar { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.abar-track { position: relative; flex: 1; border-bottom: 1px dashed var(--line);
  border-top: 1px dashed var(--line); }
.abar-track i { position: absolute; left: 15%; right: 15%; border-radius: 2px; }
.abar-track i.up { background: var(--up); }
.abar-track i.down { background: var(--down); }
.abar-l { text-align: center; font-size: 9px; color: var(--faint); margin-top: 3px;
  white-space: nowrap; overflow: hidden; }
.abar-l.dim { opacity: .4; }

.acurve { width: 100%; height: 62px; display: block; }
.ahist { display: flex; align-items: flex-end; gap: 2px; height: 62px; }
.ahist i { flex: 1; border-radius: 2px 2px 0 0; min-height: 1px; }
.ahist i.up { background: var(--up); } .ahist i.down { background: var(--down); }

/* Two or more labelled bars, one under the other (`pairBars`): the label,
   the track, the figure at the end. The bar is what is read; the figure
   is what is quoted. */
.apair { display: flex; flex-direction: column; gap: 7px; }
.apr { display: grid; grid-template-columns: minmax(120px, 1.5fr) minmax(50px, 1.5fr) auto;
  gap: 10px; align-items: center; font-size: 11.5px; }
.apr > span { color: var(--dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.apr b { font: 600 12.5px var(--mono); font-variant-numeric: tabular-nums; min-width: 52px; text-align: end; }
.aprt { height: 9px; border-radius: 5px; overflow: hidden;
  background: color-mix(in srgb, var(--panel-3) 75%, transparent); }
.aprt i { display: block; height: 100%; border-radius: 5px; background: var(--accent); }
.aprt i.up { background: var(--up); } .aprt i.down { background: var(--down); }
.aprt i.dim { background: var(--faint); opacity: .55; }
.ancard .asub { margin: 8px 0 0; font-size: 11.5px; color: var(--dim); }

/* A group of trades as bars (`groupBars`): name, count · win, the net as a
   bar from the middle (right for a gain, left for a loss - the axis runs
   left to right in both languages, like every chart here), the net, PF,
   and heat where there is any. */
.gbars { display: flex; flex-direction: column; gap: 3px; margin-bottom: 10px; }
.gb { display: grid; grid-template-columns: minmax(72px, 1.1fr) 76px minmax(80px, 2fr) minmax(80px, auto) 40px;
  gap: 8px; align-items: center; font-size: 12px; min-height: 22px; }
.gbars.withheat .gb { grid-template-columns: minmax(72px, 1.1fr) 76px minmax(80px, 2fr) minmax(80px, auto) 40px 46px; }
.gb.head { font-size: 9.5px; letter-spacing: .8px; text-transform: uppercase; color: var(--faint);
  min-height: 0; margin-bottom: 2px; }
.gb .gname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gb .gn, .gb .gpf { font: 11px var(--mono); color: var(--dim); font-variant-numeric: tabular-nums; }
.gb b { font: 600 12px var(--mono); font-variant-numeric: tabular-nums; text-align: end; white-space: nowrap; }
.gb b.up { color: var(--up-ink); } .gb b.down { color: var(--down-ink); }
.gtrack { position: relative; height: 9px; border-radius: 5px; overflow: hidden; direction: ltr;
  background: color-mix(in srgb, var(--panel-3) 75%, transparent); }
.gtrack::before { content: ""; position: absolute; left: 50%; top: 0; bottom: 0; width: 1px;
  background: var(--line); }
.gtrack i { position: absolute; top: 0; bottom: 0; }
.gtrack i.up { left: 50%; background: var(--up); border-radius: 0 5px 5px 0; }
.gtrack i.down { right: 50%; background: var(--down); border-radius: 5px 0 0 5px; }
body.fa .gb .gn, body.fa .gb .gpf, body.fa .gb b, body.fa .apr b { direction: ltr; unicode-bidi: isolate; }

/* A rate drawn behind its own figure, in a table cell (`--w` is the rate). */
.abarin { display: inline-block; min-width: 64px; padding: 1px 5px; border-radius: 4px;
  direction: ltr; unicode-bidi: isolate; text-align: left;
  background: linear-gradient(to right, color-mix(in srgb, var(--up) 28%, transparent) var(--w, 0%), transparent var(--w, 0%)); }

/* A section head that folds its section, and remembers it. The head keeps
   the look of every other section head; only the chevron says it is a key. */
button.anfold { display: flex; align-items: center; gap: 6px; width: 100%; background: none;
  border: 0; border-radius: 6px; cursor: pointer; text-align: start; font: inherit;
  padding: 12px 0 4px; font-size: 10px; letter-spacing: 1.2px; text-transform: uppercase;
  color: var(--faint); }
button.anfold:hover { color: var(--text); }
button.anfold:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.anfold .chev { font-size: 11px; transition: transform .15s; display: inline-block; }
.ansec.folded .anfold .chev { transform: rotate(-90deg); }
body.fa .ansec.folded .anfold .chev { transform: rotate(90deg); }
.ansec.folded .anbody { display: none; }
@media (prefers-reduced-motion: reduce) { .anfold .chev { transition: none; } }
/* The line over the heat card when it is at the top. */
.ancard-wrap .annow, .anbody > .annow { margin: 0 0 8px; font-size: 11.5px; color: var(--dim); }

/* «Every figure» with a band beside each: label, the bar, the figure. */
.anfigs { display: flex; flex-direction: column; }
.fg { display: grid; grid-template-columns: minmax(120px, 1.4fr) minmax(60px, 1.6fr) auto;
  gap: 12px; align-items: center; padding: 5px 0; border-bottom: 1px dashed var(--line-soft); font-size: 12.5px; }
.fg .fk { color: var(--dim); }
.fg .fv { font: 12.5px var(--mono); font-variant-numeric: tabular-nums; text-align: end; min-width: 64px; }
.fg .fv.up { color: var(--up-ink); } .fg .fv.down { color: var(--down-ink); }
.fg .fv.good { color: var(--up-ink); } .fg .fv.bad { color: var(--down-ink); }
.fg .fv.fair { color: var(--gold-ink); }
.fband { height: 7px; border-radius: 4px; overflow: hidden;
  background: color-mix(in srgb, var(--panel-3) 75%, transparent); }
.fband:empty { background: none; }
.fband i { display: block; height: 100%; border-radius: 4px; background: var(--faint); }
.fband i.b { background: var(--accent); }
.fband i.good { background: var(--up); } .fband i.bad { background: var(--down); }
.fband i.fair { background: var(--gold, #d9a441); }
body.fa .fg .fv { direction: ltr; unicode-bidi: isolate; }

/* Under the two curves of a what-if card: dashed is what happened. */
.alegend { display: flex; gap: 14px; margin-top: 6px; font-size: 10.5px; color: var(--dim); }
.alegend span::before { content: ""; display: inline-block; width: 14px; height: 0; margin-inline-end: 5px;
  vertical-align: middle; border-top: 2px solid var(--accent); }
.alegend .was::before { border-top: 2px dashed var(--faint); }

.atwo { display: flex; gap: 10px; }
.atwo > div { flex: 1; background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 8px; padding: 9px; text-align: center; }
.atwo b { display: block; font-size: 17px; }
.atwo span { font-size: 10px; color: var(--dim); }

.abw { display: flex; flex-direction: column; gap: 6px; }
.abw > div { display: flex; justify-content: space-between; padding: 7px 10px;
  border-radius: 8px; background: var(--panel-2); border: 1px solid var(--line); font-size: 12.5px; }
.abw .up { border-color: color-mix(in srgb, var(--up) 55%, var(--line)); }
.abw .down { border-color: color-mix(in srgb, var(--down) 55%, var(--line)); }
.abw .up span { color: var(--up-ink); } .abw .down span { color: var(--down-ink); }

/* The empty-state preview of what Trade Analysis will hold once there are trades. */
.anempty { padding: 4px 2px; }
.anpreview { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 10px 0; }
@media (max-width: 640px) { .anpreview { grid-template-columns: 1fr; } }
.anpreview > div { background: var(--panel); border: 1px solid var(--line);
  border-radius: 10px; padding: 10px 12px; }
.anpreview b { display: block; font-size: 13px; margin-bottom: 3px; }
.anpreview span { font-size: 11px; color: var(--dim); }

/* Jump, promoted out of the More menu to sit beside the clock. */
.jumpbtn { font-size: 12px; padding: 5px 11px; white-space: nowrap; }

.sechead.sub2head { font-size: 11px; color: var(--dim); padding-top: 8px; }
/* day × hour heatmap */
.heat { display: flex; flex-direction: column; gap: 3px; overflow-x: auto; }
.heatrow { display: grid; grid-template-columns: 34px repeat(24, 1fr); gap: 2px; align-items: center; }
.heatrow.head .hh { font-size: 8px; color: var(--faint); text-align: center; }
/* Never wrapped. A label that does not fit should run out of room in a way
   somebody can see, not fold itself into a row half its height and lose the
   bottom line where nothing reports it. */
.heatrow .hlab { font-size: 9.5px; color: var(--dim); text-align: end;
  padding-inline-end: 4px; white-space: nowrap; }
.heatrow .hc { height: 15px; border-radius: 2px; background: var(--panel-2); min-width: 8px; }
.heatrow .hc.up { background: var(--up); }
.heatrow .hc.down { background: var(--down); }
.heatrow .hc.empty { background: var(--line); }

/* ---------- top-bar dropdown menus ---------- */
/* One grid for the whole menu, so the icons share a column and the figures
   share one. As a flex column of flex rows every figure started wherever its
   own label happened to end, which on a menu that now carries a figure on
   most rows reads as a list that has not been laid out. */
.topmenu {
  position: fixed; z-index: 40; min-width: 170px;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 9px; box-shadow: 0 10px 30px rgba(0,0,0,.5);
  display: grid; grid-template-columns: 16px 1fr auto;
  column-gap: 9px; row-gap: 1px;
  /* The side inset lives here rather than on the rows. It used to be a
     `margin-inline` on each row, and a margin on a subgrid item shifts its
     tracks off the lines it is borrowing: the icon kept its 16px track while
     everything after it slid left, and the 9px column-gap was eaten. Measured,
     the label started one pixel *before* the icon ended. */
  padding: 5px 10px;
}

/* Persian is written the other way, so the whole menu turns - and with it the
   icon, the label and the figure, which are laid out in logical order and need
   no mirroring of their own. Where it hangs from the button is decided in
   `openTopMenu`, which anchors it to the correct edge. */
body.fa .topmenu { direction: rtl; }
/* The timeframe chip's own menu, which is a menu like these (app.css), and
   the clock's card: in Persian it was laid out left to right, the note's
   full stop beside its first word (the adversary, 2026-09-29, pass 2). */
body.fa .tfmenu, body.fa .clockcard { direction: rtl; }
/* A divider, a heading, and everything else that is one thing rather than
   three: none of them are rows of icon-label-figure. */
.topmenu > hr, .topmenu > .mhead, .topmenu > .mtop, .topmenu > .mcap,
.topmenu > .mcard, .topmenu > .mrow, .topmenu > .msaid { grid-column: 1 / -1; }
.topmenu button {
  display: grid; grid-column: 1 / -1; grid-template-columns: subgrid;
  align-items: center;
  padding: 7px 10px; border: 0; border-radius: 6px; background: none;
  color: var(--text); cursor: pointer; font: inherit; font-size: 13px;
  text-align: start; white-space: nowrap;
}
/* The row's own padding would shift the tracks it borrowed, so the gutter it
   needs is made by the menu's padding instead. */
.topmenu button { padding-inline: 0; margin-inline: 0; }
/* Without subgrid the row keeps its own three tracks - ragged, which is what
   it was, rather than broken. */
@supports not (grid-template-columns: subgrid) {
  .topmenu button { grid-template-columns: 16px 1fr auto; }
  .topmenu .mhint { min-width: 54px; }
}
.topmenu button:hover { background: var(--panel-2);
  box-shadow: 0 0 0 10px var(--panel-2); clip-path: inset(0 -10px); }
.topmenu .mdiv { border: 0; border-top: 1px solid var(--line); margin: 4px 2px; }
/* The row follows the menu.
 *
 * This used to be pinned to `ltr` so that a Persian menu kept its icon on the
 * left, matching the English one. The owner asked for the opposite: in Persian
 * the panel is written right to left and the icon belongs on the leading edge,
 * which is the right one. Pinning it here was also invisible from the rule
 * that turns the menu round - that one set `direction` on the menu and this
 * one quietly undid it on every row inside. */
body.fa .topmenu button { direction: rtl; }
/* A figure is a figure in both languages: isolated so a minus sign stays on
   the correct end of its number, whichever way the row runs. */
body.fa .topmenu .mhint { direction: ltr; unicode-bidi: isolate; }
/* The head's distance runs with the language: the figure inside it is its own
   left-to-right island (chartmenu.js), and «پوینت» follows it, not before it
   (the area-13 adversary, F7). */
body.fa .topmenu .mtop .mpts { direction: rtl; }
.topmenu .mi { width: 16px; text-align: center; color: var(--dim);
  display: inline-flex; align-items: center; justify-content: center; }
.topmenu .mi svg { display: block; }

/* ---------- the challenge window ----------
 *
 * One window with three tabs, where there were three windows behind a menu of
 * three words. Going from making a challenge to looking at the ones you had
 * made meant closing a window, pressing the button again, and reading the
 * menu again.
 */
#challenge .sheet { width: 560px; max-width: 94vw; }
#challenge .chaltabs {
  display: flex; gap: 6px; flex: 0 0 auto;
  padding: 10px 14px;
  background: var(--top), var(--panel-2);
  border-bottom: 1px solid var(--line-soft);
}
#challenge .chaltabs .tab {
  flex: 0 0 auto; padding: 6px 13px; border-radius: 999px;
  border: 1px solid var(--line-soft); background: var(--panel);
  color: var(--faint); font: inherit; font-size: 11.5px; cursor: pointer;
  white-space: nowrap;
  transition: background .12s, border-color .12s, color .12s;
}
#challenge .chaltabs .tab:hover { color: var(--text); border-color: var(--line); }
#challenge .chaltabs .tab.active {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  border-color: var(--accent); color: var(--text);
}
#challenge .chalpanes {
  flex: 1 1 auto; display: block;
  /* A floor, so an empty shelf does not collapse the window to its tabs and
     the three tabs do not jump about as you move between them. */
  min-height: 300px;
}
/* A block the one visible pane fills, rather than three flex items competing.
   As flex items the two hidden ones took no width and the third - an empty
   shelf, so content of no width - was granted none either: measured at 0 by 0,
   which on screen is a window with nothing under its tabs. */
#challenge .chalpane { height: 100%; display: flex; }
#challenge .chalpane.hidden { display: none; }
#challenge .chalpane > .pad { flex: 1 1 auto; min-width: 0; overflow-y: auto; }

/* ---------- the laboratory ----------
 *
 * Nine tabs in one row, two of them folding onto a second line, and they were
 * never nine of a kind: three are about writing a rule and six are questions
 * you ask of one. Two rows, each with its own name, and the row you use most
 * is chips rather than a ruled tab strip.
 */
/* What a run is, above every tab rather than inside one of them. A band
   rather than a panel: it is the sentence the whole window is a question
   about, and it has to read as a header and not as another section. */
/* One saved run per line: its name, what it is, and the two things you can
   do with it. The name takes whatever room is left, because that is the part
   somebody is reading down the list for. */
#sessions .sheet { max-width: 620px; }
.sessrow {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 11px; border: 1px solid var(--line); border-radius: 11px;
  margin-bottom: 7px; background: var(--panel-2);
}
/* The name on its own line above what it holds. Side by side with the
   details and two buttons it had a third of the row, and a Persian name of
   any length came out cut in the middle. */
.sessrow > .sesstext {
  display: flex; flex-direction: column; gap: 2px;
  flex: 1 1 auto; min-width: 0;
}
/* The name is whatever the customer typed, so it gets its own direction rather
 * than the window's — the same rule the captions and tooltips already follow.
 *
 * It matters here because the name is CUT. `text-overflow` takes it off the
 * end of the paragraph, and in a right-to-left row a Latin name was one
 * right-to-left paragraph: the end was on the left, so the ellipsis ate the
 * FRONT of the name and left «…be second 1788890676712». The part that tells
 * two runs apart is usually at the front. Under `plaintext` a Latin name is
 * its own left-to-right paragraph, begins on the left like any English line in
 * a Persian window, and loses its tail instead. */
.sesstext > .sessname { font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  unicode-bidi: plaintext; text-align: start; }
.sesstext > .sesswhat { color: var(--dim); font-size: 12px; }
.sessrow > .chip { flex: 0 0 auto; }

/* One to press, one that is a danger — direction 3 of five, chosen from
   `design/sessrow-five.html`.
 *
 * There is one reason anybody opens this window, and it is to get a run back.
 * So opening is the only thing here shaped like a button: a filled block in
 * the accent, which is what this interface uses for the one action that
 * commits something. Deleting is not its equal and must not read as its twin —
 * it cannot be undone and nobody came here to do it — so it is a grey bin that
 * reddens under the hand. Quiet, but never hidden: a destructive action that
 * only appears on hover cannot be reached without a mouse.
 *
 * A word for the one you want and a mark for the one you don't also buys the
 * name its room. In a 360px sheet with two words on the end, a Persian name of
 * any length came out cut in the middle. */
.sessrow > .chip.open {
  background: var(--accent-2); border-color: transparent; color: #fff;
  font-weight: 500; font-size: 11.5px; padding: 6px 13px; border-radius: 8px;
}
.sessrow > .chip.open:hover { background: var(--accent); border-color: transparent; }
.sessrow > .chip.del {
  width: 30px; height: 30px; flex: 0 0 30px; padding: 0; border-radius: 8px;
  display: grid; place-items: center;
  background: transparent; border-color: transparent; color: var(--faint);
}
.sessrow > .chip.del:hover {
  background: var(--panel); border-color: var(--line-strong); color: var(--sell);
}
/* Focus has to be as loud as hover here, because this is the one control in
   the window that destroys something and the keyboard is how it is reached
   without a hover to warn you. */
.sessrow > .chip.del:focus-visible {
  background: var(--panel); border-color: var(--sell); color: var(--sell);
}
.sessrow > .chip.del svg { display: block; }

/* ---------- the laboratory, as two questions ------------------------------
 *
 * It was: a header, then two rows of near-identical pills eighteen pixels
 * apart, then a row of five fields, then a fold with nine controls in it, and
 * only then whatever the tab had to say. Measured at 1366x768: 226px of that
 * before the first word, which is 38% of the window, and ten tabs that read
 * as one group of ten because nothing but a nine-pixel caption said they were
 * two groups.
 *
 * They were never one group. One asks where a rule COMES FROM and the other
 * what to DO with it, and the answer is not a better caption, it is two
 * places. The sources are a column down the side, with the loaded rule and
 * its numbers under them; the body is the six things you can do and the
 * answer to whichever is chosen. The instrument, timeframe and range move
 * into the header, where the sentence they belong to already was.
 *
 * The fold goes with them. Nine controls nobody could see they had are nine
 * controls in a column that is always there.
 */
/* Ten, and the pills seven inside and five between their parts, and the
   dates 76px: at the sheet's 860 the name read «Strat…» with the pills
   sized on eights and twelves and the dates at 92 - a title cut to five
   letters over a window that had room for it once the pills were
   measured rather than padded. Measured 2026-09-14: the head needs 759 of
   858 in English and less in Persian, and «Strategy laboratory» is whole
   in both; the line under it still ends in an ellipsis, which is what a
   line under a name may do. The name still gives way first if a window is
   ever narrower than the sheet (min-width: 0 below). */
#lab .labhead { gap: 10px; }
/* The name and its line keep one line each. The pills beside them are what
   squeezed the subtitle onto a second row, and a header that is two lines on
   one instrument and one on another is a window that moves when you change
   symbol. */
/* Both lines of the name keep to one line each and end in an ellipsis when
   the pills leave them no room. `> b` was wrong: `dressWindows` moves the
   name INSIDE `.wtitle` at start-up, so that selector matched nothing and the
   name wrapped to two rows, which grew the header by 22px. */
#lab .labhead .wtitle { min-width: 0; }
#lab .labhead .wtitle > b,
#lab .labhead .wtitle > span {
  display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* The line under the name takes two lines rather than end in «…»: beside the
   pills it had 190 pixels in English and 259 in Persian for a sentence of
   285, so «and try to disprove it», the point of the window, was cut at
   every width (the round-3 area-13 adversary, F10). The name keeps one. */
#lab .labhead .wtitle > span {
  white-space: normal; display: -webkit-box;
  -webkit-box-orient: vertical; -webkit-line-clamp: 2;
}
/* The pills give way before the window's own buttons do.
   Sized to their content they pushed minimise and maximise off the end of the
   header, which is a window you cannot put away. */
/* Who gives way when the header is short of room, and it is not the controls.
 *
 * First it was nobody, and the pills pushed minimise and maximise off the
 * end: a window you cannot put away. Then it was the pills, with
 * `overflow: hidden` on them, and the range quietly lost its second date: a
 * control that is simply not there, which is worse than a missing button
 * because nothing looks wrong.
 *
 * So it is the window's own NAME that shrinks. It already ends in an ellipsis
 * when it has to, which is what a title is allowed to do and a control is
 * not. */
#lab .labctx {
  display: flex; align-items: center; gap: 6px; flex: 0 0 auto;
}
/* All the way, not down to ninety.
 *
 * `min-width: 90px` meant the name stopped giving way with 90 pixels still
 * taken, and the header needed 880 in a sheet that is 860 wide - so the close
 * button was pushed off the end and had been missing in English since this
 * window was rebuilt. `min-width: 0` is what actually lets a flex item
 * shrink; the ellipsis then does the rest, and the name is the one thing here
 * that is allowed to disappear (the icon beside it and the title bar above
 * both still say which window this is). */
#lab .labhead > .wtitle { flex: 1 1 auto; min-width: 0; }
#lab .labhead > .x, #lab .labhead > .labstop { flex: 0 0 auto; }
/* Narrower than the sheet's own 860, the pills go to a line of their own.
 *
 * Giving way by the name alone ended with no name: «Strategy laboratory» was
 * 87 of its 126 pixels at 853 (a maximised window on a 1280 screen at 150%),
 * and nothing at all from 760 down - one letter in Persian - while the three
 * pills kept their 506 (the area-13 adversary, F12). Below the width at which
 * both fit, the header takes two lines: the name and the window's buttons on
 * the first, the instrument, timeframe and range under them, wrapping among
 * themselves if they have to. The sheet is 96vw there, so 896 is where it
 * starts being narrower than 860. */
@media (max-width: 896px) {
  #lab .labhead { flex-wrap: wrap; row-gap: 6px; }
  #lab .labhead > .wtitle { min-width: min(12em, 40%); }
  #lab .labctx { order: 10; flex: 1 1 100%; flex-wrap: wrap; row-gap: 6px; }
}
#lab .ctxpill {
  display: flex; align-items: center; gap: 5px; flex: 0 0 auto;
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 9px; padding: 4px 7px; height: 30px;
}
#lab .ctxpill > i {
  font-style: normal; color: var(--faint); font-size: 10.5px; white-space: nowrap;
}
/* The control inside a pill is the pill: no second border, no second ground. */
#lab .ctxpill > b,
#lab .ctxpill > select,
#lab .ctxpill > input {
  border: 0; background: none; padding: 0; min-width: 0;
  color: var(--text); font: 600 12.5px var(--ui);
  /* One height, told rather than left to three sets of font metrics. A `b`
     came out 20px against a select's 22 and an input's 22, which is two
     pixels nobody sees on its own and is what stops a row being a row. The
     same fault this window had in the row these pills replaced. */
  height: 22px; line-height: 22px; box-sizing: border-box;
}
/* The pill draws the box, so the select inside it draws none - but
   `background: none` above took the chevron away with the ground, and a
   select with no mark at all is a word that does not say it can be spoken to.
   Small, and with no soft square behind it: the pill is the square. */
#lab .ctxpill > select {
  padding-inline-end: 14px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23aeb0b3' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1px center;
  background-size: 10px 6px;
}
body.fa #lab .ctxpill > select {
  padding-inline-end: 14px; background-position: left 1px center;
}
/* Either shape: the laboratory's two are text boxes carrying `data-date`
   now, for the reason set out at the top of `datefield.js`. */
#lab .ctxpill > input[type="date"],
#lab .ctxpill > input[data-date] { width: 76px; }   /* ten digits and two dashes at 12.5px, and no more */
#lab .ctxrange > em { font-style: normal; color: var(--faint); font-size: 11px; }
#lab .ctxpill > .pickopen { margin: 0; padding: 0 1px; }

#lab .labbody { flex: 1; min-height: 0; display: flex; }
#lab .labwork {
  flex: 1; min-width: 0; display: flex; flex-direction: column;
  padding: 13px 15px 0;
}
#lab .labside {
  /* Both axes said, not one.
   *
   * `overflow-y: auto` on its own is not "scrolls vertically": the moment
   * either axis stops being `visible` the other computes to `auto` too, so
   * this column has been offering a horizontal scrollbar all along and showed
   * one the first time a caption did not fit. A column of stacked fields has
   * nothing to scroll sideways to, and saying so is one word.
   *
   * It is not a way of hiding an overflow - `scrollWidth` still reports one,
   * which is what `design/side-overflow.mjs` reads, so anything too wide is
   * still caught and named. */
  width: 250px; flex: 0 0 auto; overflow: hidden auto;
  padding: 13px 14px 16px;
  border-inline-start: 1px solid var(--line);
  background: var(--panel-2);
}
/* The two headings that say which question a column is answering.
 *
 * White, and big enough to read. They were 10.5px in `--faint`, which is the
 * grey this window was marked down for in the first place: a nine-pixel grey
 * caption was the whole reason nobody could see the old tab rows were two
 * questions, and writing the replacement in the same grey would have carried
 * the fault across the rebuild.
 *
 * Smaller and lighter than a card's own title, so it still reads as a label
 * rather than competing with the six names under it. */
#lab .labsec {
  margin: 0 0 8px; font-size: 11px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--text);
}
#lab .labside .labsec:not(:first-child) { margin-top: 15px; }

/* Six things to do, each with the sentence that says what it is. They were
   six pills with one word on them, which is a list of six words. */
#lab .labask {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px;
  flex: 0 0 auto; margin: 0 0 12px;
}
#lab .labask .actcard,
#lab .labside .srccard {
  display: block; text-align: start; width: 100%;
  background: var(--panel-2); border: 1px solid var(--line);
  border-radius: 11px; padding: 9px 11px; box-shadow: var(--sheen);
  border-bottom-color: var(--line);
  transition: background .12s, border-color .12s;
}
#lab .labask .actcard > b,
#lab .labside .srccard > b {
  display: block; font: 600 12.5px var(--ui); color: var(--text);
}
#lab .labask .actcard > span,
#lab .labside .srccard > span {
  display: block; color: var(--faint); font-size: 10.5px; line-height: 1.5;
}
#lab .labask .actcard:hover,
#lab .labside .srccard:hover { border-color: var(--line-strong); }
#lab .labask .actcard.active,
#lab .labside .srccard.active {
  background: color-mix(in srgb, var(--accent) 16%, var(--panel-2));
  border-color: var(--accent); border-bottom-color: var(--accent);
  box-shadow: var(--sheen), 0 0 24px -12px var(--accent);
}
#lab .labside .srccard { margin-bottom: 7px; }
#lab .labside .labgrid { gap: 7px; }
#lab .labside .labfield { flex: 1 1 100%; }

/* Only ever on the screen while a sweep is running, so it is allowed to be
   loud: it is the answer to "how do I get out of this". In the header now,
   because the row it used to sit in is gone. */
/* Only ever on the screen while a sweep is running, so it is allowed to be
   loud: it is the answer to "how do I get out of this". */
#lab .labstop {
  align-self: center; border-color: var(--sell); color: var(--sell);
}
#lab .labstop:hover { background: rgba(177,62,34,.14); }
/* What used to be here: `.labnav`, its nine-pixel captions, the segmented
   control and the six chips. All of it described a shell that no longer
   exists - two rows of pills in one bar - and every rule of it came AFTER the
   two-column rules above, so `display: flex` on `.labask` quietly beat the
   grid and laid the six cards out in a squeezed row. Out-specifying it would
   have left two descriptions of one thing in the file, which is how they
   drift apart. */

/* The answer, at the top of the answer. It was three sections down a scroll,
   under the button that draws it. */
#lab .labhero {
  position: relative; border-radius: 11px; overflow: hidden;
  background: var(--bg); border: 1px solid var(--line-soft);
  min-height: 150px;
}
#lab .labhero .labchart { display: block; width: 100%; }
#lab .labherolab {
  position: absolute; inset-inline-start: 12px; top: 10px;
  font-size: 9px; font-weight: 700; letter-spacing: 1.1px;
  text-transform: uppercase; color: var(--faint); pointer-events: none;
}
#lab .labheroempty {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 8px; text-align: center;
  background: var(--bg);
}
#lab .labheroempty b { font-size: 12.5px; font-weight: 600; }
#lab .labheroempty .nowtsaid {
  font-size: 11px; color: var(--faint); max-width: 240px; line-height: 1.55;
}
#lab .labheroempty .nowtmark {
  width: 38px; height: 38px; border-radius: 12px;
  display: grid; place-content: center; font-size: 17px;
  background: color-mix(in srgb, var(--accent) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent);
  /* The fill stays the accent and only the glyph is lifted - see --accent-ink. */
  color: var(--accent-ink, var(--accent)); box-shadow: var(--sheen);
}
#lab .labheroempty.hidden { display: none; }

/* ---------- a panel with nothing in it yet ----------
 *
 * Every part of the program that has something in it was given a shape; the
 * parts that have nothing were not, and they are what a new customer sees
 * first. Three of them are on screen at once on a fresh copy - the watchlist,
 * the trades dock, the challenge shelf - and each was a different length of
 * grey sentence floating in the middle of a panel.
 *
 * One shape: a mark in the panel's own colour with the halo the rest of the
 * program uses, a line naming what belongs here, a line saying how to put it
 * there, and the button that does it where there is one.
 */
.nowt {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 9px;
  padding: 26px 20px; text-align: center;
  /* The writing weight of the accent, not the fill weight: this mark is a
     glyph on the chart ground, and the plain accent measures 4.19 there. */
  --nowt: var(--accent-ink, var(--accent));
}
.nowt[data-tone="gold"] { --nowt: var(--gold-ink); }
.nowt[data-tone="up"] { --nowt: var(--up-ink); }
.nowt::before {
  content: ''; position: absolute; pointer-events: none;
  inset-inline-start: 50%; bottom: -34px; transform: translateX(-50%);
  width: 150px; height: 90px; border-radius: 50%;
  background: var(--nowt); filter: blur(34px); opacity: .16;
}
:root.light .nowt::before { opacity: .08; filter: blur(26px); }
.nowt .nowtmark {
  position: relative;
  width: 38px; height: 38px; border-radius: 12px;
  display: grid; place-content: center; font-size: 17px;
  background: color-mix(in srgb, var(--nowt) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--nowt) 40%, transparent);
  color: var(--nowt); box-shadow: var(--sheen);
}
.nowt b { position: relative; font-size: 12.5px; font-weight: 600; }
.nowt .nowtsaid {
  position: relative;
  font-size: 11px; color: var(--faint); line-height: 1.55; max-width: 210px;
}
.nowt .chip { position: relative; margin-top: 2px; }

/* The watchlist opens 240 tall, which the full shape overflows - and what it
   cut off was the button. Same shape, drawn tighter. */
/* Measured against the panel at its opening height: 172 of content in 158
   of panel left it scrolling by a hair, which on an empty panel reads as
   something below the fold that is not there.

   Tightening the message got it to 159 in 158 and no further, and a second
   round of tightening moved it by one pixel - which is the sign that the
   padding was never what was overflowing. The block renders 148 tall and
   reports 176 of content: the glow behind the mark is placed `bottom: -28px`,
   so it hangs 28 pixels below the block, and an absolutely positioned
   descendant still counts towards what a scroll box thinks it has to reach.
   A decoration was making the panel scroll.

   Kept inside instead. The glow is a soft blur either way - it does not need
   to leave the block to be seen - and the padding goes back to what it was,
   because it was not the fault. */
/* And four pixels off the foot, which is the whole of the rest of it: the
   block came to 159 in a 158 box once the glow was no longer part of the
   sum. Landing on the exact height is how this came back the first time,
   so it is left with room rather than with none. */
/* An empty watchlist must not scroll at ANY height, and the panel is
   draggable so "any height" is the whole point.
 *
 * This was reported, fixed, and reported again. The first fix took a phantom
 * 28 pixels off a glow that hung below the block and was measured at the one
 * height the window happened to open at, where it came out three pixels under
 * - which was read as three to spare and was in fact three over. Walked
 * across every height afterwards, the truth was plain: the block is a fixed
 * 155px tall and the box scrolls whenever it is shorter than that, which
 * includes the application's own default of 240.
 *
 * Trimming padding again would move the number and not the shape. The block
 * has to FIT whatever it is given, so it takes the box's height and drops
 * what it cannot afford - the description first, then the mark, then the
 * heading. The button is last because it is the only thing in there that
 * does anything; a message you cannot read is a pity, a button you cannot
 * reach is a dead end.
 *
 * Which rule does the work was found by taking each one out again, not by
 * reading them. `height: 100%` is the one: without it the container queries
 * alone still leave a pixel over, and a pixel is a scrollbar. `overflow:
 * hidden` carries nothing today - removed, nothing overflows - and it stays
 * as the guard for the day a longer translation makes the block taller than
 * the box again. Said plainly, because a rule that looks like the fix and is
 * not is exactly how the previous attempt at this went wrong.
 *
 * And it is hidden only while the list is EMPTY. A list with rows must still
 * scroll: thirty rows in a 52px box are checked for actually moving, not
 * merely for being taller than it. */
#watchList { container-type: size; container-name: watch; }
#watchList:has(.nowt) { overflow: hidden; }
#watchList .nowt { padding: 10px 14px 8px; gap: 6px;
                   height: 100%; justify-content: center; }
@container watch (max-height: 190px) { #watchList .nowt .nowtsaid { display: none; } }
@container watch (max-height: 140px) { #watchList .nowt .nowtmark { display: none; } }
@container watch (max-height:  74px) { #watchList .nowt b { display: none; } }
#watchList .nowt .nowtmark { width: 32px; height: 32px; font-size: 15px; }
#watchList .nowt .nowtsaid { max-width: 190px; }
#watchList .nowt::before { bottom: 0; height: 70px; }

/* ---------- draggable favourites popup ---------- */
/* Over the strip, not under it: this is a panel somebody opened. */
#favPopup {
  position: absolute; z-index: var(--layer-float); width: 168px;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 12px; box-shadow: var(--elev-2), 0 8px 26px rgba(0,0,0,.5);
  overflow: hidden;
}
/* The halo the rest of the program uses, in the star's own colour, because
   that is what this shelf is: the things you starred. */
#favPopup::before {
  content: ''; position: absolute; pointer-events: none;
  inset-inline-start: -26px; bottom: -34px;
  width: 120px; height: 80px; border-radius: 50%;
  background: var(--news); filter: blur(30px); opacity: .22;
}
:root.light #favPopup::before { opacity: .10; filter: blur(24px); }

#favPopup .favhead {
  position: relative;
  display: flex; align-items: center; gap: 7px;
  padding-block: 7px; padding-inline: 11px 8px; cursor: move;
  background: var(--top), var(--panel-2);
  border-bottom: 1px solid var(--line-soft);
  font-size: 11px; color: var(--dim); user-select: none;
}
#favPopup .favhead b { font-weight: 600; }
#favPopup .favstar { color: var(--gold-ink); font-size: 12px; }
#favPopup .favgap { flex: 1; }
#favPopup .favx {
  border: 0; background: none; color: var(--faint); cursor: pointer;
  font-size: 11px; padding: 0; line-height: 1;
}
#favPopup .favx:hover { color: var(--sell); }

/* Tiles rather than a huddle of glyphs: four across, square, and large enough
   to be aimed at without looking. */
#favPopup .favbody {
  position: relative;
  display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 3px; padding: 8px;
}
#favPopup .favbody button {
  aspect-ratio: 1; width: 100%; padding: 0;
  display: grid; place-content: center;
  border: 1px solid var(--line-soft); border-radius: 8px;
  background: var(--panel-2); color: var(--dim);
  cursor: pointer; font-size: 15px; box-shadow: var(--sheen);
  transition: background .12s, border-color .12s, color .12s;
}
#favPopup .favbody button:hover {
  background: var(--panel-3); color: var(--text); border-color: var(--line);
}
/* The one in hand. The accent means "this is the one you are on", which here
   is exactly what it is. */
#favPopup .favbody button.on {
  background: color-mix(in srgb, var(--accent) 20%, transparent);
  border-color: var(--accent); color: var(--text);
}
#favPopup .favbody button svg { display: block; }

/* It could always be dragged and nothing said so. */
#favPopup .favfoot {
  position: relative; padding: 0 11px 9px;
  font-size: 9.5px; color: var(--faint);
}
/* One line. It was capped at 130 across, which folded a six-word sentence
   into three lines in a popup that is free to be as wide as it needs — it
   floats over the chart with nothing beside it to crowd. */
#favPopup .favempty {
  position: relative;
  padding: 10px 12px; color: var(--faint); font-size: 11px; white-space: nowrap;
}

/* ---------- what is downloading ---------- */
/* Small, draggable, and only there while it has something to say. Built from
   the same jobs the picker rows read. */
#dlPanel {
  position: absolute; z-index: var(--layer-float); width: 214px;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 9px; box-shadow: 0 8px 26px rgba(0,0,0,.5); overflow: hidden;
}
#dlPanel.hidden { display: none; }
#dlPanel .dlhead {
  display: flex; align-items: center; justify-content: space-between;
  padding: 4px 6px 4px 9px; cursor: move;
  background: var(--panel-2); border-bottom: 1px solid var(--line);
  font-size: 11px; color: var(--dim); user-select: none;
}
#dlPanel .dlx { border: 0; background: none; color: var(--dim); cursor: pointer; font-size: 12px; }
#dlPanel .dlx:hover { color: var(--sell); }
#dlPanel .dlbody { padding: 7px 9px 9px; display: flex; flex-direction: column; gap: 9px; }
#dlPanel .dlline { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
#dlPanel .dlsym { font-family: var(--mono); font-size: 12px; color: var(--text); }
/* The figures line up under each other however wide the number is. */
#dlPanel .dlpct {
  font-family: var(--mono); font-size: 11px; color: var(--dim);
  font-variant-numeric: tabular-nums;
}
#dlPanel .dlbar {
  margin-top: 4px; height: 4px; border-radius: 3px;
  background: var(--panel-2); overflow: hidden;
}
#dlPanel .dlbar i {
  display: block; height: 100%; background: var(--accent);
  transition: width .3s ease;
}
#dlPanel .dlrow.bad .dlbar i { background: var(--sell); }
#dlPanel .dlwhy { margin-top: 4px; font-size: 11px; color: var(--sell); line-height: 1.4; }

/* ---------- top bar ---------- */

/* Grouped rather than one run of eighteen buttons. The chart controls sit with
   the symbol, the clock and transport hold the middle, and everything that
   opens a window is pushed to the end where it can be scanned instead of
   read. */
#topbar { gap: 6px; }
#topbar .grp {
  display: flex; align-items: center; gap: 6px;
  flex-shrink: 0;
}
#topbar .grp + .grp::before {
  content: ""; width: 1px; height: 20px;
  background: var(--line); margin-inline-end: 2px;
}
#symbolBtn {
  background: var(--panel-2);
  padding: 6px 12px;
}
#symbolBtn:hover { border-color: var(--accent); }

/* ---------- small screens ---------- */

@media (max-width: 1100px) {
  :root { --sidebar: 260px; --watch: 180px; }
}
/* Narrower, never gone.
 *
 * This used to be `#side { display: none }`, and it took the watchlist, the
 * order ticket, the account figures and the two buttons somebody trades with
 * off the screen at once - with nothing said and no way back except making
 * the window wider again. Measured: at 900 CSS pixels exactly the column is
 * there at 260px, and at 899 it is not there at all.
 *
 * A CSS pixel is a device pixel divided by the display's scale, which is why
 * it looked random: on a 1280-wide screen at 150% a *maximised* window is 853
 * of them. The rail is where somebody puts this column away, and it is the
 * only thing that should. */
@media (max-width: 900px) {
  :root { --sidebar: 224px; --watch: 150px; }
}

/* ---------- the rail as a place to go, not only a set of switches ---------- */
#rail .railgo {
  width: 30px; height: 30px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: 7px;
  color: var(--dim); cursor: pointer;
}
#rail .railgo:hover { color: var(--text); background: var(--panel-2); }
#rail .railgo.on { color: var(--accent); background: var(--panel-2); }
#rail .railsep {
  width: 18px; height: 1px; background: var(--line); margin: 5px 0;
}
/* The account keeps its initial rather than an icon: whose account it is
   matters more than that it is an account. */
#rail .person .avatar.mini { width: 20px; height: 20px; font-size: 10px; }
/* In the rail the account is a circle and nothing else.
   The old top bar hid the name with `.chip.person > span:last-child` and
   brought it back above 1650px, where there was room. Moving the button in
   here changed its class, so that rule stopped applying and the name spilled
   out of a thirty-pixel column. There is no width at which a name fits in
   this rail, so unlike the top bar nothing brings it back: it is on the
   button's tooltip and in the dialog the button opens. */
#rail .person > span:last-child { display: none; }

/* Icons in the top bar sit on the optical centre line, which emoji did not. */
#topbar .iconbtn { display: inline-flex; align-items: center; justify-content: center; }
#topbar .iconbtn svg { display: block; }


/* ---------- the Journal window, turned round for Persian ----------
 *
 * Only this window. The rest of the application stays left-to-right on
 * purpose (see the note in i18n.js): turning the whole document round moves
 * every panel and column to the opposite edge and makes the two languages two
 * different applications to learn. The Journal is the one window that is
 * read rather than operated - a table of sentences, a diary, notes somebody
 * typed in Persian - and it was asked for on its own.
 *
 * On `.sheet`, not on `#journal`. The modal is the flex box that decides
 * where the sheet sits on the screen; turning that round would slide the whole
 * window to the other edge, which is not what turning the text round means.
 */
/* The challenge windows read from the right, like every other window that
   is mostly Persian sentences. The pieces inside them that are not Persian -
   a code, a date, a column of money - are put back one by one below. */
body.fa .chalcode b, body.fa .chalrow .cd, body.fa #chalJoinCode,
body.fa .chalwhat b[dir=ltr], body.fa .chalreveal span[dir=ltr] {
  direction: ltr; unicode-bidi: isolate;
}

/* What must NOT turn round, and why each one is here.
 *
 * A price, a time, a duration and an R multiple are Latin-script numbers. Left
 * inside a right-to-left box they are not merely mirrored, they are re-ordered
 * by the bidi algorithm: `12:30` keeps its digits but a cell holding a time
 * and then a date swaps the two, and `2418.35 → 2418.62` puts the arrow at
 * the wrong end. Every one of those is a number a person reads off the screen
 * and compares, so each numeric cell is its own left-to-right island. */
body.fa #journal .jrtab .mono,
body.fa #journal .jrtab .sub2,
body.fa #journal .jdet .kv .mono,
body.fa #journal .cal .cday .cn,
body.fa #journal .cal .cday .cp {
  direction: ltr; unicode-bidi: isolate;
}

/* The summary line is Persian words with a money figure and a percentage
   sitting inside them. Isolated rather than reversed: the sentence reads
   right-to-left, the figure inside it reads left-to-right, and neither drags
   the other's punctuation along. */
body.fa #journal .jrsum b { unicode-bidi: isolate; }

/* Typed by a person, in whichever language they choose sentence by sentence.
   `plaintext` decides the direction from the first strong character of each
   paragraph, which is the only rule that is right for a diary that mixes a
   Persian note with a Latin symbol name. */
body.fa #journal [data-jdiary],
body.fa #journal [data-jf="text"],
body.fa #journal .jnote,
body.fa #journal .tag {
  unicode-bidi: plaintext; text-align: start;
}

/* A date field's own controls are laid out by the browser and belong in the
   order it draws them. */
body.fa #journal input[type="date"] { direction: ltr; }

/* The table head and body are centred already, so the columns simply run from
   the right, which is what a right-to-left table is. The two cells that are
   not centred need their alignment expressed as a side that follows the
   direction rather than a fixed edge. */
body.fa #journal .jrtab th,
body.fa #journal .jrtab td { text-align: center; }
body.fa #journal .jdet .kv b { text-align: start; }

/* Screenshots sit side by side and carry captions; the pair reads from the
   right like everything else, and the captions stay centred under them. */
body.fa #journal .jshots figcaption { text-align: center; }


/* ---------- Trade Analysis, turned round for Persian ----------
 *
 * The second window that is read rather than operated, and the same rule as
 * the Journal: on the sheet, never on the modal, so the window stays where it
 * is and only its contents turn.
 */

/* What must NOT turn round here, and this is the whole difficulty of the
 * window: half of it is not text, it is measurement.
 *
 * A bar chart of Monday to Sunday, a histogram of R from worst to best, and a
 * heat map of the hours 0 to 23 all carry meaning in the ORDER of their
 * columns. Turning those round does not translate them, it reverses the axis
 * and says something false: it would put Sunday where Monday is and midnight
 * where 11pm is, with no label changing to admit it. So every ordered
 * graphic stays left to right, and only the words around it turn.
 */
body.fa #analysis .abars,
body.fa #analysis .ahist,
body.fa #analysis .heat,
body.fa #analysis .heatrow,
body.fa #analysis svg {
  direction: ltr;
}
/* The heat map's own row label sits at the end of its column, which is the
   side nearest the grid in either direction. */
body.fa #analysis .heatrow .hlab { text-align: end; }
/* And that column is wider in Persian, because the day names are. Three of
   the seven - Tuesday, Wednesday and Thursday - want 35, 39 and 36 pixels
   against the 34 the English three-letter names need. They wrapped onto a
   second line inside a fourteen-pixel row, so the bottom half of the word was
   never drawn: reported as "from Tuesday on they are broken and cut off",
   which is exactly what it was. Ten pixels off twenty-four cells is nothing;
   half a word is not. */
body.fa #analysis .heatrow { grid-template-columns: 46px repeat(24, 1fr); }

/* A figure decides its own direction, because these cells hold two different
   kinds of value. `-20.85` has no strong character at all, so it falls to
   left-to-right and keeps its minus sign in front - isolating it was not
   enough, and it was drawn as `20.85-`. `0 پوینت` begins with a Persian word
   and must read right-to-left, which is exactly what forcing them all
   left-to-right broke. `plaintext` reads the first strong character and gets
   both right, which is the same rule the tooltip uses.

   The last selector carries `:not(.nothing)` for the other half of that. The
   slot under a card's heading usually holds a figure or a small drawing, and
   those want the rule above; when it holds a *sentence* instead, saying there
   is not enough data yet, it is program copy and reads the way the window
   reads. Without the exclusion this rule is the more specific one and silently
   beat the rule that says so, further down. */
body.fa #analysis .stats .v,
body.fa #analysis .atwo b,
body.fa #analysis .tagtable td,
body.fa #analysis .ancard h3 + div:not(.nothing) {
  unicode-bidi: plaintext;
}

/* The key/value grid and the group tables read from the right like the rest
   of the window; their numbers stay upright because of the rule above. */
body.fa #analysis .stats .k,
body.fa #analysis .sechead,
body.fa #analysis .annote,
body.fa #analysis .anpreview b,
body.fa #analysis .anpreview span,
body.fa #analysis .anempty p {
  text-align: start;
}
body.fa #analysis .tagtable th { text-align: start; }

/* An empty state is a sentence, so it is aligned like one rather than
   centred. Asked for on the Journal, and true in both windows. */
body.fa #journal .nothing,
body.fa #analysis .nothing { text-align: start; }

/* And these read the way the window reads, even when they open with a Latin
   term.
   
   `body.fa p` carries `unicode-bidi: plaintext`, which takes a paragraph's
   direction from its own first strong character. That is right for the two
   other things it covers - a note somebody typed, and a name that is Latin all
   the way through - and wrong for a third: a Persian sentence that happens to
   begin with a technical term. «SQN بالای ۲ خوبه.» is Persian, and three Latin
   letters at the front turned the whole line round and left its full stop at
   the far end.
   
   A card note and an empty state are the program talking, never something a
   person wrote, so they take the window's direction. The Latin term inside
   still runs left-to-right - that is the bidi algorithm doing its job on a run,
   rather than the whole paragraph being handed over to it. */
body.fa #analysis .annote,
body.fa #analysis .nothing,
body.fa #journal .nothing {
  unicode-bidi: normal;
}


/* ---------- the Strategy laboratory, turned round for Persian ----------
 *
 * The third window that is read rather than operated, and the most text of
 * any of them. Same rule as the other two: on the sheet, never on the modal.
 */

/* Code is not prose and must never turn round.
 *
 * `#labSrc` holds Pine Script and `#expCode` holds JavaScript. Both are
 * programs: their indentation is meaningful, their brackets have a side, and
 * a right-to-left box would put the caret at the wrong end of every line
 * somebody types. `#labPrompt` is the text handed to an AI, which is English
 * and is meant to be copied out exactly as it stands.
 *
 * `#labIdea` is the one box in this window a person writes their own words
 * in, so it takes its direction from what they actually type, sentence by
 * sentence — Persian, English, or a Persian sentence naming a Latin
 * indicator.
 */
body.fa #lab #labSrc,
body.fa #lab #expCode,
body.fa #lab #labPrompt,
body.fa #lab .codebox {
  direction: ltr; text-align: left;
}
body.fa #lab #labIdea { unicode-bidi: plaintext; text-align: start; }

/* Figures decide their own direction, for the reason set out on Trade
   Analysis: isolating a run does not give it one, and a leading minus sign in
   a right-to-left paragraph drifts to the far end. */
body.fa #lab .statgrid b,
body.fa #lab .labtable td,
body.fa #lab .labfield input,
body.fa #lab .labfield select {
  unicode-bidi: plaintext;
}
/* The figure column sits at the end of its row whichever way the row runs. */
body.fa #lab .statgrid b { text-align: end; }

/* A Latin name inside a Persian sentence is isolated, or the bidi algorithm
   drags its punctuation off: `MQL5\Files` and `File → Open Data Folder` are
   full of characters that go wandering — the backslash and the arrow are both
   neutral, and a neutral between two directions belongs to whichever side
   claims it first. These names are deliberately left in English (a customer
   has to find those words on MetaTrader's own screen), so they have to be
   fenced off rather than translated. */
body.fa #lab b, body.fa #lab code { unicode-bidi: isolate; }

/* Prose, headings and list items read from the start edge, which is the right
   one here. The numbered steps keep their numbers on the same side as the
   text they belong to, which `padding-inline-start` already does. */
body.fa #lab .sub,
body.fa #lab p,
body.fa #lab li,
body.fa #lab .stathead,
body.fa #lab .jobnote,
body.fa #lab .joberr,
body.fa #lab .labfield span,
body.fa #lab .nothing {
  text-align: start;
}


/* ---------- the trades panel, turned round for Persian ----------
 *
 * Its place on the screen does not change: it stays under the chart, the same
 * height, the same tabs in the same order. Only what is inside it turns.
 *
 * On `#dock` itself rather than on a sheet, because this panel is not a
 * floating window - it is a column in the layout, and turning it round does
 * not move it anywhere.
 */
body.fa #dock { direction: rtl; }

/* What must NOT turn round.
 *
 * Every column in these tables except the first is a number: a price, a
 * volume, a stop, a profit. Left in a right-to-left cell the bidi algorithm
 * re-orders them, and a leading minus sign ends up on the wrong end of a
 * loss - which is the single most expensive character to misplace in a
 * trading application. Each figure decides its own direction from its first
 * strong character, which keeps `-120.50` upright and still lets a cell
 * holding «۳ ساعت» read the way it should.
 *
 * The result of a closed trade is in this list for the same reason and was
 * missing from it: a loss on a history card was being drawn as `1.90-`. The
 * line of detail underneath it is a different problem and is solved further
 * down, by `.figs`, because it is words and figures together rather than one
 * figure on its own.
 */
body.fa #dock .postab td,
body.fa #dock .postab .mono,
body.fa #dock .statgrid b,
body.fa #dock #chartAcct b,
body.fa #dock .pos .pl {
  unicode-bidi: plaintext;
}

/* A run of figures inside a line of words, resolved on its own.
 *
 * Not `body.fa` only: an isolate costs nothing in English and this way the
 * line reads the same way whichever language is on, rather than being correct
 * in one of them by luck. */
.figs { direction: ltr; unicode-bidi: isolate; }

/* The row of buttons on each position keeps its own order: close, half,
   edit. They are a fixed sequence of actions, not a sentence. */
body.fa #dock .postab .acts { direction: ltr; }

/* Headings and labels sit at the start edge, which is now the right. */
body.fa #dock .postab th,
body.fa #dock .stathead,
body.fa #dock .sub {
  text-align: start;
}

/* And the cells sit under them, which `start` alone did not manage.
 *
 * `unicode-bidi: plaintext` above gives each cell its own direction, taken
 * from its first strong character - which is what keeps a minus sign on the
 * correct end of a loss. A cell holding nothing but digits has no strong
 * character at all, so it resolves left-to-right, and `start` in a
 * left-to-right cell means the left edge while `start` in its right-to-left
 * heading means the right one. Every number in the table was therefore
 * aligned to the opposite side from the word naming it.
 *
 * Named as a side rather than as a start, because the whole point is that
 * these cells no longer agree on which way round they are. */
body.fa #dock .postab td { text-align: right; }
/* The buttons are laid out by their own flex row and take the far end. */
body.fa #dock .postab td.acts { text-align: left; }
/* `.nothing` is deliberately not in that list. Inside `.tabbody`, which is a
   flex container, its `margin: auto` centres the box itself and shrinks it to
   its content, so `text-align` has nothing left to do. An empty pane with one
   line centred in it is the right look anyway; the same class inside the
   Journal's cards sits in a block and does align to the start, which is where
   it was asked for. */

/* The account strip under the tabs: each figure follows its own label rather
   than being pushed to a fixed side. */
body.fa #dock #chartAcct { direction: rtl; }


/* ---------- what the light theme needs beyond its palette ----------
 *
 * A colour that works as a solid block on near-black does not work as one on
 * white. The accent is a gold: filled, with white on top, it reads on the
 * dark theme as a lit key and on the light theme as mustard - a heavy,
 * dated-looking slab, and the loudest thing on an otherwise quiet screen.
 *
 * That was written for a teal accent whose only legible ink was dark, and
 * against it the rule was right. This accent is a blue at 5.23 to one under
 * white, which is an ordinary primary rather than a brick - and the cost of
 * the tint was a control that reads as plainly on in one theme and nearly off
 * in the other. Measured before this was written: the chosen half of a switch
 * stood 5.58 to one against the unchosen half on dark, and 1.00 to one on
 * light. So both themes say "this one" the same way now, and the border rule
 * below is what is left of the tint.
 */
:root.light .modeswitch button.on,
:root.light .icon.play.on,
:root.light #btnContext.on {
  background: var(--accent);
  color: var(--ink);
  border-color: var(--accent);
}

/* A panel sitting on the ground gets its edge from the shadow rather than
   from a line, which is what stops a light interface looking like a
   spreadsheet. The border stays for the cases where two panels meet. */
:root.light #right, :root.light #dock, :root.light .sheet {
  box-shadow: var(--elev-1);
}


/* ---------- the instruments window, turned round for Persian ----------
 *
 * On the sheet, never on the modal, like the others: the window stays where
 * it is and only its contents turn.
 */

/* What must NOT turn round, and here it is most of the row.
 *
 * An instrument's ticker is Latin by definition - EURUSD, XAUUSD, US30 - and
 * so is its name. The coverage column is a pair of dates with an arrow
 * between them, and the arrow is a neutral character: in a right-to-left row
 * it changes ends, so «2020-01 → 2025-06» would be drawn pointing from the
 * later date to the earlier one. Each of those is its own left-to-right
 * island, and the row around them reads from the right.
 */
body.fa #picker .pickrow .sym,
body.fa #picker .pickrow .nm,
body.fa #picker .pickrow .cov {
  /* `right` rather than `start`: see the note in the watchlist below. These
     are forced left-to-right, so `start` would put them at the far left of
     their column instead of where the row begins. */
  direction: ltr; unicode-bidi: isolate; text-align: right;
}

/* The search box takes its direction from what is typed in it. Almost
   everything typed here is a Latin ticker, but a name in Persian should not
   come out backwards while it is being written. */
body.fa #picker #pickSearch { unicode-bidi: plaintext; text-align: start; }

/* The group heading and its count sit at opposite ends of the line, which
   `space-between` already does; they simply swap sides with the direction.
   The footer is a sentence and reads from the start edge. */
body.fa #picker footer { text-align: start; }

/* The two buttons follow the direction like everything else here. They are
   words, not symbols: pinning them left-to-right put «باز کردن» at the far
   left, which is the last thing read, while in English it is the first of
   the pair. The row's own `flex-end` moves the group to the left as a unit;
   inside it the order is the reading order.

   (The trades panel does the opposite on purpose. Its buttons are ✕ ½ ⋯ - a
   fixed sequence of symbols the hand learns, with nothing to read.) */


/* ---------- the trading account settings, turned round for Persian ----------
 *
 * On the sheet, never on the modal, like every other window here.
 */

/* Each field is a label stacked over its input, so turning the window round
   moves the label to the right edge and leaves the number where it was. The
   number is the part that must not move: every field in this window is a
   figure - a balance, a leverage, a spread in points, a commission - and a
   right-to-left input puts the caret at the wrong end of it while it is being
   typed. They are also `var(--mono)`, which has no Persian in it at all.
   Left to right, and aligned to the left inside the box, exactly as before. */
body.fa #settings .pad.grid2 label { text-align: start; }
/* A figure belongs against the unit that says what it is, in both languages.
   Left-aligned, a balance of 1 sat a hand's width from the ETH beside it and
   read as two unrelated things. The digits still run left to right - that is
   what a number is - they are simply put at the end nearest the unit. */
/* Left, because the unit is on the left in a right-to-left row: the digits
   belong against the word that says what they are. Right put them against the
   icon instead, a whole field away from «پوینت». */
body.fa #settings .pad.grid2 input { direction: ltr; text-align: left; }

/* The two sentences under the fields, and the warning shown when there is no
   account yet. */
body.fa #settings .sub,
body.fa #settings .joberr { text-align: start; }

/* The button is deliberately not touched. `#settings .actions` already uses
   `justify-content: flex-end`, and `flex-end` follows the direction on its
   own: bottom right in English, bottom left in Persian, which is the mirror
   a confirm button is supposed to have. Forcing it to the start edge, which
   is what I tried first, moves it to the right in Persian and breaks that. */


/* ---------- the order ticket and account panel, turned round ----------
 *
 * On `#paneTrade` itself. Like the trades panel, this is a column in the
 * layout rather than a floating window, so turning it round moves nothing:
 * the panel keeps its place on the side of the screen and only its contents
 * read from the right.
 */
body.fa #paneTrade { direction: rtl; }

/* SELL stays left of BUY, and Sell stays left of Buy.
 *
 * This is the same rule as the strip on the chart, for the same reason: every
 * platform a customer will ever open puts the sell price on the left and the
 * buy price on the right, and the hand learns which side to hit long before
 * it reads the word. Mirroring the pair for Persian would put the hand on the
 * wrong button, which is the one mistake in this window that costs money.
 * `flex-end` and friends still move each group as a whole; only the order
 * within these three is pinned. */
body.fa #paneTrade .quote,
body.fa #paneTrade .buttons { direction: ltr; }

/* A stepper is minus, number, plus - a fixed shape, not a sentence, and the
   minus belongs on the left of a number in every language. The number inside
   it is a number, so it stays upright too. */
body.fa #paneTrade .stepper { direction: ltr; }
body.fa #paneTrade input { direction: ltr; text-align: left; }
/* The reason above is about which END the minus sits at, and it does not
   reach the alignment of the number between the two buttons. That was left
   aligned in Persian and centred in English, so the same control had the
   figure in a different place depending on the language, for no reason
   anybody could give. Centred, as it is in English. */
body.fa #paneTrade .stepper input { text-align: center; }

/* Every figure in the account block. `plaintext` rather than a forced
   direction because these cells hold two kinds of value: `-1.30` has no
   strong character and must keep its minus in front, while `0.00 / 10 000.00`
   and a bare percentage are read left to right either way. */
body.fa #paneTrade .account .row b,
body.fa #paneTrade .account .row .mono,
body.fa #paneTrade .quote .price,
body.fa #paneTrade .spreadbox .mono {
  unicode-bidi: plaintext;
}

/* Labels read from the start edge, which is now the right.
 *
 * The two lines under the ticket used to be in this list, on the reasoning
 * that they are sentences and sentences follow the text. Taken out on the
 * owner's word, and the reasoning was wrong in a way worth writing down: they
 * are not sentences about the trade, they are the SUM of the fields above
 * them. Every one of those fields puts its label at the start and its value
 * at the end, so in Persian the whole column of figures runs down the left
 * and these two lines were the only numbers on the far side of the panel.
 * They belong under what they add up. See `#riskLine` in app.css. */
body.fa #paneTrade .account .row span,
body.fa #paneTrade .field > span:first-child {
  text-align: start;
}

/* A unit written beside a label - USDT, %, the base coin - is Latin and is
   fenced off so the label around it cannot drag its characters about. */
body.fa #paneTrade .unit { unicode-bidi: isolate; }

/* Same fence around every figure in the two lines under the ticket. Each of
   them is `<b>` around a number in a sentence of Persian, and without the
   fence the dots that separate them drift on to the wrong side of the number
   they follow: `لانگ 54012.35· شورت` instead of `لانگ 54012.35 · شورت`. */
body.fa #paneTrade .riskline b,
body.fa #paneTrade .cxliq b { unicode-bidi: isolate; }

/* The keyboard hints are a list of Latin keys with Persian words between
   them. Each key is left to right inside a line that is not. */
body.fa #paneTrade .hint { unicode-bidi: plaintext; text-align: center; }
/* Each pair reads from the key outwards, which in Persian is from the right.
   Turning the row round rather than reversing it: reversed, every pair was
   pushed to the far end of its own cell, so a short label and a long one put
   their keys at different places and the column of keys came out ragged.
   Given a direction, each pair packs from the start of its cell instead and
   the keys line up down the column. */
body.fa #paneTrade .keyrow { direction: rtl; }
body.fa #paneTrade .keyshead { text-align: right; }


/* The count pill against a Persian label.
 *
 * The pill is centred on the label's line box, which is correct and looks
 * wrong. Persian carries its weight on the baseline - the connecting strokes
 * are the body of the word, and what rises above them is ascenders and dots -
 * so the eye reads the baseline band as the middle of the word while the box
 * middle sits above it. The font's own metrics put the difference at about a
 * pixel, which is why this is small; the size of it was chosen by looking at
 * the strip magnified five times, not calculated.
 *
 * Latin does not need it: a line of Latin sits far closer to the middle of
 * its own box, and the pill already looks centred there.
 *
 * Half a pixel, and the number was arrived at properly on the third attempt.
 * Guessing by eye at a magnified screenshot put it at 1.5, which was visibly
 * low. The font's own metrics - the ink of the word against the middle of its
 * line box - say between half a pixel and one. Rendering the same tab at a
 * ladder of offsets from -1 to +2 and comparing them side by side agrees:
 * -1 and -0.5 sit high, +1.5 and +2 sit low, and half a pixel is the smallest
 * correction that reads as centred.
 */
body.fa #dockHead .tab span { transform: translateY(0.5px); }


/* ---------- the watchlist, turned round for Persian ----------
 *
 * On the section itself. Like the trades panel and the order ticket, this is
 * a column in the layout rather than a floating window, so turning it round
 * moves nothing: it keeps its place on the side and only its contents read
 * from the right.
 *
 * Most of this column was already written in logical properties - the row's
 * accent bar is a `border-inline-start` and the star is pinned with
 * `inset-inline-end` - so both follow the direction on their own and neither
 * needed a rule here. The grid columns follow it too.
 */
body.fa #paneWatch { direction: rtl; }

/* What must not turn round. A ticker is Latin by definition, an instrument's
   name is Latin, and the coverage cell is a figure - `40k`, `1.2M`, an arrow
   or a dash - which a right-to-left row would re-order against the ticker
   beside it. Each is its own left-to-right island, aligned to the start edge
   so it still sits where the eye expects within the row. */
body.fa #paneWatch .wrow .sym,
body.fa #paneWatch .wrow .nm,
body.fa #paneWatch .wrow .cov {
  /* `right`, not `start`. `start` resolves against the element's own
     direction, and these are forced to `ltr` - so it meant the far left, and
     the ticker sat across the column from the dot that leads its row with a
     gap between them. The content reads left to right; the box it sits in
     begins where the row begins. */
  direction: ltr; unicode-bidi: isolate; text-align: right;
}

/* Typed by a person: almost always a Latin ticker, but a name written in
   Persian should not come out backwards while it is being written. */
body.fa #paneWatch #watchFilter { unicode-bidi: plaintext; text-align: start; }

/* The group heading and the empty-state sentence read from the start edge. */
body.fa #paneWatch .wgroup,
body.fa #paneWatch .wnothing { text-align: start; }


/* ---------- the favourites popup, turned round for Persian ----------
 *
 * The small window the star in the drawing rail opens. It floats over the
 * chart and can be dragged, so turning it round moves nothing: its position
 * is set in `style.left` and `style.top` by hand.
 */
body.fa #favPopup { direction: rtl; }
body.fa #favPopup .favempty { text-align: start; }


/* ---------- the chart settings, turned round for Persian ----------
 *
 * On the sheet, never on the modal, like every other window here.
 */

/* The two rows of switches were written `direction: ltr` on purpose, back when
   the window was only ever read in English: it kept the box in the same place
   in both languages. In a window that now reads from the right that leaves the
   box stranded on the far side of its own sentence, so here - and only here -
   they follow the window. The box leads from the right, the sentence starts
   beside it. Scoped by id so the rule outranks the `.pad.grid2` one it undoes
   without weakening that one for anything else. */
body.fa #chartSettings .checklist .tick,
body.fa #chartSettings .shadelist .shaderow {
  direction: rtl; text-align: right;
}

/* What stays left to right inside those rows. A session's hours are a range of
   clock times - `08:00 - 16:00 UTC` - which a right-to-left row reads back to
   front, so the closing hour appears to be the opening one. Isolated, it keeps
   its own order wherever the row puts it, and `margin-inline-start: auto` still
   pushes all four ranges to the same far edge so they line up as one day. */
body.fa #chartSettings .shadelist .shaderow .when {
  direction: ltr; unicode-bidi: isolate;
}

/* Each colour row is a name and its swatch. `justify-content: space-between`
   follows the direction on its own, so the name leads from the right and the
   swatch sits at the far edge; nothing to do but let the name start there. */
body.fa #chartSettings .colgrid .colrow > span { text-align: start; }

/* The stacked fields: label above, control below. */
body.fa #chartSettings .pad.grid2 label { text-align: start; }

/* Two of those controls are not Persian and must not be turned round. The
   screenshots folder is a Windows path - drive letter first, backslashes -
   and the daily pause is a clock time; a right-to-left box puts the caret at
   the wrong end of both while they are being typed.

   Named by the CONTROL rather than by the window it happens to sit in. The
   folder box moved out of Chart settings and into the journal-setup window,
   and this rule stayed pointing at its old address: it matched nothing from
   that day on, and the Windows path came out right to left again with nothing
   anywhere to say so. A rule that names a room instead of the thing in it
   breaks silently the first time the thing is moved. */
body.fa #shotDir,
body.fa #setPauseAt {
  direction: ltr; text-align: left;
}

/* The explanatory paragraphs and the section headings read from the right. */
body.fa #chartSettings .sub,
body.fa #chartSettings .secline,
body.fa #chartSettings p { text-align: start; }


/* ---------- the indicator browser, turned round for Persian ----------
 *
 * On the sheet, never on the modal, like every other window here.
 *
 * Most of this window follows on its own once it is turned: the shelves and
 * the list are laid out with flexbox, the divider between them is already a
 * `border-inline-end`, and the count beside each shelf is pushed out by
 * `justify-content: space-between`. What is written here is only the handful
 * of places that were pinned to a physical edge or must not turn at all.
 */

/* The search box. What gets typed into it is usually a Latin name - `RSI`,
   `MACD` - but «میانگین» is just as likely, so the box follows what is in it
   rather than being forced either way, and starts at the edge it is read from.
 */
body.fa #indicators #indSearch {
  unicode-bidi: plaintext; text-align: start;
}

/* The button that removes an indicator from the chart was pushed to the far
   end with a physical `margin-left`, which in a right-to-left row is the near
   end - it landed on top of the name. The logical property puts it at the far
   end in both languages. */
body.fa #indicators .indrow .rm { margin-left: 0; margin-inline-start: auto; }

/* Pine Script, which is code: left to right, and left-aligned inside the box,
   or every line of it starts at the wrong end while it is being pasted. The
   line-by-line report underneath quotes that same code, so it is an island of
   its own inside a Persian sentence. */
body.fa #indicators #pineSrc {
  direction: ltr; text-align: left; unicode-bidi: normal;
}
body.fa #indicators #pineOut .joberr,
body.fa #indicators #pineOut .jobnote { text-align: start; }

/* An indicator written by the customer is listed by name and by the key it
   registers itself under. The key is an identifier, always Latin, and reads
   backwards in a right-to-left row. */
body.fa #indicators .customrow .cov {
  direction: ltr; unicode-bidi: isolate; text-align: end;
}

/* The empty states and the paragraph above the customer's own folder. */
body.fa #indicators .nothing,
body.fa #indicators .sub,
body.fa #indicators p { text-align: start; }


/* ============================================================ the tile frame
 *
 * The window is a set of tiles on a ground, not a grid of slabs with lines
 * between them. Three things make that: air (`--tilegap`), a corner
 * (`--tile`), and a shadow small enough to survive a long session.
 *
 * The shadow is the measured one: the owner was shown four amounts of depth
 * and picked the second lightest. It is two soft steps, no glow, and nothing
 * of it is visible against a single tile - only against the gap.
 */
:root {
  --tile: 18px;      /* the corner every region takes */
  --tilegap: 10px;   /* the air between them, and around the whole set */
  --tilelift: inset 0 1px 0 rgba(255,255,255,.03),
              0 1px 1px rgba(0,0,0,.12),
              0 5px 14px -14px rgba(0,0,0,.4);
}
:root.light {
  --tilelift: inset 0 1px 0 rgba(255,255,255,.9),
              0 1px 1px rgba(30,40,60,.05),
              0 6px 16px -14px rgba(30,40,60,.22);
}

/* The top bar is a tile too, so the ground shows above and beside it. `main`
   gives back exactly the height the bar's new top margin takes. */
#topbar {
  margin: var(--tilegap) var(--tilegap) 0;
  border: 1px solid var(--line);
  border-radius: var(--tile);
  box-shadow: var(--tilelift);
  padding: 0 12px;
}

main {
  height: calc(100% - var(--topbar) - var(--titlebar) - var(--tilegap));
  padding: var(--tilegap);
  gap: var(--tilegap);
}

#center { gap: var(--tilegap); }

/* Every region: a tile. The border-inline rules that used to divide them are
   cleared, because the air does that job now and a line in a gap is a line
   floating in nothing. */
.tools, #side, #rail, #chartWrap, #dock {
  border: 1px solid var(--line);
  border-radius: var(--tile);
  background: var(--panel);
  box-shadow: var(--tilelift);
  overflow: hidden;
}
.tools { border-inline-end: 1px solid var(--line); }
#side { border-inline-start: 1px solid var(--line); }
#rail { border-inline-start: 1px solid var(--line); }
#dock { border-top: 1px solid var(--line); }

/* The chart's own ground is the window's, so the plot reads as a hole cut in
   the tile rather than as another panel. */
#chartWrap { background: var(--bg); }

/* The grip between the chart and the trades sits in the gap now, so it needs
   no ground of its own - only something to take hold of. */
#dockGrip {
  height: var(--tilegap); background: transparent; position: relative;
}
#dockGrip::after {
  content: ''; position: absolute; inset-inline: 50% ; top: 50%;
  width: 46px; height: 3px; margin: -1.5px -23px; border-radius: 2px;
  background: var(--line); opacity: 0; transition: opacity .12s;
}
#dockGrip:hover { background: transparent; }
#dockGrip:hover::after { opacity: 1; }

/* Inside a tile, the first and last rows have a corner to respect. */
#dockHead { border-bottom: 1px solid var(--line-soft); }
#paneWatch { border-bottom: 1px solid var(--line-soft); }

/* With the dock full the chart takes none of the column, and that state may
   not leave a gap hanging.

   `body.tradeshut` used to be in this rule too, hiding `#side` and `#rail`
   with the order panel. It made the switch one-way: the rail is where the
   panel is turned back on, so closing the panel closed the only way to
   reopen it, and `#tradeMode` - which `applyTradeShown` keeps on screen for
   exactly that reason - went with the column it lives in. The panel folding
   shut is handled where it says it is, further up this file; emptying the
   whole column is `body.no-watch.no-trade`, which is the rail's own doing. */
body.dock-full #chartWrap, body.dock-full #dockGrip { display: none; }

/* The second-timeframe pane is a tile of its own beside the chart. */
#context {
  border-radius: var(--tile);
  border: 1px solid var(--line);
  background: var(--panel);
  box-shadow: var(--tilelift);
  margin-inline-start: var(--tilegap);
  overflow: hidden;
}

/* The rounded corner has to be cut into the canvas as well, or the chart
   draws square into it. */
#chartWrap, #chart, #context canvas { border-radius: inherit; }


/* ======================================================================
 * The height ladder, kept at the end of the file on purpose.
 *
 * Every rule here decides that something is NOT shown, and every rule it has
 * to beat is a plain `display` on the same element further down the column of
 * this stylesheet. Written where it reads best - beside the panels it talks
 * about - it lost every one of those fights silently: the picture said the
 * floating bar was still there at 660 pixels and the ladder looked applied.
 * So it lives last, and the class names are qualified, and neither of those
 * is decoration.
 * ====================================================================== */

/* ---- the order things leave in ----
 *
 * A terminal at a low height should show fewer things, not the same things
 * smaller. Which things, and in what order, is a decision - so it is written
 * down here rather than left to whatever the layout happens to cut off.
 *
 * The heights are counted rather than chosen: the column has about the window
 * height less 315 pixels to work in, and each part below is worth what it was
 * measured at - the keyboard card 90, the dials 106, the floating bar 35, a
 * row 16.
 *
 * The keyboard card is not on the ladder at all, and measuring is what took
 * it off. It sits below the buttons, and the buttons are pinned to the bottom
 * of the ticket - so it costs nothing above them and can simply be scrolled
 * to. A reference you can reach is better than a reference deleted, and it
 * was only ever on the list because the old single scroller let it push the
 * buttons off the screen. */

/* When the dials go, the flat Equity row takes over. Hiding the only place a
   number appears is how a layout loses information while looking tidier. */
@media (max-height: 930px) {
  .account .acctdials { display: none; }
  #paneTrade .panel { padding-block: 9px; }
}
.account .row.dEquity { display: none; }
@media (max-height: 930px) { .account .row.dEquity { display: flex; } }

@media (max-height: 890px) { .account .acctbar { display: none; } }
@media (max-height: 870px) { .account .row.dDD { display: none; } }
@media (max-height: 820px) { .account .row.dRoe, .account .row.dLev { display: none !important; } }
@media (max-height: 780px) { .account .row.dBalance { display: none; } }

/* The last rung, and the only one that takes a caption rather than a reading.
   "Order ticket and account" names a column that has a price and two coloured
   buttons in it; at 560 pixels the room it costs is worth more than the
   naming. The switch under it stays - it is a control, and a control that is
   hidden is a control that is gone. */
@media (max-height: 620px) { #paneTrade .sidehead { display: none; } }

/* Equity and margin over free are never dropped: one is how much there is,
   the other is how much of it can still be traded with. Below this the middle
   of the column scrolls, and the invariant above is what keeps that safe. */

/* ---- the same argument along the other axis ----
 *
 * The trades table is pressed for width rather than height. The date under
 * the entry time goes first because the time alone still places the trade in
 * the session; then the percent, which is the profit beside it scaled; then
 * the entry time itself. Money is what is read. */
@media (max-width: 1240px) { .postab .c-when .sub2 { display: none; } }
@media (max-width: 1120px) { .postab .c-pct { display: none; } }
@media (max-width: 1000px) { .postab .c-when { display: none; } }

/* The two rungs below are not "hide a thing" - there is nothing left to hide.
   They change the shape of what stays. */

/* The readings, laid along one line instead of down a column. Two facts, one
   row: the same two facts, in a fifth of the height. */
@media (max-height: 620px) {
  #paneTrade > .panel.account {
    display: flex; flex-wrap: wrap; gap: 4px var(--s4);
    padding-block: var(--s2);
  }
  #paneTrade > .panel.account > .row { flex: 0 1 auto; gap: 6px; }
}

/* And the price, which is the one thing on the column that can never be
   dropped - so it is the one thing that is allowed to get smaller. Below 580
   the boxes lose their padding and the figure drops from 21 to 15, which is
   still the largest type on the column and still the first thing read. */
@media (max-height: 580px) {
  .quote { margin-block: var(--s2) var(--s1); }
  .quote .price { font-size: 15px; }
  .quote .side { padding-block: 3px; }
  .quote .spreadbox { padding-block: 3px; }
  #paneTrade > .panel.ticket { min-height: 125px; }
}

/* The keyboard list is the only thing in the ticket nobody operates, so it is
 * still the first to go - but much later than it used to.
 *
 * The bar was 860px, and it was right for what the block was then: a hundred
 * and ten pixels INSIDE the scrolling middle, which on a short window was
 * most of what that middle had. It is a fixed part of the ticket now and a
 * wrapping row rather than a grid, which is about 46 - so 860 was hiding it
 * on most screens for a cost it no longer has. Measured after the move: every
 * row shows at 960 and the fields still have their room at 700.
 *
 * The shortcuts keep working below this either way; only the picture of them
 * is gone. */
@media (max-height: 660px) {
  #paneTrade .keys { display: none; }
}

/* The watchlist is the one part of the column that is a LIST, so shortening
   it is the cheapest honest thing on the ladder: fewer instruments at a
   glance, which is exactly what a shorter window means.

   That ladder used to be three rules here, and they never once applied.
   `shell.js` sets `--watch` INLINE on the root element, because the divider
   between the two panes is draggable, and an inline custom property beats
   every rule in every stylesheet. So the height is clamped there instead,
   against what the ticket below needs rather than against zero; the reasoning
   and the numbers are in `applyLayout`. Nothing about it belongs here, and
   leaving three dead rules that read as if they were in force is worse than
   having none. */

/* ======================================================================
 * The chart settings window: five tabs over one preview.
 *
 * It was a thousand pixels of scroll with the preview at the top, so the
 * session colours sat 632 pixels below the thing they changed and the shade
 * strength 780. Whatever you touched, the picture of what it did had scrolled
 * away. Five tabs under a preview that never moves is the whole idea; every
 * rule below serves it.
 * ====================================================================== */
.csheet { width: 720px; max-width: 94vw; height: auto; max-height: 88vh;
          display: flex; flex-direction: column; }
.csheet > header { flex: 0 0 auto; flex-wrap: wrap; }
.csheet > header .sub { flex: 1 1 100%; order: 3; margin-top: 2px;
                        font-size: 11.5px; }
.csheet > header .x { order: 2; }

/* The preview, fixed at the top of the room and never scrolled. */
.csheet .cspreview { flex: 0 0 auto; height: 210px; margin: 0;
                     border-block: 1px solid var(--line); }

/* The bar: five sections on one side, the theme on the other.
   The theme is not a tab because it is wanted FROM every tab - somebody
   setting a candle colour needs to see it on both grounds without leaving
   what they are doing. */
.cstabs {
  flex: 0 0 auto; display: flex; align-items: center; gap: var(--s3);
  padding: 0 var(--s4); border-bottom: 1px solid var(--line);
  background: var(--panel);
}
.cstabrow { display: flex; align-items: stretch; gap: 2px; flex: 1 1 auto;
            min-width: 0; overflow-x: auto; scrollbar-width: none; }
.cstabrow::-webkit-scrollbar { display: none; }
.cstab {
  display: inline-flex; align-items: center; gap: var(--s2);
  padding: 12px var(--s3) 11px; border: 0; background: transparent;
  color: var(--dim); font: inherit; font-size: 12.5px; cursor: pointer;
  white-space: nowrap; border-bottom: 2px solid transparent;
  transition: color .12s, border-color .12s;
}
.cstab:hover { color: var(--text); }
.cstab.on { color: var(--accent-ink, var(--accent)); border-bottom-color: var(--accent); }
/* Six tabs since the fault switch has its own (the area-13 adversary, F10),
   and in English five already scrolled. Measured, not guessed
   (chartsettings.js `fitCsTabs`): first the tabs draw closer, then, only if
   that is still not enough, they give up their icons and keep their words -
   never a tab whose name is past the edge. */
.cstabrow.snug { gap: 0; }
.cstabrow.snug .cstab { padding-inline: 8px; gap: 5px; }
.cstabrow.bare .cstab svg { display: none; }
.cstab svg { flex: 0 0 auto; }
.csthemebox {
  display: flex; align-items: center; gap: var(--s2); flex: 0 0 auto;
  align-self: center;
}
.csthemebox > * { margin-block: 0; }
.csthemebox .lbl { font-size: 11px; color: var(--faint); }

/* One pane at a time, and it is the only thing in this window that scrolls. */
.csheet .pad.cspad { flex: 1 1 auto; min-height: 0; overflow-y: auto;
                     scrollbar-width: thin; padding: var(--s5); }
.cspane { display: none; }
.cspane.on { display: block; }
.paneled { margin-bottom: var(--s4); }
.paneled b { display: block; font-size: 14px; }
.paneled .sub { display: block; margin-top: 3px; font-size: 11.5px; }

/* A box around a group inside a pane, with its name sitting on the edge. */
.csbox {
  position: relative; padding: var(--s4) var(--s3) var(--s3);
  border: 1px solid var(--line-soft); border-radius: 12px;
  margin-bottom: var(--s4); background: var(--panel-2);
}
.csbox .boxcap {
  position: absolute; top: -7px; inset-inline-start: var(--s3);
  padding: 0 6px; background: var(--panel);
  font-size: 10.5px; letter-spacing: .8px; text-transform: uppercase;
  color: var(--faint);
}

/* How a candle is drawn, as tiles rather than a dropdown: the shapes are the
   difference, so the shapes are what you pick from. */
.typetiles { display: grid; gap: var(--s2);
             grid-template-columns: repeat(auto-fit, minmax(112px, 1fr)); }
.typetile {
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  padding: var(--s3) var(--s2); border-radius: 10px;
  border: 1px solid var(--line-soft); background: var(--panel);
  color: var(--dim); font: inherit; font-size: 11.5px; cursor: pointer;
  transition: border-color .12s, color .12s, background .12s;
}
.typetile:hover { color: var(--text); border-color: var(--line-strong); }
.typetile.on {
  color: var(--text); border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, var(--panel));
}
.typetile .ti { display: flex; height: 26px; align-items: center; }
.typetile .ti svg { width: 22px; height: 22px; }

/* The foot. One Reset, and the two words that end the window.
   Reset is at the far end from Done on purpose: they are the two buttons that
   cannot be taken back by pressing the other one. */
.csfoot { flex: 0 0 auto; padding: var(--s3) var(--s4);
          border-top: 1px solid var(--line); background: var(--panel); }
.csfoot .isgap { flex: 1 1 auto; }

/* The sessions, as chips. Four rows of eighty-five pixels each was a list,
   not a setting, and it was what made this pane the tall one. */
.shadelist { display: grid; gap: var(--s2);
             grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.shaderow {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s2) var(--s3); border-radius: 10px;
  background: var(--panel-2); border: 1px solid var(--line-soft);
  cursor: pointer;
}
.shaderow.off { opacity: .5; }
.shaderow .name { font-size: 12.5px; }
.shaderow .when { font-size: 11px; color: var(--faint);
                  margin-inline-start: auto; }
.shaderow.strength {
  grid-column: 1 / -1; background: transparent; border: 0; padding: var(--s2) 0;
}

/* ---- the account window, on one page ----
 *
 * It had three tabs, because it is twenty-four decisions and cutting it in
 * three made each screen short. The cut cost more than it bought: a cost you
 * changed on one tab was invisible from another, and Apply - which restarts
 * the account - was pressed with the changes off screen. So the page is whole
 * again and the crowding is answered at the top instead, where the settings
 * are turned into money.
 */
#settings .sheet { display: flex; flex-direction: column; }
#settings .billpad { flex: 0 0 auto; padding-bottom: var(--s3); }
/* Room at the bottom of the scroll. Scrolled to the very end, the last field
   came to rest with its border on the footer's: measured at 1,281 against a
   footer top of 1,280. */
#settings .pad.grid2 { flex: 1 1 auto; min-height: 0; padding-bottom: var(--s4); }

/* ---------- the account, as one object ----------
 *
 * Three tiles and a receipt became one card. Not a skin change: the tiles
 * repeated themselves (nothing open, and the balance and the free margin are
 * the same number), and the pair of them took 319 of this window's 1,168
 * pixels before anything editable. A card carries the same facts in a third
 * of the room and reads as ONE account rather than as four unrelated boxes.
 *
 * Blue rather than the receipt's gold. Gold here means "worth a second look"
 * - the favourite star, the news flag, the tip card - and a card that is
 * always on the window is not a second look; it is the subject.
 */
.acctcard {
  border-radius: 14px; padding: 13px 15px 12px;
  border: 1px solid color-mix(in srgb, var(--accent) 34%, transparent);
  background: linear-gradient(140deg,
    color-mix(in srgb, var(--accent) 13%, transparent),
    color-mix(in srgb, var(--accent) 2%, transparent) 62%);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 5%);
}
.acctcard.hidden { display: none; }
.actop { display: flex; align-items: flex-start; gap: 12px; }
.awho { flex: 1 1 auto; min-width: 0; }
.alead { display: block; color: var(--faint); font-size: 11px; }
.aamt { display: flex; align-items: baseline; gap: 6px; margin-top: 1px; }
.aamt > b { font: 700 25px/1.1 var(--mono); letter-spacing: .01em; }
.aamt > .aunit { color: var(--faint); font-size: 12.5px; }
.aroom { margin: 3px 0 0; color: var(--faint); font-size: 11.5px; }
.aroom:empty { display: none; }
.asym {
  flex: 0 0 auto; border-radius: 999px; padding: 3px 11px;
  border: 1px solid var(--line); color: var(--dim); font-size: 12px;
  background: color-mix(in srgb, var(--panel) 70%, transparent);
}
/* Four facts, and they wrap rather than squeeze: at the narrowest this window
   gets they would otherwise each be four characters wide. */
.afacts { display: flex; flex-wrap: wrap; gap: 10px 26px; margin-top: 13px; }
.afacts > div { min-width: 0; }
.afacts i { display: block; font-style: normal; color: var(--faint); font-size: 11px; }
.afacts b { font: 600 14.5px var(--mono); }
.afacts em {
  display: block; font-style: normal; color: var(--faint); font-size: 10.5px;
  margin-top: 1px;
}
.afacts em:empty { display: none; }

/* The seams that were tabs. A caption in the window's own quiet voice, not
   the blue note box `.setnote` wears - it names a group, it does not warn. */
.setsec {
  margin: 14px 0 2px; color: var(--faint); font-size: 11px;
  letter-spacing: .3px;
}
.setsec:first-of-type { margin-top: 0; }

.atfoot {
  flex: 0 0 auto; padding: var(--s3) var(--s4);
  border-top: 1px solid var(--line); background: var(--panel);
}


/* ---------- choosing a colour ---------- */
/*
 * The sheet that replaced the operating system's colour popup. See picker.js
 * for why it exists at all; what is here is only its skin.
 *
 * Fixed to the viewport rather than hung inside the window it belongs to,
 * because every one of these swatches sits in a pane that scrolls and clips,
 * and a sheet drawn inside such a pane is cut off by it. picker.js puts it
 * back where the swatch is, and closes it when anything moves.
 */
.pkpop {
  position: fixed; z-index: 120; width: max-content; padding: 13px;
  border-radius: 14px; background: var(--panel);
  border: 1px solid var(--line); box-shadow: 0 20px 48px rgba(0, 0, 0, .55);
  outline: 0;
}
:root.light .pkpop { box-shadow: 0 20px 48px rgba(20, 26, 38, .22); }
.pkpop .pkhead { display: flex; align-items: center; gap: 9px; margin-bottom: 11px; }
.pkpop .pknow {
  width: 24px; height: 24px; flex: 0 0 auto; border-radius: 7px;
  border: 1px solid var(--line-soft);
}
.pkpop .pkname { flex: 1; font-size: 12.5px; color: var(--text); white-space: nowrap; }
.pkpop .pkcode { font-family: var(--mono); font-size: 11px; color: var(--faint); }
.pkpop .pkgrid { display: grid; grid-template-columns: repeat(6, 26px); gap: 7px; }
.pkpop .pkchip {
  width: 26px; height: 26px; padding: 0; border: 0; border-radius: 8px;
  cursor: pointer; box-shadow: inset 0 0 0 1px rgba(128, 128, 128, .28);
  transition: transform .1s;
}
.pkpop .pkchip:hover { transform: scale(1.08); }
/* The ring is drawn in the panel's own colour first and the accent second, so
   it reads as a ring on a dark chip as well as on a white one. */
.pkpop .pkchip.on,
.pkpop .pkchip:focus-visible {
  outline: 0;
  box-shadow: inset 0 0 0 1px rgba(128, 128, 128, .28),
              0 0 0 2px var(--panel), 0 0 0 4px var(--accent);
}
.pkpop .pkfoot {
  display: flex; align-items: center; gap: 8px;
  margin-top: 12px; padding-top: 11px; border-top: 1px solid var(--line-soft);
}
.pkpop .pkfoot .sub { flex: 1; font-size: 11px; color: var(--faint); white-space: nowrap; }
.pkpop .pkhex {
  width: 92px; padding: 6px 8px; border-radius: 8px;
  background: var(--panel-2); border: 1px solid var(--line-soft);
  color: var(--text); font-family: var(--mono); font-size: 11.5px;
  /* A colour code is written the same way in every language. */
  text-align: center; direction: ltr;
}
.pkpop .pkhex:focus { outline: 0; border-color: var(--accent); }
.pkpop .pkhex.bad { border-color: var(--sell, #e04f5f); }

/* The cell that asks. It is a span with a button's job rather than a button,
   because in the candle window it has a real button inside it - the way back
   to following the body - and a button inside a button is not a thing. */
.colcell:focus-visible, .swcell:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 9px;
}
.swcell { display: inline-flex; cursor: pointer; }

/* The sheet hangs on the body, not inside the window that opened it, so it
   does not inherit that window's `direction: rtl` and needs its own. The code
   box keeps its own `ltr`: a colour code is written the same way in every
   language. */
body.fa .pkpop { direction: rtl; }

/* ---------- a number and the rail beside it ---------- */
/*
 * A bounded field carries a rail: the same number reachable two ways, by
 * dragging for the shape of it and by typing for the exact one. See
 * numfield.js for why only bounded fields get one.
 *
 * The row is laid out with `flex` and no side named, so it turns round with
 * the window it is in: in English the rail runs from the left and the box
 * sits at the right end of the row, in Persian both are mirrored. The fill
 * inside the rail is turned by paintSlider, which is the only place in the
 * program that knows which way a fill runs.
 */
.numrow { display: flex; align-items: center; gap: 10px; width: 100%; }
.numrow > .numrail { flex: 1 1 auto; min-width: 56px; }
.numrow > .numfield { flex: 0 0 auto; }
.numrow > .numfield > input[type=number] {
  width: 62px; text-align: center; font-variant-numeric: tabular-nums;
}
.numrow > .numrail::-webkit-slider-runnable-track {
  background: linear-gradient(to right,
    var(--accent) 0 var(--at-x),
    color-mix(in srgb, var(--text) 16%, transparent) var(--at-x) 100%);
}
.numrow > .numrail.rtl::-webkit-slider-runnable-track {
  background: linear-gradient(to right,
    color-mix(in srgb, var(--text) 16%, transparent) 0 var(--at-x),
    var(--accent) var(--at-x) 100%);
}

/* ---------- The light theme's flatter surfaces ----------
 *
 * Four changes that belong to the light theme only, and each one is scoped to
 * `:root.light` for that reason: the dark theme was measured and left alone.
 */

/* The bar across the top, plain. Every control in it used to carry its own
   grey fill, so the bar read as twenty small tiles rather than as one surface.
   The fills go and the icons stay, which is enough to find them - and anything
   switched ON keeps its blue, because that is the only thing the fills were
   really saying. The bar's bottom line goes too: the chart's frame below it is
   1.18 against the bar, which draws the edge without a rule needing to. */
:root.light #topbar { border-bottom-color: transparent; }
:root.light #topbar .chip:not(.on):not(.active),
:root.light #topbar .icon:not(.on):not(.active),
:root.light #topbar .iconbtn:not(.on):not(.active),
:root.light #topbar select,
:root.light #topbar .seg > button:not(.on):not(.active) {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
}
/* `background` is a shorthand, so the line above also took the chevron off
   the one control in the bar that opens a list. The fill goes; the mark that
   says it opens does not. */
:root.light #topbar select {
  background-image: var(--caret);
  background-repeat: no-repeat;
  background-size: 16px 16px;
  background-position: right 6px center;
}
:root.light body.fa #topbar select { background-position: left 6px center; }

/* The watchlist and the column it sits in change places. The panel was the
   grey one and the column around it the white; measured surface by surface,
   because three guesses about which was which were all wrong. The order ticket
   below keeps the white it paints for itself. */
:root.light #side { background: var(--bg); }
:root.light #paneWatch { background: var(--panel); }
:root.light #paneWatch .sidehead { background: var(--bg); }

/* And the mode switch: the button that is not chosen gets a surface of its own
   instead of showing the strip behind it. Only the unchosen one - the chosen
   one keeps its blue, which is the whole way of telling them apart. */
:root.light #tradeMode button:not(.on) {
  background: var(--panel);
  border-color: var(--line-soft);
}

/* The writing that sits on the chart itself carries a glow so it survives
   whatever candle happens to be behind it. That glow is black, which is right
   over a dark chart and wrong over a light one: under dark grey letters on a
   near-white canvas it reads as a smudge rather than as a lift. Reported from
   a screenshot of the readout, and measured - `#hud` was drawing
   `rgb(0,0,0) 0 1px 3px` under `rgb(59,61,63)` on `#f3f4f4`. */
:root.light #hud,
:root.light #indLegend .lbl { text-shadow: none; }

/* ---- the shareable card ----
 *
 * The preview is the picture itself, scaled down to whatever the window has:
 * the canvas is 1080x1920 whatever happens here, so this is only how big it
 * is being LOOKED at. Capped by height rather than width, because a story is
 * tall and a window is not. */
.cardprev { display: flex; justify-content: center; margin: 12px 0 4px; }
.cardprev img {
  max-height: 46vh; max-width: 100%; border-radius: 12px;
  border: 1px solid var(--line-soft); box-shadow: 0 18px 44px rgba(0,0,0,.45);
}
/* The one choice in this window, and it was the smallest thing in it.
 *
 * `.seg` is the application's dense settings control - 12px type, five pixels
 * of padding top and bottom and NONE at the sides - which is right in a column
 * of settings and wrong here: the two words sat in a pill barely wider than
 * themselves, measured at 29x27 and 39x27 pixels, under a heading and above a
 * picture five hundred pixels tall. So it is given its own size here rather
 * than everywhere.
 *
 * Both halves the same width, so neither reads as the one that was meant. */
#cardMake .seg {
  align-self: center; gap: 4px; padding: 5px; margin: 6px 0 4px;
}
#cardMake .seg button {
  flex: 0 0 auto; min-width: 108px; padding: 10px 22px; font-size: 14px;
}

/* The four ways out of this window, all the same size.
 *
 * None of them is the one the customer is supposed to want, so none of them
 * is a primary button - which is the whole shape of this row. The icon is
 * above the word rather than beside it so four of them fit across a 360-pixel
 * dialog in either language without any of the words being shortened. */
/* The foot the keys live in: outside the scrolling middle, so the row cannot
   be the thing that falls off the bottom on a short screen. */
#cardMake .cardfoot {
  flex: 0 0 auto; padding: 4px 14px 14px;
  border-top: 1px solid var(--line); display: flex; flex-direction: column; gap: 6px;
}
#cardMake .cardfoot #cardSay { margin: 0; min-height: 1.2em; }
.shareway { display: flex; gap: 8px; }
/* The word where the Telegram key would be, when there is no Telegram: a
   key's slot, a key's height, the sentence in its title. */
.shareway .noway {
  flex: 1 1 0; min-width: 0; display: grid; place-items: center; text-align: center;
  padding: 6px 4px; border: 1px dashed var(--line); border-radius: var(--field-r, 9px);
  color: var(--faint); font-size: 11px; line-height: 1.35;
}
.shareway button {
  flex: 1 1 0; min-width: 0;
  /* One height for all four, so the row reads as a set however long the words
     are. Held here rather than by the tallest label, which is what made this
     worth measuring: on a 360-pixel dialog each button has 63 pixels for its
     word, and "Copy the picture" wants 80. */
  height: 82px; justify-content: center;
  display: flex; flex-direction: column; align-items: center; gap: 7px;
  padding: 8px 4px;
  background: var(--panel-2); color: var(--dim);
  border: 1px solid var(--line); border-radius: 10px;
  font: inherit; font-size: 11px; cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.shareway button span {
  /* Two lines rather than an ellipsis. A label cut to "Copy the pic…" is a
     label nobody reads, and shortening the words to fit would have cost the
     English row its verbs - "Folder" is not something a button does. */
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden; line-height: 1.25; max-width: 100%; text-align: center;
}
.shareway button:hover { background: var(--sel); color: var(--text); }
.shareway button:active { transform: translateY(1px); }
.shareway button svg { flex: 0 0 auto; }
.shareway button.hidden { display: none; }
/* And the heading over it belongs to it, rather than floating a row above. */
#cardMake #cardLangRow { justify-content: center; margin-bottom: 0; }

/* ---------------------------------------------------------------------
   Persian letters join up, so they are not spaced.

   The small caps labels all carry letter-spacing - 1.1px on the lab's
   captions, 1.2px on the panel captions, and so on down to a fifth of a
   pixel on the buy and sell buttons. That is right for a short uppercase
   Latin word, which is what they were drawn for. Arabic script is joined,
   and tracking prises the joins apart: «قانونی که داری آزمایشش می‌کنی»
   was being drawn with its letters pushed off each other.

   Measured before this was written: seventy-three elements on screen were
   holding Persian text with a spacing value on it, and they sat on plain
   `span`, `b` and `label` as often as on a class of their own. So the rule
   is blanket rather than a list - a list built by reading would have fixed
   the two captions that were reported and left the other seventy.

   It is `!important` because the twenty-eight rules that set the spacing
   are mostly more specific than anything that could sit here, and this has
   to win against all of them without being rewritten every time one is
   added.

   English is untouched: none of this applies unless the interface is in
   Persian. --------------------------------------------------------- */
body.fa, body.fa * { letter-spacing: 0 !important; }

/* Except the codes, which are spaced ON PURPOSE and are never Persian.
   A licence code, a challenge code and the code typed to join one are
   Latin and digits, read a character at a time, and each of them already
   says so where it is written. Their own values are restored rather than
   guessed at - the widths differ, and flattening them all to one number
   would be a second bug wearing the first one's clothes. */
body.fa .paycode { letter-spacing: 2px !important; }
body.fa #welcome .paycode { letter-spacing: 3px !important; }
body.fa #chalJoinCode { letter-spacing: 4px !important; }
body.fa .chalcode b { letter-spacing: 3px !important; }
body.fa .chalrow .cd { letter-spacing: 2px !important; }

/* ---------- the curtain, while the interface is being built ----------
 *
 * The markup in index.html is not the arrangement anybody uses. The script
 * moves the rail buttons out of the top bar, folds the top bar together,
 * builds the timeframes, dresses the windows and translates all of it - so
 * until it has run, what is on screen is the raw page: English, left to
 * right, and the buttons in the wrong place. Measured: it was in plain view
 * for about half a second here and several seconds on a laptop, and then
 * snapped into shape.
 *
 * The title bar is deliberately NOT covered. It is correct in the markup, it
 * does not rearrange, and it is how somebody closes a start that is taking
 * too long.
 *
 * `visibility` rather than `display`: the interface has to keep its size
 * while it is hidden, or everything measured during the start - the chart's
 * canvas, the column widths, the fold - would be measured against nothing.
 */
html[data-booting] body > *:not(#booting):not(#titlebar):not(#tbgrips) {
  visibility: hidden;
}
#booting {
  position: fixed;
  inset-block: var(--titlebar, 0px) 0; inset-inline: 0;
  z-index: 500;
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: var(--s4);
  background: var(--bg);
}
html:not([data-booting]) #booting { display: none; }

.bootdots { display: flex; gap: 6px; }
.bootdots i {
  width: 5px; height: 5px; border-radius: 50%; background: var(--panel-3);
  animation: bootdot 1.1s ease-in-out infinite;
}
.bootdots i:nth-child(2) { animation-delay: .16s; }
.bootdots i:nth-child(3) { animation-delay: .32s; }
@keyframes bootdot {
  0%, 70%, 100% { opacity: .35; }
  35%           { opacity: 1; }
}
/* A machine told to keep still is told for a reason. The curtain still does
   its job without the dots moving. */
@media (prefers-reduced-motion: reduce) {
  .bootdots i { animation: none; opacity: .6; }
}

/* --------------------------------------------------------------------------
   The blind review's report card (analysis.js showReviewCard; the owner's
   option 2 of three, 2026-10-02). Two equal columns - where you had entered,
   where you had not traded - with the gap between them; the overall figure
   and its reading above; each setup under the entries; earlier reviews at
   the foot. Entries carry the accent, the moments without a trade the gold
   ink, wherever either appears. Desktop widths: the window's own minimum
   (900) and up; below that the columns stack rather than break.
   -------------------------------------------------------------------------- */
#reviewCard > .sheet.rcsheet {
  width: min(860px, calc(100vw - 32px)); max-width: none;
  height: auto; max-height: calc(100dvh - var(--titlebar, 0px) - 32px);
}
.rcsheet > .pad.rcbody { gap: 12px; padding: 16px 18px 18px; }
.rcsheet .rcfoot {
  display: flex; gap: 10px; align-items: center; justify-content: flex-start;
  padding: 12px 18px 14px; border-top: 1px solid var(--line-soft);
}
.rcsheet .rcfoot .primary { min-height: 40px; padding: 9px 22px; font: inherit; font-weight: 600; font-size: 14px; }
.rcsheet > header .x { width: 34px; height: 34px; }
.rcsheet .primary:focus-visible, .rcsheet > header .x:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
.rcsheet .secline { margin-top: 4px; }
.rcsheet .rcdelta { color: var(--dim); font-weight: 600; margin-inline-start: 10px; }
.rcsheet .rcverdict { margin: 0; font-size: 16px; line-height: 1.8; font-weight: 600; color: var(--text); }
.rcsheet .rchint { margin: 0; font-size: 13px; line-height: 1.8; color: var(--dim); }
.rcsheet .rchow { margin: 2px 0 0; font-size: 12px; line-height: 1.8; color: var(--faint);
  padding-inline-start: 10px; border-inline-start: 2px solid var(--line-strong); }
.rcsheet .rcempty { margin: 0; font-size: 13px; line-height: 1.8; color: var(--dim);
  padding: 12px 14px; border-radius: 12px; border: 1px dashed var(--line); }
.rcsheet .rcnote { margin: 0; font-size: 12px; line-height: 1.8; color: var(--faint); }
.rcsheet .rcuntag { color: var(--dim); }
.rcsheet bdi { unicode-bidi: isolate; }
.rcsheet .halo { position: absolute; border-radius: 50%; filter: blur(34px); opacity: .28;
  background: currentColor; pointer-events: none; }
:root.light .rcsheet .halo { opacity: .12; }

/* the top band: the overall figure, and the sentence that reads it */
.rcsheet .rctop { display: grid; grid-template-columns: minmax(150px, 210px) 1fr; gap: 18px; align-items: center;
  padding: 10px 16px; border-radius: 14px; background: var(--top), var(--panel-2); box-shadow: var(--sheen); }
.rcsheet .rcall { display: flex; flex-direction: column; align-items: flex-start;
  padding-inline-end: 16px; border-inline-end: 1px solid var(--line-soft); }
.rcsheet .rcall span { font-size: 12px; color: var(--faint); }
.rcsheet .rcall b { font-size: 34px; font-weight: 700; line-height: 1.15; font-variant-numeric: tabular-nums; color: var(--text); }
.rcsheet .rcall em { font-style: normal; font-size: 12px; line-height: 1.7; color: var(--dim); }
.rcsheet .rcsay { min-width: 0; display: flex; flex-direction: column; gap: 4px; }

/* the two columns and the gap between them */
.rcsheet .rccols { display: grid; grid-template-columns: minmax(0, 1fr) 76px minmax(0, 1fr);
  grid-template-areas: "eh gap oh" "eb . ob"; }
.rcsheet .rch, .rcsheet .rcb { position: relative; min-width: 0; border: 1px solid var(--line-soft);
  background: color-mix(in srgb, var(--panel-2) 78%, transparent); }
.rcsheet .rch { grid-area: eh; overflow: hidden; color: var(--accent); padding: 12px 16px 8px; border-bottom: 0;
  border-radius: 16px 16px 0 0; background: var(--lift), color-mix(in srgb, var(--panel-2) 78%, transparent); box-shadow: var(--sheen); }
.rcsheet .rch.o { grid-area: oh; color: var(--gold-ink); }
.rcsheet .rch .halo { width: 180px; height: 120px; inset-inline-start: 0; top: -70px; }
.rcsheet .rch .k { font-size: 14px; font-weight: 600; color: var(--text); }
.rcsheet .rch .v { font-size: 40px; line-height: 1.1; font-weight: 700; margin-top: 4px; font-variant-numeric: tabular-nums; }
.rcsheet .rch .n { font-size: 12px; line-height: 1.7; color: var(--dim); margin-top: 2px; }
.rcsheet .rcb { grid-area: eb; border-top: 0; border-radius: 0 0 16px 16px; padding: 6px 16px 12px;
  display: flex; flex-direction: column; gap: 10px; color: var(--accent); --c: var(--accent); }
.rcsheet .rcb.o { grid-area: ob; color: var(--gold-ink); --c: var(--gold-ink); }
.rcsheet .rcgap { grid-area: gap; align-self: center; display: flex; justify-content: center; }
.rcsheet .rcgap span { font-size: 13px; font-weight: 700; color: var(--text); line-height: 1.3; text-align: center;
  min-width: 64px; padding: 8px 6px; border-radius: 999px;
  background: var(--top), var(--panel-2); border: 1px solid var(--line); box-shadow: var(--sheen); }
.rcsheet .rcmid { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 18px; }
.rcsheet .rcdots { display: flex; flex-wrap: wrap; gap: 4px; max-width: calc(11 * 11px + 10 * 4px); }
.rcsheet .rcdots i, .rcsheet .rcsaid .sw { width: 11px; height: 11px; border-radius: 50%; box-sizing: border-box; }
.rcsheet .rcdots i.on, .rcsheet .rcsaid .sw.on { background: var(--c); }
.rcsheet .rcdots i.ring, .rcsheet .rcsaid .sw.ring { border: 1.5px solid var(--c); }
.rcsheet .rcdots i.dim, .rcsheet .rcsaid .sw.dim { background: color-mix(in srgb, var(--c) 30%, transparent); }
.rcsheet .rcsaid { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.rcsheet .rcsaid li { display: grid; grid-template-columns: 12px 2em 1fr; gap: 8px; align-items: center; font-size: 13px; color: var(--dim); }
.rcsheet .rcsaid b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
.rcsheet .rcb .secline { color: var(--faint); }

/* each setup, under the entries */
.rcsheet .rcsetups { display: flex; flex-direction: column; gap: 4px; color: var(--text); }
.rcsheet .rcsrow { display: grid; grid-template-columns: minmax(90px, 1.5fr) minmax(36px, 1fr) 4.6em 4.4em;
  gap: 8px; align-items: center; font-size: 13px; min-height: 26px; }
.rcsheet .rcsrow .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rcsheet .rcsrow .tr { height: 8px; border-radius: 4px; overflow: hidden;
  background: color-mix(in srgb, var(--panel-3) 70%, transparent); }
.rcsheet .rcsrow .tr i { display: block; height: 100%; width: var(--w); border-radius: 4px; background: var(--accent); }
.rcsheet .rcsrow b { font-weight: 600; text-align: end; font-variant-numeric: tabular-nums; white-space: nowrap; }
.rcsheet .rcsrow .c { color: var(--faint); font-size: 12px; text-align: end; font-variant-numeric: tabular-nums; white-space: nowrap; }
.rcsheet .rcsrow.thin .tr { background: none; border: 1px dashed var(--line); }
.rcsheet .rcsrow.thin b { color: var(--faint); font-weight: 400; }

/* too few to read: the figures stay, quieter */
.rcsheet.isfew .rch, .rcsheet.isfew .rcb { color: var(--dim); --c: var(--dim); }
.rcsheet.isfew .rcgap span { color: var(--dim); }

/* the earlier reviews */
/* left to right, as the chart reads time: the newest at the right end, as
   the owner's picked drawing has it */
.rcsheet .rchist { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 8px; direction: ltr; }
.rcsheet .hcell > * { direction: rtl; unicode-bidi: isolate; }
body:not(.fa) .rcsheet .hcell > * { direction: ltr; }
.rcsheet .hcell { min-width: 0; display: flex; flex-direction: column; align-items: center; padding: 6px;
  border-radius: 12px; border: 1px solid var(--line-soft); background: var(--top), var(--panel-2); box-shadow: var(--sheen); }
.rcsheet .hcell .dt { font-size: 12px; color: var(--faint); white-space: nowrap; }
.rcsheet .hcell b { font-size: 16px; font-weight: 700; line-height: 1.5; color: var(--dim); font-variant-numeric: tabular-nums; }
.rcsheet .hcell .pp { display: flex; gap: 8px; font-size: 12px; }
.rcsheet .hcell .pp em { font-style: normal; font-weight: 600; font-variant-numeric: tabular-nums; }
.rcsheet .hcell .pp .e { color: var(--accent); }
.rcsheet .hcell .pp .o { color: var(--gold-ink); }
.rcsheet .hcell.now { border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  background: var(--top), color-mix(in srgb, var(--accent) 12%, var(--panel-2)); }
.rcsheet .hcell.now .dt, .rcsheet .hcell.now b { color: var(--text); font-weight: 600; }

/* narrower than the desktop window can be: one column, nothing cut */
@media (max-width: 720px) {
  .rcsheet .rctop { grid-template-columns: 1fr; gap: 8px; }
  .rcsheet .rcall { padding: 0 0 8px; border-inline-end: 0; border-bottom: 1px solid var(--line-soft); }
  .rcsheet .rccols { grid-template-columns: minmax(0, 1fr); grid-template-areas: "eh" "eb" "gap" "oh" "ob"; }
  .rcsheet .rcgap { padding: 10px 0; }
  .rcsheet .rchist { gap: 4px; }
}
