/* Mirror design system — mobile-first, dark by default, light on request. */

:root {
  --bg: #07080B;
  --bg-elev: #0E1016;
  --surface: #12151D;
  --surface-2: #171B25;
  --surface-3: #1E2331;
  --line: rgba(255, 255, 255, 0.07);
  --line-strong: rgba(255, 255, 255, 0.13);
  --text: #F3F5F9;
  --text-dim: #A2ABBF;
  --text-faint: #57606A;
  --brand: #7C5CFF;
  --brand-2: #22D3EE;
  --brand-soft: rgba(79, 123, 255, 0.14);
  --pos: #2FD98A;
  --pos-soft: rgba(47, 217, 138, 0.13);
  --neg: #FF5F6D;
  --neg-soft: rgba(255, 95, 109, 0.13);
  --warn: #FFB020;
  --warn-soft: rgba(255, 176, 32, 0.14);
  --gold: #F0C24B;
  --violet: #7FD6D0;
  --radius: 18px;
  --radius-sm: 12px;
  --radius-lg: 18px;
  --shadow: 0 18px 44px rgba(0, 0, 0, 0.5);
  --shadow-sm: 0 4px 16px rgba(0, 0, 0, 0.3);
  --nav-h: 66px;
  --maxw: 1240px;
  --sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Inter, Roboto, Helvetica, Arial, sans-serif;
  /* The brand face for headings and the word mark: Space Grotesk, self-hosted
     (public/fonts, its OFL licence beside it) as one variable latin subset,
     22 KB, weights 300 to 700. display: swap means text with the system stack
     never blocks and never goes unseen waiting for it; the fallback above has
     the same width class, so the swap shifts headings less than half a line. */
  --display: 'Meror Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  /* Names a few rules use that were never defined, so tooltips had no
     background and some cards no border. Aliases of the real tokens, so they
     follow the theme. */
  --border: var(--line); --card: var(--surface); --muted: var(--text-faint); --r-lg: var(--radius-lg);
  --green: var(--pos); --red: var(--neg); --amber: var(--warn); --amber-bg: rgba(255, 176, 32, 0.16);
  --amber-ink: #FFD37A; --amber-line: rgba(255, 176, 32, 0.45);
  --mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;
  /* Six type sizes, and no others anywhere in this file (the Pro shapes at
     the end keep the sizes their spec gave them). There were 38 pixel sizes
     and five clamps: a label at 11px beside one at 11.5px beside one at
     12.5px, each chosen on the day and none of them agreeing, which reads as
     noise before anybody has read a word. A caption, small print, the body,
     a lead line, a title and the one big figure. */
  --t-cap: 12px; --t-small: 13.5px; --t-body: 15px; --t-lead: 17px; --t-title: 22px; --t-hero: 34px;
  color-scheme: dark;
}

[data-theme='light'] {
  --amber-bg: #FFF7E6; --amber-ink: #7A4B00;
  --bg: #F6F7FB;
  --bg-elev: #FFFFFF;
  --surface: #FFFFFF;
  --surface-2: #F2F4F9;
  --surface-3: #E8ECF4;
  --line: rgba(15, 20, 35, 0.09);
  --line-strong: rgba(15, 20, 35, 0.16);
  --text: #0E1420;
  --text-dim: #5B6579;
  --text-faint: #57606A;
  --brand: #2563EB;
  --brand-soft: rgba(37, 99, 235, 0.1);
  --pos: #0FA968;
  --pos-soft: rgba(15, 169, 104, 0.12);
  --neg: #E23744;
  --neg-soft: rgba(226, 55, 68, 0.1);
  --shadow: 0 16px 40px rgba(16, 24, 48, 0.1);
  --shadow-sm: 0 2px 10px rgba(16, 24, 48, 0.07);
  color-scheme: light;
}

* { box-sizing: border-box; }
/* `hidden` means hidden. The browser's own rule is the weakest in the
   cascade, so any class that sets `display` beat it: the contact form showed
   an empty error box as a beige bar, and the reset page said "a reset link is
   on its way" before anything had been sent. Two one-off workarounds for the
   same thing were already further down. Everything that shows a hidden
   element does it by clearing the attribute, which this leaves working. */
[hidden] { display: none !important; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  font-size: var(--t-body);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  padding-bottom: calc(var(--nav-h) + env(safe-area-inset-bottom, 0px));
  overflow-x: hidden;
}
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: none; }

/* The brand face, above the tokens it needs. Headings and the word mark only:
   the body and every figure stay on the system stack, whose tabular numerals
   a dashboard and a leaderboard need. */
@font-face {
  font-family: 'Meror Grotesk';
  src: url('/fonts/meror-display.woff2') format('woff2-variations');
  font-weight: 300 700;
  font-display: swap;
}
h1, h2, h3, h4, .logo-word { font-family: var(--display); }
h1, h2, h3, h4 { margin: 0; line-height: 1.15; letter-spacing: -0.02em; font-weight: 600; }
p { margin: 0 0 10px; }

/* Vertical rhythm inside a page.
   Headings are reset to margin:0 above, which means every page has to remember
   to space its own sections and the ones that forgot ran together: on Contact,
   "Ask the community" sat directly on the Send button with nothing between
   them. This gives a heading that follows content room to breathe, and uses
   :where() so it carries no specificity at all: any rule a page already has,
   or any inline style, still wins.
   A heading that opens a block gets nothing, so nothing gains a stray gap. */
.page :where(h2) { margin-top: 38px; }
.page :where(h3) { margin-top: 26px; }
.page :where(h4) { margin-top: 20px; }
.page :where(h2, h3, h4):first-child { margin-top: 0; }
.page :where(h2, h3, h4) + :where(p, ul, ol) { margin-top: 10px; }
/* A line of text 1,500 pixels wide is unreadable whatever else is right. */
.page :where(p, li) { max-width: 74ch; }
svg { display: block; }
::selection { background: var(--brand-soft); }

.num { font-variant-numeric: tabular-nums; font-feature-settings: 'tnum' 1; }
/* The six sizes as classes, for markup that would otherwise carry its own. */
.t-cap { font-size: var(--t-cap); line-height: 1.35; }
.t-small { font-size: var(--t-small); line-height: 1.45; }
.t-body { font-size: var(--t-body); line-height: 1.5; }
.t-lead { font-size: var(--t-lead); line-height: 1.4; }
.t-title { font-size: var(--t-title); line-height: 1.25; }
.t-hero { font-size: var(--t-hero); line-height: 1.1; letter-spacing: -0.03em; }
.pos { color: var(--pos); }
.neg { color: var(--neg); }
.dim { color: var(--text-dim); }
.faint { color: var(--text-faint); }
.mono { font-family: var(--mono); }
.center { text-align: center; }
.row { display: flex; align-items: center; gap: 10px; }
.row.wrap { flex-wrap: wrap; }
.spread { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.col { display: flex; flex-direction: column; }
.grow { flex: 1; min-width: 0; }
.hide { display: none !important; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mt { margin-top: 14px; } .mt-lg { margin-top: 26px; } .mb { margin-bottom: 14px; }

/* ---------------------------------------------------------------- layout -- */
.shell { max-width: var(--maxw); margin: 0 auto; padding: 0 16px; }
/* One motion vocabulary.
   Fourteen rules said `transition: .15s` with no curve, which means the
   browser's default `ease`: it starts slow, and that hesitation at the start of
   every hover is most of what makes an interface feel cheap. One decisive
   curve, out fast and settling gently, used everywhere. */
:root {
  --ease: cubic-bezier(.2, .7, .3, 1);
  --ease-soft: cubic-bezier(.33, 1, .68, 1);
  /* Four speeds and two curves, and nothing else anywhere in this file.
     There were twelve durations before: twenty rules at .15s, nine at .18s,
     and singles at .12, .16, .2, .25, .3, .35, .4, .45, .5, .55 and .6, each
     defensible on its own and none of them agreeing with the two tokens they
     sat beside. No single one looks wrong; together they read as a thing
     assembled rather than designed. Expensive software moves at two or three
     speeds, always the same ones. */
  --press: .04s;   /* the give under a finger: felt, never seen */
  --fast: .14s;    /* anything under the pointer changing colour or lifting */
  --base: .24s;    /* a panel, a switch, a surface settling */
  --slow: .45s;    /* something travelling a distance: a bar filling, a number rolling */
  /* A loop (the live pulse, the spinner, the loading shimmer) is a multiple
     of --slow, so even the things that never stop keep the same clock. */
}
/* No slide on arrival.
   The page used to lift eight pixels and fade on every navigation. That was
   written when a navigation took a second and the animation covered the wait;
   navigations take about ten milliseconds now, so the animation IS the wait,
   and a whole screen jumping under the cursor reads as a twitch rather than as
   motion. A fast page should simply be there. The short fade that followed is
   gone too: the page blanking and coming back on every navigation read as a
   glitch. */
.page { padding-top: 14px; padding-bottom: 40px; }

/* Look here.
   A ring that grows out of the edge and fades. Nothing moves and nothing
   bounces: an element that jumps under the cursor reads as a toy, and a shake
   reads as an accusation. Somebody pressing the acceptance box before they have
   finished reading has done the obvious thing, not the wrong thing. This says
   "that one, there", and then stops. */
@keyframes attention {
  from { box-shadow: 0 0 0 0 var(--brand-soft), 0 0 0 1px var(--brand); }
  to   { box-shadow: 0 0 0 10px transparent, 0 0 0 1px transparent; }
}
.attention { animation: attention var(--slow) var(--ease-soft) 1; }
/* The hint below it is a line of text, so it simply deepens for a moment. */
.reader-hint { transition: color var(--base) var(--ease); }
.reader-hint.urging { color: var(--text); }
@media (prefers-reduced-motion: reduce) { .attention { animation: none; } }

/* A disabled control fires no pointer events at all, so a press on the greyed
   checkbox or the greyed Next button reached nothing and the interface stayed
   silent about why. Making them transparent to the pointer lets the press
   through to the panel behind, which is listening and knows what is in the
   way. They are still disabled; they just no longer swallow the question. */
#reader-next:disabled, .reader-agree input:disabled, #legal-accept:disabled { pointer-events: none; }
.reader-agree { cursor: pointer; }

/* A long list behind one line. */
.slip-more { margin-top: 10px; }
.slip-more > summary {
  cursor: pointer; font-size: var(--t-small); color: var(--text-dim); font-weight: 600;
  padding: 7px 0; list-style: none; user-select: none;
}
.slip-more > summary::-webkit-details-marker { display: none; }
.slip-more > summary::before { content: '▸'; display: inline-block; margin-right: 7px; color: var(--text-faint); transition: transform var(--fast) var(--ease); }
.slip-more[open] > summary::before { transform: rotate(90deg); }
.slip-more > summary:hover { color: var(--text); }


/* An anchor jump lands under the sticky top bar unless the page is told how
   tall it is, and it lands with a jolt unless it is told to ease. */
/* The scrollbar's space is always reserved.
   Going from a short page to a long one adds a scrollbar, and the scrollbar
   takes about ten pixels out of the viewport, so every centred thing on the
   page, the top bar included, jumps sideways at the moment the new page
   arrives. That is the whole screen twitching on every navigation, and it is
   one line. */
html { scrollbar-gutter: stable; }
html { scroll-behavior: smooth; scroll-padding-top: 76px; }
@media (max-width: 1023px) { html { scroll-padding-top: 64px; } }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* Text rendered the way the rest of the design was drawn. Without this the
   weights look heavier on a Mac than they were designed to be. */
body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-rendering: optimizeLegibility; }

/* The page's own scrollbar, rather than the operating system's. A wide grey
   default bar down the side of a dark interface is one of the loudest cheap
   tells there is, and it is two rules to fix. */
* { scrollbar-width: thin; scrollbar-color: var(--line-strong) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 999px; border: 2px solid transparent; background-clip: content-box; }
::-webkit-scrollbar-thumb:hover { background: var(--text-faint); background-clip: content-box; }
::-webkit-scrollbar-corner { background: transparent; }

/* One focus ring, on keyboard focus only, in the brand colour. Without this
   every control gets whatever the browser draws, which differs between them. */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}
/* `*` alone does not match pseudo-elements: the live dots' pulse on ::before
   kept running for ever with Reduce Motion on. */
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition: none !important; } }

.topbar {
  position: sticky; top: 0; z-index: 60;
  /* Declared here and nowhere else. Two later rules set it to 78% and 62%,
     and at 62% the words of the page showed through the bar while it scrolled. */
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--line);
}
.topbar-in { max-width: var(--maxw); margin: 0 auto; padding: 10px 16px; display: flex; align-items: center; gap: 12px; }
.logo { display: flex; align-items: center; gap: 9px; font-weight: 700; letter-spacing: -0.03em; font-size: var(--t-lead); }
.logo-mark {
  width: 27px; height: 27px; border-radius: 8px; flex: none;
  background: linear-gradient(140deg, var(--brand), var(--brand-2));
  display: grid; place-items: center; color: #fff; font-size: var(--t-small); font-weight: 700;
  box-shadow: 0 4px 14px rgba(79, 123, 255, 0.35);
}
.topbar-actions { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.topsearch { display: flex; align-items: center; gap: 6px; margin-left: 12px; padding: 6px 12px; border: 1px solid var(--line); border-radius: 12px; background: var(--surface-2); color: var(--text-faint); min-width: 220px; max-width: 320px; flex: 1; }
.topsearch input { border: 0; background: transparent; color: var(--text); font: inherit; font-size: var(--t-small); outline: none; width: 100%; min-width: 0; }
.topsearch input::placeholder { color: var(--text-faint); }
.topsearch:focus-within { border-color: var(--brand); }
.topsearch-btn { display: none; }
@media (max-width: 760px) { .topsearch { display: none; } .topsearch-btn { display: inline-flex; } }

.iconbtn {
  width: 38px; height: 38px; border-radius: 12px; display: grid; place-items: center;
  border: 1px solid var(--line); background: var(--surface); color: var(--text-dim);
  transition: var(--fast) var(--ease); position: relative;
}
.iconbtn:hover { color: var(--text); border-color: var(--line-strong); }
/* A saved bookmark reads as saved: the mark fills in the brand colour rather
   than staying the same hollow outline it had before the tap. */
.iconbtn.on { color: var(--brand); border-color: var(--brand); }
.iconbtn.on svg { fill: currentColor; fill-opacity: .18; }
.iconbtn .dot {
  position: absolute; top: 8px; right: 8px; width: 8px; height: 8px; border-radius: 50%;
  background: var(--neg); border: 2px solid var(--bg);
}

/* bottom nav (mobile) */
.bottomnav {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 70;
  height: calc(var(--nav-h) + env(safe-area-inset-bottom, 0px));
  padding-bottom: env(safe-area-inset-bottom, 0px);
  /* Opaque. At 92% the page showed through it: not frosted, because there is
     no blur behind it, just a tab bar you can read the page through, and on a
     phone the strip between it and the browser's own toolbar picked up
     whatever was scrolling underneath. */
  background: var(--bg-elev);
  border-top: 1px solid var(--line);
  display: grid; grid-template-columns: repeat(5, 1fr);
}
.bottomnav a {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  font-size: var(--t-cap); color: var(--text-faint); font-weight: 600; transition: var(--fast) var(--ease);
  /* A hair tighter than the caption size elsewhere, so "How it works" still
     fits a fifth of a 375px phone on one line. */
  letter-spacing: -0.01em;
}
/* One line each, so every icon sits at the same height: a label that wrapped
   lifted its own icon 9px above the rest. "Real money" is 67px at this size and
   a fifth of a 320px phone is 64, so the smallest phones close it up a little. */
.bottomnav a > span { white-space: nowrap; }
@media (max-width: 339px) { .bottomnav a { letter-spacing: -0.035em; } }
.bottomnav a.active { color: var(--text); }
.bottomnav a.active svg { color: var(--brand); transform: translateY(-1px); }
.bottomnav svg { width: 21px; height: 21px; transition: var(--fast) var(--ease); }

.sidebar { display: none; }
@media (min-width: 1024px) {
  body { padding-bottom: 0; }
  .bottomnav { display: none; }
  .layout { display: grid; grid-template-columns: 224px minmax(0, 1fr); gap: 30px; max-width: var(--maxw); margin: 0 auto; padding: 0 16px; }
  .sidebar { display: block; position: sticky; top: 62px; align-self: start; height: calc(100vh - 62px); padding: 18px 0; overflow-y: auto; }
  .sidebar a {
    display: flex; align-items: center; gap: 12px; padding: 8px 12px; border-radius: 12px;
    color: var(--text-dim); font-weight: 600; font-size: var(--t-body); margin-bottom: 2px; transition: var(--fast) var(--ease);
  }
  .sidebar a:hover { background: var(--surface); color: var(--text); }
  .sidebar a.active { background: var(--brand-soft); color: var(--text); }
  .sidebar a.active svg { color: var(--brand); }
  .sidebar svg { width: 19px; height: 19px; flex: none; }
  .sidebar .group { font-size: var(--t-cap); text-transform: uppercase; letter-spacing: .07em; color: var(--text-faint); padding: 16px 12px 6px; font-weight: 600; }
  /* Beside the sidebar the content column fills its grid cell; `margin: 0 auto` would shrink it to its content. */
  .shell { padding: 0; max-width: none; margin: 0; width: 100%; justify-self: stretch; }
}

/* ----------------------------------------------------------------- cards -- */
.card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 16px; position: relative;
}
.card.pad-lg { padding: 20px; }
.card.flat { background: var(--surface-2); }
.card-title { font-size: var(--t-cap); text-transform: uppercase; letter-spacing: .07em; color: var(--text-faint); font-weight: 600; }
.section-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin: 26px 0 12px; }
.section-head h2 { font-size: var(--t-lead); }
.section-head .link { color: var(--brand); font-size: var(--t-small); font-weight: 600; }
.grid { display: grid; gap: 12px; }
/* On a phone a card is the full width, so the only thing separating one from
   the next is this gap, and at 12px against 16px of padding inside them they
   read as one squashed stack rather than as separate things. */
@media (max-width: 559px) {
  .grid { gap: 16px; }
  .card + .card, .card + .grid, .grid + .card { margin-top: 16px; }
  .mt-lg { margin-top: 30px; }
}
/* The confirm page's "up to date" card and the channels card below it were
   flush on desktop: the 16px sibling rule inside the phone media query above
   never applied to them at full width, because only one of the two is a plain
   .card. 20px between top-level cards and sections here, desktop and phone. */
.page.narrow > * + section.card { margin-top: 20px; }
.grid.g2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
/* At 390px each of these columns is about 170px, which cannot hold "Portfolio
   concentration" beside "1.2% in top 5". One column, and every label reads. */
@media (max-width: 559px) { .grid.g2 { grid-template-columns: 1fr; } }
/* Leaderboard boards: never narrower than a readable row; wrap to as many columns as fit. */
.grid.boards { grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 14px; }
/* minmax(0, 1fr), not 1fr: a grid track's default minimum is the content's own
   minimum width, so one long word or a wide stat row pushed every card past the
   phone. At 320 the trader cards ran 33px off the edge and took the Paper copy
   button with them, and on /sources 83px of every card went, including all 128
   values and every health badge. `overflow-x: clip` on the shell meant no
   scrollbar ever revealed it. */
.grid.auto { grid-template-columns: minmax(0, 1fr); }
.grid > * { min-width: 0; }
@media (min-width: 760px) {
  .grid.auto { grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); }
}

/* --------------------------------------------------------------- buttons -- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  padding: 12px 12px; border-radius: 12px; font-weight: 600; font-size: var(--t-body);
  border: 1px solid transparent; transition: var(--fast) var(--ease); white-space: nowrap;
}

.btn-primary { background: linear-gradient(135deg, var(--brand), #6D4DF2); color: #fff; box-shadow: 0 6px 20px rgba(79, 123, 255,.32); }
.btn-primary:hover { filter: brightness(1.08); box-shadow: 0 8px 26px rgba(79, 123, 255,.42); }
.btn-ghost { background: var(--surface-2); border-color: var(--line); color: var(--text); }
.btn-ghost:hover { border-color: var(--line-strong); background: var(--surface-3); }
.btn-outline { border-color: var(--line-strong); color: var(--text); }
.btn-outline:hover { background: var(--surface-2); }
.btn-danger { background: var(--neg-soft); color: var(--neg); border-color: transparent; }
.btn-sm { padding: 8px 12px; font-size: var(--t-cap); border-radius: 12px; }
.btn-lg { padding: 12px 18px; font-size: var(--t-body); border-radius: 12px; }
.btn-block { width: 100%; }
.btn[disabled] { opacity: .5; pointer-events: none; }
.btn svg { width: 16px; height: 16px; }

/* --------------------------------------------------------------- badges --- */
.badge {
  display: inline-flex; align-items: center; gap: 5px; padding: 2px 7px; border-radius: 8px;
  font-size: var(--t-cap); font-weight: 600; letter-spacing: .01em; white-space: nowrap;
  background: var(--surface-3); color: var(--text-dim); border: 1px solid transparent;
}
.badge svg { width: 12px; height: 12px; }
.badge.green { background: var(--pos-soft); color: var(--pos); }
.badge.red { background: var(--neg-soft); color: var(--neg); }
.badge.amber { background: var(--warn-soft); color: var(--warn); }
.badge.orange { background: rgba(255,138,60,.14); color: #FFA25C; }
.badge.blue { background: rgba(34,211,238,.13); color: var(--brand-2); }
.badge.violet { background: var(--brand-soft); color: var(--violet); }
.badge.gold { background: rgba(240,194,75,.14); color: var(--gold); }
.badge.muted { background: var(--surface-3); color: var(--text-faint); }
/* Risk tags are off the cards by default (owner, 2026-10-04); the profile
   toggle sets html[data-risk-tags=on] to bring them back on that device.
   This also hides badges already painted before the flag was read. */
html:not([data-risk-tags="on"]) .risk-badge { display: none; }
.badge.lg { padding: 5px 11px; font-size: var(--t-cap); }

.chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 8px 12px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--surface); font-size: var(--t-small); font-weight: 600;
  color: var(--text-dim); transition: var(--fast) var(--ease); white-space: nowrap;
}
.chip:hover { border-color: var(--line-strong); color: var(--text); }
/* One selected state for a chip, a pill and the mode switch: the colours
   swapped, ink ground and page-coloured words. A tint, a brand fill and an
   inverted fill were three ways to say "on". */
.chip.on { background: var(--text); border-color: var(--text); color: var(--bg); }
.chiprow { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 4px; scrollbar-width: none; -webkit-overflow-scrolling: touch; }
.chiprow::-webkit-scrollbar { display: none; }

/* --------------------------------------------------------------- avatars -- */
.avatar {
  border-radius: 50%; flex: none; display: grid; place-items: center;
  font-weight: 700; color: #fff; letter-spacing: -.02em; overflow: hidden;
  width: 44px; height: 44px; font-size: var(--t-body);
}
.avatar.sm { width: 34px; height: 34px; font-size: var(--t-cap); }
.avatar.lg { width: 68px; height: 68px; font-size: var(--t-title); }
.avatar.xl { width: 84px; height: 84px; font-size: var(--t-hero); }

/* ---------------------------------------------------------- trader cards -- */
.tcard { display: block; transition: var(--fast) var(--ease); }
.tcard:hover { border-color: var(--line-strong); box-shadow: var(--shadow-sm); }
.tcard .head { display: flex; gap: 11px; align-items: center; }
.tcard .name { font-weight: 600; font-size: var(--t-body); letter-spacing: -.015em; }
.tcard .sub { font-size: var(--t-cap); color: var(--text-faint); }
.tcard .ret { font-size: var(--t-title); font-weight: 700; letter-spacing: -.03em; line-height: 1; }
.tcard .metrics { display: flex; gap: 14px; flex-wrap: wrap; font-size: var(--t-cap); color: var(--text-dim); }
.tcard .metrics b { color: var(--text); font-weight: 600; }

.rank {
  width: 18px; height: 18px; border-radius: 8px; display: grid; place-items: center; flex: none;
  font-size: var(--t-cap); font-weight: 700; background: var(--surface-3); color: var(--text-dim);
}
.rank.r1 { background: linear-gradient(140deg, #F0C24B, #E09B2D); color: #241a03; }
.rank.r2 { background: linear-gradient(140deg, #D3D9E4, #A8B0BF); color: #1b1f28; }
.rank.r3 { background: linear-gradient(140deg, #D08B58, #B4703C); color: #241203; }

.lb-row {
  display: flex; align-items: center; gap: 12px; padding: 12px 12px; border-radius: 12px;
  transition: var(--fast) var(--ease); border: 1px solid transparent; min-width: 0;
}
.lb-row .lb-stats { flex: none; text-align: right; white-space: nowrap; }
.lb-row:hover { background: var(--surface-2); border-color: var(--line); }

/* --------------------------------------------------------------- metrics --
   A figure is mono and tabular, so "C$1,793.51" is ten fixed-width
   characters, about 100px at 17px, and a row of a fixed number of equal
   cells put the cell below that on a phone and again at five across on a
   tablet: the last digit was painted over by the next cell's background and
   the card read "C$1,793.5". The row now takes as many cells as the card has
   room for, each at least as wide as the figure it carries, and wraps the
   rest onto another line, and a figure with no room even so breaks inside
   the number instead of leaving its box.
   Nothing here clips a number.
   The dividers are drawn by each cell towards the cell before it rather than
   by the grid's own background showing through the gaps, so the empty slots
   a wrapped row leaves are the same surface as the cells and a short last
   row has no line hanging off its left edge; the outermost of them fall
   outside the grid's rounded box and are clipped with it. */
.metricgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(112px, 100%), 1fr)); gap: 1px; background: var(--surface); border-radius: var(--radius-sm); overflow: hidden; }
/* How wide a cell has to be is how wide the figure in it is. Below 400px the
   figure is stepped down by its own clamp, 14.7px at 320px, where ten
   characters measure 86px and 112px of cell holds them with room over, so a
   320px phone keeps two figures side by side instead of five stacked rows.
   From 400px the figure is back at its full 17px and needs the wider cell:
   without that floor, five 112px cells fit the band from about 630 to 700px
   and the last digit would be cut there again. */
@media (min-width: 400px) { .metricgrid { grid-template-columns: repeat(auto-fit, minmax(min(140px, 100%), 1fr)); } }
.metricgrid .m { background: var(--surface); padding: 13px 14px; min-width: 0; box-shadow: -1px 0 0 var(--line), 0 -1px 0 var(--line); }
.metricgrid .m .k { font-size: var(--t-cap); color: var(--text-faint); font-weight: 600; margin-bottom: 3px; }
.metricgrid .m .v { font-size: clamp(var(--t-small), 4.6vw, var(--t-lead)); font-weight: 600; letter-spacing: -.02em; min-width: 0; overflow-wrap: anywhere; }
.metricgrid .m .v.na { color: var(--text-faint); font-size: var(--t-small); font-weight: 600; }

/* A prediction contract's two sides, as one bar. */
.prob-bar { position: relative; height: 12px; border-radius: 999px; background: color-mix(in srgb, var(--text-faint) 28%, transparent); overflow: hidden; }
.prob-bar .prob-yes { position: absolute; inset: 0 auto 0 0; background: var(--good, #17913F); border-radius: 999px; transition: width var(--base) var(--ease); }

/* The "how to send SOL" guide: numbered steps and a coin crossing the wire. */
.sol-anim { display: flex; align-items: center; gap: 12px; margin: 12px 0 6px; padding: 12px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface-2); }
.sol-end { display: flex; flex-direction: column; align-items: center; gap: 3px; color: var(--text-dim); font-size: var(--t-cap); min-width: 64px; }
.sol-end b { font-weight: 600; }
.sol-wire { position: relative; flex: 1; height: 2px; background: linear-gradient(90deg, var(--line), var(--brand), var(--line)); border-radius: 2px; }
.sol-coin { position: absolute; top: -10px; left: 0; font-size: var(--t-lead); line-height: 1; color: var(--brand); animation: sol-travel calc(var(--slow) * 6) var(--ease-soft) infinite; }
@keyframes sol-travel { 0% { left: 0; opacity: 0; } 12% { opacity: 1; } 88% { opacity: 1; } 100% { left: calc(100% - 10px); opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .sol-coin { animation: none; left: 50%; } }
.sol-steps { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 14px; }
.sol-step { display: flex; gap: 11px; align-items: flex-start; }
.sol-n { flex: none; width: 23px; height: 23px; border-radius: 50%; background: var(--brand); color: #fff; font-size: var(--t-cap); font-weight: 700; display: grid; place-items: center; margin-top: 1px; }
.sol-t { font-weight: 600; font-size: var(--t-small); }
.sol-b { font-size: var(--t-small); color: var(--text-dim); margin-top: 3px; line-height: 1.5; }
.sol-b a { color: var(--brand); }
.sol-qr { display: flex; justify-content: center; margin: 9px 0; }
.sol-qr svg { background: #fff; padding: 6px; border-radius: 8px; }

/* The total on the portfolio page, split into the two places money sits. */
.split-tiles { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
.split-tile { display: block; padding: 12px 12px; border: 1px solid var(--line); border-radius: var(--radius-sm); color: inherit; text-decoration: none; transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease); }
.split-tile:hover { border-color: var(--brand); background: var(--surface-2); }
.split-tile .k { font-size: var(--t-cap); color: var(--text-faint); font-weight: 600; }
.split-tile { min-width: 0; }
.split-tile .v { font-size: var(--t-lead); font-weight: 700; letter-spacing: -.03em; margin-top: 3px; overflow-wrap: anywhere; }
.split-tile .u { font-size: var(--t-cap); margin-top: 2px; }
/* Two tiles side by side leave 84px of room on a small phone, and
   `overflow-wrap: anywhere` then breaks a dollar amount mid-number rather
   than letting it out of the box: "$10,000.0" over "0". One per row below
   400px, where the figure fits whole. */
@media (max-width: 400px) { .split-tiles { grid-template-columns: 1fr; } }

/* A label on the left and a figure on the right, and on a narrow screen the
   figure used to be pushed off the edge rather than wrapping: "97.5% in top 5"
   ran past 320px with nothing to scroll it. Both halves may shrink, the figure
   keeps its own line, and a long label wraps instead of shoving. */
.statline { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 9px 0; border-bottom: 1px solid var(--line); font-size: var(--t-small); min-width: 0; }
.statline:last-child { border-bottom: none; }
/* `anywhere` breaks a word even when it would have fitted on the next line,
   and it lets the element report a min-content width of one character, which
   is what let a two-column grid squeeze to nothing: "Portfolio concentration"
   came out as "concentrat / ion" and "Not available" as "availabl / e".
   `break-word` breaks only a word that cannot fit a line on its own, which is
   the case this was reaching for. */
/* The label never breaks mid-word to make room; a long value wraps instead.
   "Publishes" was being split in half beside "prices · exact timestamps ·
   full portfolio" in the side rail. */
.statline .k { color: var(--text-dim); flex: 0 1 auto; overflow-wrap: normal; word-break: keep-all; }
.statline .v { font-weight: 600; flex: 1 1 auto; min-width: 0; text-align: right; overflow-wrap: break-word; }
.statline .v.na { color: var(--text-faint); font-weight: 400; font-size: var(--t-small); }

.bar { height: 6px; border-radius: 999px; background: var(--surface-3); overflow: hidden; }
.bar > i { display: block; height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--brand), var(--brand-2)); transition: width var(--slow) var(--ease); }

/* --------------------------------------------------------------- hero ----- */
.hero { padding: 34px 0 26px; }
.hero h1 {
  font-size: var(--t-hero); line-height: 1.02; letter-spacing: -.04em; font-weight: 700;
}
.hero h1 .grad {
  background: linear-gradient(105deg, var(--brand) 10%, var(--brand-2) 90%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.hero p.lede { font-size: var(--t-lead); color: var(--text-dim); max-width: 560px; margin-top: 16px; }
.hero .cta { display: flex; gap: 10px; margin-top: 24px; flex-wrap: wrap; }

.statstrip { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px; background: var(--line); border-radius: var(--radius); overflow: hidden; }
@media (min-width: 700px) { .statstrip { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.statstrip .s { background: var(--surface); padding: 16px; }
.statstrip .s .v { font-size: var(--t-title); font-weight: 700; letter-spacing: -.03em; }
.statstrip .s .k { font-size: var(--t-cap); color: var(--text-faint); margin-top: 2px; }

/* ---------------------------------------------------------------- feed ---- */
.feedwrap { max-width: 480px; margin: 0 auto; }
.feedcard {
  border-radius: var(--radius-lg); padding: 18px; border: 1px solid var(--line);
  background: linear-gradient(165deg, var(--surface-2), var(--surface));
  box-shadow: var(--shadow-sm); margin-bottom: 16px; position: relative; overflow: hidden;
}
.feedcard::before {
  content: ''; position: absolute; inset: -60% 40% 60% -20%; pointer-events: none;
  background: radial-gradient(circle, var(--brand-soft), transparent 65%); opacity: .5;
}
.feedcard > * { position: relative; }
.feedcard .big { font-size: var(--t-hero); font-weight: 700; letter-spacing: -.045em; line-height: 1; }
.feedcard .tag {
  display: inline-flex; align-items: center; gap: 6px; font-size: var(--t-cap); font-weight: 700;
  text-transform: uppercase; letter-spacing: .07em; color: var(--brand-2);
}

/* --------------------------------------------------------------- charts --- */
.chartbox { position: relative; width: 100%; }
.chartbox svg { width: 100%; height: auto; overflow: visible; }
.chart-legend { display: flex; gap: 16px; font-size: var(--t-cap); color: var(--text-dim); }
.chart-legend i { width: 8px; height: 8px; border-radius: 2px; display: inline-block; margin-right: 6px; }
.sparkline { width: 88px; height: 30px; flex: none; }

/* --------------------------------------------------------------- tables --- */
.tbl { width: 100%; border-collapse: collapse; font-size: var(--t-small); }
.tbl th {
  text-align: left; font-size: var(--t-cap); text-transform: uppercase; letter-spacing: .06em;
  color: var(--text-faint); font-weight: 600; padding: 9px 10px; border-bottom: 1px solid var(--line); white-space: nowrap;
}
.tbl td { padding: 11px 10px; border-bottom: 1px solid var(--line); }
.tbl tr:last-child td { border-bottom: none; }
.tbl tbody tr:hover { background: var(--surface-2); }
.tbl .r { text-align: right; }
.scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* --------------------------------------------------------------- trades --- */
.trade { display: flex; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--line); }
.trade:last-child { border-bottom: none; }
.trade .act {
  width: 42px; height: 42px; border-radius: 12px; display: grid; place-items: center; flex: none;
  font-size: var(--t-cap); font-weight: 700; letter-spacing: .02em;
}
.act.buy { background: var(--pos-soft); color: var(--pos); }
.act.sell { background: var(--neg-soft); color: var(--neg); }
.act.up { background: rgba(34,211,238,.12); color: var(--brand-2); }
.act.down { background: var(--warn-soft); color: var(--warn); }
.timing {
  display: grid; grid-template-columns: auto 1fr; gap: 3px 12px; font-size: var(--t-cap);
  padding: 12px 12px; background: var(--surface-2); border-radius: var(--radius-sm); margin-top: 8px;
}
.timing .k { color: var(--text-faint); min-width: 0; }
.timing .v { color: var(--text-dim); min-width: 0; overflow-wrap: break-word; }
/* A transaction reference is one long token with nothing to break on, so it is
   the one value here allowed to break mid-string rather than run off the
   screen. */
.timing .v.mono { overflow-wrap: anywhere; }
@media (max-width: 559px) {
  .timing { grid-template-columns: 1fr; gap: 0 0; }
  .timing .k { margin-top: 7px; }
  .timing .k:first-child { margin-top: 0; }
  .timing .v { color: var(--text); margin-bottom: 1px; }
}

/* --------------------------------------------------------------- inputs --- */
.field { margin-bottom: 13px; }
.field label { display: block; font-size: var(--t-cap); color: var(--text-dim); margin-bottom: 6px; font-weight: 600; }
.input, select.input, textarea.input {
  width: 100%; padding: 12px 12px; border-radius: 12px; background: var(--surface-2);
  border: 1px solid var(--line); color: var(--text); outline: none; transition: var(--fast) var(--ease); font-size: var(--t-lead);
}
/* Anything under 16px in a field makes iOS Safari zoom the whole page on focus. */
.input:focus { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }
.input::placeholder { color: var(--text-faint); }
select.input { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M3 4.5L6 8l3-3.5' stroke='%238A94A8' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 12px center; padding-right: 32px; }
.searchbar { position: relative; }
.searchbar .input { padding-left: 40px; height: 46px; border-radius: 12px; }
.searchbar svg { position: absolute; left: 13px; top: 50%; transform: translateY(-50%); width: 18px; height: 18px; color: var(--text-faint); }

.switch { width: 42px; height: 25px; border-radius: 999px; background: var(--surface-3); position: relative; transition: var(--base) var(--ease); flex: none; border: 1px solid var(--line); }
.switch::after { content: ''; position: absolute; top: 2px; left: 2px; width: 19px; height: 19px; border-radius: 50%; background: var(--text-faint); transition: var(--base) var(--ease); }
.switch.on { background: var(--brand); border-color: transparent; }
.switch.on::after { transform: translateX(17px); background: #fff; }

input[type='range'] { -webkit-appearance: none; width: 100%; height: 6px; border-radius: 999px; background: var(--surface-3); outline: none; }
input[type='range']::-webkit-slider-thumb { -webkit-appearance: none; width: 18px; height: 18px; border-radius: 50%; background: var(--brand); cursor: pointer; box-shadow: 0 2px 8px rgba(79, 123, 255,.5); }

/* ---------------------------------------------------------------- modal --- */
.modal-bg {
  position: fixed; inset: 0; z-index: 200; background: rgba(4, 5, 8, .68);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  display: flex; align-items: flex-end; justify-content: center;
  animation: fade var(--fast) var(--ease);
  /* A blurred backdrop behind a scrolling panel is re-composited on every
     frame of that scroll, and on some machines that shows as the card
     flickering. Its own layer, declared up front, keeps it stable. */
  will-change: backdrop-filter; transform: translateZ(0);
}
@keyframes fade { from { opacity: 0; } }
@media (min-width: 640px) { .modal-bg { align-items: center; } }
.modal {
  background: var(--bg-elev); border: 1px solid var(--line); width: 100%; max-width: 480px;
  border-radius: 18px 18px 0 0; padding: 18px; padding-bottom: calc(18px + env(safe-area-inset-bottom, 0px)); max-height: 90vh; max-height: 90dvh; overflow-y: auto;
  /* Dragging past the end of a sheet scrolled the page behind it, and on iOS
     could fire pull-to-refresh, including on the legal gate, which is the one
     dialog somebody cannot dismiss. The reader inside already did this; the
     sheet around it did not. */
  overscroll-behavior: contain;
  animation: slideup var(--base) var(--ease); box-shadow: var(--shadow);
}
@media (min-width: 640px) { .modal { border-radius: 18px; } .modal:has(.story-panel) { max-width: 760px; } }
.clamp2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
@keyframes slideup { from { transform: translateY(24px); opacity: 0; } }
.modal h3 { font-size: var(--t-lead); margin-bottom: 4px; }

.toasts { position: fixed; left: 50%; transform: translateX(-50%); bottom: calc(var(--nav-h) + 16px + env(safe-area-inset-bottom, 0px)); z-index: 300; display: flex; flex-direction: column; gap: 8px; width: calc(100% - 32px); max-width: 420px; }
@media (min-width: 1024px) { .toasts { bottom: 20px; left: auto; right: 20px; transform: none; } }
.toast {
  background: var(--surface-3); border: 1px solid var(--line-strong); border-radius: 12px;
  padding: 12px 14px; font-size: var(--t-small); box-shadow: var(--shadow); animation: slideup var(--base) var(--ease);
  display: flex; gap: 10px; align-items: flex-start;
}
.toast.good { border-left: 3px solid var(--pos); }
.toast.bad { border-left: 3px solid var(--neg); }
.toast.warn { border-left: 3px solid var(--warn, #d9932b); }

/* ---------------------------------------------------------------- misc ---- */
.disclaimer { font-size: var(--t-cap); color: var(--text-faint); line-height: 1.5; }
/* A centred disclaimer is a block, not a line. Left to the full width of a
   desktop page it ran to the last word and dropped "results." underneath on
   its own, which reads as a mistake rather than as centring. Bounded and
   balanced, it sits as two even lines under the middle of the page. */
.disclaimer.center {
  max-width: 62ch; margin-inline: auto; text-wrap: balance;
}
.notice {
  display: flex; gap: 12px; padding: 12px 12px; border-radius: 12px; font-size: var(--t-small);
  background: var(--surface-2); border: 1px solid var(--line); color: var(--text-dim);
}
.notice.warn { background: var(--warn-soft); border-color: transparent; color: var(--warn); }
.notice.info { background: var(--brand-soft); border-color: transparent; color: var(--text-dim); }
.notice svg { width: 17px; height: 17px; flex: none; margin-top: 1px; }

.skel { background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%); background-size: 400% 100%; animation: shimmer calc(var(--slow) * 3.5) var(--ease-soft) infinite; border-radius: 12px; }
/* Shaped like the row it stands in for, so nothing moves when the real one
   arrives and the wait reads as assembly rather than as breakage. */
.skelcard { display: flex; align-items: center; gap: 12px; padding: 12px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); }
.skel-avatar { width: 40px; height: 40px; border-radius: 50%; flex: none; }
.skel-lines { display: flex; flex-direction: column; gap: 8px; flex: 1; min-width: 0; }
.skel-line { height: 12px; display: block; }
.skel-sub { height: 10px; opacity: .65; }
.skel-figure { width: 72px; height: 26px; flex: none; }
@media (max-width: 520px) { .skel-figure { width: 52px; } }
@keyframes shimmer { from { background-position: 100% 0; } to { background-position: -100% 0; } }

/* The skeleton hands over to the real thing with a short rise, so the content
   landing reads as the wait settling rather than a hard swap. The global
   reduced-motion rule above turns it off with everything else. */
.fill-in { animation: fillin var(--base) var(--ease); }
@keyframes fillin { from { opacity: 0; transform: translateY(2px); } }

.empty { text-align: center; padding: 44px 20px; color: var(--text-faint); }
.empty svg { width: 42px; height: 42px; margin: 0 auto 12px; opacity: .45; }

.ticker {
  display: flex; gap: 8px; align-items: center; font-size: var(--t-cap); color: var(--text-dim);
  padding: 8px 12px; background: var(--surface-2); border-radius: 12px; border: 1px solid var(--line);
}
.pulse { width: 8px; height: 8px; border-radius: 50%; background: var(--pos); flex: none; animation: blink calc(var(--slow) * 4) var(--ease-soft) infinite; }
@keyframes blink { 0%,100% { opacity: 1; } 50% { opacity: .3; } }

.tabbar { display: flex; gap: 2px; background: var(--surface-2); padding: 6px; border-radius: 12px; border: 1px solid var(--line); }
.tabbar button { flex: 1; padding: 8px 12px; border-radius: 12px; font-size: var(--t-small); font-weight: 600; color: var(--text-dim); transition: var(--fast) var(--ease); }
/* The chosen option is filled, not faintly tinted: in the light theme the
   old white-on-off-white pill could not be told from its neighbour, so a
   person could not see whether CAD or USD, Light or Dark, was on. */
.tabbar button.on { background: var(--text); color: var(--bg); box-shadow: var(--shadow-sm); }

.progress-ring { transform: rotate(-90deg); }
.score-big { font-size: var(--t-hero); font-weight: 700; letter-spacing: -.04em; line-height: 1; }

/* One column on a phone, and a column that cannot grow past the screen: a
   grid track defaults to the width of its widest child, and a grid item to
   min-width:auto, which together pushed the trader page off the side of an
   iPhone. */
.split { display: grid; gap: 16px; grid-template-columns: minmax(0, 1fr); }
.split > * { min-width: 0; }
@media (min-width: 980px) { .split { grid-template-columns: minmax(0, 1.65fr) minmax(0, 1fr); align-items: start; } }
/* Sticky, with its own scroll, only where there are two columns. On a phone it
   is a block in the page's own flow: a nested scroller there traps the thumb. */
@media (min-width: 980px) { .sticky-side { position: sticky; top: 74px; align-self: start; max-height: calc(100vh - 90px); overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; } }

.footer { border-top: 1px solid var(--line); margin-top: 44px; padding: 24px 0 30px; color: var(--text-faint); font-size: var(--t-cap); }
.footer a { color: var(--text-dim); }
.footer .links { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 12px; }

/* ---------------------------------------------------------- onboarding --- */
.onboard { max-width: 940px; margin: 0 auto; padding: 40px 20px 60px; }
.onboard-head { text-align: center; margin-bottom: 34px; }
.onboard-head h1 { font-size: var(--t-hero); margin: 16px 0 6px; }
.brandmark.lg {
  width: 52px; height: 52px; border-radius: 12px; margin: 0 auto;
  display: grid; place-items: center; font-weight: 700; font-size: var(--t-title); color: #fff;
  background: linear-gradient(140deg, var(--brand), var(--brand-2));
}
/* min(300px, 100%): a track can never be wider than the screen, so the cards
   are whole at 320px rather than cut off at the right edge. */
.choice-grid { display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); }
.choice {
  text-align: left; padding: 18px; border-radius: var(--r-lg); cursor: pointer;
  background: var(--surface); border: 1px solid var(--line); color: inherit;
  font: inherit; transition: border-color var(--fast) var(--ease), transform var(--fast) var(--ease), background var(--fast) var(--ease);
  display: flex; flex-direction: column;
}
.choice:hover:not(:disabled) { border-color: var(--brand); }
.choice:disabled { opacity: .6; cursor: default; }
.choice.accent { background: linear-gradient(155deg, var(--brand-soft), var(--surface)); }
.choice h2 { font-size: var(--t-title); margin: 14px 0 8px; }
.choice-icon { color: var(--brand); }
.choice p { font-size: var(--t-small); line-height: 1.6; margin: 0 0 14px; }
.choice-cta { margin-top: auto; padding-top: 16px; color: var(--brand); font-weight: 600; font-size: var(--t-small);
  display: inline-flex; align-items: center; gap: 6px; }

.ticks { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; }
.ticks li { position: relative; padding-left: 22px; font-size: var(--t-small); color: var(--text-dim); line-height: 1.5; }
.ticks li::before {
  content: ''; position: absolute; left: 4px; top: 6px; width: 5px; height: 9px;
  border: solid var(--pos); border-width: 0 2px 2px 0; transform: rotate(42deg);
}
.ticks.tight li { font-size: var(--t-small); gap: 6px; }
.ticks.muted li::before { border-color: var(--text-faint); }

/* The inverse of .ticks — what a plan explicitly does NOT grant. */
.crosses { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; }
.crosses li { position: relative; padding-left: 22px; font-size: var(--t-small); color: var(--text-faint); line-height: 1.5; }
.crosses li::before,
.crosses li::after {
  content: ''; position: absolute; left: 3px; top: 9px; width: 10px; height: 1.5px;
  background: var(--text-faint); border-radius: 2px;
}
.crosses li::before { transform: rotate(45deg); }
.crosses li::after { transform: rotate(-45deg); }

/* -------------------------------------------------------------- plans ---- */
.plan-grid { display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(min(272px, 100%), 1fr)); }
.plan {
  position: relative; padding: 18px 18px; border-radius: var(--r-lg);
  background: var(--surface); border: 1px solid var(--line); display: flex; flex-direction: column;
}
.plan.featured { border-color: var(--brand); background: linear-gradient(160deg, var(--brand-soft), var(--surface)); }
.plan-badge {
  position: absolute; top: -12px; right: 18px; padding: 2px 12px; border-radius: 6px;
  background: var(--brand); color: #fff; font-size: var(--t-cap); font-weight: 700; letter-spacing: .01em;
}
.plan-name { font-size: var(--t-body); font-weight: 600; margin-bottom: 6px; }
.plan-price { font-size: var(--t-hero); font-weight: 700; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.plan-price .faint { font-size: var(--t-body); font-weight: 600; }
.plan .ticks { flex: 1; }
.split-lite { display: grid; gap: 22px; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.page.narrow { max-width: 780px; margin: 0 auto; }

@media (max-width: 560px) {
  .onboard { padding: 24px 14px 48px; }
  .onboard-head h1 { font-size: var(--t-title); }
  .plan-price { font-size: var(--t-hero); }
}

/* ======================================================================
   Redesign pass — calmer, higher-contrast, more whitespace, mobile-first.
   Trust comes from restraint: fewer gradients, clearer hierarchy, one accent.
   ====================================================================== */
:root {
  --brand: #6C63FF;
  --brand-2: #22D3EE;
  --brand-soft: rgba(79, 123, 255, 0.13);
  --radius: 12px;
  --radius-sm: 12px;
  --radius-lg: 18px;
  --shadow-sm: 0 2px 10px rgba(0, 0, 0, 0.22);
}
[data-theme='light'] { --brand: #2563EB; --brand-soft: rgba(37, 99, 235, 0.12); }

.btn-primary { background: var(--brand); box-shadow: none; }
.btn-primary:hover { filter: brightness(1.06); box-shadow: 0 6px 18px rgba(79, 123, 255,.28); }
h1 { letter-spacing: -.025em; font-weight: 700; }
h2 { letter-spacing: -.015em; font-weight: 700; }
.eyebrow { font-size: var(--t-cap); text-transform: uppercase; letter-spacing: .08em; color: var(--text-faint); font-weight: 600; margin-bottom: 6px; }

/* ------------------------------------------------------------ the hub --- */
.hub-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 20px; }
.hub-head h1 { font-size: var(--t-title); margin: 0; }

.mode-grid { display: grid; gap: 14px; grid-template-columns: 1fr; }
@media (min-width: 720px) { .mode-grid { grid-template-columns: 1fr 1fr; } }
.mode-card {
  text-align: left; font: inherit; color: inherit; cursor: pointer; display: flex; flex-direction: column;
  padding: 18px; border-radius: var(--radius-lg); background: var(--surface); border: 1px solid var(--line);
  transition: border-color var(--fast) var(--ease), transform var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.mode-card:hover { border-color: var(--line-strong); }
.mode-card.on { border-color: var(--brand); box-shadow: 0 0 0 4px var(--brand-soft); }
.mode-card.busy { opacity: .6; pointer-events: none; }
.mode-top { display: flex; justify-content: space-between; align-items: center; }
.mode-icon { width: 44px; height: 44px; border-radius: 12px; display: grid; place-items: center; background: var(--brand-soft); color: var(--brand); }
.mode-title { font-size: var(--t-title); font-weight: 700; letter-spacing: -.02em; margin-top: 14px; }
.mode-sub { color: var(--text-dim); font-size: var(--t-small); margin-top: 3px; }
.mode-cta { margin-top: auto; padding-top: 16px; font-weight: 600; font-size: var(--t-small); color: var(--brand); display: inline-flex; align-items: center; gap: 6px; }
.mode-card.on .mode-cta { color: var(--pos); }

/* Choosing a sportsbook is one tap on the book itself, not a dropdown, a
   checkbox and a Save. Tiles wide enough to hit on a phone, two or three to a
   row as the card allows. */
.book-grid { display: grid; gap: 8px; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.book-tile {
  display: flex; align-items: center; gap: 8px; font: inherit; color: inherit; cursor: pointer; text-align: left;
  padding: 12px 12px; border-radius: 12px; background: var(--surface); border: 1px solid var(--line);
  font-weight: 600; font-size: var(--t-small); transition: var(--fast) var(--ease); min-height: 46px;
}
.book-tile:hover:not(:disabled) { border-color: var(--brand); }
.book-tile.on { border-color: var(--brand); background: var(--brand-soft); color: var(--brand); }
.book-tile:disabled { opacity: .55; cursor: default; }

.style-grid { display: grid; gap: 12px; grid-template-columns: 1fr; }
@media (min-width: 560px) { .style-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1100px) { .style-grid { grid-template-columns: repeat(4, 1fr); } }
.style-card {
  text-align: left; font: inherit; color: inherit; cursor: pointer; padding: 18px; border-radius: var(--radius);
  background: var(--surface); border: 1px solid var(--line); transition: var(--fast) var(--ease); display: flex; flex-direction: column;
}
.style-card:hover:not(:disabled) { border-color: var(--brand); box-shadow: var(--shadow-sm); }
.style-card:disabled { opacity: .5; cursor: default; }
.style-emoji { font-size: var(--t-title); line-height: 1; }
.style-title { font-size: var(--t-lead); font-weight: 700; margin-top: 12px; letter-spacing: -.01em; }
.style-sub { font-size: var(--t-small); color: var(--text-dim); margin-top: 3px; line-height: 1.45; }
.style-stats { display: flex; gap: 14px; margin-top: 14px; font-size: var(--t-cap); color: var(--text-faint); }
.style-stats b { color: var(--text); font-size: var(--t-body); margin-right: 3px; }

/* ------------------------------------------------- real-money checklist --- */
.progress { height: 6px; border-radius: 999px; background: var(--surface-3); overflow: hidden; }
.progress > div { height: 100%; background: var(--brand); border-radius: 999px; transition: width var(--slow) var(--ease); }
.steps { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 10px; }
.step { display: flex; gap: 12px; padding: 12px; border-radius: var(--radius); background: var(--surface); border: 1px solid var(--line); }
.step.done { border-color: rgba(47, 217, 138, .35); }
.step-mark {
  flex: 0 0 30px; width: 30px; height: 30px; border-radius: 999px; display: grid; place-items: center;
  background: var(--surface-3); color: var(--text-dim); font-weight: 700; font-size: var(--t-small);
}
.step.done .step-mark { background: var(--pos-soft); color: var(--pos); }
.step-title { font-weight: 600; font-size: var(--t-body); }

/* ------------------------------------------------------------ mobile --- */
@media (max-width: 560px) {
  .hub-head h1 { font-size: var(--t-title); }
  .mode-card { padding: 18px; }
  .mode-title { font-size: var(--t-lead); }
  .step { padding: 13px; gap: 11px; }
}


/* ---------------------------------------------------------------- brand --- */
.logo { display: inline-flex; align-items: center; gap: 10px; text-decoration: none; color: var(--text); }
.logo-mark {
  width: 34px; height: 34px; border-radius: 12px; display: grid; place-items: center; color: #fff;
  background: linear-gradient(145deg, var(--brand), var(--brand-2)); box-shadow: 0 4px 14px rgba(79,123,255,.3);
}
.logo-word { display: flex; flex-direction: column; line-height: 1.05; font-weight: 700; letter-spacing: -.02em; font-size: var(--t-lead); }
.logo-word small { font-size: var(--t-cap); font-weight: 600; letter-spacing: .09em; text-transform: uppercase; color: var(--text-faint); margin-top: 2px; }
@media (max-width: 420px) { .logo-word small { display: none; } }
.brandmark.lg { color: #fff; }

.site-foot { margin-top: auto; padding: 14px 12px 6px; font-size: var(--t-cap); color: var(--text-faint); letter-spacing: .01em; }

/* =====================================================================
   Meror identity. An editor's dark theme and its paper-white twin: ink and
   panel greys, amber like a string, teal like a keyword, git-green and
   git-red for gains and losses, monospace for labels and figures. The hero
   carries a soft glow that follows a fine pointer and a dotted grid that
   only shows near it — quiet, not tacky.
   ===================================================================== */
:root {
  --bg: #0B0E14; --bg-elev: #0F131B; --surface: #121722; --surface-2: #171D2A; --surface-3: #1E2634;
  --line: rgba(230, 237, 243, 0.08); --line-strong: rgba(230, 237, 243, 0.16);
  --text: #E6EDF3; --text-dim: #A8B3BF; --text-faint: #8B949E;
  --brand: #4F7BFF; --brand-2: #39E0CF; --brand-soft: rgba(79, 123, 255, 0.14);
  --pos: #3FB950; --pos-soft: rgba(63, 185, 80, 0.13);
  --neg: #F85149; --neg-soft: rgba(248, 81, 73, 0.13);
  --warn: #D29922; --warn-soft: rgba(210, 153, 34, 0.14);
  --gold: #E3B341; --violet: #7FD6D0; --ink-on-brand: #FFFFFF;
  --radius: 12px; --radius-sm: 12px; --radius-lg: 18px;
  --shadow: 0 24px 60px rgba(0, 0, 0, 0.5); --shadow-sm: 0 6px 20px rgba(0, 0, 0, 0.3);
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, "Liberation Mono", monospace;
}
[data-theme='light'] {
  --bg: #F6F8FA; --bg-elev: #FFFFFF; --surface: #FFFFFF; --surface-2: #F0F3F6; --surface-3: #E6EAEF;
  --line: rgba(31, 35, 40, 0.10); --line-strong: rgba(31, 35, 40, 0.18);
  --text: #1F2328; --text-dim: #4B535D; --text-faint: #57606A;
  --brand: #2563EB; --brand-2: #12A5A0; --brand-soft: rgba(37, 99, 235, 0.11);
  --pos: #1A7F37; --pos-soft: rgba(26, 127, 55, 0.11);
  --neg: #CF222E; --neg-soft: rgba(207, 34, 46, 0.11);
  --warn: #9A6700; --warn-soft: rgba(154, 103, 0, 0.12);
  --gold: #9A6700; --violet: #0E8F8B; --ink-on-brand: #FFFFFF;
  --shadow: 0 24px 60px rgba(31, 35, 40, 0.12); --shadow-sm: 0 6px 20px rgba(31, 35, 40, 0.08);
}
body { font-size: var(--t-body); line-height: 1.55; }
h1, h2, h3, h4 { font-weight: 600; letter-spacing: -0.028em; }
.page h1 { font-size: var(--t-title); }
.section-head h2 { font-size: var(--t-title); }
.section-head { margin: 34px 0 14px; }
.num, .statstrip .s .v, .lb-stats, .rank { font-family: var(--mono); font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
/* Not .statline .k: that is a label in words ("Two-factor authentication"),
   and in monospace it read like a line of code. */
.statstrip .s .k { font-family: var(--mono); }
.badge { font-weight: 600; }
.card {
  background: linear-gradient(180deg, var(--surface), var(--bg-elev));
  border: 1px solid var(--line); box-shadow: none;
  transition: border-color var(--fast) var(--ease), transform var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
a.card:hover, .card.hoverable:hover { border-color: var(--line-strong); box-shadow: var(--shadow-sm); }
.btn { font-family: var(--sans); }
.btn-primary { background: linear-gradient(135deg, #5C86FF, #4F6BFF); color: var(--ink-on-brand); box-shadow: 0 8px 24px rgba(79, 123, 255, 0.25); font-weight: 600; }
.btn-primary:hover { filter: brightness(1.05); box-shadow: 0 10px 28px rgba(79, 123, 255, 0.34); }
.btn-outline { border-color: var(--line-strong); }
.btn-outline:hover { border-color: var(--brand); color: var(--brand); }
.badge.red { background: transparent; border: 1px solid rgba(248, 81, 73, .45); color: var(--neg); }
.badge.amber { background: transparent; border: 1px solid rgba(210, 153, 34, .5); color: var(--warn); }
.badge.violet { background: transparent; border: 1px solid rgba(57, 224, 207, .45); color: var(--violet); }
.badge.blue { background: rgba(79, 123, 255, .13); color: var(--brand); }
.rank.r1 { background: linear-gradient(140deg, #EEC257, #D8A128); color: #1A1204; }
.rank.r2 { background: linear-gradient(140deg, #D9DEE6, #AEB6C4); color: #1b1f28; }
.rank.r3 { background: linear-gradient(140deg, #D2925F, #B57742); color: #241203; }
.logo-mark, .brandmark.lg { background: linear-gradient(140deg, #39E0CF, #4F7BFF); color: #06201D; }
.section-head .link, a.link { color: var(--brand); }

/* ---- hero -------------------------------------------------------------- */
.hero { position: relative; padding: 44px 0 26px; --mx: 50%; --my: 30%; }
.hero .dots { position: absolute; inset: -12% -8%; pointer-events: none; }
.hero .dots {
  background-image: radial-gradient(rgba(230, 237, 243, 0.3) 1px, transparent 1.2px);
  background-size: 22px 22px;
  -webkit-mask-image: radial-gradient(360px circle at var(--mx) var(--my), #000 0%, rgba(0,0,0,.4) 40%, transparent 72%);
          mask-image: radial-gradient(360px circle at var(--mx) var(--my), #000 0%, rgba(0,0,0,.4) 40%, transparent 72%);
  opacity: .4;
}
[data-theme='light'] .hero .dots { background-image: radial-gradient(rgba(31, 35, 40, 0.3) 1px, transparent 1.2px); }
.hero > *:not(.dots) { position: relative; }
.hero .eyebrow { font-size: var(--t-cap); letter-spacing: .1em; text-transform: uppercase; color: var(--text-faint); margin-bottom: 14px; }
/* Display type: the landing headline grows with the window from the hero
   size to 56px. One of three named exceptions to the six sizes
   (tests/styleBudget.test.js). */
.hero h1 { font-size: clamp(var(--t-hero), 5vw, 56px); line-height: 1.06; max-width: 820px; }
.hero h1 em { font-style: normal; color: var(--brand); }
.hero p.lede { font-size: var(--t-lead); line-height: 1.6; max-width: 600px; margin-top: 18px; color: var(--text-dim); }
.hero .cta { margin-top: 26px; }
.hero .disclaimer { margin-top: 18px; max-width: 560px; }

@media (pointer: coarse) { .hero .dots { -webkit-mask-image: none; mask-image: none; opacity: .18; } }

/* ---- what copying looks like ------------------------------------------ */
.proof { display: grid; gap: 12px; grid-template-columns: 1fr; margin-top: 22px; }
/* Two up on a tablet, then every card in one row: the panel tells the grid how many it holds. */
@media (min-width: 560px) { .proof { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 960px) { .proof { grid-template-columns: repeat(var(--n, 4), minmax(0, 1fr)); } }
.hero .proof-note { margin: 18px 0 0; max-width: 640px; font-size: var(--t-cap); line-height: 1.55; color: var(--text-faint); }
.proof .p { padding: 12px 18px; border-radius: var(--radius); border: 1px solid var(--line); background: linear-gradient(180deg, var(--surface), var(--bg-elev)); transition: border-color var(--fast) var(--ease), transform var(--fast) var(--ease); }
.proof .p:hover { border-color: var(--line-strong); }
.proof .p .who { font-size: var(--t-small); color: var(--text-dim); display: flex; align-items: center; gap: 8px; min-width: 0; }
.proof .p .who .n { font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.proof .p .then { font-size: var(--t-cap); color: var(--text-faint); margin-top: 10px; font-variant-numeric: tabular-nums; }
.proof .p .now { font-size: var(--t-lead); line-height: 1.1; margin-top: 2px; letter-spacing: -.02em; font-weight: 700; font-family: inherit; font-variant-numeric: tabular-nums; }
.proof .p .d { font-size: var(--t-cap); margin-top: 4px; font-variant-numeric: tabular-nums; }
/* A card is a div now, so it can hold its Copy on paper button: the face and
   the name are the link. The button sits at the foot of every card, level
   across a row however long the lines above it run. */
.proof .p { display: flex; flex-direction: column; }
.proof .p .who-link { display: flex; align-items: center; gap: 8px; min-width: 0; color: inherit; }
.proof .p .d { margin-bottom: 12px; }
.proof .p [data-copy] { margin-top: auto; align-self: flex-start; }
.hero .hero-brokers { margin: 14px 0 0; max-width: 600px; font-size: var(--t-small); line-height: 1.5; color: var(--text-dim); }
/* On a phone the cards are one row that scrolls sideways, not a stack: a
   stack put the second card below the first screen. The row bleeds to the
   glass like .chipscroll, which is what says it scrolls; each card leaves the
   next one showing at its edge. */
@media (max-width: 559px) {
  .proof { display: flex; gap: 10px; overflow-x: auto; scrollbar-width: none; margin-inline: -20px; padding: 2px 20px 6px; overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch; }
  .proof::-webkit-scrollbar { display: none; }
  .proof .p { flex: 0 0 min(84%, 300px); }
}
.trust { display: flex; flex-wrap: wrap; gap: 8px 18px; margin-top: 18px; font-size: var(--t-small); color: var(--text-dim); }
.trust span::before { content: '·'; color: var(--brand); margin-right: 8px; font-weight: 700; }

/* ---- futurist touches, kept quiet --------------------------------------- */
/* Same reason as the modal backdrop: a blurred bar the page scrolls under is
   the commonest cause of a page that shimmers while it moves. */
.topbar { backdrop-filter: saturate(140%) blur(10px); -webkit-backdrop-filter: saturate(140%) blur(10px); border-bottom: 1px solid var(--line); will-change: backdrop-filter; transform: translateZ(0); }
.topbar::after { content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px; background: linear-gradient(90deg, transparent, rgba(79, 123, 255, .6), rgba(139, 123, 255, .6), transparent); opacity: .7; pointer-events: none; }
.card:hover { border-color: rgba(79, 123, 255, .4); }
.btn-primary { position: relative; }
.btn-primary::after { content: ''; position: absolute; inset: -2px; border-radius: inherit; box-shadow: 0 0 0 0 rgba(79, 123, 255, .45); transition: box-shadow var(--slow) var(--ease); pointer-events: none; }
.btn-primary:hover::after { box-shadow: 0 0 0 4px rgba(79, 123, 255, .16); }
.badge.green.live::before, .livedot::before { content: ''; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--pos); margin-right: 6px; vertical-align: 1px; box-shadow: 0 0 0 0 rgba(63,185,80,.6); animation: livepulse calc(var(--slow) * 4) var(--ease-soft) infinite; }
@keyframes livepulse { 0% { box-shadow: 0 0 0 0 rgba(63,185,80,.6); } 70% { box-shadow: 0 0 0 7px rgba(63,185,80,0); } 100% { box-shadow: 0 0 0 0 rgba(63,185,80,0); } }
.statstrip { border-radius: var(--radius); overflow: hidden; border: 1px solid var(--line); }
.statstrip .s .v { color: var(--text); }
.statstrip .s:first-child .v { color: var(--brand); }
::selection { background: rgba(79, 123, 255, .3); }

/* ---- page glow: one faint light that follows the pointer across the whole
   home page, under the header and the sidebar, never over content ------- */
/* A full-viewport gradient layer existed on every page and was composited on
   every scroll even at zero opacity. It belongs to the landing page, so it is
   only there. */
#pageglow { display: none; }
body.glow-on #pageglow {
  display: block;
  position: fixed; inset: 0; z-index: 0; pointer-events: none; opacity: 0; transition: opacity var(--slow) var(--ease);
  background: radial-gradient(760px circle at var(--gx, 50vw) var(--gy, 30vh), rgba(79, 123, 255, 0.05), rgba(57, 224, 207, 0.022) 42%, transparent 66%);
}
body.glow-on #pageglow { opacity: 1; }
/* On white a five percent blue is invisible, so the light theme's glow is a
   little stronger and cooler, and stays a soft light rather than a spotlight. */
/* The light theme cannot be lit with light: a translucent colour laid on an
   off-white page is either invisible or a smudge, and both readings were
   tried. What works is the page's own ground. `--bg` is #F6F7FB, not white,
   so the pointer brings the paper up to white and lets it fall back to the
   off-white away from it, with a whisper of the brand in the falloff. It is
   the same idea as the dark theme's glow, done with the material that is
   actually there. */
/* `body.glow-on #pageglow` is an element, a class and an id; a bare
   `[data-theme='light'] #pageglow` is an attribute and an id, and loses by
   one point. That is why the light theme never showed a glow however the
   colours were tuned: the dark gradient was winning the whole time. Matching
   the class as well settles it. */
[data-theme='light'] body.glow-on #pageglow {
  background:
    radial-gradient(620px circle at var(--gx, 50vw) var(--gy, 30vh), rgba(255, 255, 255, .95), rgba(255, 255, 255, 0) 62%),
    radial-gradient(900px circle at var(--gx, 50vw) var(--gy, 30vh), rgba(37, 99, 235, .07), rgba(34, 211, 238, .05) 45%, transparent 70%);
}
@media (pointer: coarse), (prefers-reduced-motion: reduce) { body.glow-on #pageglow { display: none; } }
/* The glow above sits behind everything, so the page's own layers need a
   stacking context. The top bar and the sidebar already have one from their
   own `position: sticky`, and naming them here overwrote it: both scrolled
   away off the top of a seventeen-thousand-pixel page, on every screen size,
   because this rule comes later at equal specificity. */
#app, main, .page { position: relative; z-index: 1; }
.sidebar { background: color-mix(in srgb, var(--bg-elev) 92%, transparent); }

/* ---- footer links: one quiet line ---------------------------------------- */
.site-foot { font-size: var(--t-cap); line-height: 1.5; }
.site-foot .foot-links { display: flex; flex-wrap: wrap; gap: 2px 6px; margin-top: 6px; }
.site-foot .foot-links a { display: inline; padding: 0; margin: 0; font-size: var(--t-cap); color: var(--text-faint); text-decoration: none; border: 0; background: none; line-height: 1.5; }
.site-foot .foot-links a:hover { color: var(--text); }
.site-foot .foot-links .sep { color: var(--text-faint); opacity: .6; }

/* ---- live balance: an odometer in the corner ------------------------------ */
#livebalance {
  /* Centre, not baseline: the odometer is an inline-flex box with overflow
     hidden, whose baseline is its bottom edge, which dropped the "paper" label
     below the figure. Nowrap keeps the pill on one line at every width. */
  display: inline-flex; align-items: center; flex-wrap: nowrap; white-space: nowrap; gap: 8px; padding: 6px 12px; border-radius: 12px;
  border: 1px solid var(--line); background: color-mix(in srgb, var(--surface) 70%, transparent);
  font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: var(--t-small); font-weight: 600; color: var(--text);
  text-decoration: none; transition: border-color var(--base) var(--ease), color var(--base) var(--ease), background var(--base) var(--ease); margin-right: 4px;
}
#livebalance .lbl { font-size: var(--t-cap); letter-spacing: .08em; text-transform: uppercase; color: var(--text-faint); }
#livebalance .lbl::before { content: ''; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--pos); margin-right: 6px; vertical-align: middle; animation: livepulse calc(var(--slow) * 4) var(--ease-soft) infinite; }
#livebalance.up { color: var(--pos); border-color: rgba(63,185,80,.45); }
#livebalance.down { color: var(--neg); border-color: rgba(248,81,73,.45); }
#livebalance.up, #livebalance.down { animation: settle calc(var(--slow) * 4) var(--ease) forwards; }
@keyframes settle { to { color: var(--text); border-color: var(--line); } }
.odo { display: inline-flex; height: 1.15em; line-height: 1.15em; overflow: hidden; }
.odo .odo-sep { display: inline-block; }
.odo .odo-col { display: inline-block; height: 1.15em; overflow: hidden; width: .62em; text-align: center; }
.odo .odo-strip { display: flex; flex-direction: column; transition: transform var(--slow) var(--ease); will-change: transform; }
.odo .odo-strip span { display: block; height: 1.15em; line-height: 1.15em; }
@media (max-width: 640px) { #livebalance .lbl { display: none; } #livebalance { padding: 5px 8px; font-size: var(--t-small); } }
@media (prefers-reduced-motion: reduce) { .odo .odo-strip { transition: none; } }

/* A figure that moved while somebody was looking at it: a tint in the
   direction it went, which fades back. The whole effect is one animation, so
   the global prefers-reduced-motion rule above removes it and leaves the
   number, which is the part that matters; the ticker then puts the direction
   back as a plain .pos or .neg, which stay.

   --slow, under half a second, not one: a figure repainting once a second at a one
   second fade is never not tinted, which reads as a colour the number has
   rather than a move it just made. */
.tick-up { animation: tickup var(--slow) var(--ease); }
.tick-down { animation: tickdown var(--slow) var(--ease); }
@keyframes tickup { 0% { color: var(--pos); } 100% { color: inherit; } }
@keyframes tickdown { 0% { color: var(--neg); } 100% { color: inherit; } }

/* A holdings row repaints on its own every second while the rest of the table
   does not; containment keeps that repaint inside the row. */
.tbl.holdings tbody tr { contain: content; }

/* A grid item defaults to min-width:auto and will grow past its track to fit
   content; the main column must yield to the track and let the grid inside it
   wrap. Without this the rightmost cards were clipped on desktop. */
main, .shell, .page, #results { min-width: 0; max-width: 100%; }

/* The board chips sit in a flex row; as a flex item the strip must be allowed
   to shrink to its track (min-width:0) so it scrolls inside itself instead of
   pushing the page sideways. On wide screens it simply wraps. */
.chiprow { min-width: 0; flex: 1 1 auto; max-width: 100%; }
.spread > .chiprow { min-width: 0; }
@media (min-width: 760px) { .chiprow { flex-wrap: wrap; overflow: visible; } }

/* A trader card's footer holds badges and the copy button; on a narrow card
   the button drops to its own line rather than running past the edge. */
.tcard-foot { flex-wrap: wrap; row-gap: 8px; }
.tcard-foot .btn { white-space: nowrap; }

/* ---- the reader: documents are accepted only after being scrolled to the end */
.reader-steps { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 12px 0 8px; font-size: var(--t-cap); color: var(--text-faint); }
.reader-steps .on { color: var(--text); font-weight: 600; }
.reader-steps .done { color: var(--pos); }
.reader-head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; flex-wrap: wrap; margin: 8px 0 6px; }
.reader-title { font-weight: 600; font-size: var(--t-small); }
.reader { max-height: min(46vh, 420px); overflow-y: auto; padding: 12px 12px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--surface-2); font-size: var(--t-small); line-height: 1.65; outline: none; overscroll-behavior: contain; }
.reader:focus-visible { border-color: var(--brand); }
.reader h4 { margin: 12px 0 4px; font-size: var(--t-small); }
.reader h4:first-child { margin-top: 0; }
.reader p { margin: 0 0 8px; color: var(--text-dim); }
.reader .reader-end { text-align: center; color: var(--text-faint); font-size: var(--t-cap); margin: 16px 0 0; }
.reader.read { border-color: rgba(63, 185, 80, .5); }
.reader-track { height: 2px; background: var(--surface-3); border-radius: 2px; margin-top: 8px; overflow: hidden; }
/* The one duration in this file that is not on the ladder, deliberately. This
   bar is a reading of where the finger has got to, not a motion of its own: it
   has to track the scroll, so it is linear and short enough to feel attached. */
.reader-bar { height: 100%; width: 0; background: linear-gradient(90deg, var(--brand), var(--brand-2)); transition: width var(--fast) var(--ease); }
.reader-hint { font-size: var(--t-cap); color: var(--text-faint); margin-top: 6px; }
.reader.read + .reader-track + .reader-hint { color: var(--pos); }
.reader-agree input:disabled + span { opacity: .55; }

/* ---- trust pass: quieter surfaces, solid actions, restrained badges ------ */
:root { --radius: 12px; --radius-sm: 8px; --radius-lg: 18px; }
h1, h2, h3, h4 { font-weight: 600; letter-spacing: -0.02em; }
.card { background: var(--surface); border: 1px solid var(--line); }
.card:hover { border-color: var(--line); }
a.card:hover, .card.hoverable:hover, .style-card:hover, .mode-card:hover, .tcard:hover { border-color: rgba(79, 123, 255, .45); box-shadow: var(--shadow-sm); }
.btn-primary { background: var(--brand); box-shadow: none; }
.btn-primary:hover { background: color-mix(in srgb, var(--brand) 88%, #000); filter: none; box-shadow: none; }
.btn-primary::after { display: none; }
/* The caption size is half a pixel larger than the 11.5px badges were; this
   tracking, with a pixel less padding and gap in the rule above, gives the
   width back, so a row of badges that fitted on one line still does. */
.badge { font-weight: 600; letter-spacing: -0.01em; }
.badge.green { background: transparent; border: 1px solid rgba(63, 185, 80, .45); }
.badge.violet { border-color: rgba(57, 224, 207, .45); }
.badge.gold { color: var(--gold); border-color: rgba(240, 194, 75, .45); }
.statstrip .s:first-child .v { color: var(--text); }
.home-foot { margin-top: 44px; padding: 22px 0 8px; border-top: 1px solid var(--line); }
[data-theme='light'] .card { box-shadow: 0 1px 2px rgba(20, 24, 33, .04); }


/* --- launch surface: tooltips, field errors, consent, FAQ, thank-you, 404, related links ------------- */
.tip { position: relative; display: inline-grid; place-items: center; width: 18px; height: 18px; margin-left: 6px; border-radius: 50%;
  color: var(--muted); cursor: help; vertical-align: -3px; outline: none; }
.tip:hover, .tip:focus-visible, .tip.open { color: var(--brand); }
.tip::after { content: attr(data-tip); position: absolute; left: 50%; bottom: calc(100% + 8px); transform: translateX(-50%); width: max-content; max-width: min(280px, 80vw);
  padding: 8px 12px; border-radius: 8px; background: var(--card); color: var(--text); border: 1px solid var(--border); box-shadow: 0 8px 18px rgba(0,0,0,.28);
  font-size: var(--t-cap); font-weight: 400; line-height: 1.45; text-align: left; white-space: normal; z-index: 40; display: none; }
.tip:hover::after, .tip:focus-visible::after, .tip.open::after { display: block; }
@media (hover: none) { .tip:hover::after { display: none; } .tip.open::after { display: block; } }
.field { display: grid; gap: 5px; margin-bottom: 12px; }
.field label { font-size: var(--t-small); font-weight: 600; }
.input.invalid, .input[aria-invalid="true"] { border-color: var(--neg); box-shadow: 0 0 0 3px var(--neg-soft); }
.field-error { font-size: var(--t-cap); color: var(--neg); line-height: 1.4; }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; }
@media (max-width: 640px) { .grid2 { grid-template-columns: 1fr; } }
.contact-form .hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
.consent { position: fixed; left: 16px; right: 16px; bottom: 16px; z-index: 60; display: flex; flex-wrap: wrap; gap: 12px; align-items: center; justify-content: space-between;
  max-width: 760px; margin: 0 auto; padding: 12px 12px; border-radius: 12px; background: var(--bg-elev); border: 1px solid var(--line-strong, var(--line)); box-shadow: 0 12px 40px rgba(0,0,0,.35); isolation: isolate; }
.consent-text { flex: 1 1 360px; font-size: var(--t-small); line-height: 1.5; color: var(--text); }
.consent-actions { display: flex; gap: 8px; }
@media (max-width: 640px) { .consent { left: 10px; right: 10px; bottom: calc(64px + env(safe-area-inset-bottom)); } }
.faq-list { display: grid; gap: 8px; }
.faq-item { border: 1px solid var(--border); border-radius: 12px; background: var(--card); padding: 0 12px; }
.faq-item summary { cursor: pointer; padding: 13px 0; font-weight: 600; list-style: none; display: flex; justify-content: space-between; align-items: center; gap: 12px; }
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after { content: '+'; color: var(--muted); font-weight: 600; font-size: var(--t-lead); line-height: 1; }
.faq-item[open] summary::after { content: '–'; }
.faq-item p { margin: 0 0 14px; color: var(--muted); line-height: 1.6; font-size: var(--t-small); }
.faq-teaser { margin: 36px 0 12px; }
.thanks-mark { width: 56px; height: 56px; margin: 0 auto; border-radius: 12px; display: grid; place-items: center; color: #fff;
  background: linear-gradient(140deg, var(--brand), var(--brand-2)); }
.notfound-code { font-size: var(--t-hero); font-weight: 700; letter-spacing: -.04em; line-height: 1;
  background: linear-gradient(140deg, var(--brand), var(--brand-2)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.notfound code { font-size: var(--t-cap); padding: 2px 6px; border-radius: 6px; background: var(--card); border: 1px solid var(--border); }
.related { display: flex; flex-wrap: wrap; gap: 6px 8px; align-items: center; margin-top: 28px; padding-top: 14px; border-top: 1px solid var(--border); font-size: var(--t-small); }
.related a { color: var(--text); text-decoration: none; } .related a:hover { color: var(--brand); }
.public-foot { margin-top: 36px; }
.public-foot a { color: inherit; }

/* The tab bar is fixed over the bottom of the page below 1024px, and the body's
   padding reserves exactly its height, so the footer's last lines ended up
   sitting against the bar with nothing between them. The footer keeps its own
   gap above the bar, grown by the home-indicator inset on the phones that have
   one, so the disclaimer and the link row stay readable. Above 1024px the bar
   is gone and the footer keeps its original spacing. */
@media (max-width: 1023px) {
  .home-foot, .public-foot { padding-bottom: calc(22px + env(safe-area-inset-bottom, 0px)); }
}
/* Until cookies are answered the consent card is fixed over the bottom of the
   page, so the footer grows by the card's height and its last row can scroll
   clear of it. */
body:has(#consent) .home-foot, body:has(#consent) .public-foot { padding-bottom: calc(22px + var(--consent-h, 0px) + env(safe-area-inset-bottom, 0px)); }

/* Links laid out as a list with room between them; the dots that used to
   separate them read as one run-on line on a phone. */
.home-foot .foot-links, .public-foot .foot-links { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 18px; }
.home-foot .foot-links .sep, .public-foot .foot-links .sep { display: none; }
.home-foot .foot-links a, .public-foot .foot-links a { padding: 2px 0; }
.home-foot .foot-legal, .public-foot .foot-legal { margin-top: 10px; }
@media (max-width: 559px) {
  .home-foot .foot-links, .public-foot .foot-links { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; }
  .home-foot .foot-links a, .public-foot .foot-links a { font-size: var(--t-small); display: flex; align-items: center; min-height: 44px; padding: 0; }
  .home-foot .foot-legal, .public-foot .foot-legal { margin-top: 18px; }
  .home-foot > .spread, .public-foot > .spread { flex-direction: column; align-items: flex-start; gap: 18px; }
  .home-foot .foot-fine, .public-foot .foot-fine { font-size: var(--t-cap); }
}
.home-foot .foot-links a, .public-foot .foot-links a { text-decoration: none; }
.home-foot .foot-links a:hover, .public-foot .foot-links a:hover { color: var(--brand); }

/* Nothing may widen the page: a stray wide element scrolls inside its own box, never the viewport. */
.layout, main.shell { overflow-x: clip; }
@supports not (overflow-x: clip) { .layout, main.shell { overflow-x: hidden; } }

.style-emoji { display: inline-grid; place-items: center; color: var(--brand); }
.style-emoji svg { display: block; }

/* Phone widths: the top bar must never be wider than the screen. The data
   badge moves out of the bar (the pages still say it), the balance counter
   tightens and the icon buttons shrink. */
@media (max-width: 560px) {
  .topbar-in { min-width: 0; gap: 8px; }
  .topbar-in > .badge { display: none; }
  .topbar-actions { gap: 6px; min-width: 0; }
  /* 38, not 32: a 32px control is under every platform's minimum tap target,
     and the bar has the room once the logo word goes at 380px. */
  .iconbtn { width: 38px; height: 38px; }
  #livebalance { font-size: var(--t-cap); padding: 4px 8px; gap: 4px; }
  #livebalance .lbl { display: none; }
}
@media (max-width: 380px) {
  .logo-word { display: none; }
  #livebalance { font-size: var(--t-cap); padding: 4px 6px; }
}

/* The server's crawler-readable copy of a page is for machines and for browsers without
   JavaScript; people with JavaScript see the app's own version, so the copy stays hidden
   until the app replaces it (index.html turns it back on inside <noscript>). */
main[data-seo-body] { visibility: hidden; }

/* board rows: a strict grid, so the name and its meta line truncate instead of
   wrapping under the return figure at narrow widths */
.lb-row { display: grid !important; grid-template-columns: 26px 30px minmax(72px, 1fr) auto; align-items: center; gap: 10px; min-width: 0; container-type: inline-size; }
/* The same row without the rank number in front of it. Four tracks against
   three children put the avatar in the rank's 26px, the name in the avatar's
   30px and the figure in the name's, so a name rendered as "Sr…" beside a
   198px-wide "n/a". */
.lb-row.norank { grid-template-columns: 30px minmax(72px, 1fr) auto; }
/* A board row you can act on. Below twelfth place the list was readable and
   not actionable: every row cost a page load and a trip back to the board.
   The control gets its own track so the name and the figure keep their room,
   and on a narrow row the word goes while the icon, and its tap target, stay. */
.lb-row.withcopy { grid-template-columns: 26px 30px minmax(72px, 1fr) auto auto; }
.lb-row.withcopy.norank { grid-template-columns: 30px minmax(72px, 1fr) auto auto; }
.lb-row .lb-copy { flex: none; gap: 6px; white-space: nowrap; }
/* The line under the figure has a long form and a phone form (components.js
   lbSub); the score stands in for the all-time figure on the narrowest row. */
.lb-row .lb-short, .lb-row .lb-narrow, .lb-row .lb-note { display: none; }
/* A warned row's own line on a phone (lbNote): under the name, no wider than
   the row (76px is the rank and avatar tracks and their gaps; the tracks can
   total more than a 320px screen's row). A figure keeps its "all time". */
.lb-row .lb-note { grid-column: 3 / -1; grid-row: 2; min-width: 0; max-width: calc(100cqw - 76px); color: var(--text-faint); }
.lb-row .lb-note > span { white-space: nowrap; }
.lb-row:has(> .lb-note) { row-gap: 2px; }
@container (max-width: 420px) {
  .lb-row .lb-copy-word { display: none; }
  .lb-row .lb-long { display: none; }
  .lb-row .lb-short { display: inline; }
  .lb-row .lb-note { display: block; }
  .lb-row .lb-copy { padding-inline: 10px; }
  /* Tighter tracks so the control costs the name as little room as possible. */
  .lb-row.withcopy { gap: 8px; }
}
/* A narrow board card: the name keeps its room, the stats give up the period word. */
@container (max-width: 340px) {
  .lb-row .lb-period { display: none; }
  .lb-row .lb-first { display: none; }
  .lb-row .lb-narrow { display: inline; }
  .lb-row .lb-stats .num { font-size: var(--t-body) !important; }
  .lb-row .lb-stats .lb-sub { font-size: var(--t-cap) !important; }
}
/* The "From $X" chip under the score widens the figures' track: smaller on a
   narrow row, and left to the cards on the narrowest, where it would push the
   copy control off the row (measured at 320 and 360 px wide screens). */
@container (max-width: 280px) { .lb-row .lb-from .badge { font-size: var(--t-cap); padding: 1px 5px; } }
@container (max-width: 265px) { .lb-row .lb-from { display: none; } }
/* The boards in one row on a wide screen, two by two below, one on a phone; the two notes beneath them side by side. */
.grid.boards.four { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 16px; }
.grid.two { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px; }
.lb-row > .grow { min-width: 0; }
.lb-row .lb-meta { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: var(--t-cap); color: var(--text-faint); }
/* In a row the freshness label (components.js freshChip, compact) leads the meta
   line as plain text and is never cut: floated, it keeps its width and the rest
   of the line truncates beside it at 320px. */
.lb-row .lb-meta .fresh { float: left; display: block; padding: 0; border: 0; background: none; font-size: inherit; font-weight: inherit; letter-spacing: inherit; white-space: pre; }
.lb-row .lb-meta .fresh.live { font-weight: 600; }
.lb-row .lb-meta .fresh::after { content: ' \00B7  '; color: var(--text-faint); font-weight: 400; }
.lb-row .lb-stats { text-align: right; white-space: nowrap; flex: none; }
.lb-row .lb-stats .num { line-height: 1.15; }

/* explore: the sort strip folds behind one line */
#sortstrip { margin-top: 10px; }

/* compositor layers: the fixed glow and the blurred top bar each get their own,
   so scrolling does not repaint them against each other (the flicker on Mac
   browsers when a backdrop blur sits over a fixed gradient). */
/* Any strip of chips that is hidden is hidden; the rule used to name one id, so the search page's filter strip showed at all times. */
.sortstrip[hidden], #sortstrip[hidden] { display: none; }

/* the tape strip eases in once with the page, never on each update */
#liveticker { animation: none; }

/* folded explanations */
.explain { margin: 12px 0 0; border: 1px solid var(--line); border-radius: 12px; background: var(--surface); }
.explain summary { cursor: pointer; list-style: none; display: flex; align-items: center; gap: 7px; padding: 9px 12px; font-size: var(--t-small); font-weight: 600; color: var(--text-dim); }
.explain summary::-webkit-details-marker { display: none; }
.explain summary::after { content: '+'; margin-left: auto; color: var(--text-faint); font-weight: 600; font-size: var(--t-body); }
.explain[open] summary::after { content: '–'; }
.explain[open] summary { color: var(--text); }
.explain-body { padding: 0 12px 12px; font-size: var(--t-small); line-height: 1.6; color: var(--text-dim); }
.explain-body dl { display: grid; grid-template-columns: max-content 1fr; gap: 6px 14px; margin: 0; }
.explain-body dt { font-weight: 600; color: var(--text); }
.explain-body dd { margin: 0; }
.explain-body p { margin: 0 0 8px; }
@media (max-width: 560px) { .explain-body dl { grid-template-columns: 1fr; gap: 2px 0; } .explain-body dd { margin-bottom: 8px; } }

/* While the page is moving, hover effects do not animate: lifts and border
   transitions firing as cards pass beneath the cursor look like flicker.

   This suppresses the animation, not the input. It used to read
   `main.shell * { pointer-events: none }`, which made every control on the
   page dead for 140 ms after any scroll — filter chips, links, cards, the
   scrollers inside a board — so a tap landing just after a flick did nothing,
   which on a phone is most taps. */
body.scrolling main.shell .card,
body.scrolling main.shell .tcard,
body.scrolling main.shell .lb-row {
  transition: none !important;
}
body.scrolling main.shell .card:hover,
body.scrolling main.shell .tcard:hover,
body.scrolling main.shell .lb-row:hover { transform: none !important; }

/* Hover lifts are for a real mouse. A tap on a phone leaves :hover stuck on
   the card it touched, so the card sat raised after the finger left it. */
@media (hover: hover) and (pointer: fine) {
  .iconbtn:hover { transform: translateY(-1px); }
  .tcard:hover { transform: translateY(-2px); }
  .choice:hover:not(:disabled) { transform: translateY(-2px); }
  .mode-card:hover { transform: translateY(-1px); }
  .book-tile:hover:not(:disabled) { transform: translateY(-1px); }
  .style-card:hover:not(:disabled) { transform: translateY(-2px); }
  a.card:hover, .card.hoverable:hover { transform: translateY(-2px); }
  .proof .p:hover { transform: translateY(-2px); }
  .ops-kpi:hover { transform: translateY(-1px); }
  .oauth-btn:hover:not(:disabled) { transform: translateY(-1px); }
}

/* real money: one segment per step */
.stepbar { height: 6px; border-radius: 999px; background: var(--surface-3); overflow: hidden; margin-bottom: 18px; }
.stepbar i { display: block; height: 100%; border-radius: 999px; background: var(--brand); transition: width var(--slow) var(--ease); }

/* sidebar footer: short aligned rows */
.site-foot .foot-line { display: block; color: var(--text-faint); text-decoration: none; font-size: var(--t-cap); line-height: 1.5; }
.site-foot a.foot-line:hover { color: var(--text); }
.site-foot .foot-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 3px 10px; margin: 8px 0 0; }
.site-foot .foot-grid a { font-size: var(--t-cap); color: var(--text-faint); text-decoration: none; line-height: 1.5; white-space: nowrap; }
.site-foot .foot-grid a:hover { color: var(--text); }
.site-foot .foot-note { margin-top: 10px; font-size: var(--t-cap); line-height: 1.5; color: var(--text-faint); opacity: .85; }

/* the sidebar is a card like everything else: rounded, with a little breathing room */
@media (min-width: 760px) {
  /* Sized to its content, never taller than the viewport, and compact on
     shorter screens so it fits without scrolling inside itself. */
  .sidebar { border-radius: 12px; border: 1px solid var(--line); margin: 12px 0 12px 12px; top: 72px; height: auto; max-height: calc(100vh - 84px); overflow: hidden auto; }
  .site-foot { margin-top: 12px; }
}
@media (min-width: 760px) and (max-height: 900px) {
  .sidebar { padding: 10px 0; }
  .sidebar a { padding: 6px 12px; }
  .sidebar .group { padding: 8px 12px 3px; }
  .site-foot { padding-top: 8px; }
  .site-foot .foot-grid { margin-top: 5px; gap: 1px 10px; }
  .site-foot .foot-note { display: none; }
}
@media (min-width: 760px) and (max-height: 760px) {
  .sidebar a { padding: 5px 12px; font-size: var(--t-small); }
  .sidebar .group { display: none; }
}
/* Shorter still: the foot keeps only where we are from, then goes entirely,
   so the navigation never needs its own scrollbar. */
@media (min-width: 760px) and (max-height: 680px) {
  .sidebar a { padding: 4px 12px; margin-bottom: 1px; }
  .site-foot { margin-top: 6px; padding-top: 4px; }
  .site-foot .foot-grid { display: none; }
}
@media (min-width: 760px) and (max-height: 560px) {
  .site-foot { display: none; }
}

.tbl tr.more-row[hidden] { display: none; }

/* The X account behind a named wallet, on the trader page. */
.x-handle { color: var(--text-dim); text-decoration: none; border-bottom: 1px solid var(--line-strong); }
.x-handle:hover { color: var(--text); border-bottom-color: var(--text-dim); }

/* Owner dashboard: one bar per day. */
.dash-bars { display: flex; align-items: flex-end; gap: 2px; height: 120px; margin-top: 8px; }
.dash-bars .b { flex: 1; height: 100%; display: flex; align-items: flex-end; min-width: 2px; }
.dash-bars .b i { display: block; width: 100%; border-radius: 2px 2px 0 0; opacity: .8; transition: opacity var(--fast) var(--ease); }
.dash-bars .b:hover i, .dash-bars .b:last-child i { opacity: 1; }

/* A Pro-only section: the shape of what is there, blurred, with one way in. */
/* The blurred rows and the card sit in the same grid cell, so the wrapper is
   as tall as whichever is taller. It used to be the rows alone, with the card
   absolutely positioned on top of them and the wrapper clipping the overflow:
   on a phone "See Pro, US$9 a month" was sliced in half by its own container. */
.locked-wrap { position: relative; border-radius: 12px; overflow: hidden; display: grid; }
.locked-rows { grid-area: 1 / 1; filter: blur(6px); opacity: .55; padding: 4px 2px; pointer-events: none; user-select: none; }
.locked-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 11px 4px; border-bottom: 1px solid var(--line); }
.locked-row:last-child { border-bottom: 0; }
.locked-bar { height: 12px; border-radius: 6px; background: var(--line); display: block; }
.locked-bar.sm { width: 62px; opacity: .7; }
.locked-over { grid-area: 1 / 1; display: grid; place-items: center; padding: 12px; }
.locked-card { text-align: center; padding: 12px 12px; border-radius: 12px; border: 1px solid var(--line); background: var(--bg); box-shadow: 0 6px 18px rgba(0,0,0,.16); max-width: 320px; }

/* The unconfirmed-address line: present, not shouting, and never covering the app. */
.verify-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 9px 14px; font-size: var(--t-small); line-height: 1.45;
  background: color-mix(in srgb, var(--warn, #d29922) 14%, var(--surface));
  border-bottom: 1px solid color-mix(in srgb, var(--warn, #d29922) 35%, var(--line));
  color: var(--text);
}
.verify-bar span { flex: 1 1 260px; }
.verify-bar .btn { flex: 0 0 auto; }
/* A dismiss is not a second action. It was inheriting the raised white box
   every other icon button on the site has, which on a coloured bar made it
   the largest and brightest thing in the row: a close box competing with the
   thing it closes. A mark, at the size of a mark. */
.verify-bar .iconbtn {
  width: 28px; height: 28px; flex: 0 0 auto;
  border: none; background: none; color: var(--text-dim); box-shadow: none;
}
.verify-bar .iconbtn:hover { background: color-mix(in srgb, var(--text) 6%, transparent); color: var(--text); }
.verify-bar .iconbtn svg { width: 15px; height: 15px; }
/* On a phone the sentence takes the whole width, the action takes the next
   line, and the dismiss sits in the corner. Wrapping all three as equals put
   a button and a close box side by side on a line of their own with a gap
   between them, which reads as a layout that has come apart. */
/* The action is a word inside the sentence. As a button beside it the bar had
   to be tall enough to hold a control, and on a phone it wrapped to three
   lines and took a quarter of the screen above every page. */
.linkish {
  font: inherit; color: var(--brand); font-weight: 600; text-decoration: underline;
  text-underline-offset: 2px; padding: 0; background: none; border: none; cursor: pointer;
}
.linkish:hover { color: var(--text); }
/* The one place to cancel Pro: the last line of the profile. Quiet, not
   hidden: a real button, 44px to tap, in the page's faint text colour, with
   the underline arriving on hover the way a footer link's does. */
.cancel-line {
  display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 0 4px;
  margin-top: 28px; text-align: center; font-size: var(--t-cap); line-height: 1.35; color: var(--text-faint);
  min-height: 44px;
}
/* Once only a sentence is left ("Pro is cancelled."), focus is handed back to
   the line itself (focusCancelLine) so a keyboard user is not dropped at the
   top of the page. It is not a control, so it gets no ring, and the 44px above
   keeps the page from jumping when the button in it goes. */
.cancel-line:focus { outline: none; }
.linkish.quiet {
  color: var(--text-faint); font-weight: 400; text-decoration: none;
  min-height: 44px; padding: 0 6px;
}
.linkish.quiet:hover, .linkish.quiet:focus-visible { color: var(--text); text-decoration: underline; }
/* Keep Pro, the undo once a cancel is pending, has to look pressable without
   a hover, which a phone never has: underlined and a step brighter than the
   sentence around it. Cancel Pro stays quiet. */
.linkish.quiet.undo { color: var(--text-dim); text-decoration: underline; }
/* Paused: "Pro is paused until ... Resume Pro now · Cancel Pro". On a phone it
   does not fit one row, so the dot becomes the line break instead of being
   left hanging at the end of the first row. */
.cancel-sep { padding: 0 2px; }
@media (max-width: 559px) { .cancel-line .cancel-sep { flex-basis: 100%; height: 0; overflow: hidden; } }
/* The exit sheet (components/exitFlow.js) on every screen: a string with no
   break in it, an email address on the done screen or support's in an error,
   wraps inside the column instead of running past it at 320px. */
.exit-sheet { overflow-wrap: anywhere; }
.exit-lead { margin-top: 12px; font-size: var(--t-body); line-height: 1.5; }
.exit-sub { margin-top: 8px; font-size: var(--t-small); line-height: 1.5; color: var(--text-dim); }
.exit-list { margin: 12px 0 0; padding: 0; list-style: none; display: grid; gap: 6px; font-size: var(--t-small); line-height: 1.5; color: var(--text-dim); }
.exit-actions { display: grid; gap: 10px; margin-top: 18px; }
/* The exit survey's offer screen (components/exitFlow.js). The decline lines
   are long and must stay whole and readable at 320px: they wrap inside a
   full-width, normal-weight button rather than being cut. On one line it is
   the accept's height (.btn: 12px padding and a 1px border each side, around
   the page's 1.55 line), so the stacked pair is even; the tighter 1.35 is only
   seen once it wraps. */
.btn-wrap { white-space: normal; min-height: calc(1.55em + 26px); line-height: 1.35; font-weight: 400; text-align: center; }
/* The optional answer, its label above it (aw-a11y, 3.3.2): the box's old
   12px above it, and nothing added below. */
.exit-field { margin: 12px 0 0; }
.exit-help { margin-top: 14px; font-size: var(--t-cap); text-align: center; }
.exit-help a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
/* On a touch screen the link is a 44px target, like the toast's offer. */
@media (pointer: coarse) { .exit-help a { display: inline-flex; align-items: center; min-height: 44px; } }
@media (max-width: 559px) {
  .verify-bar { padding: 9px 40px 9px 20px; position: relative; }
  .verify-bar .iconbtn { position: absolute; top: 5px; right: 8px; }
}

/* The chart readout: a crosshair panel that says what the line is worth at the
   point under the cursor, the way a brokerage chart does. */
/* pan-y hands a horizontal drag to the chart and leaves vertical scrolling to
   the page, which is what makes press-and-slide readable on a phone. */
.chart-plot { position: relative; touch-action: pan-y; }
/* The hint is for somebody who has not found the gesture; it goes once the
   screen is too narrow to spend the room on it. */
.chart-hint { color: var(--text-faint); }
/* The selection wash. Quiet enough to read the curve through, and it eases
   between up and down so dragging across a turn is one movement. */
.cx-band { opacity: .1; transition: fill var(--fast) var(--ease), opacity var(--fast) var(--ease); }
@media (max-width: 520px) { .chart-hint { display: none; } }
.chart-plot svg { display: block; width: 100%; cursor: crosshair; outline: none; }
/* Axis figures are HTML laid over the drawing, so they are drawn at their
   own size at every width; the SVG under them is stretched to its box and
   would squash any text inside it. A return chart keeps a gutter on the
   right for them; the day timeline hangs its times under the track. */
.chart-plot.has-y { padding-right: 52px; }
.chart-y, .chart-x { position: absolute; inset: 0; pointer-events: none; }
.chart-y span, .chart-x span {
  position: absolute; font-size: var(--t-cap); line-height: 1; color: var(--text-faint);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.chart-y span { right: 0; transform: translateY(-50%); }
.chart-x span { transform: translate(-50%, -100%); }
/* The ring is the keyboard's slider over the drawing (charts.js scrubHtml),
   drawn where the drawing's own ring was. */
.chart-scrub:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: 6px; }
.chart-tip {
  position: absolute; z-index: 3; min-width: 158px; max-width: 230px; pointer-events: none;
  padding: 8px 12px; border-radius: 12px; border: 1px solid var(--line);
  background: var(--surface); box-shadow: 0 8px 26px rgba(0,0,0,.18);
  font-size: var(--t-cap); line-height: 1.5;
}
.chart-tip .tip-date { font-weight: 600; margin-bottom: 5px; font-size: var(--t-cap); color: var(--text); }
.chart-tip .tip-row { display: flex; justify-content: space-between; gap: 14px; }
.chart-tip .tip-row span { color: var(--text-dim); }
.chart-tip .tip-diff { margin-top: 4px; padding-top: 4px; border-top: 1px solid var(--line); }

/* While the next page loads the current one stays on screen, slightly faded,
   rather than being replaced by an empty skeleton. */
body.nav-busy .shell { opacity: .55; transition: opacity var(--fast) var(--ease); }
body.nav-busy { cursor: progress; }

/* The day view is a different kind of chart from the others, so it says so. */
.chart-caption { font-size: var(--t-cap); line-height: 1.5; color: var(--text-dim); margin: 0 0 10px; }

/* The day view: a timeline of the day's trades and the same trades in words,
   so nothing about it has to be hovered to be understood. */
.dayview .day-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; }
.dayview .day-head .k { font-size: var(--t-small); color: var(--text-dim); }
.dayview .day-head b { font-size: var(--t-title); letter-spacing: -.01em; }
.day-fills { font-size: var(--t-small); table-layout: fixed; width: 100%; }
.day-fills td:nth-child(3) { overflow-wrap: anywhere; }
.day-fills th { padding: 7px 8px; }
.day-fills td { padding: 8px; }
.day-fills tbody tr.is-on td { background: var(--surface-2, rgba(127,127,127,.09)); }
.day-fills tbody tr:hover td { background: var(--surface-2, rgba(127,127,127,.06)); }
@media (max-width: 560px) {
  /* The price is the one column a phone can lose: the time, the move, the
     security and the size are what the day is. */
  .day-fills th:nth-child(4), .day-fills td:nth-child(4), .day-fills col:nth-child(4) { display: none; }
  .day-fills col:nth-child(1) { width: 20%; }
  .day-fills col:nth-child(2) { width: 28%; }
  .day-fills col:nth-child(3) { width: 22%; }
  .day-fills col:nth-child(5) { width: 30%; }
  .day-fills th, .day-fills td { padding: 8px 4px; font-size: var(--t-small); }
  /* "2:44 p.m." in mono is 72px at the small size, wider than its column
     on a 375 phone; at the caption size it stays out of the next one. */
  .day-fills td:nth-child(1) { font-size: var(--t-cap); }
}

/* A trade waiting on the account holder. Money does not move until they act,
   so this is the one bar on the site allowed to be loud: full amber, dark text
   for contrast, and a solid button. Toned down to the site's own surface it
   was invisible, which for the only blocking action in the product is worse
   than garish. */
.waiting-bar {
  /* Under the top bar, which is sticky too and drawn above it: at top: 0 the
     two met at the same edge and the top bar won, so the bar with the trades
     waiting on the person vanished the moment the page scrolled. */
  position: sticky; top: var(--nav-h); z-index: 59;
  display: flex; align-items: center; gap: 11px;
  padding: 11px 16px; font-size: var(--t-small); font-weight: 600;
  color: #1A1204; background: var(--warn);
  box-shadow: 0 2px 14px rgba(0, 0, 0, .35);
}
.waiting-text { flex: 1; min-width: 0; }
.waiting-bar .waiting-icon { flex: 0 0 auto; display: grid; place-items: center; }
.waiting-bar .waiting-go {
  flex: 0 0 auto; background: #1A1204; color: #fff; border: 0;
  font-weight: 700; padding: 6px 14px; white-space: nowrap;
}
.waiting-bar .waiting-go:hover { background: #000; color: #fff; }
/* One slow breath, so the eye catches it without the page feeling anxious. */
.waiting-bar { animation: waiting-attn calc(var(--slow) * 8) var(--ease-soft) 3; }
@keyframes waiting-attn {
  0%, 100% { filter: none }
  50% { filter: brightness(1.12) }
}
@media (prefers-reduced-motion: reduce) { .waiting-bar { animation: none } }
@media (max-width: 520px) {
  .waiting-bar { padding: 10px 12px; font-size: var(--t-small); gap: 9px }
  .waiting-bar .waiting-go { padding: 6px 11px }
}

/* A trade waiting on the account holder. This is the one screen where a person
   decides about their own money, so the side of the trade is the loudest thing
   on the card and the two answers are the only controls. */
.confirm-card { border-color: color-mix(in srgb, var(--amber, #b8791a) 30%, var(--line)); }
.confirm-card.soon { border-color: color-mix(in srgb, var(--red, #c0392b) 45%, var(--line)); }
/* The one a notification named, so it is found without reading the list. It
   is already first in the list, so when the same card is also about to lapse
   the red warning keeps the border: the deep link is a convenience, the
   expiry is the thing that costs somebody a trade. */
.confirm-card.named:not(.soon) { border-color: var(--brand); }
.confirm-side {
  flex: 0 0 auto; display: inline-grid; place-items: center; min-width: 52px;
  padding: 8px 12px; border-radius: 12px; font-size: var(--t-cap); font-weight: 700;
  letter-spacing: .03em; color: #fff;
}
.confirm-side.buy { background: var(--green, #17913f); }
.confirm-side.sell { background: var(--red, #c0392b); }
@media (max-width: 520px) {
  .confirm-card .spread { flex-direction: column; align-items: stretch }
  .confirm-card .spread .row { justify-content: stretch }
  .confirm-card .spread .row .btn { flex: 1 }
}

/* The one nav item that is a job rather than a place. */
#sidebar a.nav-urgent { color: var(--warn); font-weight: 600; }
#sidebar a.nav-urgent svg { color: var(--warn); }

/* The way back to a waiting trade, wherever the customer happens to be. */
.waiting-card { border-color: var(--warn); background: var(--warn-soft); }
.waiting-card-icon { flex: 0 0 auto; display: grid; place-items: center; color: var(--warn); }

/* A trade row that is waiting on the customer is a control, so it behaves
   like one. */
.trade-waiting { cursor: pointer; border-radius: 12px; margin: 0 -8px; padding-left: 8px !important; padding-right: 8px !important; }
.trade-waiting:hover, .trade-waiting:focus-visible { background: var(--warn-soft); outline: none; }

/* Review, from any page, on any device. It sits in the top bar next to the
   bell because that is the one strip present on every screen. */
.topbar-review {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--warn); color: #1A1204; border: 0; font-weight: 700;
  padding: 6px 11px; white-space: nowrap;
}
.topbar-review:hover { filter: brightness(1.08); }
.topbar-review-n {
  display: inline-grid; place-items: center; min-width: 18px; height: 18px;
  padding: 0 6px; border-radius: 999px; background: #1A1204; color: #fff;
  font-size: var(--t-cap); font-weight: 700;
}
@media (max-width: 640px) { .topbar-review-label { display: none } }
/* A phone header has room for the essentials only: the admin console is a
   sidebar item on anything wider. The theme toggle stays — the owner asked
   for it (2026-10-05) after the Account page's own pair, and a 44 px button
   is a tap, unlike the console. */
@media (max-width: 480px) {
  .topbar-actions a[href="/admin"] { display: none; }
  .topbar-in { gap: 8px; }
}

/* Choosing where to be asked. A row per channel, quiet until it is on, so the
   card reads as a setting rather than as a pitch. */
/* One grouped panel, rows split by a hairline that starts past the mark, the
   way a phone's own settings are laid out. */
.chan-list {
  border: 1px solid var(--line); border-radius: 12px; background: var(--surface);
  overflow: hidden;
}
.chan {
  position: relative;
  display: flex; align-items: center; gap: 14px;
  padding: 15px 18px; min-height: 68px;
  transition: background var(--fast) var(--ease);
}
.chan + .chan::before {
  content: ''; position: absolute; top: 0; right: 0; left: 68px;
  height: 1px; background: var(--line);
}
@media (hover: hover) { .chan:hover { background: color-mix(in srgb, var(--surface-2) 60%, transparent); } }
.chan-mark {
  flex: none; width: 36px; height: 36px; border-radius: 12px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--mark, var(--text-dim));
  background: color-mix(in srgb, var(--mark, var(--text-faint)) 12%, transparent);
  transition: background var(--base) var(--ease), color var(--base) var(--ease);
}
.chan-mark-telegram { --mark: #2AABEE; }
.chan-mark-discord { --mark: #5865F2; }
.chan-mark-whatsapp { --mark: #1FAF55; }
.chan-mark-sms { --mark: #34A853; }
.chan-mark-device { --mark: var(--brand); }
.chan-mark-email { --mark: var(--text-dim); }
.chan-text { flex: 1; min-width: 0; }
.chan-label { font-weight: 600; font-size: var(--t-body); letter-spacing: -.005em; }
.chan-note { font-size: var(--t-cap); line-height: 1.45; margin-top: 2px; color: var(--text-faint); }
.chan-side { flex: none; display: flex; align-items: center; gap: 14px; }
.chan-quiet {
  background: none; border: 0; padding: 6px 2px; cursor: pointer;
  font: inherit; font-size: var(--t-small); font-weight: 600; color: var(--text-faint);
  transition: color var(--fast) var(--ease);
}
.chan-quiet:hover { color: var(--text); }
.chan-quiet[data-disconnect]:hover { color: var(--red, #d64545); }
.chan-quiet:disabled { opacity: .5; cursor: default; }
.chan .switch { cursor: pointer; }
.chan .switch[aria-busy='true'] { opacity: .45; pointer-events: none; }
/* Connecting is an invitation, not an alarm: a soft pill in the brand's tint
   rather than a row of solid buttons. */
.chan-connect {
  border: 0; cursor: pointer; white-space: nowrap;
  padding: 8px 12px; border-radius: 999px;
  font: inherit; font-size: var(--t-small); font-weight: 600; letter-spacing: -.005em;
  color: var(--brand); background: color-mix(in srgb, var(--brand) 11%, transparent);
  transition: background var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.chan-connect:hover { background: color-mix(in srgb, var(--brand) 18%, transparent); }
.chan-connect:active { transform: scale(.96); }
.chan-connect:disabled { opacity: .55; cursor: default; }
@media (max-width: 520px) {
  .chan { padding: 14px; gap: 12px; }
  .chan + .chan::before { left: 64px; }
  .chan-side { gap: 10px; }
  .chan-connect { padding: 7px 13px; }
}

/* Offered once the person has just answered a trade and can see the point of
   answering it faster. Quiet, one line, and gone once a chat is connected. */
.chat-offer {
  display: flex; gap: 8px; align-items: flex-start;
  margin-top: 12px; padding: 8px 12px; border-radius: 8px;
  background: var(--surface-2); color: var(--text-dim); font-size: var(--t-cap); line-height: 1.5;
}
.chat-offer svg { flex: 0 0 auto; margin-top: 1px; color: var(--brand); }
.chat-offer a { color: var(--brand); font-weight: 600; }

/* The moment an emailed button lands: acting, then done. */
.spin {
  width: 18px; height: 18px; border-radius: 50%;
  border: 3px solid var(--line); border-top-color: var(--brand);
  /* Linear, not a curve: an eased turn stops at the top of every revolution. */
  animation: spin calc(var(--slow) * 1.5) linear infinite;
}
@keyframes spin { to { transform: rotate(360deg) } }
.done-mark {
  width: 52px; height: 52px; margin: 0 auto; border-radius: 50%;
  display: grid; place-items: center; color: #fff;
  background: var(--green, #17913F);
}
@media (prefers-reduced-motion: reduce) { .spin { animation: none } }

/* A holdings row that answers the question it raises: click it, see the price
   line for that security. It has to look clickable without shouting. */
.row-chart { cursor: pointer; }
.row-chart:hover { background: var(--surface-2, rgba(127, 127, 127, 0.06)); }
.row-chart:hover td:first-child b { text-decoration: underline; text-underline-offset: 2px; }
.notice.step-link { cursor: pointer; }
.notice.step-link:hover { border-color: var(--warn, #ffb020); }

/* A key or an address: something to be read carefully once, and copied. */
.keybox {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: var(--t-cap); line-height: 1.55; word-break: break-all;
  background: var(--surface-3); border: 1px solid var(--line);
  border-radius: 12px; padding: 12px 12px; color: var(--text);
  user-select: all;
}

/* Three answers to one question, inside one card rather than three. */
.band { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line); }
.band:first-of-type { margin-top: 12px; }

/* ------------------------------------------------------------ the press --
   Last in the file, so nothing can take it back. */
/* A press should feel like a press.
   A button was a rectangle that changed colour; with this there is a moment of
   give under the finger and the interface feels like it has weight. The
   transform gets its own --press, because the --fast the rest of the button
   uses reads as lag on a press rather than as a response.

   These live at the end of the stylesheet on purpose. A component that
   declared its own `transition` later in the file silently took the press
   timing back: .chip did exactly that, and its give ran at 140ms instead of
   40, which is the difference between a button that answers and one that
   catches up. Nothing can override them from here. */
.btn:active, .iconbtn:active, .chip:active, .mode-card:active { transform: scale(.972); }
.tcard:active { transform: scale(.992); }
.btn, .iconbtn, .chip, .mode-card, .tcard {
  transition-property: transform, background, border-color, color, box-shadow, opacity;
  transition-duration: var(--press), var(--fast), var(--fast), var(--fast), var(--fast), var(--fast);
  transition-timing-function: var(--ease);
}

/* Everything else a finger lands on.
   The give above covered buttons and cards and nothing else, so a tab, a
   style card, a sportsbook tile, a bottom-bar item and a disclosure summary
   all answered a press with nothing at all until their page had loaded. On a
   phone that reads as the tap not having registered, which is what makes
   somebody press twice. Same 40ms give, same curve.

   The grey box iOS paints over a tapped element goes with it, on exactly
   these selectors and no others: removing that flash without putting a press
   state in its place would leave the interface quieter than it is today. */
.choice, .style-card, .book-tile, .tabbar button, .sortbar-toggle, .bottomnav a,
.faq-item summary, .explain summary, .reader-agree, .chan .switch, .lb-copy,
.lb-row, #sidebar a {
  -webkit-tap-highlight-color: transparent;
  transition-property: transform, background, border-color, color, box-shadow, opacity;
  transition-duration: var(--press), var(--fast), var(--fast), var(--fast), var(--fast), var(--fast);
  transition-timing-function: var(--ease);
}
.btn, .iconbtn, .chip, .mode-card, .tcard { -webkit-tap-highlight-color: transparent; }
.choice:active, .style-card:active:not(:disabled), .book-tile:active:not(:disabled),
.tabbar button:active, .sortbar-toggle:active, .faq-item summary:active,
.explain summary:active, .lb-copy:active, #sidebar a:active { transform: scale(.972); }
/* A leaderboard row is as wide as the page, so it gives like a card does. */
.lb-row:active { transform: scale(.992); }
/* The four card surfaces that lift on hover give on the press too: the press
   answered only on a mouse otherwise, and this is the touch answer. */
a.card:active, .card.hoverable:active, .proof .p:active, .ops-kpi:active { transform: scale(.992); }
/* The bottom bar is a fixed strip; scaling the whole item looks loose, so the
   icon dips instead, which is what a native tab bar does. */
.bottomnav a:active { transform: scale(.94); }
@media (prefers-reduced-motion: reduce) {
  .choice:active, .style-card:active, .book-tile:active, .tabbar button:active,
  .sortbar-toggle:active, .faq-item summary:active, .explain summary:active,
  .lb-copy:active, .bottomnav a:active, .lb-row:active, #sidebar a:active,
  a.card:active, .card.hoverable:active, .proof .p:active, .ops-kpi:active { transform: none; }
}

/* ------------------------------------------------------------------ setup --
   The real-money flow, one question per screen, is the whole viewport: the
   frame is hidden here. Its own styles live in css/golive.css, loaded by
   pages/golive.js before it draws. */
body.focus-mode #sidebar,
body.focus-mode #bottomnav,
body.focus-mode #topbar { display: none; }
body.focus-mode { padding-bottom: 0; }
body.focus-mode .layout { display: block; max-width: none; margin: 0; padding: 0; }
body.focus-mode .shell { max-width: none; padding: 0; }

/* ---------------------------------------------------------------- explore --
   The controls above the list.

   A filter row is not the product; the traders are. What is left here is one
   title, one count, one row of chips that scrolls sideways rather than wrapping
   to three lines, and one row of three controls. Everything else moved into the
   Filters sheet or under the list, which is where a glossary is actually read. */
.exp-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 14px; }
.exp-head h1 { font-size: var(--t-title); }
.exp-count { color: var(--text-faint); font-size: var(--t-small); white-space: nowrap; }

/* One line, always. Wrapping put seven chips on three rows and pushed the
   first trader most of a screen down the page; a sideways scroll keeps the
   same seven reachable in the height of one. The gutters are negative so the
   row bleeds to the edges of the phone, which is what says "this scrolls". */
.chipscroll {
  display: flex; gap: 7px; overflow-x: auto; scrollbar-width: none;
  margin: 0 -16px 10px; padding: 2px 16px 6px;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
}
/* No scroll snapping. A snap point aligns a child to the scrollport's snapport,
   which starts at the border box and not after the padding, so the first snap
   pulled the row left by exactly the gutter and left "Top Today" against the
   glass while the row above it still sat inboard. Snapping buys a filter row
   nothing anyway: these are things to read past, not pages to land on. */
.chipscroll::-webkit-scrollbar { display: none; }
.chipscroll > * { flex: none; }
.sortstrip { margin-top: -4px; margin-bottom: 12px; }

/* The control row scrolls sideways like the chips above it rather than
   wrapping: signed in it holds four pills and a Clear, which broke onto a
   second line on a phone. The 4px of padding keeps a focus ring inside the
   scroller, and the negative margin gives it back so nothing moves. */
.exp-controls {
  display: flex; align-items: center; gap: 8px; flex-wrap: nowrap;
  overflow-x: auto; scrollbar-width: none; overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch;
  margin: -4px -16px 8px; padding: 4px 16px;
}
.exp-controls::-webkit-scrollbar { display: none; }
.exp-controls > * { flex: none; }
.exp-controls .link { font-size: var(--t-small); color: var(--text-faint); margin-left: auto; }
.exp-controls .link:hover { color: var(--text); }

/* A control, not a card. One height, one radius, one border, and an on state
   that is a fill rather than an outline, so which ones are active is legible
   at a glance instead of read one at a time. */
.pill {
  display: inline-flex; align-items: center; gap: 6px;
  height: 36px; padding: 0 12px; border-radius: 12px;
  border: 1px solid var(--line); background: var(--surface);
  font-size: var(--t-small); color: var(--text); white-space: nowrap;
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease), transform var(--press) var(--ease);
}
.pill b { font-weight: 600; }
.pill svg { color: var(--text-faint); }
.pill:hover { border-color: var(--line-strong); background: var(--surface-2); }
.pill:active { transform: scale(.97); }
.pill.on { background: var(--text); border-color: var(--text); color: var(--bg); }
.pill.on svg { color: inherit; }
.pill-n {
  display: inline-grid; place-items: center; min-width: 18px; height: 18px;
  padding: 0 6px; border-radius: 8px; background: var(--brand); color: #fff;
  font-size: var(--t-cap); font-weight: 700; font-variant-numeric: tabular-nums;
}
.pill.on .pill-n { background: var(--bg); color: var(--text); }

/* What this list is, on one line, directly above it. */
.exp-board {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  font-size: var(--t-small); margin-bottom: 12px;
}
.exp-board .dim { min-width: 0; }
.exp-board .faint { white-space: nowrap; }

/* Three figures, side by side, large enough to be the answer rather than a
   footnote. The panel exists to say an unflattering number plainly, so it is
   not tucked into small print. */
.exec-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 16px; }
.exec-n { font-size: var(--t-title); font-weight: 600; letter-spacing: -0.02em; line-height: 1.1; }
.exec-grid .faint { font-size: var(--t-cap); margin-top: 3px; }

/* ---------------------------------------------------------------- the hub --
   Paper or real money is a switch, not a decision to be re-made every visit.
   The two explaining cards stay for somebody who has never copied anybody;
   after that the same choice is a segmented control beside the balance. */
.hub-value { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; margin-top: 2px; }
.hub-value b { font-size: var(--t-hero); letter-spacing: -0.025em; }
.hub-value .faint { font-size: var(--t-cap); flex-basis: 100%; }
.hub-value:hover b { color: var(--brand); }

.modeseg {
  display: inline-flex; flex: none; padding: 3px; gap: 2px;
  border: 1px solid var(--line); border-radius: 12px; background: var(--surface-2);
}
.modeseg button {
  padding: 8px 12px; border-radius: 8px; font-size: var(--t-small); font-weight: 600;
  color: var(--text-dim); white-space: nowrap;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.modeseg button.on { background: var(--text); color: var(--bg); box-shadow: var(--shadow-sm); }
.modeseg button.busy { opacity: .6; }

/* The hub header on a phone: the balance is the thing, the switch goes under
   it at full width. Side by side, the switch was landing beside the percentage
   and pushing the label that explains the number onto its own line below. */
@media (max-width: 559px) {
  .hub-head { display: block; }
  .hub-value .faint { flex-basis: auto; }
  .modeseg { display: flex; width: 100%; margin-top: 14px; }
  .modeseg button { flex: 1; text-align: center; }
  /* A two-line heading with its own subtitle squeezed alongside is two
     columns of ragged text where one line of each would do. */
  .section-head { display: block; }
  .section-head > span, .section-head .link { display: block; margin-top: 3px; }
  /* Two to a row: eight of these stacked one-up is eight screens of scrolling
     to see a list of eight words. */
  .style-grid { grid-template-columns: 1fr 1fr; gap: 10px; }
  .style-card { padding: 14px; }
  .style-title { font-size: var(--t-body); margin-top: 10px; }
  .style-sub { font-size: var(--t-cap); }
  /* The count at the caption size, carried by its weight and ink, so "105
     traders 93 ranked" still fits a half-width card on one line. A count and
     its word stay together; where two cannot sit side by side (a 320px phone)
     the second goes under the first rather than each breaking in half. */
  .style-stats { gap: 2px 10px; flex-wrap: wrap; font-size: var(--t-cap); margin-top: 10px; }
  .style-stats > span { white-space: nowrap; }
  .style-stats b { font-size: var(--t-cap); }
}

/* A badge row inside a card never runs past the card. `.row` does not wrap by
   default and every caller has to remember `.wrap`; inside a card footer the
   answer is always the same, so it is settled here rather than at each call. */
.tcard-foot > .row { flex-wrap: wrap; min-width: 0; }

/* ------------------------------------------------------------ the reader --
   One pane, every document in it, scrolling both ways.

   The sheet itself used to scroll a little as well as the pane inside it, so a
   flick moved whichever of the two the finger happened to land on and neither
   went anywhere useful. The sheet is a fixed frame now: heading and steps at
   the top, the acceptance and the button at the bottom, and the only thing
   that moves is the documents. */
.modal:has(.reader-modal) { display: flex; overflow: hidden; padding-bottom: max(20px, env(safe-area-inset-bottom, 0px)); }
.reader-modal { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; max-height: 100%; }
.reader-top, .reader-bottom { flex: none; }
.reader-mid { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
.reader-mid .reader { flex: 1 1 auto; min-height: 140px; max-height: none; }
.reader-bottom { padding-top: 14px; }

/* Three places to go, not three stages passed. They were spans; a thing you
   can click has to look like one and answer the keyboard. */
.reader-steps button {
  font: inherit; font-size: var(--t-cap); color: var(--text-faint); padding: 3px 0;
  border-bottom: 1.5px solid transparent;
  transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.reader-steps button:hover { color: var(--text); }
.reader-steps button.on { color: var(--text); font-weight: 600; border-bottom-color: var(--brand); }
.reader-steps button.done { color: var(--pos); }

/* Each document keeps its own heading inside the one scroll, so scrolling back
   up lands somewhere named rather than in the middle of anonymous prose. */
.reader section + section { margin-top: 26px; padding-top: 20px; border-top: 1px solid var(--line); }
.reader section > .reader-head {
  position: sticky; top: -14px; z-index: 1; margin: 0 0 8px;
  padding: 10px 0 6px; background: var(--surface-2);
}

/* A plan is the same kind of choice as paper-or-real-money, so it is the same
   card. It reads rather than acts, so it neither lifts nor takes a cursor. */
.mode-card.plan-card { cursor: default; }
.mode-card.plan-card:hover { transform: none; border-color: var(--line); }
.mode-card.plan-card.on:hover { border-color: var(--brand); }
.mode-price { font-size: var(--t-title); font-weight: 700; letter-spacing: -.02em; margin-top: 6px; }
.mode-price .faint { font-size: var(--t-cap); font-weight: 400; }

/* The side you are on already, on the screen that compares the two. */
.choice.on { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft); }
@media (max-width: 559px) { .hub-head .col { align-items: stretch !important; } }
/* A badge is a label on a thing, not a band across it. Inside the choice cards
   these stretched to the full column width and read as empty input boxes. */
.choice > .badge { align-self: flex-start; }

/* A phone needs a wider gutter than 16px.
   Everything on the page aligns to it, so the first chip in a scrolling row
   sits exactly where the title above it does; at 16 that put a filled pill
   almost against the glass. Widened here rather than nudging the chip row on
   its own, which would have bought the chip some air by breaking the one
   vertical line the whole page reads along. The row's bleed follows it, or
   the scroller would stop short of the edge and stop looking like one. */
@media (max-width: 559px) {
  .shell { padding: 0 20px; }
  .chipscroll, .exp-controls { margin-inline: -20px; padding-inline: 20px; }
}

/* The action somebody came to a trader's page to take gets the whole width on
   a phone, and everything else sits under it. Four controls on one line did
   not fit: "Copy with real money" was cut off inside its own button. */
.tactions { display: grid; gap: 9px; margin-top: 16px; }
.tactions-main .btn { width: 100%; }
.tactions-rest { display: flex; gap: 9px; }
@media (min-width: 560px) {
  .tactions { display: flex; align-items: center; }
  .tactions-main { flex: 1; min-width: 0; }
}

/* Pro wears the same wash wherever it is offered. */
.mode-card.accent { background: linear-gradient(155deg, var(--brand-soft), var(--surface)); }
.mode-card.accent .mode-icon { color: var(--brand); }

/* Where the next chunk of a long list arrives. It is a marker, not a control:
   it holds one line of text while a fetch is in flight and is empty, and then
   removed, the rest of the time. */
.more-sentinel { min-height: 1px; padding: 14px 0; text-align: center; color: var(--text-faint); font-size: var(--t-small); }

/* ------------------------------------------------------- long-list paint --
   A row the reader has not reached is not styled, laid out or painted.

   Explore holds up to two hundred rows and a trader's page holds sixty
   holdings; every one of them was being laid out on every render and on every
   resize, which is most of what makes a long list feel heavy under a thumb.
   The intrinsic size is the row's real height, so the scrollbar is honest and
   nothing jumps when a row comes into view. Rows only: cards are twelve at a
   time and the containment would buy nothing. */
/* No content-visibility on ranked rows: a row that wraps on a phone is taller
   than the placeholder height, so rows scrolling into view changed the page
   height under the finger and the screen shifted and settled. Sixty rows
   paint fast enough without it. */
.day-fills tr { content-visibility: auto; contain-intrinsic-size: auto 38px; }

/* A photo fills the same circle the initials do, and is never distorted to
   fit it: a face cropped to a square is right, a face stretched is not. */
img.avatar { object-fit: cover; padding: 0; background: var(--surface-2); }

/* The photo is the control that changes it. One tap, on the thing itself,
   rather than a row in a settings list two screens away. */
.avatar-edit { position: relative; flex: none; padding: 0; border: none; background: none; line-height: 0; border-radius: 50%; }
.avatar-edit-mark {
  position: absolute; right: -2px; bottom: -2px; width: 24px; height: 24px;
  display: grid; place-items: center; border-radius: 50%;
  background: var(--surface); border: 1px solid var(--line); color: var(--text-dim);
  transition: color var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.avatar-edit:hover .avatar-edit-mark { color: var(--brand); border-color: var(--brand); }
.avatar-edit:active { transform: scale(.97); }

/* Three steps, numbered, on the landing page. A number a person can count
   rather than a paragraph they have to parse. */
.grid.three { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.step-n {
  width: 26px; height: 26px; display: grid; place-items: center; margin-bottom: 10px;
  border-radius: 8px; background: var(--brand-soft); color: var(--brand);
  font-weight: 700; font-size: var(--t-small); font-variant-numeric: tabular-nums;
}


/* ---- a section that folds: open by default, the heading is the handle ---- */
details.fold > summary { list-style: none; cursor: pointer; display: flex; justify-content: space-between; align-items: center; gap: 12px; }
details.fold > summary::-webkit-details-marker { display: none; }
details.fold > summary .fold-mark { display: inline-flex; color: var(--text-faint); transition: transform var(--fast) var(--ease); flex: none; }
details.fold[open] > summary .fold-mark { transform: rotate(180deg); }
details.fold > summary h2 { display: inline; }
details.fold > summary .row { min-width: 0; }
details.fold:not([open]) > summary { margin-bottom: 0 !important; }

/* Centred copy below a centred block sits under it, not flush left. */
.page p.center { margin-inline: auto; }

/* A result that is being refreshed stays readable and reads as busy. */
#out.is-stale .grid { opacity: .55; transition: opacity var(--fast) var(--ease); }

/* Air between a page title and the line under it. */
.page > h1 + p.dim, .page > h1 + .dim { margin-top: 20px; }

/* ---- FAQ: groups with air, questions that read as questions ---- */
.faq-group { font-size: var(--t-small); font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--text-faint); margin: 30px 0 10px; }
.faq-item summary { font-size: var(--t-body); line-height: 1.4; padding: 14px 0; }
.faq-item p { font-size: var(--t-body); line-height: 1.65; color: var(--text-dim); }

/* A toast that offers one action, such as Undo. */
.toast { display: flex; align-items: center; gap: 12px; }
.toast-act { flex: none; font: inherit; font-size: var(--t-cap); font-weight: 700; color: var(--brand-2, var(--brand)); padding: 6px 6px; border-radius: 8px; }
/* The white 8% the original used only ever showed in the dark theme; on the
   light body it was invisible, so the hover followed the text instead. */
.toast-act:hover { background: color-mix(in srgb, var(--text) 8%, transparent); }


/* A tap is a tap, never the first half of a double-tap-to-zoom: the browser
   no longer waits to see whether a second tap is coming before it delivers
   the click. This is the single biggest cause of a control feeling a beat
   late on a phone. */
a, button, [role='button'], summary, label, input, select, textarea, .chip, .pill, .tcard, .lb-row { touch-action: manipulation; }

/* A finger is not a cursor. A chip and a small button are about 31px tall,
   which is under every touch-target guideline there is and under what a thumb
   actually hits, so on a touch screen they get 44px. Only there: a mouse
   pointer loses nothing to a 31px target, and the desktop rows stay as dense
   as they were drawn. The width is left alone, so nothing on a narrow screen
   is pushed off the side by this. */
@media (pointer: coarse) {
  .chip, .btn-sm { min-height: 44px; }
  /* Explore's Sort, Filters and switches sit right under the market chips:
     the same height, so the two rows read as one family of controls. */
  .pill { height: 44px; }
  /* The offer in a toast is the whole of "Change amount" and "Undo", and it
     goes in a few seconds: it cannot be the smallest target on the screen. */
  .toast-act { min-height: 44px; display: inline-flex; align-items: center; }
}

/* The three buttons on a bet slip do not fit one line on a 320px screen:
   "Open this bet at FanDuel" is most of the width on its own, and the money
   buttons end up side by side a few pixels apart, which is the densest
   mis-tap risk in the product. Narrow screens stack them full width, in the
   order they are read. */
@media (max-width: 400px) {
  .slip-acts { flex-direction: column; align-items: stretch; }
  .slip-acts > .btn { width: 100%; }
  /* The row a slip lands in once it is opened at the book is now the common
     one, and "Didn't place" is the only way back out of it. On one line with
     the stake and odds boxes it ran off the side of a 320px screen, where the
     page does not scroll sideways, so the undo was unreachable. The name and
     the controls stack, and the two buttons take a line of their own under
     the boxes. */
  [data-placedslip] { flex-direction: column; align-items: stretch; }
  [data-placedslip] .v { text-align: left; }
  [data-placedslip] .v.row { flex-wrap: wrap; }
  [data-placedslip] .v.row > .btn { flex: 1 1 45%; }
}

/* ---- admin console: the business on one screen, alive ---- */
.ops-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 16px; margin-bottom: 14px; }
.ops-now { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: var(--t-small); color: var(--text-dim); }
.ops-now b { color: var(--text); }
.ops-now .sep { opacity: .45; margin: 0 2px; }
.ops-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--pos); box-shadow: 0 0 0 0 rgba(47, 217, 138, .6); animation: opsdot calc(var(--slow) * 4) var(--ease-soft) infinite; margin-right: 6px; }
@keyframes opsdot { 0% { box-shadow: 0 0 0 0 rgba(47, 217, 138, .55); } 70% { box-shadow: 0 0 0 9px rgba(47, 217, 138, 0); } 100% { box-shadow: 0 0 0 0 rgba(47, 217, 138, 0); } }
.ops-tools { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ops-search .input { width: 300px; max-width: 70vw; padding: 8px 12px; font-size: var(--t-small); }
.ops-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 14px; }
/* The card is a stack of rows, not a canvas with a chart floated on top of it:
   the spark used to be pulled out of the flow and sat over the sub-line, so on
   a 150px card the two overlapped. The sub-line takes the slack (1fr), which
   keeps every spark on the row sitting on the same baseline. */
.ops-kpi { display: grid; grid-template-rows: auto auto 1fr auto auto; overflow: hidden; background: var(--surface); border: 1px solid var(--line); border-radius: 12px; padding: 12px 12px 12px; min-height: 96px; transition: border-color var(--fast) var(--ease), transform var(--fast) var(--ease); }
.ops-kpi:hover { border-color: var(--line-strong); }
.ops-kpi.neg { border-color: rgba(255, 95, 109, .35); }
.ops-kpi[role='button'] { cursor: pointer; }
.ops-kpi-k { font-size: var(--t-cap); font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--text-faint); }
.ops-kpi-v { font-size: var(--t-title); font-weight: 700; letter-spacing: -.03em; margin-top: 6px; line-height: 1; }
.ops-kpi-s { font-size: var(--t-cap); color: var(--text-dim); margin-top: 6px; line-height: 1.4; }
.ops-kpi-sl { font-size: var(--t-cap); color: var(--text-faint); margin-top: 9px; line-height: 1.2; }
.ops-kpi .spark { display: block; width: 100%; height: 30px; margin-top: 3px; opacity: .8; }
.ops-cols { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 14px; align-items: start; }
/* One column below 980px. minmax(0, 1fr), not 1fr (the same trap as
   .grid.auto): a bare 1fr track is as wide as its widest card's content.
   One long address in the Live feed set the column's minimum, so on a
   390px phone the column ran to 477-486px and the Live and Needs-you
   cards, and the right-hand counts on every card, were cut off by the
   shell's clip (body clips x). */
@media (max-width: 979px) { .ops-cols { grid-template-columns: minmax(0, 1fr); } .ops-side { order: -1; } }
.ops-main, .ops-side { min-width: 0; }
.ops-side { display: flex; flex-direction: column; gap: 14px; position: sticky; top: calc(var(--nav-h) + 10px); }
@media (max-width: 979px) { .ops-side { position: static; } }
.ops-card { border-radius: 12px; }
/* Every standing card opens its own detail on a click, the way the KPI cards
   do, so a clickable one carries the same affordances. */
.ops-card[data-chart], .ops-card[data-card] { cursor: pointer; }
.ops-card[data-chart]:hover, .ops-card[data-card]:hover { border-color: var(--line-strong); }
.ops-card[data-chart]:focus-visible, .ops-card[data-card]:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.ops-need { font-size: var(--t-cap); padding: 8px 12px; border-radius: 12px; margin-bottom: 6px; background: var(--surface-2); border: 1px solid var(--line); }
.ops-need.warn { background: var(--warn-soft); border-color: rgba(255, 176, 32, .35); }
.ops-need.neg { background: var(--neg-soft); border-color: rgba(255, 95, 109, .35); }
.ops-need.pos { background: var(--pos-soft); border-color: rgba(47, 217, 138, .3); }
.ops-feed { display: flex; flex-direction: column; max-height: 62vh; overflow: auto; margin: 0 -6px; padding: 0 6px; }
.ops-ev { display: grid; grid-template-columns: 18px minmax(0, 1fr) auto; gap: 8px; align-items: start; padding: 8px 6px; border-radius: 12px; font-size: var(--t-cap); border-left: 2px solid transparent; }
.ops-ev[role='button'] { cursor: pointer; }
.ops-ev[role='button']:hover { background: var(--surface-2); }
.ops-ev.in { animation: opsin var(--slow) var(--ease); }
@keyframes opsin { from { opacity: 0; transform: translateY(-8px); background: var(--brand-soft); } to { opacity: 1; transform: none; } }
.ops-ev-i { color: var(--text-faint); display: inline-flex; margin-top: 1px; }
.ops-ev.pos .ops-ev-i { color: var(--pos); } .ops-ev.neg .ops-ev-i { color: var(--neg); } .ops-ev.warn .ops-ev-i { color: var(--warn); } .ops-ev.brand .ops-ev-i { color: var(--brand); }
.ops-ev-t { display: flex; flex-direction: column; min-width: 0; }
.ops-ev-t > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ops-ev-s { font-size: var(--t-cap); color: var(--text-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ops-ev-w { font-size: var(--t-cap); color: var(--text-faint); white-space: nowrap; }


/* The top bar can never be wider than the screen, whatever lands in it. */
.topbar { overflow-x: clip; }
.topbar-in > * { min-width: 0; }

/* When a trade is waiting the Review button joins the top bar; on a phone
   it takes the logo word's room, so the bar keeps every control at full tap
   size and never widens the page. */
@media (max-width: 560px) { .topbar-in:has(.topbar-review) .logo-word { display: none; } }

/* The supply goal on the admin dashboard: one number, one bar, one pace. */
.ops-goal-v { font-size: var(--t-hero); font-weight: 700; letter-spacing: -.035em; line-height: 1; }
.ops-goal-bar { height: 10px; margin-top: 10px; }
.ops-goal-bar i { background: linear-gradient(90deg, var(--brand), var(--brand-2)); }

/* ---- sign in with a provider: one row of marks, not four sentences ---- */
.oauth-label { font-size: var(--t-cap); font-weight: 600; letter-spacing: .04em; margin-bottom: 8px; text-align: center; }
.oauth-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); gap: 8px; }
.oauth-btn {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px;
  padding: 12px 6px 12px; border: 1px solid var(--line); border-radius: 12px;
  background: var(--surface); color: var(--text); text-decoration: none; min-width: 0;
  transition: border-color var(--fast) var(--ease), transform var(--fast) var(--ease), background var(--fast) var(--ease);
  -webkit-tap-highlight-color: transparent;
}
.oauth-btn:hover:not(:disabled) { border-color: var(--line-strong); background: var(--surface-2); }
.oauth-btn:active:not(:disabled) { transform: scale(.97); }
.oauth-btn:disabled { opacity: .4; cursor: not-allowed; }
.oauth-btn svg { width: 20px; height: 20px; display: block; flex: none; }
.oauth-name { font-size: var(--t-cap); font-weight: 600; color: var(--text-dim); line-height: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
.oauth-btn:hover:not(:disabled) .oauth-name { color: var(--text); }
@media (prefers-reduced-motion: reduce) { .oauth-btn:hover, .oauth-btn:active { transform: none; } }

/* The way back out, under a trade that was just placed. */
.undo-strip { border-top: 1px solid var(--line); padding-top: 14px; text-align: left; }
.undo-strip .btn { justify-content: center; }

/* Each mark is drawn to a different measure: GitHub and Google fill their
   box, Apple's glyph is tall and narrow with air around it and Discord's is
   wide and short. Matched by eye rather than by number, so the row reads as
   one set. */
.oauth-btn[data-provider='apple'] svg { width: 23px; height: 23px; margin-top: -1px; }
.oauth-btn[data-provider='discord'] svg { width: 22px; height: 22px; }
.oauth-btn[data-provider='github'] svg { width: 21px; height: 21px; }

/* The device row sits in the channel list where the others have a switch;
   its bell takes the switch's width so the labels line up. */


/* A wallet's pattern fills its circle edge to edge. */
.avatar-wallet { padding: 0; background: var(--surface-3); }
.avatar-wallet svg { width: 100%; height: 100%; display: block; }
/* Two initials have to fit a 20px circle, so they stay under the caption size. */
.avatar.xs { width: 20px; height: 20px; font-size: 9px; }

/* A firm's logo is not a photo: cropping it to a circle cuts its edges off,
   and a dark mark vanishes on the dark theme. Logos we stored (PNG) sit whole
   on a light plate; photos keep filling the circle. */
/* Padding in em: a percentage would be a share of the parent's width, which
   on a trader page is a hundred pixels, not a share of the avatar. Each size
   class sets its own font-size, so .4em is about 14% of every size. */
img.avatar[src^="/ta/"][src$=".png"] { object-fit: contain; padding: .4em; background: #fff; box-shadow: inset 0 0 0 1px rgba(15, 20, 35, .08); }

/* Connecting in another app: what is left to do there, said in the row itself. */
.chan-wait { font-size: var(--t-cap); margin-top: 2px; line-height: 1.45; color: var(--brand); font-weight: 600; }
.chan-wait::before { content: ''; display: inline-block; width: 8px; height: 8px; margin-right: 8px; border-radius: 50%; background: currentColor; vertical-align: 1px; animation: chanpulse calc(var(--slow) * 3) var(--ease-soft) infinite; }
@keyframes chanpulse { 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) { .chan-wait::before { animation: none; } }

/* A trader's kind of trading, inline in a card's sub-line. */
/* What a copied trader trades, beside their name on a copy card. A quiet
   tint, smaller than the status badge, so it reads as part of the name line.
   When the name and the tag do not fit side by side the tag moves under the
   name instead of cutting the name short. */
.sleeve-name { display: flex; align-items: center; flex-wrap: wrap; gap: 3px 8px; min-width: 0; font-weight: 600; }
.sleeve-name > .truncate { min-width: 0; max-width: 100%; }
.kind { --k: var(--text-faint); flex: none; display: inline-flex; align-items: center; height: 18px; padding: 0 8px; border-radius: 999px;
  font-size: var(--t-cap); font-weight: 600; line-height: 1; letter-spacing: 0; white-space: nowrap;
  color: color-mix(in srgb, var(--k) 80%, var(--text)); background: color-mix(in srgb, var(--k) 13%, transparent); }
/* On a white card the tint's own colour is too light to read; more text in the mix. */
[data-theme='light'] .kind { color: color-mix(in srgb, var(--k) 55%, var(--text)); }
.kind.meme { --k: #E0A000; }
.kind.sports { --k: #2F7BF0; }
.kind.stocks { --k: #0E9F8E; }
.kind.predict { --k: #8B5CF6; }
.kind.crypto { --k: #F07A1A; }
.kind.options { --k: #D9468F; }
.kind.forex { --k: #0EA5C6; }
/* An odd number of figures leaves a hole in a two-column grid; the last one takes the row.
   The five-across rule that used to sit under this one is gone: a copy card
   with a slippage figure has five of them, and forcing five columns from
   640px up is what cut "C$1,793.51" short. How many fit is now the grid's
   own decision. */
@media (max-width: 639px) { .metricgrid .m:last-child:nth-child(odd) { grid-column: 1 / -1; } }

/* "Sell when the trader sells" on a copy card: one quiet line with its switch. */
.autosell-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 12px; padding: 12px 12px; border: 1px solid var(--line); border-radius: var(--radius-sm); }
.autosell-title { font-size: var(--t-small); font-weight: 600; }
.autosell-row .switch { cursor: pointer; }
.autosell-row .switch[aria-busy='true'] { opacity: .45; pointer-events: none; }

/* ------------------------------------------------------------------ flow --
   One pass over the places this file still cut rather than moved: a dialog
   arriving and leaving, a toast arriving, a figure repainted where it
   stands. Nothing here touches layout. Every rule animates opacity,
   transform or colour only, and every one of them ends in exactly the state
   the page holds today, so a reader who never sees the first frame sees the
   same screen as before. Appended as one block so it can be read, judged and
   taken back out whole. */

/* Nothing here touches the swap between two pages: .page already fades on
   arrival, further up this file, and a second fade over the top of it buys
   nothing but a dip in the middle. */

/* A dialog opens and closes rather than appearing and vanishing.
   On a phone the dialog is a sheet, anchored to the bottom of the screen, and
   a sheet slides up: that is the idiom the reader already knows from every
   native app, and it is the one this file drew before. It keeps it, at the
   length everything else here runs at. */
.modal-bg { animation: flow-fade var(--base) var(--ease); }
@keyframes flow-fade { from { opacity: 0; } }
.modal-bg > .modal { animation: flow-up var(--base) var(--ease); }
@keyframes flow-up { from { opacity: 0; transform: translateY(24px); } to { opacity: 1; transform: none; } }
/* Centred on a wide screen it is a card, not a sheet, and there is no edge
   for it to come from: it settles from 98%, the smallest scale that reads as
   arriving rather than as blinking on. Same breakpoint as the rule that
   centres it. */
@keyframes flow-pop { from { opacity: 0; transform: scale(.98); } to { opacity: 1; transform: none; } }
@media (min-width: 640px) { .modal-bg > .modal { animation: flow-pop var(--base) var(--ease); } }
/* Leaving is quicker than arriving: nobody wants to watch a dismissal. The
   panel goes with its backdrop, and stops taking presses the instant the
   close is asked for, so a second press cannot land on a dialog that is on
   its way out. ui.js removes the node once this has run. */
.modal-bg.flow-out { animation: flow-fade-out var(--fast) var(--ease) both; pointer-events: none; }
@keyframes flow-fade-out { to { opacity: 0; } }

/* A toast rises into place from the edge it already lives on. */
.toast { animation: flow-rise var(--base) var(--ease); }
@keyframes flow-rise { from { opacity: 0; transform: translateY(10px); } }

/* A figure repainted in place changes colour rather than swapping it: the
   odometer's total going green, a percentage turning over on a live price. */
.num { transition: color var(--base) var(--ease); }

/* Asked for less motion, this pass is not there at all. The blanket rule
   further up this file already says so; this repeats it for the rules added
   here alone, so the pass survives being read on its own. */
@media (prefers-reduced-motion: reduce) {
  .modal-bg, .modal-bg > .modal, .modal-bg.flow-out, .toast { animation: none; }
  .num { transition: none; }
}

/* ------------------------------------------------------------- calendar --
   The Activity performance calendar (ws78): a real month grid over the last
   30 UTC days — a month heading, a weekday row, and one tall cell a day with
   its number on top and its realized figure under it. Every colour is a token
   the page already uses: the pos/neg softs for the treatments, surface-3 for
   the neutral days, so dark and light themes both read without new colours. */
.pcal-card .cal-week { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; margin: 12px 0 6px; }
.cal-week span { text-align: center; font-size: var(--t-cap); font-weight: 600; color: var(--text-faint); letter-spacing: .04em; }
.pcal { display: grid; grid-template-columns: repeat(7, 1fr); gap: 6px; }
/* A pad holds an empty place in the first or last week; without a height of
   its own the rows it opens and closes would be shorter than the rest. */
.cal-pad { min-height: 64px; }
/* A button reset is already global (button rule near the top), so the cell is
   a rounded tile: a 46px column at 360px wide, 52px tall so a day number and
   a figure stack without the tile feeling cramped. */
.cal-day {
  min-height: 64px; border-radius: 8px; border: 1px solid transparent;
  background: var(--surface-3); color: var(--text-faint);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; padding: 5px 2px; min-width: 0;
  font-size: var(--t-cap); font-weight: 600;
  transition: transform var(--fast) var(--ease);
}
@media (hover: hover) and (pointer: fine) { .cal-day:hover { transform: scale(1.06); } }
.cal-day:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
.cal-day.pos { background: var(--pos-soft); color: var(--pos); border-color: color-mix(in srgb, var(--pos) 32%, transparent); }
.cal-day.neg { background: var(--neg-soft); color: var(--neg); border-color: color-mix(in srgb, var(--neg) 32%, transparent); }
/* A day that closed flat: trades happened, nothing was banked. Neutral but
   with a visible edge so it does not read as an empty day. */
.cal-day.flat { background: transparent; border-color: var(--line-strong); color: var(--text-dim); }
/* The window runs back past the first of the month, so its oldest days sit
   under another month's name: dimmer, not deader — they still click. */
.cal-day.out { opacity: .55; }
/* Today gets a quiet outline, the way a calendar marks where you are. */
.cal-day.today { box-shadow: inset 0 0 0 1.5px var(--brand); }
.cal-day.sel { border-color: var(--brand); box-shadow: 0 0 0 1.5px var(--brand); }
.cal-dom { line-height: 1; }
/* The whole-dollar figure under the number, tinted with the day's own colour;
   a flat day's dot takes the same place. It keeps its size from before the
   type scale: at the caption size "+$45.50" is 51px in a 42px cell (a named
   exception in tests/styleBudget.test.js). */
.cal-fig { font-size: 9.5px; font-weight: 700; line-height: 1; letter-spacing: -.01em; }
.cal-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; opacity: .85; }
.cal-legend { gap: 6px 14px; margin-top: 10px; font-size: var(--t-cap); color: var(--text-dim); }
.cal-key { display: inline-block; width: 12px; height: 12px; border-radius: 2px; margin-right: 6px; vertical-align: -1px; }
.cal-key.pos { background: var(--pos-soft); border: 1px solid color-mix(in srgb, var(--pos) 32%, transparent); }
.cal-key.neg { background: var(--neg-soft); border: 1px solid color-mix(in srgb, var(--neg) 32%, transparent); }
.cal-key.flat { background: transparent; border: 1px solid var(--line-strong); }
.cal-key.none { background: var(--surface-3); }
/* The strip under the grid that a hover or a tap fills in: one line, always
   the same height, so pointing around never moves the page. */
.cal-detail { margin-top: 10px; padding-top: 9px; border-top: 1px solid var(--line); font-size: var(--t-cap); min-height: 20px; }
.cal-chip { margin-top: 8px; padding: 8px 12px; border: 1px solid var(--line); border-radius: var(--radius-sm); font-size: var(--t-cap); }
@media (max-width: 374px) {
  .cal-pad { min-height: 46px; }
  .cal-day { border-radius: 9px; font-size: var(--t-cap); min-height: 54px; }
  .cal-fig { font-size: 8.5px; }
}

/* --------------------------------------------------------- shared rows --
   The shapes Home, /portfolio, the copy window, the trader page and
   /activity draw with, built only from the tokens at the top of this file.
   The pages write the markup; the look lives here.

   A copy, one row each:
     <a class="copyrow is-paused" href>avatar(t, 'sm')
       <span><span class="copyrow-name">Name</span><span class="copyrow-sub">Paused</span></span>
       <span><span class="copyrow-val num">$1,006.20</span><span class="copyrow-pct num pos">+0.6%</span></span></a>
   What happened since the last visit:
     <div class="since"><div class="since-when">Since Tuesday</div><div class="since-line">14 trades, +$42.10</div></div>
   Something that needs a decision:
     <div class="needs"><span>Words</span><span class="needs-actions"><button class="btn btn-sm">..</button></span></div>
   One more copy:
     <a class="addrow" href>${icon('plus', 20)}Copy another trader</a>
   A window's head and its close, and the button row that stays on screen:
     <div class="modal-head"><h3>..</h3><button class="modal-close" data-close aria-label="Close">${icon('x', 18)}</button></div>
     <div class="sheet-foot"><button class="btn btn-primary">..</button></div>
   A holding on a phone:
     <div class="holdrow"><span><b>NVDA</b><span>Trader · 4.35M</span></span><span><b class="num">$1,006.20</b><span class="num pos">+0.6%</span></span></div>
   The trader page's first figures:
     <div class="trader-hero"><div><div class="fig-k">This week</div><div class="fig num pos">+8.6%</div><div class="fig-b">S&P +1.8%</div></div>..</div>
   Your copy of this trader, as a link: <a class="mycopy" href>..</a>
   A line of the trade feed: <div class="feedrow"><span>..</span><span>..</span></div> */
.copyrow {
  display: grid; grid-template-columns: 36px 1fr auto; align-items: center; gap: 12px;
  min-height: 64px; padding: 10px 0; border-bottom: 1px solid var(--line); color: inherit;
}
.copyrow > .avatar { width: 36px; height: 36px; font-size: var(--t-cap); }
.copyrow > span { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.copyrow > span:last-child { align-items: flex-end; text-align: right; }
.copyrow-name {
  font-size: var(--t-body); line-height: 1.35; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.copyrow-sub {
  font-size: var(--t-cap); line-height: 1.35; color: var(--text-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.copyrow.is-paused .copyrow-sub { color: var(--warn); }
.copyrow-val { font-size: var(--t-body); line-height: 1.35; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.copyrow-pct { font-size: var(--t-cap); line-height: 1.35; font-variant-numeric: tabular-nums; white-space: nowrap; }
a.copyrow:hover .copyrow-name { color: var(--brand); }

.since { background: var(--surface-2); border-radius: var(--radius); padding: 14px 16px; }
.since-when { font-size: var(--t-cap); line-height: 1.35; color: var(--text-dim); }
.since-line { font-size: var(--t-body); line-height: 1.5; margin-top: 2px; }

/* The words keep at least 220px; two buttons that would squeeze them into
   a column of single words go under them instead, and below 360px they
   always do. */
.needs { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 12px; }
.needs > :first-child { flex: 1 1 220px; min-width: 0; }
.needs-actions { display: flex; flex: none; align-items: center; gap: 8px; }
@media (max-width: 359px) {
  .needs { flex-direction: column; align-items: stretch; }
  .needs > :first-child { flex: none; }
  .needs-actions { flex-wrap: wrap; }
}

.addrow {
  display: flex; align-items: center; gap: 12px; min-height: 56px;
  font-size: var(--t-body); font-weight: 600; color: var(--text-dim);
  transition: color var(--fast) var(--ease);
}
.addrow > svg {
  flex: none; width: 36px; height: 36px; padding: 8px; box-sizing: border-box;
  border: 1px dashed var(--line-strong); border-radius: 50%;
}
.addrow:hover { color: var(--text); }

.modal-head { position: relative; padding-right: 48px; }
.modal-close {
  position: absolute; top: -12px; right: -12px; width: 44px; height: 44px;
  display: grid; place-items: center; border-radius: var(--radius-sm); color: var(--text-dim);
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.modal-close:hover { background: var(--surface-2); color: var(--text); }

.sheet-foot {
  position: sticky; bottom: 0; z-index: 1;
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
  background: var(--surface); border-top: 1px solid var(--line);
}
/* Inside a window it reaches the window's edges and its bottom, so the
   button row is the last thing on the sheet rather than a box inside it. A
   sticky box stops at the scroller's padding, so it is told to go past it. */
.modal > .sheet-foot {
  margin: 16px -18px calc(-18px - env(safe-area-inset-bottom, 0px));
  bottom: calc(-18px - env(safe-area-inset-bottom, 0px));
}

.holdrow {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 12px;
  min-height: 56px; padding: 8px 0; border-bottom: 1px solid var(--line);
}
.holdrow > span { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.holdrow > span > * { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.holdrow > span > :first-child { font-size: var(--t-body); line-height: 1.35; font-weight: 600; }
.holdrow > span > :nth-child(2) { font-size: var(--t-cap); line-height: 1.35; }
.holdrow > span:first-child > :nth-child(2) { color: var(--text-dim); }
.holdrow > span:last-child { align-items: flex-end; text-align: right; font-variant-numeric: tabular-nums; }

.trader-hero { display: flex; gap: 24px; }
.trader-hero .fig { font-size: var(--t-title); line-height: 1.25; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.trader-hero .fig-k, .trader-hero .fig-b { font-size: var(--t-cap); line-height: 1.35; color: var(--text-dim); }

.mycopy {
  display: block; padding: 14px 16px; color: inherit;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.mycopy:hover { border-color: var(--line-strong); background: var(--surface-2); }

.feedrow { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 12px; min-height: 56px; }

/* ------------------------------------------- Home and /portfolio (WS-B) --
   One column of the reader's own money: the total, what changed since the
   last visit, the copies a limit stopped, then one row per copy. Built from
   the rows above and the tokens; no size or timing of its own. */
.hub-copies { max-width: 720px; margin-inline: auto; }
.pf-col { max-width: 760px; margin-inline: auto; }
.hub-since { margin-top: 6px; }
/* 44 px to press: the total, and the Portfolio link over the rows. */
.hub-value { min-height: 44px; align-content: center; }
.hub-list { margin-top: 24px; }
.hub-list-head { align-items: center; border-bottom: 1px solid var(--line); }
.hub-list-head .link { display: inline-flex; align-items: center; min-height: 44px; }
.copylist-all { display: inline-block; padding: 12px 0 4px; }
button.copyrow { width: 100%; text-align: left; border-radius: 0; }
button.copyrow:hover .copyrow-name { color: var(--brand); }
.needs-list { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius); padding: 0 16px; }
.needs-list > .needs { padding: 14px 0; }
/* Sentences, not labels: no word left alone on a last line. */
[data-since] .since-line, .needs-list .t-small, .needs-list .t-body { text-wrap: pretty; }
.needs-list > .needs + .needs { border-top: 1px solid var(--line); }
.needs-list > .link, .needs-list > .t-small { display: block; padding: 0 0 14px; }
/* /portfolio: the choice under its own row (copyRows needsUnderHtml), set in
   to the name so the row and its choice read as one. */
.copyrow:has(+ .needs-under) { border-bottom: 0; padding-bottom: 2px; }
.needs-under { padding: 0 0 14px 48px; border-bottom: 1px solid var(--line); }
.needs-under > :first-child { text-wrap: pretty; }
/* "Make it real" the same way: under its own copy, not floating between two
   lines where it read as the next copy's. */
.copyrow:has(+ .make-real) { border-bottom: 0; padding-bottom: 0; }
.copylist > .make-real { margin-top: 0; padding-left: 48px; border-top: 0; border-bottom: 1px solid var(--line); }
/* The folds on /portfolio and in the copy window: 44 px to press. */
.pf .explain summary, .risk-limits summary { min-height: 44px; }
.pf-total { margin-top: 4px; font-weight: 700; }
/* Real money not read yet: the words where the figure would be. */
.pf-note { margin-top: 4px; }
.pf-line { margin-top: 6px; }
.pf-side { display: grid; gap: 16px; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 980px) { .pf-side { grid-template-columns: repeat(3, minmax(0, 1fr)); align-items: start; } }
.pf-holdings { padding-top: 4px; padding-bottom: 4px; }
/* The rest of /portfolio on a phone: one card, a fold per part, "+" like
   every other disclosure. */
.pf-holdings-head { margin: 28px 0 12px; border-bottom: 0; padding-bottom: 0; }
.pf-folds { padding-top: 0; padding-bottom: 0; }
.pf-fold + .pf-fold { border-top: 1px solid var(--line); }
.pf-fold > summary { list-style: none; cursor: pointer; display: flex; align-items: center; min-height: 52px; }
/* The fold titles in sentence case, set like the page's other fold ("How
   your money is split"), not as upper-case card labels beside it. */
.pf-fold > summary { font-size: var(--t-small); font-weight: 600; color: var(--text-dim); }
.pf-fold[open] > summary { color: var(--text); }
.pf-fold > summary::-webkit-details-marker { display: none; }
.pf-fold > summary::after { content: '+'; margin-left: auto; color: var(--text-faint); font-size: var(--t-body); font-weight: 600; letter-spacing: 0; }
.pf-fold[open] > summary::after { content: '\2013'; }
.pf-fold-body { padding-bottom: 16px; }
@media (max-width: 599px) { .card.pad-lg.pf-hero { padding: 16px; } }
.holdrow.go { grid-template-columns: minmax(0, 1fr) auto 14px; cursor: pointer; }
.holdrow.go > svg { color: var(--text-faint); }
.holdrows > .holdrow:last-child { border-bottom: 0; }
.second-copy { margin-top: 16px; }
.second-copy .copyrow:last-child { border-bottom: 0; }
.second-pick .copyrow-sub { white-space: normal; text-wrap: pretty; }
.second-pick { grid-template-columns: 36px minmax(0, 1fr) auto; }
/* Under 360px there is no room for a button beside the words: each pick's
   button goes under its words, and "Not now" to the foot of the card, so
   the title and the names keep a line each. */
@media (max-width: 359px) {
  .second-copy { display: flex; flex-direction: column; }
  .second-copy > .spread { display: contents; }
  .second-copy [data-second-later] { order: 1; align-self: flex-start; margin-top: 8px; }
  .second-pick { grid-template-columns: 36px minmax(0, 1fr); row-gap: 8px; }
  .second-pick > .avatar { align-self: start; }
  .second-pick > span:last-child { grid-column: 2; align-items: flex-start; }
}
/* A holding's money column, right-aligned like a copy's: the chart mark is
   the row's last child, so span:last-child above does not reach it. */
.holdrow.go > span:nth-child(2) { align-items: flex-end; text-align: right; font-variant-numeric: tabular-nums; }
/* Real money's steps (Home's strip, /portfolio's card) on a phone: the words
   take the width and the button goes under them, set in to the words. */
.steps-strip { align-items: center; }
@media (max-width: 599px) {
  .steps-strip { flex-wrap: wrap; align-items: flex-start; }
  .steps-strip > .grow { flex: 1 1 calc(100% - 29px); min-width: 0; }
  .steps-strip > .btn { margin-left: 29px; }
}

/* The copy sheet: four figures two by two, two actions of the same size,
   then a quiet list. */
/* Two by two in the sheet, whatever its width: a 480px window with four
   figures read as a table otherwise, and on a phone as three and one. Each
   cell is half the sheet, wide enough for "$4,486.83" at full size. */
.sleeve-sheet [data-sheet-figures] { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
/* A long trader name takes "<name> same period" to two lines; the two
   figures of a row still sit on one line, at the foot of their cells. */
.sleeve-sheet [data-sheet-figures] .m { display: flex; flex-direction: column; }
.sleeve-sheet [data-sheet-figures] .m > .v { margin-top: auto; }
.sheet-status-held { color: var(--warn); }
.sheet-actions { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 16px; }
.sheet-actions > :only-child { grid-column: 1 / -1; }
.sheet-rows { margin-top: 12px; border-top: 1px solid var(--line); }
.sheet-row {
  display: flex; align-items: center; gap: 12px; width: 100%; min-height: 48px; padding: 0 4px;
  border-bottom: 1px solid var(--line); font-size: var(--t-body); text-align: left; color: var(--text);
  transition: background var(--fast) var(--ease);
}
.sheet-row > span { flex: 1; min-width: 0; }
.sheet-row > svg { flex: none; color: var(--text-dim); }
.sheet-row > svg:last-child { color: var(--text-faint); }
.sheet-row:hover { background: var(--surface-2); }
.sheet-row:last-child { border-bottom: 0; }

/* The copy window's start: two ways to begin, as one control, each label
   free to take two lines on a narrow phone rather than run off it. */
.entryseg { display: flex; width: 100%; margin-top: 0; }
/* Inside a window the chip row reaches the window's own edges. */
.modal .chipscroll { margin-inline: -18px; padding-inline: 18px; }
.entryseg button { flex: 1 1 0; min-width: 0; white-space: normal; line-height: 1.3; min-height: 44px; }
.risk-limits { margin-top: 16px; }
/* A line of "What this buys today": what and how much on the first line,
   the detail of each under it, so neither side wraps where it happens to. */
.pv-row { align-items: flex-start; }
.pv-row > span:first-child { min-width: 0; }
.pv-row > span:last-child { text-align: right; }
.pv-row .pv-sub { display: block; font-size: var(--t-cap); line-height: 1.5; }
.pv-row > span:last-child > .pv-sub, .pv-row .pv-price { white-space: nowrap; }

/* ------------------------------------------------------------------ Pro --
   One language for Pro across the site (the go-live pass, 2026-09-30).

   The fill first. White on the dark theme's #4F7BFF is 3.74:1, under the 4.5
   that text of button size needs, and every primary button is white text on
   it. The same blue with 12% black is #466CE0, 4.69:1. The light theme's
   #2563EB already passes at 5.17:1 and stays as it was. Hover darkens from
   the fill rather than from the brand, or in the dark theme the resting and
   hovered colours would be the same. */
:root { --brand-fill: color-mix(in srgb, var(--brand) 88%, #000); }
[data-theme='light'] { --brand-fill: var(--brand); }
.btn-primary { background: var(--brand-fill); }
.btn-primary:hover { background: color-mix(in srgb, var(--brand-fill) 88%, #000); }

/* The one Pro visual: an outlined PRO beside an action that needs Pro. A
   label, so it never moves and is never filled. On a filled button it takes
   the button's own ink, or it would be blue on blue. */
.pro-mark {
  display: inline-flex; align-items: center; flex: none; height: 20px; padding: 0 7px;
  border: 1px solid color-mix(in srgb, var(--brand) 45%, transparent); border-radius: 999px;
  background: none; color: var(--brand);
  font-family: var(--sans); font-size: 11px; font-weight: 700; letter-spacing: .08em; line-height: 1;
  white-space: nowrap; vertical-align: middle;
}
.btn-primary .pro-mark { color: inherit; border-color: color-mix(in srgb, currentColor 55%, transparent); }

/* One quiet line that offers real money: a hairline box, dim words and an
   arrow. The link is the whole row; the x, when there is one, is its own
   control beside it, 32px to the eye and 44px to a finger. Two sentences go
   one to a span inside a span, so a phone breaks the row at the full stop.
     <div class="pro-line"><a href data-link>Words ${icon('arrow', 16)}</a>
       <button type="button" aria-label="Not now">${icon('x', 16)}</button></div> */
.pro-line {
  display: flex; align-items: center; min-height: 44px;
  border: 1px solid var(--line); border-radius: 14px; background: transparent;
  font-size: 13.5px; line-height: 1.4; color: var(--text-dim);
}
.pro-line > a {
  flex: 1; min-width: 0; align-self: stretch; display: flex; align-items: center; gap: 10px;
  padding: 10px 14px; color: inherit; text-wrap: pretty;
}
.pro-line > a > svg { flex: none; margin-left: auto; }
.pro-line > a > span > span { white-space: nowrap; }
.pro-line > a:hover { color: var(--text); }
.pro-line > button {
  position: relative; flex: none; display: grid; place-items: center; width: 32px; height: 32px; margin-right: 6px;
  padding: 0; border: 0; border-radius: 10px; background: none; color: var(--text-faint); cursor: pointer;
}
.pro-line > button:hover { color: var(--text); background: var(--surface-2); }
.pro-line > button::after { content: ''; position: absolute; inset: -6px; }

/* Under a trader's copy button, for somebody on paper: the way to real money,
   as quiet as a caption. It lands after the page has drawn, so it fades in
   rather than appearing with a jump of colour. */
.real-link {
  display: flex; align-items: center; justify-content: center; gap: 8px; min-height: 44px;
  font-size: 13.5px; font-weight: 500; color: var(--text-dim);
  animation: fade var(--fast) var(--ease);
}
.real-link:hover { color: var(--text); }
@media (min-width: 560px) { .tactions:has(.real-link) { align-items: flex-start; } }

/* The shapes the go-live entry points use. The pages write the markup and
   this file owns the look, so none of them carries a style of its own.

   The real-money row at the top of the dashboard:
     <div class="real-row"><span class="real-logos">marks</span>
       <span class="real-text"><b>Title</b><small>Meta</small></span>
       <button class="btn">Switch</button></div>
   The card after a first paper copy:
     <div class="moment">avatar(t, '')<span class="real-text"><b>Copying Name on paper</b>
       <small>Make it real?</small></span><a class="btn">Go live</a>
       <button type="button" aria-label="Close">${icon('x', 16)}</button></div>
   The row on a paper copy's card:
     <a class="make-real" href data-link>Make it real ${proMark()} ${icon('arrow', 16)}</a>
   Inside modal(), when a Free limit is reached:
     <div class="limit-sheet">${proMark()}<h3>..</h3><p>..</p>
       <a class="btn btn-primary">..</a><button class="btn" data-close>Not now</button></div> */
.real-row {
  display: flex; align-items: center; gap: 12px; min-height: 56px; padding: 8px 8px 8px 12px;
  background: var(--surface); border: 1px solid var(--line); border-radius: 16px;
}
/* Side by side, 8px apart, as the go-live flow's .gl-stack draws them: a
   brand's mark runs close to the edge of its tile, so any overlap covers
   part of it. */
.real-logos { display: flex; flex: none; align-items: center; gap: 8px; }
.real-logos > * { flex: none; }
/* The mark in the middle of its tile, as the go-live flow draws it (these
   pages do not load golive.css), and a white tile edged on the white row. */
.real-logos > .vmark { display: grid; place-items: center; overflow: hidden; }
[data-theme='light'] .real-logos > .vmark { box-shadow: 0 0 0 1px rgba(31, 35, 40, .08); }
/* Under 360px three marks left the title 45px: "Finish / going / live", one
   word a line. The title and its button are the row; the marks give way. */
@media (max-width: 359px) { .real-logos { display: none; } }
.real-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
/* A title that does not fit takes a second line: at 320px an ellipsis cut
   "Copying Warren on paper" to "Copying Wa..." and "Real money is ready" to
   "Real money is rea...". */
.real-text b { font-size: 14px; font-weight: 600; color: var(--text); text-wrap: balance; overflow-wrap: anywhere; }
.real-text small { font-size: 12.5px; color: var(--text-dim); }
/* Their one button is monochrome, the way the go-live flow's is: ink on the
   page's own ground, never the brand fill. It is 36px to the eye and 44px to
   a finger; the reach is vertical only, so it never meets the x beside it,
   and it is 5px because an inset counts from inside the .btn's 1px border. */
.real-row > .btn, .moment > .btn {
  position: relative; flex: none; min-height: 36px; padding: 0 14px; border-radius: 10px; font-size: 13.5px;
  background: var(--text); color: var(--bg); border-color: transparent; box-shadow: none;
}
.real-row > .btn:hover, .moment > .btn:hover { background: color-mix(in srgb, var(--text) 86%, var(--bg)); }
.real-row > .btn::after, .moment > .btn::after { content: ''; position: absolute; inset: -5px 0; }

/* Above the phone's tab bar, and in the corner on a desktop. It rises into
   place once; the page decides when it goes. */
.moment {
  position: fixed; z-index: 80; left: 12px; right: 12px;
  bottom: calc(var(--nav-h) + env(safe-area-inset-bottom, 0px) + 8px);
  display: flex; align-items: center; gap: 12px; padding: 12px 8px 12px 12px;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: 20px; box-shadow: var(--shadow);
  animation: moment-rise var(--slow) var(--ease-soft) both;
}
@keyframes moment-rise { from { opacity: 0; transform: translateY(16px); } }
@media (min-width: 1024px) { .moment { left: auto; right: 24px; bottom: 24px; width: 360px; } }
.moment > .avatar { flex: none; width: 36px; height: 36px; font-size: 13px; }
/* Under 360px the avatar gives its width to the words: with it, the title
   had 85px and took three or four lines. */
@media (max-width: 359px) { .moment > .avatar { display: none; } }
.moment .real-text small { font-size: 13px; }
/* The x sits right above the tab bar, so it reaches 44px the way the
   .pro-line x does, and a thumb that lands near it still closes the card. */
.moment > button:not(.btn) {
  position: relative; flex: none; display: grid; place-items: center; width: 32px; height: 32px;
  padding: 0; border: 0; border-radius: 10px; background: none; color: var(--text-faint); cursor: pointer;
}
.moment > button:not(.btn):hover { color: var(--text); background: var(--surface-3); }
.moment > button:not(.btn)::after { content: ''; position: absolute; inset: -6px; }

/* A row inside the card, not another button beside Pause and Stop. */
.make-real {
  display: flex; align-items: center; gap: 8px; min-height: 44px; margin-top: 12px;
  border-top: 1px solid var(--line);
  font-size: 14px; font-weight: 600; color: var(--text);
}
.make-real > svg:last-child { margin-left: auto; color: var(--text-dim); }
.make-real:hover > svg:last-child { color: var(--text); }

.limit-sheet { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 8px 4px 0; }
.limit-sheet h3 { margin: 14px 0 0; font-size: 21px; }
.limit-sheet p { margin: 6px 0 0; font-size: 14px; color: var(--text-dim); }
.limit-sheet .btn { width: 100%; }
.limit-sheet .btn-primary { margin-top: 24px; min-height: 48px; }
.limit-sheet .btn:not(.btn-primary) {
  margin-top: 4px; min-height: 44px; background: none; border-color: transparent; color: var(--text-dim); font-weight: 500;
}
.limit-sheet .btn:not(.btn-primary):hover { color: var(--text); }

/* ---- inside the iPhone app:
   The site always fills the display (viewport-fit=cover above), so inside the
   app's shell the notch and home indicator would lie over our own furniture.
   Everything here is behind .in-shell, which the head script adds only when
   Capacitor is present, so a browser is untouched. */
.in-shell .topbar { padding-top: env(safe-area-inset-top, 0px); }
.in-shell .modal { padding-top: max(18px, env(safe-area-inset-top, 0px)); }

/* ---- inside the iPhone app: the Apple design pass.
   The app is this site in a native shell, and everything that made the shell
   read as "a website on a phone" is restated here under HIG rules, behind
   .in-shell, which only the shell adds. A browser is untouched in both
   orientations. Radii, heights and blur are Apple's own numbers where one
   exists: 44pt targets, ~10-14pt corner radius on controls, 20pt on sheets,
   .5pt hairlines, the tab bar and top bar of every system app (translucent,
   49pt of tab bar, blur with saturation so the page stays visible and alive
   behind them). ---- */

/* Controls adopt Apple's radii and the 44pt minimum target everywhere. */
:root.in-shell {
  --radius: 14px;
  --radius-sm: 10px;
  --radius-lg: 20px;
  --nav-h: 52px;
}

/* The machinery of a browser, gone: grey highlights on every touch,
   scrollbars running down the side of a phone. */
.in-shell { -webkit-tap-highlight-color: transparent; }
.in-shell ::-webkit-scrollbar { width: 0; height: 0; }

/* The tab bar: the one at the bottom of every Apple app. Translucent glass
   (saturation boost, 20pt blur), a .5 hairline, 49pt of content. The tokens
   keep it honest in light and dark. Theme tokens are used as-is. */
.in-shell .bottomnav {
  height: calc(var(--nav-h) + env(safe-area-inset-bottom, 0px));
  padding-bottom: env(safe-area-inset-bottom, 0px);
  background: color-mix(in srgb, var(--bg-elev) 76%, transparent);
  -webkit-backdrop-filter: saturate(1.8) blur(20px);
  backdrop-filter: saturate(1.8) blur(20px);
  border-top: .5px solid color-mix(in srgb, var(--text) 16%, transparent);
}
.in-shell .bottomnav svg { width: 23px; height: 23px; }
.in-shell .bottomnav a.active svg { color: var(--brand); transform: translateY(-2px); }
.in-shell .bottomnav a:active { transform: scale(.92); opacity: .7; }

/* The top bar: taller glass matching the tab bar it faces; the decorative
   gradient underline the browser drew goes away, a real app's bar has a
   hairline or nothing at all. */
.in-shell .topbar {
  background: color-mix(in srgb, var(--bg) 74%, transparent);
  -webkit-backdrop-filter: saturate(1.8) blur(22px);
  backdrop-filter: saturate(1.8) blur(22px);
}
.in-shell .topbar::after { display: none; }

/* Buttons and rows: 44pt targets, a spring on the press. The overshoot curve
   is the one iOS buttons return with; scale(.96) at rest of press is what a
   native button does when you thumb it. */
.in-shell .btn { min-height: 44px; padding: 12px 16px; transition: transform var(--fast) var(--ease-soft); }
.in-shell .btn:active { transform: scale(.96); }
.in-shell .chip, .in-shell [role="button"], .in-shell summary { min-height: 44px; }
.in-shell .choice, .in-shell .style-card, .in-shell .book-tile, .in-shell .tabbar button, .in-shell .sortbar-toggle { transition: transform var(--fast) var(--ease-soft); }
.in-shell .choice:active, .in-shell .tabbar button:active { transform: scale(.97); }

/* Sheets: the modal is already bottom-anchored; inside the shell it gets the
   iOS grabber, the 20pt crown and a slower, softer rise. */
.in-shell .modal { position: relative; border-radius: 20px 20px 0 0; padding-top: 22px; }
.in-shell .modal::before {
  content: ''; position: absolute; top: 6px; left: 50%; transform: translateX(-50%);
  width: 36px; height: 5px; border-radius: 3px;
  background: color-mix(in srgb, var(--text) 28%, transparent);
}

/* Text fields: iOS grows the view to a 16px caret otherwise, and the zoom on
   every focus read as a bug. --t-lead (17px) is Apple's own form size, and it
   is one of the six so the type scale stays whole. */
.in-shell input:not([type="checkbox"]):not([type="range"]):not([type="radio"]),
.in-shell select, .in-shell textarea { font-size: var(--t-lead); }

/* Decorative page glow is browser furniture; the app's background is plain,
   deep, and quiet behind the glass bars, the way a native app sits. */
.in-shell #pageglow { display: none; }

/* ---- the app's chrome (inside the shell, phone width): the words and boxes
   a website needs are not an app's. Nothing here runs in a browser: the
   markup only exists when app.js shellPhone() drew it. ---- */

/* The bar: the screen's name centred (the condensed title), actions trailing,
   no wordmark and no search box - the search is its own screen behind the
   magnifier. The glass above already comes from the .in-shell pass. */
.topbar-in.shellbar {
  position: relative;
  display: flex; align-items: center; gap: 4px;
  padding: 6px 8px 6px 10px; /* the notch room comes from the .topbar rule above; counted once */
  width: 100%; /* the bar spans the screen itself now, not a 1240px grid */
}
.shellbar .badge { flex: none; }
/* The centred title, laid out as a flex child so it centres in the space
   it actually owns, between the leading badge and the trailing actions.
   Absolutely centred on the screen it sat on top of the balance pill,
   which travels in the trailing group, once the big title scrolled away
   on any member screen. It fades the way a big title hands over;
   opacity follows the mount; the transition is fast and quiet. */
.shellbar [data-bar-title] {
  flex: 1 1 auto; min-width: 0; text-align: center; white-space: nowrap;
  font-size: var(--t-lead); font-weight: 700; letter-spacing: -0.02em; color: var(--text);
  opacity: 0; transition: opacity var(--fast) var(--ease); pointer-events: none;
  overflow: hidden; text-overflow: ellipsis; padding: 0 6px;
}
.shellbar [data-bar-title].show { opacity: 1; }

/* The big title: part of the page it names, scrolled away under the glass.
   34pt bold, the exact number of every Apple app that uses large titles. */
.largetitle {
  margin: 6px 2px 2px; font-size: var(--t-hero); line-height: 1.1; font-weight: 800;
  letter-spacing: -0.02em; color: var(--text);
}

/* Trailing actions sit flush, 40pt hands of icon buttons; the avatar keeps its
   ring because it is the one place in the app shaped like a person. */
.shellbar .topbar-actions { margin-left: auto; display: flex; align-items: center; gap: 2px; }
.shellbar .iconbtn { width: 40px; height: 40px; border-radius: 12px; }

/* With the large title speaking for the screen, the page's own heading is
   the same words again two lines down; where the app drew a large title it
   takes the name and the page continues with what followed the heading.
   Any claimed h1, wherever it sits: portfolio's nests in .pf-col, which is
   why the direct-child rule kept drawing its second "Portfolio". */
html.in-shell h1.bar-owned { display: none; }

/* ---- the app's surfaces: every list reads as an iOS inset grouped list and
   every card as one of its cells. Flat, quiet, hairline-separated: the
   browser's gradient cards and button glow are website furniture. ---- */

/* Cells: a flat elevated surface with a near-invisible hairline, the grouped
   cell Apple's Settings draws. No gradient, no glow. */
@media (max-width: 1023px) {
  .in-shell .card {
    background: var(--bg-elev);
    border-color: color-mix(in srgb, var(--text) 6%, transparent);
    box-shadow: none;
  }
  .in-shell .card.flat { background: var(--bg-elev); }
  .in-shell .btn-primary { box-shadow: none; }

  /* The screens' rhythm: the large title anchors the top, so the page starts
     tight and every section head sits a thumb's width below what is above. */
  .in-shell .page { padding-top: 2px; }
  .in-shell .section-head { margin: 24px 6px 10px; }

  /* A pressed cell darkens, like a row in a native list. */
  .in-shell .card.hoverable:active { background: color-mix(in srgb, var(--text) 4%, var(--bg-elev)); }

  /* Toasts: floating glass pills over the tab bar, and the outcome stripe
     becomes a quiet accent ring, the way a native notice carries its state. */
  .in-shell .toast {
    background: color-mix(in srgb, var(--bg-elev) 82%, transparent);
    -webkit-backdrop-filter: saturate(1.8) blur(20px);
    backdrop-filter: saturate(1.8) blur(20px);
    border: 1px solid color-mix(in srgb, var(--text) 12%, transparent);
    border-radius: 14px;
  }
  .in-shell .toast.good { border: 1px solid color-mix(in srgb, var(--pos) 45%, transparent); }
  .in-shell .toast.bad { border: 1px solid color-mix(in srgb, var(--neg) 45%, transparent); }
  .in-shell .toast.warn { border: 1px solid color-mix(in srgb, var(--warn, #d9932b) 45%, transparent); }

  /* The bar's hairline is as thin as Apple's: a .5 line separating the page
     from the glass, present but not drawn attention to. */
  .in-shell .topbar { border-bottom: .5px solid color-mix(in srgb, var(--text) 10%, transparent); }

  /* The money screen: the ask is a crowned sheet, the ticker is the title of
     the decision, and the answers are the buttons the card already has. */
  .in-shell .confirm-card { border-radius: var(--radius-lg); border-width: 1.5px; }
  .in-shell .confirm-tick { font-size: var(--t-title) !important; letter-spacing: -0.02em; }
}

/* ---- the app while signed out: the site's furniture leaves for good. ---- */

/* A signed-out reader in the browser was met by the full site: its footer with
   the address block, the BIN line, a links row and a fine-print paragraph. An
   app has no footer; the pages end, and anything the reader needed from that
   footer is a row in the Account screen instead. None of it runs in a
   browser. */
@media (max-width: 1023px) {
  html.in-shell footer { display: none; }

  /* The sign-in wall and the join screen are whole screens, not pages under a
     bar: they carry the app mark and their own title, so the chrome leaves and
     takes its empty glass with it. */
  html.in-shell #topbar:empty { min-height: 0; background: transparent; border-bottom: 0; }

  /* The bar while signed out: the brand tile only, no words (owner,
     2026-10-04: "since when do apps have headers, why is there so many
     words"). The tile is the logo's own colours: the teal-to-blue gradient
     with the glyph on it, not the bare dark stroke a page's text colour gave
     it, which read as a black square nobody asked for. */
  .in-shell .bar-brand {
    display: grid; place-items: center; width: 34px; height: 34px; border-radius: 12px;
    background: linear-gradient(140deg, #39E0CF, #4F7BFF);
    color: #06201D; box-shadow: 0 4px 14px rgba(79, 123, 255, .3);
    transition: transform var(--fast) var(--ease-soft);
  }
  .in-shell .bar-brand:active { transform: scale(.94); }
}

/* ---- Apple's rules, applied where the app is read: presses compress instead
   of anything sliding, the tab bar is glass the page can come through, and
   every hairline is a .5 line. Nothing here runs in a browser. ---- */
@media (max-width: 1023px) {
  /* A press on a phone is the control being held down, not a shadow moving:
     the control itself compresses a point under the hand and regains it on
     release, --press fast enough to feel springy and not playful. */
  .in-shell .btn:active,
  .in-shell .chip:active,
  .in-shell .iconbtn:active,
  .in-shell .tabbar button:active {
    transform: scale(.96);
    transition: transform var(--press) var(--ease);
  }
  /* A held cell barely moves; the weight of the list stays put. */
  .in-shell .card.hoverable:active { transform: scale(.995); transition: transform var(--fast) var(--ease); }

  /* The bottom bar is a material, not a painted strip: what you are reading
     keeps coming through it, saturated and blurred, hairline on top - the
     exact construction of the system tab bar. */
  html.in-shell .bottomnav {
    background: color-mix(in srgb, var(--bg-elev) 86%, transparent);
    -webkit-backdrop-filter: saturate(1.8) blur(20px);
    backdrop-filter: saturate(1.8) blur(20px);
    border-top: .5px solid color-mix(in srgb, var(--text) 10%, transparent);
  }
}

/* ---- build 7, the finishing pass: the leftovers that still read as a
   website. Bar icons stop being boxed chips (a nav bar's icons are bare
   glyphs), segmented controls take the system's white-pill look, feed rows
   stop clipping a button, and pulling down answers the hand. Nothing here
   runs outside the shell. ---- */

/* The bar's buttons: bare glyphs at Apple's tint, the avatar keeping its
   identity ring. In a browser these stay the chips they always were. */
@media (max-width: 1023px) {
  .in-shell .shellbar .iconbtn {
    background: transparent;
    border-color: transparent;
    color: var(--text);
  }
  .in-shell .shellbar .topbar-review {
    background: transparent;
    border-color: transparent;
    color: var(--neg);
  }
  .in-shell .shellbar .iconbtn:active { opacity: .5; }

  /* Segmented controls: the system's own construction, a track behind an
     active white pill that lifts a point. Light and dark both follow the
     tokens, so the same rule reads right in both. */
  .in-shell .tabbar {
    background: color-mix(in srgb, var(--text) 7%, var(--bg-elev));
    border-color: transparent;
    padding: 2px;
    border-radius: 13px;
  }
  .in-shell .tabbar button {
    border-radius: 11px;
    padding: 8px 12px;
  }
  .in-shell .tabbar button.on {
    background: var(--bg-elev);
    color: var(--text);
    box-shadow: 0 1px 3px rgba(0, 0, 0, .16), 0 0 0 .5px color-mix(in srgb, var(--text) 7%, transparent);
  }

  /* Feed actions: three growing buttons in 390pt clipped the middle one's
     words mid-name ("Follow with Pro"); let the row break a line instead. */
  .in-shell .feedcard .row.mt { flex-wrap: wrap; }
  .in-shell .feedcard .row.mt .btn { flex: 1 1 auto; }
}

/* Pull to refresh's own pill is gone with build 8: the refresh control and
   the rubber band are the system's, on the webview's scroll view. */

/* ---------------------------------------------------------------- onboarding --
   The app's first run: splash, one proof screen, sign in, Pro. Whole-screen
   by design (owner, 2026-10-04): no bar, no sidebar, no tab row - focus-mode
   removes the frame, and these styles are only ever seen inside it. The
   palette is the site's; the sizes are the phone's. Safe areas are answered
   here because no bar carries the notch for this screen. */
.ob {
  /* min-height:100% dies here: html/body carry no height and the shell's #view
     is auto, so the percentage collapses to 0 and every step packs the top.
     Viewport units keep the screen full regardless of the parent chain. */
  min-height: 100vh; min-height: 100dvh;
  padding: max(34px, env(safe-area-inset-top)) 22px max(22px, env(safe-area-inset-bottom));
  display: flex; align-items: stretch; justify-content: center;
}
.ob-step { width: 100%; max-width: 430px; margin: 0 auto; display: flex; }
.ob-inner {
  display: flex; flex-direction: column; gap: 14px;
  flex: 1; justify-content: center; padding-bottom: 12px;
}
/* The settle: a small rise, once, on the shared clock. Nothing loops here. */
.ob-inner > * { opacity: 0; transform: translateY(10px); transition: opacity var(--slow) var(--ease), transform var(--slow) var(--ease-soft); }
.ob-inner.up > * { opacity: 1; transform: translateY(0); }
.ob-inner.up > *:nth-child(2) { transition-delay: calc(var(--base) / 2); }
.ob-inner.up > *:nth-child(3) { transition-delay: var(--base); }
.ob-inner.up > *:nth-child(n+4) { transition-delay: calc(var(--base) * 1.2); }
.ob-splash { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 18px; padding-bottom: 18vh; }
.ob-splash > * { opacity: 0; transform: scale(.92); transition: opacity var(--slow) var(--ease), transform var(--slow) var(--ease-soft); }
.ob-splash.up > * { opacity: 1; transform: scale(1); }
.ob-tile { box-shadow: 0 18px 44px color-mix(in srgb, var(--brand) 26%, transparent); }
.ob-word { font-size: var(--t-hero); font-weight: 700; letter-spacing: -.02em; }
.ob-h { font-size: var(--t-hero); line-height: 1.15; letter-spacing: -.02em; font-weight: 700; margin: 0 0 2px; }
/* The sign-in screen's own brand block: the tile above the providers (owner:
   the Meror sign belongs on top of the Apple button), scaled down from the
   splash's 88px so the screen opens with a mark, not a poster. */
.ob-brand { display: flex; flex-direction: column; align-items: center; gap: 12px; margin-bottom: 4px; text-align: center; }
.ob-brand .ob-word { font-size: var(--t-title); }
.ob-hi { font-size: var(--t-hero); font-weight: 700; letter-spacing: -.02em; }
.ob-fine { font-size: var(--t-small); color: var(--text-dim); margin: 2px 0 6px; }
.ob-fine.center { text-align: center; }
.ob-fine-sm { font-size: var(--t-cap); }
/* The proof card stands a little proud of the screen, so the number is the
   first thing read. It must not inherit the home page's phone layout, which
   turns .proof into a horizontal scroll strip under 560px: overflow-x:auto
   clipped the card's shadow into a hard grey box and pinned the card at
   300px (owner: "there is a shadow but it gets cut off under the card").
   One whole card, centered, shadow free to blur. */
.proof.ob-proof { display: grid; grid-template-columns: 1fr; overflow: visible; padding: 0; margin: 18px 0 4px; }
.proof.ob-proof .p { width: 100%; flex: none; padding: 18px 20px; border-radius: calc(var(--radius) * 1.2); box-shadow: var(--shadow-sm); }
/* The one answer in the flow: a CTA the thumb reaches without a stretch. The
   press scale is the give under the finger (the haptics carry outcomes; this
   carries the tap). */
.ob-cta {
  width: 100%; min-height: 54px; border-radius: 18px;
  font-size: var(--t-body); font-weight: 650; margin-top: 4px;
}
/* The provider bars: Apple's own white-on-black is the pattern every app on
   the phone uses; Google keeps its coloured mark on white. */
.provider { min-height: 54px; border-radius: 18px; display: flex; align-items: center; justify-content: center; gap: 10px; font-weight: 600; font-size: var(--t-body); }
.ob-apple { background: var(--text); color: var(--bg); border: 1px solid var(--text); }
.ob-google { background: var(--bg-elev); color: var(--text); border: 1px solid var(--line); }
.ob-sep { display: flex; align-items: center; gap: 12px; margin: 2px 0; }
.ob-sep > span { font-size: var(--t-cap); color: var(--text-dim); white-space: nowrap; }
.ob-sep::before, .ob-sep::after { content: ''; flex: 1; height: 1px; background: var(--line); }
.ob-email { display: flex; flex-direction: column; gap: 10px; }
.ob-input {
  width: 100%; min-height: 52px; border-radius: 16px; padding: 0 16px;
  border: 1px solid var(--line); background: var(--bg-elev); color: var(--text);
  font-size: var(--t-body); appearance: none;
}
.ob-input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 18%, transparent); }
.ob-agree { display: flex; gap: 9px; align-items: flex-start; font-size: var(--t-small); color: var(--text-dim); line-height: 1.5; margin: 2px 0; }
.ob-agree input { margin-top: 3px; accent-color: var(--brand); }
/* The agreed tick, 2026-10-19 (after .ob-agree's own, so it wins): big
   enough to hit, quiet to look at. */
.ob-agree { gap: 10px; }
.ob-agree input { width: 20px; height: 20px; margin: 1px 0 0; }
/* The fine print under the form gets the room the old legal stack took: the
   disclaimer and the document links read quiet, small and spaced. */
.ob-inner .ob-legal { margin: 18px 0 2px; font-size: var(--t-cap); line-height: 1.55; }
.ob-inner .ob-docs { margin: 10px 0 0; }
.ob-err { padding: 10px 12px; border-radius: 12px; border: 1px solid color-mix(in srgb, var(--red, #c62828) 35%, transparent); background: color-mix(in srgb, var(--red, #c62828) 8%, var(--surface)); color: var(--text); font-size: var(--t-small); }
.ob-link { background: none; border: 0; color: var(--text-dim); font-size: var(--t-small); padding: 8px 0; cursor: pointer; }
.ob-link:active { opacity: .7; }
.ob-skip {
  background: none; border: 0; color: var(--text-dim); font-size: var(--t-small); font-weight: 600;
  padding: 12px 0; margin-top: 2px; cursor: pointer;
}
.ob-skip:active { opacity: .7; }
.ob-skip:disabled { opacity: .5; cursor: default; }
.ob-plan { text-align: left; width: 100%; cursor: pointer; }
.ob-plan.on { outline: 2px solid var(--brand); outline-offset: 2px; }

/* ------------------------------------------------------------- immersion --
   Build 8, the shell's depth pass. In the shell the screen is the app: cards
   lift off the page, everything the hand touches gives back (a press here is
   a compression at --press, like every control in the block above), and no
   tappable still reacts only in a browser's hover. Nothing runs outside the
   shell, and the palette is not touched. */
@media (max-width: 1023px) {
  /* Onboarding's own controls take the same compression the bar buttons do. */
  .in-shell .ob-cta:active,
  .in-shell .provider:active,
  .in-shell .ob-plan:active { transform: scale(.97); transition: transform var(--press) var(--ease); }
  .in-shell .ob-plan:active { transform: scale(.98); }
  .in-shell .ob-link:active,
  .in-shell .ob-skip:active { opacity: .6; transition: opacity var(--press) var(--ease); }

  /* The plan cards stand off the page: the offer is the one place a shadow is
     the product's weight, not a decoration. */
  .in-shell .ob .plan {
    border: 1px solid var(--line); box-shadow: 0 12px 32px color-mix(in srgb, var(--brand) 10%, rgba(15, 20, 35, .08));
  }
  .in-shell .ob .plan.on {
    border-color: var(--brand); box-shadow: 0 14px 36px color-mix(in srgb, var(--brand) 18%, rgba(15, 20, 35, .10));
    transform: scale(1.01); transition: transform var(--fast) var(--ease-soft);
  }

  /* One plan column in the app: 430px leaves no room for two half cards and
     the reading of both, and the auto-fit fit of the desktop grid was what
     made the Pro screens read as a website squeezing itself. */
  .in-shell .ob .plan-grid { grid-template-columns: 1fr; }

  /* The proof card on the value screen lifts higher than the page's cards:
     the number is the whole point of the screen. */
  .in-shell .proof.ob-proof .p {
    box-shadow: 0 16px 40px rgba(15, 20, 35, .14);
  }
}

.in-shell .topbar,
.in-shell .bottomnav,
.in-shell .modal-bg {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  will-change: auto;
  transform: none;
}
.in-shell .topbar { background: var(--bg); border-bottom: 1px solid var(--line); }
.in-shell .topbar::after { display: none; }
.in-shell .bottomnav { background: var(--bg-elev); border-top: .5px solid var(--line); }
.in-shell .modal-bg { background: rgba(4, 5, 8, .82); }
.in-shell .sol-coin { animation: none; opacity: .85; }
.in-shell .pulse, .in-shell .livedot::before, .in-shell .badge.green.live::before, .in-shell #livebalance .lbl::before,
.in-shell .ops-dot { animation-iteration-count: 2; }
.in-shell .chan-wait::before { animation-iteration-count: 3; }
/* The shell's own clock: the smooth scroll's easing chain (every tap with a
   slide) = the = = the = = the = = the = = the = = ... */
.in-shell { scroll-behavior: auto; }
.in-shell * { -webkit-overflow-scrolling: touch; }

/* ================================================================ aw-uxlib ==
   Forms (public/js/forms.js) and loading shapes (ui.js skeleton helpers and
   rotatingText). One block so the wave's merge is a clean union.
   ========================================================================= */

/* The grey button. Still pressable on purpose: a tap shows what is missing.
   A class, not [aria-disabled]: the cancel sheet's busy button carries that
   attribute and must not fade. */
.btn.is-disabled { opacity: .5; cursor: not-allowed; box-shadow: none; filter: none; }
.btn.is-disabled:hover { filter: none; box-shadow: none; }
.btn.is-busy { cursor: progress; }

/* "Still needed:" right above the button, each missing thing a chip that
   takes you to it. It wraps rather than scrolls: a chip scrolled out of
   sight is a missing field nobody sees. */
.fx-needed { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 12px 0 8px; font-size: var(--t-small); }
.fx-needed[hidden] { display: none; }
.fx-needed-k { font-weight: 600; color: var(--warn); margin-right: 2px; }
.fx-chip {
  display: inline-flex; align-items: center; min-height: 32px; padding: 5px 10px; border-radius: 8px;
  background: var(--warn-soft); color: var(--warn); border: 1px solid transparent;
  font: inherit; font-size: var(--t-small); font-weight: 600; cursor: pointer; text-align: left;
  transition: border-color var(--fast) var(--ease);
}
.fx-chip:hover, .fx-chip:focus-visible { border-color: var(--warn); }
@media (pointer: coarse) { .fx-chip { min-height: 40px; } }

/* The shell's Done bar for the keypads (appshell, mountKeyboard): builds 15+
   hide the accessory bar and the decimal, numeric and phone keypads have no
   Return key of their own, so the money and code boxes had no key that put
   the keyboard away. Fixed above the bottom edge; in a sheet, whose web view
   is not resized, above the keyboard itself. */
.shell-kb-done {
  position: fixed; z-index: 70; right: calc(16 * var(--u)); bottom: 0;
  display: inline-flex; align-items: center; gap: 6px; padding: 10px 16px; border: none; border-radius: 12px;
  background: var(--brand); color: #fff; font: inherit; font-size: var(--t-body); font-weight: 700;
  box-shadow: 0 4px 14px color-mix(in srgb, var(--text) 25%, transparent); cursor: pointer;
}
html.in-shell.shell-sheet-up .shell-kb-done { bottom: var(--shell-kb, 0px); }

/* The delete dialog's line about website-managed copies (profile.js): the
   styleBudget counts inline font-size in public/js, so this one lives here. */
.closure-website-note { font-size: var(--t-small); }
.fx-shake { animation: fx-shake var(--slow) var(--ease) 1; }
@keyframes fx-shake {
  0%, 100% { transform: none; }
  20% { transform: translateX(-5px); }
  40% { transform: translateX(5px); }
  60% { transform: translateX(-3px); }
  80% { transform: translateX(3px); }
}

/* The box a tap on the grey button sent you to. */
.input.fx-flag { border-color: var(--neg); box-shadow: 0 0 0 3px var(--neg-soft); }
label.fx-flag, fieldset.fx-flag, .field.fx-flag { outline: 2px solid var(--neg); outline-offset: 3px; border-radius: 8px; }
.fx-msg { margin-top: 2px; }
.fx-msg[hidden] { display: none; }
.fx-form-error { margin: 12px 0 0; }
.fx-form-error[hidden] { display: none; }

/* The password checklist: neutral until a rule is met, never red. */
.fx-checklist { list-style: none; margin: 6px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: var(--t-cap); color: var(--text-dim); }
.fx-check { display: inline-flex; align-items: center; gap: 6px; transition: color var(--fast) var(--ease); }
.fx-check[hidden], .fx-check-note[hidden] { display: none; }
.fx-tick {
  width: 15px; height: 15px; border-radius: 50%; flex: none; display: inline-grid; place-items: center;
  border: 1.5px solid var(--line-strong); color: transparent;
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.fx-tick svg { width: 10px; height: 10px; stroke-width: 3; }
.fx-check.is-met { color: var(--pos); }
.fx-check.is-met .fx-tick { background: var(--pos); border-color: var(--pos); color: var(--bg); }
.fx-check-note { flex-basis: 100%; color: var(--text-faint); }

/* ====================================================== auth card, calmed ==
   The two sign-in/sign-up cards (the website's #authform, the app's ob-email
   form) read as cheap while incomplete (owner, 2026-10-19, on the build 16
   TestFlight sign-up): three password-rule chips with circles, gold "Still
   needed" chips as alarm buttons, and the legal words stacked three times.
   The contract underneath is untouched (forms.js, SPEC decision 9): the
   grey button still answers a tap by pointing at what is missing, and the
   "Still needed:" words are still said to a screen reader as they change.
   What changes is the paint only, on these two cards:
   - the chips row is not painted here (the missing things are the grey
     button's job now, and the field's own message once it is touched);
   - the password rules are ONE quiet caption line under the box, whose parts
     drop off as they are met - no circles, no gold, and when the password is
     fine the line is gone;
   - the agreement is one tight line instead of a card of legal prose. */
.authcard .fx-needed, .ob-email .fx-needed { display: none !important; }
.authcard .fx-checklist, .ob-email .fx-checklist {
  display: flex; flex-wrap: wrap; gap: 2px 10px; margin: 2px 0 0;
  font-size: var(--t-cap); line-height: 1.45;
}
/* Met parts leave the line; what is left is what still does not hold. */
.authcard .fx-check.is-met, .ob-email .fx-check.is-met { display: none; }
.authcard .fx-tick, .ob-email .fx-tick { display: none; }

/* The agreement as one tight row: a real tick, one line of words. */
.agree-row { display: flex; align-items: flex-start; gap: 10px; margin: 4px 0 12px; min-width: 0; }
.agree-row input[type="checkbox"] {
  width: 20px; height: 20px; flex: none; margin: 1px 0 0; accent-color: var(--brand);
}
.agree-row .agree-text { font-size: var(--t-small); color: var(--text-dim); line-height: 1.5; min-width: 0; }

/* The agreed tick on the app's card: big enough to hit, quiet to look at.
   (The rules themselves live in the onboarding block below, after .ob-agree's
   own, so they win the cascade.) */

/* The app card's fine print gets the room it lost to the button: the
   disclaimer and the document links read quiet, small and spaced. */

/* Skeleton shapes. Each is the height its real component draws at, so the
   page does not move when the real one lands. */
.skel-text { display: flex; flex-direction: column; gap: 9px; }
.skel-avatar.is-row { width: 30px; height: 30px; }
.skel-avatar.is-sm { width: 34px; height: 34px; }
.skel-avatar.is-md { width: 44px; height: 44px; }
.skel-avatar.is-lg { width: 68px; height: 68px; }
.skel-avatar.is-xl { width: 84px; height: 84px; }
/* traderCard: explore.js measured 324px on a phone and 348px from 760px. */
.skel-tcard { height: 324px; }
@media (min-width: 760px) { .skel-tcard { height: 348px; } }
.skel-ret { display: block; height: 25px; margin-top: 18px; }
.skel-badge { display: block; height: 22px; border-radius: 8px; }
.skel-btn-sm { display: block; height: 36px; border-radius: 10px; }
.skel-gap-s { margin-top: 8px; }
.skel-gap-m { margin-top: 14px; }
.skel-gap-l { margin-top: 22px; }
/* leaderRow: 66px, 70px under a finger. */
.skel-row { gap: 12px; height: 66px; padding: 0 12px; }
@media (pointer: coarse) { .skel-row { height: 70px; } }
/* .metricgrid .m: a 12px caption over a 17px figure. */
.skel-k { display: block; height: 12px; width: 58%; margin: 2px 0 7px; }
.skel-v { display: block; height: 20px; width: 72%; }
.skel-chart { display: block; width: 100%; height: 210px; }
.skel-title { display: block; height: 25px; }
.skel-h2 { display: block; height: 20px; }
.skel-count { display: block; width: 64px; height: 14px; }
.skel-chip { display: block; flex: none; height: 34px; border-radius: 8px; }
@media (pointer: coarse) { .skel-chip { height: 44px; } }
.skel-btn { display: block; height: 46px; border-radius: 12px; }
.skel-total { display: block; height: 40px; margin: 6px 0 10px; }
.skel-total-sub { display: block; height: 14px; margin-bottom: 28px; }
.skel-label { display: block; width: 34%; height: 12px; margin-bottom: 4px; }
.skel-input { display: block; width: 100%; height: 48px; border-radius: 12px; }
.skel-mark { display: block; width: 44px; height: 44px; border-radius: 14px; margin: 0 auto 14px; }
.skel-center { margin-inline: auto; }
.skel-answer { display: block; height: 62px; border-radius: 12px; }
.skel-narrow { max-width: 420px; margin: 0 auto; padding-top: 20px; }
.skel-table { padding: 8px 16px; }
.skel-tr { display: grid; gap: 16px; align-items: center; height: 44px; border-bottom: 1px solid var(--line); }
.skel-tr:last-child { border-bottom: 0; }
.skel-th .skel-line { height: 10px; opacity: .65; }

/* A status line that changes: each new message fades in. */
.rotating { display: inline-block; }
.rot-in { animation: fade var(--base) var(--ease); }

/* ====================================================== aw-pay (paywall) ===
   The one paywall (components/paywall.js), the signup offer and the cancel
   sheet for an App Store subscription. Yearly and monthly side by side at
   every width (320px leaves 139px a card), the picked one outlined in the
   brand colour with a tick; "Best value" sits in the card's own top row so
   both cards stay the same height whichever is picked. */
.pw { display: flex; flex-direction: column; gap: 10px; width: 100%; text-align: left; }
.pw-plans { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.pw-plan {
  position: relative; display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  min-width: 0; padding: 12px 12px 14px; border-radius: var(--r-lg);
  background: var(--surface); border: 1.5px solid var(--line); color: var(--text);
  text-align: left; cursor: pointer; -webkit-tap-highlight-color: transparent;
  transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease), transform var(--press) var(--ease);
}
.pw-plan:active { transform: scale(.98); }
.pw-plan.on { border-color: var(--brand); background: linear-gradient(160deg, var(--brand-soft), var(--surface)); }
.pw-plan:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.pw-tag {
  align-self: flex-start; min-height: 20px; padding: 2px 8px; margin-bottom: 6px; border-radius: 6px;
  background: var(--brand); color: #fff; font-size: var(--t-cap); font-weight: 700; line-height: 16px;
}
.pw-tag-none { visibility: hidden; }
.pw-name { font-size: var(--t-small); font-weight: 650; max-width: 100%; overflow-wrap: anywhere; }
.pw-len { font-size: var(--t-cap); color: var(--text-dim); }
.pw-price { margin-top: 6px; font-size: var(--t-title); font-weight: 700; letter-spacing: -.02em; line-height: 1.15; overflow-wrap: anywhere; }
.pw-per { font-family: var(--sans); font-size: var(--t-cap); font-weight: 600; color: var(--text-dim); letter-spacing: 0; white-space: nowrap; }
.pw-sub { font-size: var(--t-cap); color: var(--text-dim); }
.pw-tick {
  position: absolute; top: 10px; right: 10px; width: 20px; height: 20px; border-radius: 50%;
  display: grid; place-items: center; border: 1.5px solid var(--line-strong); color: transparent;
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease), color var(--fast) var(--ease);
}
.pw-plan.on .pw-tick { background: var(--brand); border-color: var(--brand); color: #fff; }
.pw-go { margin-top: 4px; min-height: 52px; text-wrap: balance; }
.pw-line { margin: 0; font-size: var(--t-small); text-align: center; color: var(--text); text-wrap: balance; }
.pw-note { margin: 0; font-size: var(--t-small); text-align: center; color: var(--text-dim); display: flex; gap: 6px; justify-content: center; align-items: center; }
.pw-note:empty, .pw-err:empty { display: none; }
.pw-note svg { color: var(--pos); flex: none; }
.pw-err { display: flex; gap: 8px; align-items: flex-start; padding: 10px 12px; border-radius: 12px; font-size: var(--t-small); background: color-mix(in srgb, var(--warn) 10%, var(--surface)); border: 1px solid color-mix(in srgb, var(--warn) 35%, transparent); }
.pw-err svg { flex: none; margin-top: 2px; color: var(--warn); }
.pw-secondary { min-height: 48px; font-weight: 600; }
.pw-fine { margin: 2px 0 0; font-size: var(--t-cap); line-height: 1.45; color: var(--text-dim); text-align: center; text-wrap: balance; }
.pw-links { margin: 0; font-size: var(--t-cap); text-align: center; color: var(--text-dim); }
.pw-links a { color: var(--text-dim); text-decoration: underline; text-underline-offset: 2px; }
.pw-restore { align-self: center; font-size: var(--t-small); padding: 10px 6px; }
.pw-state { display: flex; gap: 12px; align-items: flex-start; padding: 14px 16px; border-radius: var(--r-lg); background: var(--surface); border: 1px solid var(--line); }
.pw-state svg { flex: none; color: var(--brand); margin-top: 1px; }
.pw-state-sub { margin-top: 3px; font-size: var(--t-small); color: var(--text-dim); }
.pw-done .pw-fine { text-align: left; }
/* The page's 74ch reading measure would pin these lines to the left of the button they sit under. */
.pw p { max-width: none; }
.pw-page.narrow { max-width: 520px; }
/* The loading shape: the same two boxes, a button and a line. */
.pw-skel { cursor: default; gap: 8px; min-height: 142px; }
.pw-skel-tag { display: block; width: 72px; height: 20px; border-radius: 6px; }
.pw-skel-price { display: block; width: 60%; height: 26px; margin-top: 6px; }
.pw-skel-line { display: block; width: 70%; height: 13px; margin: 2px auto 0; }
/* The upgrade page and the signup offer: a short heading, then the product. */
.pw-page { max-width: 520px; margin: 0 auto; }
.pw-head { text-align: center; margin: 4px 0 18px; }
.pw-head h1 { font-size: var(--t-title); margin: 0; text-wrap: balance; }
.pw-below { margin-top: 26px; }
.upsell .pw-head .pro-mark { margin-bottom: 10px; }
/* The go-live Pro step hosts the same paywall under its question. */
.gl .pw-host { width: 100%; max-width: 440px; margin: 18px auto 0; }
.gl .pw-host .pw-plan { background: var(--bg-elev); }
.gl .pw-host .pw-plan.on { background: linear-gradient(160deg, var(--brand-soft), var(--bg-elev)); }
/* The signup offer inside the app's first run. */
.ob .pw-host { width: 100%; }
.ob-sub { font-size: var(--t-small); color: var(--text-dim); margin: -6px 0 2px; }
/* The App Store cancel: the steps on an iPhone, in order. */
.apple-steps { margin: 10px 0 0; padding-left: 20px; font-size: var(--t-small); color: var(--text-dim); line-height: 1.6; }
@media (max-width: 359px) {
  .pw-plans { gap: 8px; }
  .pw-plan { padding: 10px 9px 12px; }
  .pw-tick { top: 8px; right: 8px; width: 18px; height: 18px; }
}
/* ================================================== end aw-pay (paywall) == */

/* ============================================================== aw-account ==
   The /support page (content/support.js). Links inside its answers are the
   whole point of the page ("open your profile", "reportaproblem.apple.com"),
   and the site's links are plain text by default, so here they look like
   links. Sections get room between them; headings are a step calmer than the
   page title so the answers read as one list. */
.support-page h1 { margin-bottom: 14px; }
.support-page .support-q { margin-top: 26px; }
.support-page .support-q h2 { font-size: var(--t-title); margin: 0 0 8px; }
.support-page .support-q p { margin: 0 0 10px; line-height: 1.6; }
.support-page p a { color: var(--brand); font-weight: 600; text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px; }
.support-page p a:hover { text-decoration-thickness: 2px; }

/* ================================================================ aw-browse ==
   The browsing pages' own placeholders (components/placeholder.js) and the
   small inputs on them. A bar is a line of the text it stands for, so it
   takes that line's height from the element it sits in; buttons and badges
   keep their real classes and so their real boxes.
   ========================================================================= */
.ph-bar { display: inline-block; vertical-align: middle; height: .8em; max-width: 100%; border-radius: 6px; }
.skel.ph-btn {
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
  background-size: 400% 100%; border-color: transparent; color: transparent; box-shadow: none; pointer-events: none;
}
.copyrow > .skel-avatar { width: 36px; height: 36px; }
.ph-block { display: block; width: 100%; }
/* More rows on their way, under the rows already there. */
.ph-more { padding-top: 2px; }
/* A trader's Key figures sit under the chart on a phone and in the side
   column from 980 px (trader.js placeWide); the placeholder draws both and
   shows the one that applies. */
@media (min-width: 980px) { .ph-narrow-only { display: none; } }
@media (max-width: 979.98px) { .ph-wide-only { display: none; } }
/* Holdings rows: a security over its name, then two figures. */
.ph-htable { margin-top: 37px; }
.ph-hrow { display: flex; align-items: center; justify-content: space-between; gap: 12px; height: 60px; border-top: 1px solid var(--line); }
.ph-hcell { flex: 1; min-width: 0; line-height: 1.35; }
.addrow > .skel-avatar { width: 36px; height: 36px; }
/* A placeholder heading sized by a .t-* class keeps the headings' own line height. */
.ph-h { line-height: 1.15; }

/* ================================================================= aw-copy ==
   The copy window and the trade-answer screens (copyModal.js, askNow.js,
   confirm.js): their skeletons, the folded start choice, the amount box's
   "Still needed" line in the footer, and the yes made the big button.
   ========================================================================= */

/* The copy window's skeleton: the Risk limits row it stands in for is 44px. */
.copy-skel-fold { display: block; height: 44px; margin-top: 16px; }

/* "How this copy starts", folded under Risk limits; closed, it says which way. */
.entry-fold { margin-top: 10px; }
.entry-fold summary { min-height: 44px; }
.entry-now { font-weight: 500; color: var(--text-faint); }
.entry-fold .entryseg { margin-top: 2px; }

/* The amount box's "Still needed" line sits in the footer, right above Start. */
.sheet-foot > .fx-needed { margin-top: 0; }
.sheet-foot > .fx-form-error { margin: 0 0 10px; }

/* A changing status line on a button stays on one line inside it. */
.btn .rotating { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }

/* A trade card's yes is the big button; on a phone it takes two thirds. */
.confirm-card .confirm-go { min-width: 120px; }
@media (max-width: 520px) {
  /* The card's inline align-items:center beat the older stretch rule, so the
     buttons sat in the middle at their own width; the row takes the width. */
  .confirm-card .spread > .row { align-self: stretch; }
  .confirm-card .spread .row .confirm-go { flex: 2 1 0; }
  .confirm-card .spread .row .confirm-no { flex: 1 1 0; }
  .skel-confirm .spread .row .skel-btn { flex: 1 1 0; }
  .skel-confirm .spread .row .skel-btn.is-go { flex: 2 1 0; }
}
/* Trade-card skeleton parts: the Buy/Sell block and the ticker line. */
.skel-side { display: block; flex: none; height: 34px; border-radius: 12px; }
.skel-tick { height: 19px; }
/* The copy window skeleton's parts (copySkeleton). */
.copy-skel-row .copy-skel-end { flex: 0 0 26%; align-items: flex-end; }
.copy-skel-notice { display: block; height: 76px; border-radius: 12px; }
.copy-skel-fold.is-second { margin-top: 10px; }
/* Inside the plan's flat card (surface-2) the bars take the next shade up,
   or they vanish into it. */
.copy-skel .card.flat .skel { background-image: linear-gradient(90deg, var(--surface-3) 25%, var(--line-strong) 37%, var(--surface-3) 63%); }
/* The ask sheet's skeleton: the one-line offer of a chat under the buttons. */
.ask-skel-offer { display: block; height: 44px; border-radius: 10px; }

/* ================================================================ aw-lows ==
   The signed-in top bar's counter before its first reading (balance.js): a
   bar where the figure goes and a short one where the label goes, in their
   boxes, instead of the word "loading". */
#livebalance .lb-skel { display: inline-block; width: 5.4em; height: .9em; border-radius: 6px; vertical-align: middle; }
#livebalance .lbl .lb-skel-lbl { display: inline-block; width: 3.4em; height: .8em; border-radius: 6px; vertical-align: middle; }
/* "Still needed" in the light theme: #9A6700 on its own tint measured about
   4.2:1 on white and 3.5:1 on surface-3, under AA for 13px text. #7A5200 is
   5.9:1 on white and 5.0:1 on surface-3. The dark theme's passes (4.8:1 and up). */
[data-theme='light'] .fx-chip, [data-theme='light'] .fx-needed-k { color: #7A5200; }
/* aw-lows: words a placeholder already knows (profile.js profilePlaceholder),
   drawn see-through on the shimmer so every grey line wraps where the real
   line will. Each wrapped line gets its own rounded bar. */
.ph-words { color: transparent !important; background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%); background-size: 400% 100%; animation: shimmer calc(var(--slow) * 3.5) var(--ease-soft) infinite; border-radius: 6px; -webkit-box-decoration-break: clone; box-decoration-break: clone; text-decoration: none; user-select: none; }
@media (prefers-reduced-motion: reduce) { .ph-words { animation: none; } }
/* The photo's place in that header: a block, as the real avatar is. */
.avatar-edit > .skel-avatar { display: block; }
/* ============================================================ end aw-lows == */

/* =============================================================== aw-smooth ==
   The smooth workstream (2026-10-17): the press drawn on pointerdown, the
   status line (offline, updating), and the page cross-fade. js/smooth.js. */

/* The press, on pointerdown. :active reaches a finger only once the browser
   is sure the touch is not a scroll (121-155 ms on an emulated phone, after
   the finger was already lifted); smooth.js puts .is-pressed on at the
   pointerdown and takes it off on release. Each line gives exactly what the
   same control's :active rule above gives, so a mouse, which gets both, sees
   one press and not two. */
.btn.is-pressed, .iconbtn.is-pressed, .chip.is-pressed, .mode-card.is-pressed,
.choice.is-pressed, .style-card.is-pressed:not(:disabled), .book-tile.is-pressed:not(:disabled),
.tabbar button.is-pressed, .sortbar-toggle.is-pressed, .faq-item summary.is-pressed,
.explain summary.is-pressed, .lb-copy.is-pressed, #sidebar a.is-pressed { transform: scale(.972); }
.tcard.is-pressed, .lb-row.is-pressed, a.card.is-pressed, .card.hoverable.is-pressed,
.proof .p.is-pressed, .ops-kpi.is-pressed { transform: scale(.992); }
.bottomnav a.is-pressed { transform: scale(.94); }
.chan-connect.is-pressed { transform: scale(.96); }
.pill.is-pressed, .avatar-edit.is-pressed, .oauth-btn.is-pressed:not(:disabled),
button.gl-icon.is-pressed, .gl-primary.is-pressed:not(:disabled), .gl-quiet.is-pressed,
.gl-tile.is-pressed:not(:disabled), .gl-book.is-pressed:not(:disabled), .gl-outline.is-pressed { transform: scale(.97); }
.gl-primary.is-disabled.is-pressed { transform: none; }
.pw-plan.is-pressed { transform: scale(.98); }
.ob-link.is-pressed, .ob-skip.is-pressed { opacity: .7; }
/* Everything else a finger lands on (a text link, a row, a disclosure):
   a dip, at the same speed as the give. */
.press-dim.is-pressed { opacity: .6; transition: opacity var(--press) var(--ease); }
.in-shell .btn.is-pressed { transform: scale(.96); }
.in-shell .choice.is-pressed, .in-shell .tabbar button.is-pressed { transform: scale(.97); }
.in-shell .bottomnav a.is-pressed { transform: scale(.92); opacity: .7; }
@media (max-width: 1023px) {
  .in-shell .btn.is-pressed, .in-shell .chip.is-pressed, .in-shell .iconbtn.is-pressed,
  .in-shell .tabbar button.is-pressed { transform: scale(.96); transition: transform var(--press) var(--ease); }
  .in-shell .card.hoverable.is-pressed { transform: scale(.995); background: color-mix(in srgb, var(--text) 4%, var(--bg-elev)); }
  .in-shell .bar-brand.is-pressed { transform: scale(.94); }
  .in-shell .shellbar .iconbtn.is-pressed { opacity: .5; }
  .in-shell .ob-cta.is-pressed, .in-shell .provider.is-pressed { transform: scale(.97); transition: transform var(--press) var(--ease); }
  .in-shell .ob-plan.is-pressed { transform: scale(.98); transition: transform var(--press) var(--ease); }
  .in-shell .ob-link.is-pressed, .in-shell .ob-skip.is-pressed { opacity: .6; transition: opacity var(--press) var(--ease); }
}
/* Asked for less motion: nothing moves, and the press is still seen. */
@media (prefers-reduced-motion: reduce) {
  .is-pressed { transform: none !important; opacity: .72; }
}

/* One quiet line for the state of the connection and of the screen:
   "You're offline. Showing what you saw last." while offline, and
   "Updating" then "Updated" over a screen drawn from an older copy. Fixed
   just above the tab bar, where the toasts rise from, so it never moves the
   page and never covers the top bar (whose height the verify and waiting
   bars change); it never takes a tap. The toasts rise above it while it is
   up (html.smooth-line, set by smooth.js). */
.smooth-status {
  position: fixed; left: 50%; bottom: calc(var(--nav-h) + 12px + env(safe-area-inset-bottom, 0px)); z-index: 290;
  transform: translateX(-50%);
  max-width: min(360px, calc(100% - 32px)); width: max-content;
  padding: 7px 14px; border-radius: 999px; text-align: center;
  background: var(--surface-3); border: 1px solid var(--line-strong); box-shadow: var(--shadow);
  color: var(--text-dim); font-size: var(--t-cap); font-weight: 600; line-height: 1.35;
  pointer-events: none; transition: opacity var(--fast) var(--ease);
}
.smooth-status[hidden] { display: none; }
.smooth-status.out { opacity: 0; }
.smooth-status[data-mode="offline"] { color: var(--text); }
html.smooth-line .toasts { bottom: calc(var(--nav-h) + 58px + env(safe-area-inset-bottom, 0px)); }
body.focus-mode .smooth-status { bottom: calc(12px + env(safe-area-inset-bottom, 0px)); }
@media (min-width: 1024px) {
  .smooth-status { bottom: 20px; }
  html.smooth-line .toasts { bottom: 20px; }
}

/* Pages hand over with a short cross-fade (app.js pageTransition), at the
   speed of anything under the pointer. */
::view-transition-old(root), ::view-transition-new(root) { animation-duration: var(--fast); animation-timing-function: var(--ease); }
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
}
/* The paywall's loading shape in the real one's boxes (components/paywall.js
   skeletonHtml): the plan cards at their drawn height, the button at the
   large button's, the line under it and the two lines of small print. It was
   72 px short at 390 px, so everything under it jumped when the plans came. */
.pw-loading .pw-skel { min-height: 151.4px; }
.pw-loading > .skel-btn { height: 52px; margin-top: 4px; }
.pw-loading > .pw-skel-line { margin: 4px auto 3.9px; }
.pw-loading::after {
  content: ''; display: block; height: 34.8px; margin: 2px 12% 0; border-radius: 10px;
  background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 37%, var(--surface-2) 63%);
  background-size: 400% 100%; animation: shimmer calc(var(--slow) * 3.5) var(--ease-soft) infinite;
}
@media (max-width: 359px) {
  .pw-loading .pw-skel { min-height: 147.4px; }
  .pw-loading > .pw-skel-line { margin-bottom: 24.8px; }
  .pw-loading::after { height: 52.2px; }
}
/* The sign-in row above the email form comes in when the providers answer
   (pages/auth.js); its place is held at its drawn height (124 px and the
   14 px under it), or the form below jumped 138 px down on every cold open
   of /signup and /login (CLS 0.10 on an emulated phone, 2026-10-17). */
#oauth-slate:empty { min-height: 138.2px; }
@media (prefers-reduced-motion: reduce) { .pw-loading::after { animation: none; } }
/* ============================================================ /aw-smooth == */

/* ================================================================ aw-fix ==
   App Store wave fixes (2026-10-17). */
/* The app's splash: the tile's centre on the screen's centre, where the
   native launch screen draws the same tile, and drawn at full opacity from
   the first frame, so the hand-over from the launch screen has no jump and
   no blank frame. The word hangs below the tile, out of the centring. */
.ob[data-ob-step="splash"] .ob-inner { align-items: center; justify-content: center; padding-bottom: 0; }
.ob-inner.ob-splash-in > .ob-splash-mark {
  position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%);
  opacity: 1; transition: none; display: grid; place-items: center;
}
.ob-splash-mark .ob-word { position: absolute; top: calc(100% + 18px); left: 50%; transform: translateX(-50%); white-space: nowrap; }
/* The proof card that lands after the value screen is up fades in on its own
   (it is not under the step's first settle). */
.ob-proof-slot:empty { display: none; }
.ob-proof-slot { opacity: 0; transition: opacity var(--slow) var(--ease); }
.ob-proof-slot.in { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .ob-proof-slot { transition: none; } }
.ob-forgot { align-self: flex-end; margin-top: -4px; padding: 4px 0; text-decoration: none; }
.ob-docs a { color: var(--text-dim); text-decoration: underline; text-underline-offset: 2px; }
.legal-sheet-body { max-height: min(62vh, 560px); overflow-y: auto; -webkit-overflow-scrolling: touch; }
.legal-sheet-body h4 { margin: 18px 0 6px; font-size: var(--t-body); }
.legal-sheet-body p { line-height: 1.6; margin: 0 0 10px; font-size: var(--t-small); }
/* Document links inside an agreement box read as links, in both themes. */
.agree-text a { color: var(--brand); text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px; }
@media (pointer: coarse) { .agree-text a { padding-block: 4px; } }
/* A forgot-password button that has just sent its link: grey like a
   button with something missing, until the address changes. */
.btn.is-sent { opacity: .5; cursor: default; box-shadow: none; filter: none; }
/* The header search: the field from 901 px, the icon button below that, and
   never both (the button's own rule lost to .iconbtn's display: grid). The
   field shows its whole placeholder where it shows at all. Its text is the
   17 px step (16 px at least), so iOS Safari does not zoom the page when it
   is tapped (an iPhone in landscape has it); the placeholder keeps the
   smaller look. */
.topbar-in .topsearch-btn { display: none; }
@media (max-width: 900px) {
  .topbar-in .topsearch { display: none; }
  .topbar-in .topsearch-btn { display: grid; }
}
.topsearch input { font-size: var(--t-lead); }
.topsearch input::placeholder { font-size: var(--t-small); }
/* The Pro offer after signing up keeps its shape while the paywall loads: the
   headline holds two lines from the first paint (it may gain "free for 14
   days" when the plans land), the app's skeleton carries Apple's lines for
   real (no fine-print placeholder there), and the screen is laid out from
   the top, so anything that still grows late grows downward, under the
   button. */
.upsell .pw-head h1 { min-height: 2.3em; }
.pw-loading-iap::after { display: none; }
.ob[data-ob-step="pro"] .ob-inner { justify-content: flex-start; }
/* Watchlist rows: the remove control shows in Edit mode only, in the copy
   button's place, so a row never carries both (they overlapped at 375 px and
   ran out of the card at 320 px). The row never grows past its card. */
.wl-item > .grow { min-width: 0; }
.wl-list .wl-remove { display: none; }
.wl-list.wl-editing .wl-remove { display: grid; }
.wl-list.wl-editing .lb-row .lb-copy { display: none; }
.wl-list.wl-editing .lb-row.withcopy { grid-template-columns: 26px 30px minmax(72px, 1fr) auto; }
@container (max-width: 300px) {
  .lb-row .lb-stats .lb-sub { display: none; }
  .lb-row .lb-copy { padding-inline: 8px; }
}
/* A 320 px phone: the rank, the avatar (34 px, in a 30 px track) and the
   name's floor give way a little, so the row fits its card (it ran 14 px
   past it). */
@media (max-width: 340px) {
  .wl-item .lb-row.withcopy { grid-template-columns: 22px 34px minmax(56px, 1fr) auto auto; column-gap: 6px; }
  .wl-list.wl-editing .wl-item .lb-row.withcopy { grid-template-columns: 22px 34px minmax(56px, 1fr) auto; }
}
/* Portfolio at desktop width: everything on the page shares the column the
   hero and the copies sit in (Holdings, the calendar and the side cards ran
   97 px wider on each side at 1280 px). The skeleton has the same box. */
.page.pf > :not(.pf-col) { max-width: 760px; margin-inline: auto; }
/* The full-screen flows' loading shapes (/golive, /setup, /onboard in a
   browser) sit in the flow's own frame: .gl's 24 px sides, .gl-body's 440 px
   column under the 56 px top row, not edge to edge. From the first paint on
   /setup and /golive (html[data-bar="none"], only while the first paint's
   placeholder is up: the attribute outlives the flow) as well as once
   focus-mode is on. */
html[data-bar="none"] .layout:has(#bootskel) { display: block; max-width: none; margin: 0; padding: 0; }
html[data-bar="none"] .shell:has(> #bootskel) { max-width: none; padding: 0; }
html[data-bar="none"] #bootskel > .page[aria-busy], body.focus-mode #bootskel > .page[aria-busy], body.focus-mode #view > .page[aria-busy] { padding: 0 24px; }
html[data-bar="none"] #bootskel .skel-narrow, body.focus-mode #bootskel .skel-narrow, body.focus-mode #view > .page[aria-busy] .skel-narrow { max-width: 440px; padding-top: calc(56px + clamp(28px, 9vh, 96px)); }
/* Explore's trader cards draw 291 px on a phone and 324 px from 760 px (measured
   2026-10-17); the loading cards are those heights, so the grid does not move. */
.skel-tcard { height: 291px; }
@media (min-width: 760px) { .skel-tcard { height: 324px; } }
/* The portfolio's loading hero holds its chart's box: 140 px on a phone and
   170 px from 600 px, the heights the page draws it at. */
.pf-skel-chart { height: 140px; }
@media (min-width: 600px) { .pf-skel-chart { height: 170px; } }
/* A paper viewer's trader page holds room under its buttons for the Real money
   line (trader.js reserveRealLine): the first paint and the router's
   placeholder do too, from the account (html[data-viewer]: the shell's head
   script sets it from the remembered account, app.js render keeps it). */
html[data-viewer="paper"] .page[aria-busy] .tactions-main { min-height: 92px; }
/* ==== aw-bf (App Store wave, 2026-10-17): the bug hunt's layout fixes ==== */
/* A placed bet slip's stake and odds messages go on their own full-width line
   under the controls (forms.js puts each straight after its box, which is
   inside this row): the "@", the odds box and Save no longer jump sideways as
   a message comes and goes, at 320, 390 or 1280 px. */
[data-placedslip] .v.row { flex-wrap: wrap; }
[data-placedslip] .v.row > .fx-msg { order: 9; flex-basis: 100%; text-align: left; }
[data-placedslip] > .fx-needed, [data-placedslip] > .fx-form-error { flex-basis: 100%; margin: 4px 0; }
/* The trader page's hero badges fit their column: "Updated 8 minutes ago"
   wraps inside the identity column at 320 px instead of running into the
   card's padding, and stays one line from 340 px up (trader.js .t-badges). */
.t-badges .badge { max-width: 100%; white-space: normal; text-align: left; }
/* A badge in a section head ("Works right now") keeps its pill on a phone:
   the phone rule above turns the head's spans into blocks, which stretched
   it into a full-width bar that read as an empty box. */
@media (max-width: 559px) {
  .section-head > .badge { width: fit-content; margin-top: 6px; }
}
/* Watchlist at 320 px: the list position (only the order traders were added
   in) gives way and the row's own sides trim, so a name has 98 px instead of
   62 ("Kwam..."); the count never splits from its word. */
@media (max-width: 340px) {
  .wl-item .lb-row.withcopy { grid-template-columns: 34px minmax(56px, 1fr) auto auto; padding-inline: 8px; }
  .wl-list.wl-editing .wl-item .lb-row.withcopy { grid-template-columns: 34px minmax(56px, 1fr) auto; }
  .wl-item .lb-row > .rank { display: none; }
}
.wl-list [data-count] { white-space: nowrap; }
/* A card's centred mark (a check on "You are up to date"): an svg is a block,
   so it sat at the card's left under centred words. */
.card-mark svg { margin: 0 auto; }
/* ==== end aw-bf ==== */
/* ==== aw-bf (2026-10-17): a signed-out app's walls carry no tab bar ==== */
/* app.js paints #bottomnav empty, marked data-none, for a signed-out app
   (Forgot password, a reset link, the documents): no bar, and no room kept
   for one. The empty bar before the first paint is left as it was. */
.bottomnav[data-none] { display: none; }
body:has(> .bottomnav[data-none]) { padding-bottom: 0; }
/* ==== end aw-bf ==== */
/* ==== aw-bf (2026-10-17): the contact form's Still needed line ==== */
/* On a phone the line is two rows of chips for a visitor and one for a
   signed-in person (name and address filled in after the first paint): it
   keeps two rows' room while it shows, so Send does not move up under the
   finger when the prefill lands. */
@media (max-width: 559px) {
  .contact-form .fx-needed:not([hidden]) { min-height: 86px; align-content: flex-start; }
}
/* ==== end aw-bf ==== */
/* ==== aw-bf (2026-10-17): the admin overview's live line ==== */
/* The live line takes its own row from the first draw (admin.js #ops-now),
   and on a phone the room its two or three lines wrap to, so the KPI tiles
   are not pushed down when it fills. */
@media (max-width: 559px) { #ops-now { min-height: 63px; align-content: flex-start; } }
/* ==== end aw-bf ==== */
/* ==== aw-bf (2026-10-17): chip rows on a desktop (components.js edgeArrows) ==== */
.edge-wrap { position: relative; }
.edge-btn { display: none; }
@media (hover: hover) and (pointer: fine) {
  .chipscroll[data-edge="end"] { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 56px), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 56px), transparent); }
  .chipscroll[data-edge="start"] { -webkit-mask-image: linear-gradient(to left, #000 calc(100% - 56px), transparent); mask-image: linear-gradient(to left, #000 calc(100% - 56px), transparent); }
  .chipscroll[data-edge="both"] { -webkit-mask-image: linear-gradient(to right, transparent, #000 56px, #000 calc(100% - 56px), transparent); mask-image: linear-gradient(to right, transparent, #000 56px, #000 calc(100% - 56px), transparent); }
  .edge-btn {
    position: absolute; top: calc(50% - 6px); transform: translateY(-50%); z-index: 2;
    width: 30px; height: 30px; border-radius: 50%; display: none; align-items: center; justify-content: center;
    background: var(--bg-elev); color: var(--text); border: 1px solid var(--line-strong); box-shadow: var(--shadow-sm); cursor: pointer; padding: 0;
  }
  .edge-btn.edge-prev { left: -6px; }
  .edge-btn.edge-next { right: -6px; }
  .edge-wrap[data-edge="end"] > .edge-next, .edge-wrap[data-edge="both"] > .edge-next,
  .edge-wrap[data-edge="start"] > .edge-prev, .edge-wrap[data-edge="both"] > .edge-prev { display: inline-flex; }
  .edge-wrap:has(> .chipscroll[hidden]) > .edge-btn { display: none; }
  /* A focused chip stops clear of the 56 px fade and the arrow (aw-a11y, 2.4.11). */
  .chipscroll[data-edges] { scroll-padding-inline: 60px; }
}
/* ==== end aw-bf ==== */
/* ==== aw-bf (2026-10-17): a loading shape's lead line ==== */
/* A page's own shape (content/skeletons.js) sits one box deeper than the
   page, inside its aria-hidden wrapper, so the 20 px a lead line takes under
   a page's h1 is given there too, or the line and all below it rose 6 px and
   dropped back when the page drew (/strategies, /accounts, /publish). */
.page > [aria-hidden="true"] > h1 + p.dim, .page > [aria-hidden="true"] > h1 + .dim { margin-top: 20px; }
/* /mode's shape: its ticks stay grey until the plans are in. */
.onboard[aria-busy="true"] .ticks li::before { border-color: var(--line-strong); }
/* ==== end aw-bf ==== */

/* ==== aw-i18n (translation wave, 2026-10-17): language picker and right-to-left ====
   The picker rows (profile, footer) stay hidden until i18n.js sets
   <html data-langs>, which it does only when there is more than English to
   pick. The [dir="rtl"] rules mirror what the main layout places by physical
   side (Arabic, Hebrew, Urdu); flex and grid rows already follow the
   direction on their own. Charts keep their time axis left to right, the
   convention finance apps use in those languages, so their labels stay
   where the plot expects them. */
html:not([data-langs]) [data-lang-row] { display: none !important; }
.lang-picker { font: inherit; font-size: var(--t-small); color: var(--text); background: var(--surface); border: 1px solid var(--line); border-radius: 10px; padding: 6px 10px; max-width: 220px; cursor: pointer; }
.foot-lang { display: inline-block; margin-left: 12px; vertical-align: middle; }
.foot-lang .lang-picker { font-size: var(--t-cap); padding: 4px 8px; }
/* iOS WebKit zooms a focused form control whose font-size is under 16px
   (about 16 divided by it), and the page stays zoomed until the person
   pinches out. On the website the picker's 13.5px / 12px did exactly that on
   a tap of the closed select; the app's own rules (17px, .in-shell select)
   already win there, and the app hides the footer. */
@media (pointer: coarse) {
  .lang-picker, .foot-lang .lang-picker { font-size: var(--t-lead); }
}

[dir="rtl"] body { text-align: right; }
[dir="rtl"] .topbar-actions, [dir="rtl"] .shellbar .topbar-actions, [dir="rtl"] .exp-controls .link,
[dir="rtl"] .explain summary::after, [dir="rtl"] .pf-fold > summary::after, [dir="rtl"] .pro-line > a > svg,
[dir="rtl"] .make-real > svg:last-child { margin-left: 0; margin-right: auto; }
[dir="rtl"] .topsearch { margin-left: 0; margin-right: 12px; }
[dir="rtl"] .tip { margin-left: 0; margin-right: 6px; }
[dir="rtl"] .foot-lang { margin-left: 0; margin-right: 12px; }
[dir="rtl"] .slip-more > summary::before { margin-right: 0; margin-left: 7px; transform: scaleX(-1); }
[dir="rtl"] .chart-legend i, [dir="rtl"] .cal-key { margin-right: 0; margin-left: 6px; }
[dir="rtl"] .style-stats b, [dir="rtl"] .fx-needed-k { margin-right: 0; margin-left: 3px; }
[dir="rtl"] .trust span::before, [dir="rtl"] .chan-wait::before { margin-right: 0; margin-left: 8px; }
[dir="rtl"] .badge.green.live::before, [dir="rtl"] .livedot::before, [dir="rtl"] #livebalance .lbl::before { margin-right: 0; margin-left: 6px; }
/* Figures that sit at the end of a row stay at the end, which is the left. */
[dir="rtl"] .lb-row .lb-stats, [dir="rtl"] .statline .v, [dir="rtl"] .tbl .r, [dir="rtl"] .copyrow > span:last-child,
[dir="rtl"] .holdrow > span:last-child, [dir="rtl"] .holdrow.go > span:nth-child(2), [dir="rtl"] .pv-row > span:last-child { text-align: left; }
[dir="rtl"] .tbl th, [dir="rtl"] button.copyrow, [dir="rtl"] .pw, [dir="rtl"] .pw-done .pw-fine, [dir="rtl"] .undo-strip,
[dir="rtl"] .ob-plan, [dir="rtl"] .tip::after, [dir="rtl"] [data-placedslip] .v { text-align: right; }
/* Toasts, sheets and the bars that hang off one side. */
[dir="rtl"] .toast.good, [dir="rtl"] .toast.bad, [dir="rtl"] .toast.warn { border-left: 0; }
[dir="rtl"] .toast.good { border-right: 3px solid var(--pos); }
[dir="rtl"] .toast.bad { border-right: 3px solid var(--neg); }
[dir="rtl"] .toast.warn { border-right: 3px solid var(--warn, #d9932b); }
@media (min-width: 1024px) {
  [dir="rtl"] .toasts { right: auto; left: 20px; }
  [dir="rtl"] .moment { right: auto; left: 24px; }
}
[dir="rtl"] .modal-head { padding-right: 0; padding-left: 48px; }
[dir="rtl"] .modal-close { right: auto; left: -12px; }
[dir="rtl"] .verify-bar .iconbtn { right: auto; left: 8px; }
[dir="rtl"] .plan-badge { right: auto; left: 18px; }
[dir="rtl"] .pw-tick { right: auto; left: 10px; }
/* Lists with a mark in front, and the search field's glass. */
[dir="rtl"] .ticks li, [dir="rtl"] .crosses li { padding-left: 0; padding-right: 22px; }
[dir="rtl"] .ticks li::before { left: auto; right: 4px; }
[dir="rtl"] .crosses li::before, [dir="rtl"] .crosses li::after { left: auto; right: 3px; }
[dir="rtl"] .searchbar .input { padding-left: 12px; padding-right: 40px; }
[dir="rtl"] .searchbar svg { left: auto; right: 13px; }
[dir="rtl"] .apple-steps { padding-left: 0; padding-right: 20px; }
[dir="rtl"] .copylist > .make-real { padding-left: 0; padding-right: 48px; }
[dir="rtl"] .chan + .chan::before { left: 0; right: 68px; }
@media (max-width: 520px) { [dir="rtl"] .chan + .chan::before { left: 0; right: 64px; } }
@media (max-width: 599px) { [dir="rtl"] .steps-strip > .btn { margin-left: 0; margin-right: 29px; } }
/* A switch slides the other way. */
[dir="rtl"] .switch::after { left: auto; right: 2px; }
[dir="rtl"] .switch.on::after { transform: translateX(-17px); }
/* Addresses, phone numbers and amounts are typed left to right in any language. */
[dir="rtl"] input[type="email"], [dir="rtl"] input[type="tel"], [dir="rtl"] input[type="url"],
[dir="rtl"] input[inputmode="decimal"], [dir="rtl"] input[inputmode="numeric"] { direction: ltr; text-align: right; }
/* A figure is read left to right in every language: "+8.6%", never "8.6%+". */
[dir="rtl"] .num, [dir="rtl"] .pos, [dir="rtl"] .neg { unicode-bidi: isolate; direction: ltr; }
/* Charts: the plot stays left to right; its value labels move with it. */
[dir="rtl"] .chart-plot, [dir="rtl"] .chart-y, [dir="rtl"] .chart-legend svg { direction: ltr; }
/* ==== end aw-i18n ==== */

/* ==== aw-i18n QA: pt-BR, it, de, ru (layout for longer words) ==== */
/* Portuguese, Italian, German and Russian run 20-40% longer than English and
   German joins words ("Abrechnungsbedingungen"). Every rule here is scoped to
   those four languages, so the English layout is untouched. */
/* The tab bar: five equal fifths whatever the words. A label too long for one
   line ("So funktioniert es", "Начать бесплатно") takes two, and every icon
   stays at the height it has in English (12px down; 2px in the app). */
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .bottomnav { grid-template-columns: repeat(5, minmax(0, 1fr)); }
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .bottomnav a { justify-content: flex-start; min-width: 0; padding: 12px 0 0; }
/* In the app the bar is 52px; a two-line label needs 4px more. */
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)).in-shell { --nav-h: 56px; }
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)).in-shell .bottomnav a { padding-top: 2px; }
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .bottomnav a > span {
  white-space: normal; text-align: center; line-height: 1.08; max-width: 100%;
  overflow-wrap: break-word; -webkit-hyphens: auto; hyphens: auto;
}

/* The footers' two columns share the width equally, and a long word breaks
   (hyphenated) instead of pushing the second column off the screen. */
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .site-foot .foot-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .site-foot .foot-grid a { white-space: normal; padding-right: 0; }
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .site-foot .foot-grid a,
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .home-foot .foot-links a,
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .public-foot .foot-links a { min-width: 0; overflow-wrap: break-word; -webkit-hyphens: auto; hyphens: auto; }
@media (max-width: 559px) {
  :root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .home-foot .foot-links,
  :root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .public-foot .foot-links { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* A button's label wraps onto a second line rather than running out of the
   button or off the screen ("Воспользуйтесь формой обратной связи"). */
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .btn { white-space: normal; text-align: center; overflow-wrap: break-word; }
/* Not a board row's Copy button: its word hides on a narrow row instead. */
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .lb-row .lb-copy { white-space: nowrap; }
/* "Mark all read" goes under the heading when both do not fit on one line. */
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .spread:has(> #readall) { flex-wrap: wrap; }
/* A board row on a phone: "Punteggio 60" / "Pontuação 60" is half again as
   wide as "Score 60" in the row's monospace figures, and pushed the Copy
   button off a 320px screen. The line under the figure may take two lines,
   a little tighter. */
@container (max-width: 420px) {
  :root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .lb-row .lb-stats .lb-sub { white-space: normal; letter-spacing: -0.04em; }
}
/* Profile's two-way switches (Theme, Risk tags) are drawn 180px wide, and
   "Ausgeblendet | Angezeigt" is wider: the switch takes the room its words
   need, and moves under its label when the row has no room for both. */
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .spread > .tabbar[style*="width:180px"] { width: auto !important; min-width: 180px; max-width: 100%; }
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .spread:has(> .tabbar[style*="width:180px"]) { flex-wrap: wrap; row-gap: 8px; }
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .spread > .tabbar[style*="width:180px"] button { white-space: nowrap; }
/* Profile's Language row: the picker is as wide as its longest option
   ("Español (Latinoamérica)") and ran past the card on a 320px phone. It
   gives way to its label instead (the chosen name is short). */
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) [data-lang-row] > [data-lang-picker] { min-width: 0; flex: 0 1 auto; }
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) [data-lang-row] .lang-picker { max-width: min(220px, 100%); }
/* The paywall's "Best value" tag ("Più conveniente", "Melhor custo-benefício")
   ran under the selection tick in the card's corner: it keeps clear of the
   tick and takes a second line when it must. */
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .pw-tag { max-width: calc(100% - 26px); }
/* In the app the plan prices are the App Store's, in the local currency, and
   "7 990,00 ₽" is wider than half a phone under 375px at the title size: the
   figure steps down to the lead size there rather than breaking in two. */
@media (max-width: 374px) {
  :root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .pw-price { font-size: var(--t-lead); }
}
/* The brand face (Meror Grotesk) has no Cyrillic: a Russian heading was drawn
   half in it (spaces, digits, "Meror") and half in the system face, two
   faces in one line. Russian headings use the system face whole; the word
   mark keeps the brand face. German, Italian and Portuguese letters are all
   in the brand face (checked per glyph). */
:root:lang(ru) :is(h1, h2, h3, h4) { font-family: var(--sans); }
/* A badge in a wrapping row of badges (the trader page's "Investitore di lungo
   periodo", "Aktualisiert vor 1 Stunde") ran past its card next to the avatar:
   it takes a second line instead. */
:root:is(:lang(de), :lang(it), :lang(pt), :lang(ru)) .row.wrap > .badge { white-space: normal; max-width: 100%; }
/* ==== end aw-i18n QA: pt-BR, it, de, ru ==== */

/* ==== aw-i18n QA: ar, tr, hi, en-XA (right to left, Turkish, Hindi, the pseudo-locale) ==== */
/* Scoped to these four languages, or to right to left, so English is untouched. */
/* The balance counter is a row of digit columns (inline-flex), and right to
   left reversed the row: $10,000.00 read "00.000,01$". A figure reads left to right. */
[dir="rtl"] .odo { direction: ltr; }
/* A leaderboard row's "26m ago ·" floats to the start of its line, which in
   Arabic is the right; floated left it was read after the style, not before. */
[dir="rtl"] .lb-row .lb-meta .fresh { float: right; }
/* A cut-off name keeps its start: a Latin name or security in an Arabic row
   ("Omar Adeyemi") was cut at the left and read "...eyemi". Only lines that
   hold a name take their direction from their own first letter (a translated
   sentence that opens with a brand keeps the page's order); the right
   alignment is the body's ([dir="rtl"] body above), so nothing moves. */
[dir="rtl"] :is(.lb-row > .grow > .truncate:first-child, .tcard .name, .copyrow-name, .gl-tile-name, .trade .row > .faint.truncate) { unicode-bidi: plaintext; }
/* A board row's tracks can total more than a 320px screen's row (the English
   copy button runs into the card's padding); right to left that overflow went
   off the screen's left edge, 8px of the button gone. On a phone that narrow the
   name gives up a little room and the button's padding tightens. (A media query:
   the row is its own container, so @container cannot size the row itself.) */
@media (max-width: 360px) {
  [dir="rtl"] .lb-row.withcopy { gap: 6px; grid-template-columns: 26px 30px minmax(56px, 1fr) auto auto; }
  [dir="rtl"] .lb-row.withcopy.norank { grid-template-columns: 30px minmax(56px, 1fr) auto auto; }
  [dir="rtl"] .lb-row .lb-copy { padding-inline: 8px; }
}
/* Figures use the monospace stack. An Arabic or Hindi word inside a figure line
   ("التقييم 60", "स्कोर 60") fell through it to Courier New, whose Arabic letters
   sit one per fixed cell and do not join, and to the old Devanagari MT. The
   system face draws those letters now; the digits still come from the monospace font. */
:root:is(:lang(ar), :lang(hi)) {
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, "Liberation Mono", Arial, "Geeza Pro", "Kohinoor Devanagari", "Noto Sans Arabic", "Noto Sans Devanagari", "Segoe UI", "Nirmala UI", sans-serif;
}
/* The tab bar: five equal fifths. A label too long for one line ("Ücretsiz
   başla", "यह कैसे काम करता है") takes two instead of running into the next
   tab, and every icon stays level (the same rule as the pt-BR/it/de/ru block). */
:root:is(:lang(ar), :lang(tr), :lang(hi), :lang(en-XA)) .bottomnav { grid-template-columns: repeat(5, minmax(0, 1fr)); }
:root:is(:lang(ar), :lang(tr), :lang(hi), :lang(en-XA)) .bottomnav a { justify-content: flex-start; min-width: 0; padding: 12px 2px 0; }
:root:is(:lang(ar), :lang(tr), :lang(hi), :lang(en-XA)).in-shell .bottomnav a { padding-top: 2px; }
:root:is(:lang(ar), :lang(tr), :lang(hi), :lang(en-XA)) .bottomnav a > span {
  white-space: normal; text-align: center; line-height: 1.08; max-width: 100%;
  overflow-wrap: break-word; -webkit-hyphens: auto; hyphens: auto;
}
/* A button's label wraps onto a second line rather than running out of the
   button ("Hayır teşekkürler, şimdilik sanal işlem yapacağım" at 320px). */
:root:is(:lang(ar), :lang(tr), :lang(hi), :lang(en-XA)) .btn { white-space: normal; text-align: center; overflow-wrap: break-word; }
/* The top bar's word buttons narrow first (and take two short lines) instead
   of pushing "Get started" off a 320px screen or squeezing the round icon
   buttons into ovals; the icons give way only after the words have. */
:root:is(:lang(ar), :lang(tr), :lang(hi), :lang(en-XA)) .topbar-actions > .btn { min-width: 0; flex-shrink: 10; line-height: 1.15; padding-inline: 10px; }
/* Going live: a brokerage tile's caption ("أجزاء أسهم بالدولار الأمريكي") gets
   two lines inside the tile, and the "Recommended" tag never grows wider than it. */
:root:is(:lang(ar), :lang(tr), :lang(hi), :lang(en-XA)) .gl-tile .gl-tile-cap { white-space: normal; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
:root:is(:lang(ar), :lang(tr), :lang(hi), :lang(en-XA)) .gl-tile .gl-tag { display: block; max-width: calc(100% + 4px); overflow: hidden; text-overflow: ellipsis; line-height: 20px; }
/* ==== end aw-i18n QA: ar, tr, hi, en-XA ==== */

/* ==== aw-i18n QA: fr-CA, fr, es-MX, es (French and Spanish layout) ==== */
/* French and Spanish run 15-30% longer than English ("Comment ça marche",
   "Copiar esta canasta en simulación"). Every rule here is
   scoped to those two languages (:lang(fr) also matches fr-CA, :lang(es)
   es-MX), so English is untouched. The rules follow the other QA blocks. */
/* The tab bar: five equal fifths. "Commencer gratuitement" ran 76px off a
   320px screen (fr now says "Démarrer sans frais") and "Portefeuille" ran
   into "Argent réel"; a label too long for one line takes two, and every
   icon stays level. */
:root:is(:lang(fr), :lang(es)) .bottomnav { grid-template-columns: repeat(5, minmax(0, 1fr)); }
:root:is(:lang(fr), :lang(es)) .bottomnav a { justify-content: flex-start; min-width: 0; padding: 12px 2px 0; }
:root:is(:lang(fr), :lang(es)).in-shell .bottomnav a { padding-top: 2px; }
:root:is(:lang(fr), :lang(es)) .bottomnav a > span {
  white-space: normal; text-align: center; line-height: 1.08; max-width: 100%;
  overflow-wrap: break-word; -webkit-hyphens: auto; hyphens: auto;
}
/* A fifth of a 320px phone is 64px and "Portefeuille" is 67px at the caption
   size: no side padding and a little less letter spacing keep every label
   to whole words. (The type scale has no smaller size; tests/styleBudget.) */
@media (max-width: 359px) { :root:is(:lang(fr), :lang(es)) .bottomnav a { padding-inline: 0; letter-spacing: -0.05em; } }
/* A button's label wraps onto a second line rather than running out of the
   button ("Copiar esta canasta en simulación" on a 320px screen). */
:root:is(:lang(fr), :lang(es)) .btn { white-space: normal; text-align: center; overflow-wrap: break-word; }
/* The top bar's word buttons narrow first (and take two short lines) instead
   of pushing "Commencer" off a 320px screen or squeezing the round icon
   buttons into ovals; the icons give way only after the words have. */
:root:is(:lang(fr), :lang(es)) .topbar-actions > .btn { min-width: 0; flex-shrink: 10; line-height: 1.15; padding-inline: 10px; }
/* Going live: a brokerage tile's caption ("Fractions en $ US", "Fracciones
   en US$") gets two lines inside the tile instead of an ellipsis. */
:root:is(:lang(fr), :lang(es)) .gl-tile .gl-tile-cap { white-space: normal; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; }
/* A row of tags wraps its tags; a tag longer than the row ("Importe
   l'historique de la valeur de votre compte" on a card, "Actualizado hace 38
   minutos" beside a trader's name) wraps inside itself instead of running out. */
:root:is(:lang(fr), :lang(es)) .row.wrap > .badge { white-space: normal; max-width: 100%; }
/* A trade's action mark is a 42px square sized for "BUY"; "NOUVEAU" ran 21px
   out of it over the ticker. The mark widens to its word. */
:root:is(:lang(fr), :lang(es)) .trade .act { width: auto; min-width: 42px; padding-inline: 6px; }
/* The paywall's "Best value" tag stays clear of the selected plan's tick on
   the narrowest phones (each plan is 136px wide at 320px); a tag that still
   does not fit wraps instead of running under the tick. */
:root:is(:lang(fr), :lang(es)) .pw-tag { max-width: calc(100% - 22px); }
@media (max-width: 359px) {
  :root:is(:lang(fr), :lang(es)) .pw-tag { padding-inline: 6px; }
}
/* A copy's line under its name ("Simulation · depuis le 17 oct.") takes a
   second line on a narrow phone rather than cutting off the date, the way the
   second-pick rows already do. */
:root:is(:lang(fr), :lang(es)) .copyrow-sub { white-space: normal; text-wrap: pretty; }
/* A board row on a phone: the line under the figure may take two lines. */
@container (max-width: 420px) {
  :root:is(:lang(fr), :lang(es)) .lb-row .lb-stats .lb-sub { white-space: normal; }
}
/* ==== end aw-i18n QA: fr-CA, fr, es-MX, es ==== */

/* ==== aw-i18n QA: ja, ko, zh-Hans, zh-Hant (CJK layout, 2026-10-17) ====
   Japanese and Chinese pick the right glyphs from <html lang> (i18n.js sets
   ja, zh-Hans or zh-Hant), but a Windows or Android browser whose own
   language is not that one falls back to whatever CJK face it finds first,
   which can be an old bitmap Gothic or the other script's forms of the same
   character. The fonts each platform ships for the language are named after
   the Latin ones, so English letters inside a sentence keep the site's face. */
:root:lang(ja) {
  --sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Inter, Roboto, 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', 'Yu Gothic UI', Meiryo, 'Noto Sans JP', 'Noto Sans CJK JP', Helvetica, Arial, sans-serif;
  --display: 'Meror Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', 'Yu Gothic UI', Meiryo, 'Noto Sans JP', 'Noto Sans CJK JP', sans-serif;
}
:root:lang(ko) {
  --sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Inter, Roboto, 'Apple SD Gothic Neo', 'Malgun Gothic', 'Noto Sans KR', 'Noto Sans CJK KR', Helvetica, Arial, sans-serif;
  --display: 'Meror Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Apple SD Gothic Neo', 'Malgun Gothic', 'Noto Sans KR', 'Noto Sans CJK KR', sans-serif;
}
:root:lang(zh-Hans) {
  --sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Inter, Roboto, 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', 'Noto Sans CJK SC', Helvetica, Arial, sans-serif;
  --display: 'Meror Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', 'Noto Sans CJK SC', sans-serif;
}
:root:lang(zh-Hant) {
  --sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Inter, Roboto, 'PingFang TC', 'Microsoft JhengHei', 'Noto Sans TC', 'Noto Sans CJK TC', Helvetica, Arial, sans-serif;
  --display: 'Meror Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang TC', 'Microsoft JhengHei', 'Noto Sans TC', 'Noto Sans CJK TC', sans-serif;
}
/* Korean is written in words with spaces between them, and a line breaks at
   a space: by default the browser broke inside a word ("카피하세" / "요." in
   the home headline, "않았" / "어요." in a notice). A word longer than its
   box still wraps rather than running out. */
:root:lang(ko) body { word-break: keep-all; overflow-wrap: break-word; }
/* Headlines take even lines instead of one long line and a last word alone,
   and a paragraph does not leave one character on its last line ("す。"). */
:root:is(:lang(ja), :lang(ko), :lang(zh)) :is(h1, h2, h3) { text-wrap: balance; }
:root:is(:lang(ja), :lang(ko), :lang(zh)) p { text-wrap: pretty; }
/* Japanese has no spaces, so a line may break mid-word ("証券口" / "座");
   where the browser can find the phrases (Chrome), it breaks between them
   instead, and a phrase wider than its box still wraps. */
:root:lang(ja) body { word-break: auto-phrase; overflow-wrap: break-word; }
/* The tab bar on a 320px phone: a fifth of the screen is 64px, and the
   Japanese labels ran into each other ("ポートフォリオ" was 80px). The size
   stays on the scale; the kana take their proportional widths (palt), as
   Japanese interface type does, and "Portfolio" is 資産 in Japanese, the
   word Japanese brokerage apps use for that tab (ja.json). */
@media (max-width: 359px) {
  :root:lang(ja) .bottomnav a > span { font-feature-settings: 'palt'; }
}
/* ==== end aw-i18n QA: ja, ko, zh-Hans, zh-Hant ==== */

/* ================================================================ aw-shell ==
   The iPhone app's own feel (public/js/shell.js): pages that slide, sheets
   that follow the finger, the long-press menu, rows that swipe, the bar's
   back button, the keyboard, and the phone-only details. Every rule is under
   html.in-shell, which only the app sets, so the website does not change.
   Motion moves transform and opacity only (the render budget), on the four
   duration tokens and two curves.
   ========================================================================= */

/* The app's chrome is not text to select or a link to call out; what people
   read and type stays selectable. */
html.in-shell { -webkit-touch-callout: none; }
html.in-shell :is(.topbar, .bottomnav, .btn, button, .chip, .pill, .tabbar, .modeseg, .iconbtn, a.card, .tcard, .lb-row, summary, label, .largetitle, .card-title, .section-head, [role="button"], [role="tab"], .toast, .modal-head, .shell-ctx, .shell-action, .shell-swipe-act) {
  -webkit-user-select: none; user-select: none;
}
html.in-shell :is(input, textarea, [contenteditable="true"]) { -webkit-user-select: text; user-select: text; -webkit-touch-callout: default; }
html.in-shell :is(img, svg) { -webkit-user-drag: none; }

/* 44 pt to the finger for every icon button, without moving a row: the small
   ones (32 and 38 px boxes) reach out with an invisible margin of their own. */
html.in-shell .iconbtn::before { content: ''; position: absolute; inset: min(0px, calc((100% - 44px) / 2)); }

/* The bar: no hairline until something scrolls under it, as on every
   large-title screen; the back button on a pushed screen, in the tint. */
html.in-shell .topbar:not(.shell-scrolled) { border-bottom-color: transparent; }
html.in-shell .shellbar .shell-back {
  display: inline-flex; align-items: center; gap: 5px; flex: none; max-width: 38vw;
  min-height: 44px; min-width: 44px; padding: 0 8px 0 4px;
  color: var(--brand); font-size: var(--t-lead); font-weight: 400; letter-spacing: -0.02em;
}
html.in-shell .shellbar .shell-back span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
html.in-shell .shellbar .shell-back:active { opacity: .45; }
html.in-shell .shellbar.has-back .bar-brand { display: none; }
html.in-shell .largetitle { transform-origin: left center; }

/* The tab bar keeps the tab a pushed screen belongs to lit. */
html.in-shell .bottomnav[data-shell-owned] a.active:not(.shell-owner) { color: var(--text-faint); }
html.in-shell .bottomnav[data-shell-owned] a.active:not(.shell-owner) svg { color: currentColor; transform: none; }
html.in-shell .bottomnav a.shell-owner { color: var(--text); }
html.in-shell .bottomnav a.shell-owner svg { color: var(--brand); transform: translateY(-2px); }

/* Push and pop: the screen left behind is a fixed layer under (or over) the
   live one, frozen where it was read; the live one slides on its own layer. */
html.in-shell .shell-ghost {
  position: fixed; inset: 0; z-index: 1; overflow: hidden; pointer-events: none;
  background: var(--bg); contain: strict;
}
html.in-shell .shell-ghost.is-top { z-index: 4; box-shadow: -10px 0 28px rgba(0, 0, 0, .14); contain: layout paint; }
html.in-shell .shell-ghost-dim,
html.in-shell .shell-pop-dim { position: absolute; inset: 0; background: rgba(0, 0, 0, .1); opacity: 0; pointer-events: none; }
html.in-shell .shell-pop-dim { position: fixed; z-index: 3; }
html.in-shell #view.shell-moving { position: relative; z-index: 2; background: var(--bg); min-height: 100vh; min-height: 100dvh; }
html.in-shell #view.shell-moving.is-over { box-shadow: -10px 0 28px rgba(0, 0, 0, .14); }
html.in-shell #view.shell-hold { visibility: hidden; }
/* The edge a back swipe starts from, on app builds without the system's own. */
html.in-shell .shell-edge { position: fixed; left: 0; top: 0; bottom: 0; width: 16px; z-index: 65; touch-action: none; }

/* Sheets: they rise from the bottom over a dim, and leave the same way
   from wherever the finger let go; the page behind steps back onto black. */
html.in-shell .modal-bg.shell-sheet { background: transparent; animation: none; }
html.in-shell .shell-dim {
  position: absolute; inset: 0; background: rgba(0, 0, 0, .4); pointer-events: none;
  animation: fade var(--slow) var(--ease) backwards;
}
html.in-shell .modal-bg.shell-sheet > .modal {
  position: relative; z-index: 1; touch-action: pan-y;
  max-height: calc(100dvh - env(safe-area-inset-top, 0px) - 10px - var(--shell-kb, 0px));
  animation: shell-sheet-in var(--slow) var(--ease) backwards;
  translate: 0 calc(-1 * var(--shell-kb, 0px));
  transition: translate var(--base) var(--ease);
}
@keyframes shell-sheet-in { from { transform: translateY(100%); } }
html.in-shell .modal-bg.shell-sheet.flow-out { animation: none; }
html.in-shell .modal-bg.shell-sheet.flow-out > .modal { animation: shell-sheet-out var(--fast) var(--ease) both; }
html.in-shell .modal-bg.shell-sheet.flow-out > .shell-dim { animation: shell-dim-out var(--fast) var(--ease) both; }
@keyframes shell-sheet-out { to { transform: translateY(105%); } }
@keyframes shell-dim-out { to { opacity: 0; } }
@media (max-width: 1023px) {
  html.in-shell.shell-sheet-up { background: #000 !important; }
  html.in-shell.shell-sheet-up body { background: transparent; }
  html.in-shell :is(.topbar, body > .verify-bar, body > .waiting-bar, .layout, .bottomnav) { transition: transform var(--slow) var(--ease); }
  html.in-shell.shell-sheet-up :is(.topbar, body > .verify-bar, body > .waiting-bar, .layout, .bottomnav) { transform: scale(.94); }
  html.in-shell.shell-sheet-drag :is(.topbar, body > .verify-bar, body > .waiting-bar, .layout, .bottomnav) { transition: none; }
}

/* An action sheet: the choices in one group, Cancel apart below. */
html.in-shell .shell-action { position: fixed; inset: 0; z-index: 260; display: flex; align-items: flex-end; justify-content: center; }
html.in-shell .shell-action-dim { position: absolute; inset: 0; background: rgba(0, 0, 0, .4); animation: fade var(--base) var(--ease) backwards; }
html.in-shell .shell-action-panel {
  position: relative; width: 100%; max-width: 480px; display: grid; gap: 8px;
  padding: 0 8px calc(8px + env(safe-area-inset-bottom, 0px));
  animation: shell-sheet-in var(--slow) var(--ease) backwards;
}
html.in-shell .shell-action-group {
  display: flex; flex-direction: column; overflow: hidden; background: var(--bg-elev); border-radius: 14px;
  max-height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px) - 96px);
}
html.in-shell .shell-action-list { overflow-y: auto; overscroll-behavior: contain; }
html.in-shell .shell-action-head { flex: none; display: grid; gap: 4px; padding: 14px 16px; text-align: center; color: var(--text-dim); font-size: var(--t-cap); border-bottom: .5px solid var(--line); }
html.in-shell .shell-action-head b { font-size: var(--t-small); font-weight: 600; }
html.in-shell .shell-action-btn {
  display: flex; align-items: center; justify-content: center; width: 100%; min-height: 57px; padding: 0 16px; border-radius: 0;
  background: var(--bg-elev); color: var(--brand); font-size: var(--t-lead); text-align: center;
}
html.in-shell .shell-action-list .shell-action-btn + .shell-action-btn { border-top: .5px solid var(--line); }
html.in-shell .shell-action-btn.is-destructive { color: var(--neg); }
html.in-shell .shell-action-btn.is-dim { color: var(--text-faint); }
html.in-shell .shell-action-btn[aria-current="true"] { font-weight: 600; }
html.in-shell .shell-action-check { flex: none; margin-left: 8px; }
html.in-shell .shell-action-btn.is-cancel { border-radius: 14px; font-weight: 600; }
html.in-shell .shell-action-btn:active { background: var(--surface-2); }
html.in-shell .shell-action.is-out .shell-action-panel { animation: shell-sheet-out var(--fast) var(--ease) both; }
html.in-shell .shell-action.is-out .shell-action-dim { animation: shell-dim-out var(--fast) var(--ease) both; }

/* The long-press menu: the trader lifted out of the list, its menu under it. */
html.in-shell .shell-ctx { position: fixed; inset: 0; z-index: 250; }
html.in-shell .shell-ctx-dim { position: absolute; inset: 0; background: rgba(0, 0, 0, .35); animation: fade var(--base) var(--ease) backwards; }
html.in-shell .shell-ctx-lift {
  position: absolute; overflow: hidden; border-radius: var(--radius); background: var(--bg-elev);
  box-shadow: 0 18px 50px rgba(0, 0, 0, .28);
  animation: shell-lift var(--base) var(--ease-soft) both;
}
html.in-shell .shell-ctx-lift > * { margin: 0 !important; }
@keyframes shell-lift { from { transform: translateY(var(--from, 0px)); box-shadow: none; } to { transform: scale(1.02); } }
html.in-shell[data-theme="dark"] .shell-ctx-dim { background: rgba(0, 0, 0, .55); }
html.in-shell .shell-ctx-menu {
  position: absolute; width: 250px; max-width: calc(100vw - 32px); overflow: hidden;
  background: color-mix(in srgb, var(--bg-elev) 90%, var(--text)); border-radius: 13px; box-shadow: 0 10px 40px rgba(0, 0, 0, .25);
  transform-origin: top left; animation: shell-menu-in var(--base) var(--ease-soft) backwards;
}
html.in-shell .shell-ctx-menu.from-right { transform-origin: top right; }
@keyframes shell-menu-in { from { opacity: 0; transform: scale(.6); } }
html.in-shell .shell-ctx-menu button {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; min-height: 46px;
  padding: 0 16px; color: var(--text); font-size: var(--t-lead); text-align: left;
}
html.in-shell .shell-ctx-menu button + button { border-top: .5px solid var(--line); }
html.in-shell .shell-ctx-menu button:active { background: var(--surface-2); }
html.in-shell .shell-ctx.is-out { animation: shell-dim-out var(--fast) var(--ease) both; }
html.in-shell .shell-lifted { visibility: hidden; }

/* Swipe to remove: the row's content slides left over its action. */
html.in-shell[data-shell-route="watchlist"] #view :is(.row, .tcard) { touch-action: pan-y; }
html.in-shell .shell-swipe-on { position: relative; overflow: hidden; }
html.in-shell .shell-swipe-host { position: relative; overflow: hidden; border-radius: var(--radius); }
html.in-shell .shell-swipe-act {
  position: absolute; top: 0; bottom: 0; left: 100%; width: 100%; padding-left: 22px;
  display: flex; align-items: center; background: var(--neg); color: #fff;
  font-size: var(--t-body); font-weight: 600; text-align: left;
}
html.in-shell .shell-swipe-host.is-gone { opacity: 0; transition: opacity var(--base) var(--ease); }

/* The keyboard covers the tab bar, as it does in any app, and a sheet on the
   keyboard needs no room for the home indicator under it. */
html.in-shell.shell-kb .bottomnav { visibility: hidden; }
html.in-shell.shell-kb .modal-bg.shell-sheet > .modal { padding-bottom: 18px; }

/* The confirm-your-email and permission bars come first in the markup, which
   in the app is under the status bar: they move to just below the bar. The
   body is a column so the order can say so (no :has(), whose checks ran on
   every change to the body's children, the slide layers' included). */
html.in-shell body { display: flex; flex-direction: column; }
html.in-shell body > * { order: 2; }
html.in-shell body > .topbar { order: 0; }
html.in-shell body > .verify-bar { order: 1; }
/* Like the bar, these bars stay put while a page slides beneath them. */
html.in-shell :is(.verify-bar, .waiting-bar) { position: relative; z-index: 5; }

/* The Account screen as an inset grouped list, the way Settings is: each
   card a group with its title as the small header above it, rows of 44 pt,
   the four on/off settings as switches, and a chevron on each agreement row,
   the whole row being the link. */
html.in-shell[data-shell-route="profile"] #view .page > .card { position: relative; margin-top: 38px; border-radius: 12px; }
html.in-shell[data-shell-route="profile"] #view .page > .card > .card-title:first-child {
  position: absolute; left: 16px; top: -24px; margin: 0;
  font-size: var(--t-cap); font-weight: 500; letter-spacing: .02em; color: var(--text-dim);
}
html.in-shell[data-shell-route="profile"] #view .card .statline { min-height: 44px; align-items: center; }
html.in-shell[data-shell-route="profile"] #view :is(#copyupdates, #shariascreen, #emailupdates, #marketingemails) {
  -webkit-appearance: none; appearance: none; position: relative; flex: none; order: 2;
  align-self: center; width: 51px; height: 31px; margin: 0 0 0 auto; border-radius: 999px;
  background: color-mix(in srgb, var(--text) 16%, transparent);
  transition: background var(--fast) var(--ease);
}
html.in-shell[data-shell-route="profile"] #view :is(#copyupdates, #shariascreen, #emailupdates, #marketingemails)::after {
  content: ''; position: absolute; top: 2px; left: 2px; width: 27px; height: 27px; border-radius: 50%;
  background: #fff; box-shadow: 0 3px 8px rgba(0, 0, 0, .15), 0 1px 1px rgba(0, 0, 0, .16);
  transition: transform var(--base) var(--ease-soft);
}
html.in-shell[data-shell-route="profile"] #view :is(#copyupdates, #shariascreen, #emailupdates, #marketingemails):checked { background: var(--brand); }
html.in-shell[data-shell-route="profile"] #view :is(#copyupdates, #shariascreen, #emailupdates, #marketingemails):checked::after { transform: translateX(20px); }
html.in-shell[data-shell-route="profile"] #view :is(#copyupdates, #shariascreen, #emailupdates, #marketingemails):disabled { opacity: .45; }
html.in-shell[data-shell-route="profile"] #view .card .col > .spread { position: relative; min-height: 44px; align-items: center; padding-right: 22px; }
html.in-shell[data-shell-route="profile"] #view .card .col > .spread + .spread { border-top: .5px solid var(--line); }
html.in-shell[data-shell-route="profile"] #view .card .col > .spread > a[href^="/legal/"]::before { content: ''; position: absolute; inset: 0; }
html.in-shell[data-shell-route="profile"] #view .card .col > .spread > a[href^="/legal/"]::after {
  content: ''; position: absolute; right: 6px; top: 50%; width: 8px; height: 8px;
  border: solid var(--text-faint); border-width: 2px 2px 0 0; transform: translateY(-50%) rotate(45deg);
}

/* Headings in the system face, as the phone's own apps set them. */
:root.in-shell { --display: var(--sans); }

/* ========================================================== end aw-shell == */

/* ================================================================ aw-a11y ==
   The accessibility wave (A11Y-SPEC.md, WCAG 2.2 AA on the website and in
   the app). Everything here is additive and keeps the look: visually hidden
   text, focus that is seen, colour that reads, and the app's own design. */

/* Skip link (pattern 4): the first thing Tab reaches, seen only when focused. */
.skip-link:focus {
  position: fixed; z-index: 1000; top: calc(8px + env(safe-area-inset-top, 0px)); inset-inline-start: 8px;
  padding: 10px 14px; border-radius: var(--radius-sm); background: var(--surface); color: var(--text);
  font-weight: 600; box-shadow: var(--shadow); text-decoration: underline;
}

/* Focus the page placed itself (a new screen's heading, a dialog's heading)
   draws no ring (pattern 3); everything a person reaches with Tab keeps it. */
[data-a11y-target]:focus { outline: none; }

/* The house x a dialog gets when it has no way out of its own (ui.js modal). */
.modal:has(> .a11y-x) { position: relative; }
.modal > .a11y-x { position: absolute; top: 6px; inset-inline-start: auto; inset-inline-end: 6px; z-index: 2; }
.a11y-x-pad { padding-inline-end: 48px; }

/* A trade's timing toggle (components.js tradeRow): the ticker and name as a
   real button that looks exactly like the text it was. */
.trade-toggle { background: none; border: 0; padding: 0; margin: 0; font: inherit; color: inherit; text-align: start; cursor: pointer; min-width: 0; }

/* A ranked row's copy button on a narrow row keeps its words for a screen
   reader and Voice Control (2.5.3): hidden by clipping, not display:none. */
@container (max-width: 420px) {
  .lb-row .lb-copy-word { display: inline; position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
}

/* The app's More button on a trader card or ranked row (pattern 11): the
   tap-only way to the long-press menu. Compact, quiet, 32 px. */
.card-more { width: 32px; height: 32px; flex: none; color: var(--text-dim); background: transparent; border-color: transparent; }
html.in-shell .tcard .head > .card-more { margin-inline-end: -6px; }
html.in-shell .lb-row:has(> .card-more) { grid-template-columns: 26px 30px minmax(72px, 1fr) auto 32px; }
html.in-shell .lb-row.norank:has(> .card-more) { grid-template-columns: 30px minmax(72px, 1fr) auto 32px; }
html.in-shell .lb-row.withcopy:has(> .card-more) { grid-template-columns: 26px 30px minmax(72px, 1fr) auto auto 32px; }
html.in-shell .lb-row.withcopy.norank:has(> .card-more) { grid-template-columns: 30px minmax(72px, 1fr) auto auto 32px; }

/* ---- Top bar (A4 A43 A121 A8 A69 A112) ---- */
/* The Review button keeps its word for a screen reader where it has no room
   on screen ("Review 1 waiting"), and always in the app's compact bar. */
@media (max-width: 640px) {
  .topbar-review-label { display: inline; position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
}
html.in-shell .shellbar .topbar-review-label { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
/* A crowded app bar gives up what iOS gives up first, and never shrinks a
   control below its tap size: the condensed title takes only what is left,
   the actions keep their size, Back becomes the chevron alone when Review is
   there (its word clipped, not removed, so the button keeps its name), and
   below 360 px the avatar goes (the tab bar's Account is the
   same place). Money is never cut. */
.shellbar [data-bar-title] { flex: 1 1 0; min-width: 0; }
.shellbar .topbar-actions > * { flex: none; }
@media (max-width: 430px) {
  html.in-shell .shellbar.has-back:has(.topbar-review) .shell-back-word { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
}
@media (max-width: 359px) {
  html.in-shell .shellbar.has-back .shell-back-word { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
  /* The avatar goes on pushed screens (this rule) AND on tab routes, where a
     long word state ("nicht verknüpft") stood in the bar unguarded: its last
     pixels sat outside a 320 px screen (the tab bar's Account is below). */
  html.in-shell .shellbar .topbar-actions > a[href="/profile"] { display: none; }
  html.in-shell .shellbar .topbar-review { padding: 6px 4px; }
}
/* The website bar at 320-360 px (400% zoom) with a trade waiting: the bar's
   Review gives way to the banner's Review it directly below (the same
   handler), the icons keep 32 px, and only a word state wraps. */
@media (max-width: 360px) {
  html:not(.in-shell) body:has(> .waiting-bar) .topbar-review { display: none; }
  html:not(.in-shell) .topbar-actions { gap: 4px; }
  html:not(.in-shell) .topbar-actions > .iconbtn { width: 32px; height: 32px; flex-shrink: 0; }
  html:not(.in-shell) .topbar-actions > a[href="/profile"] { flex-shrink: 0; }
  html:not(.in-shell) #livebalance { min-width: 0; margin-right: 0; }
  html:not(.in-shell) #livebalance .odo:not(:has(.odo-col)) { white-space: normal; height: auto; line-height: 1.1; text-align: center; }
}

/* ---- Focus never hidden under the bars (2.4.11; A3 A30 A93 A71) ---- */
/* --waiting-h is the "A trade is waiting" bar's own height (app.js pin). */
html:not(.in-shell) { scroll-padding-top: calc(76px + var(--waiting-h, 0px)); }
@media (max-width: 1023px) {
  html:not(.in-shell) { scroll-padding-top: calc(66px + var(--waiting-h, 0px)); }
  html { scroll-padding-bottom: calc(var(--nav-h) + env(safe-area-inset-bottom, 0px) + 12px); }
}
html.in-shell { scroll-padding-bottom: calc(var(--nav-h) + env(safe-area-inset-bottom, 0px) + 12px); }
/* The first-visit cookie notice (fixed, not modal): the band it covers is
   consent.js's --consent-h, which already holds the tab bar under it, so at
   every width a focused control scrolls clear of the notice. */
html:not(.in-shell):has(#consent) { scroll-padding-bottom: calc(var(--consent-h, 0px) + 12px); }
@media (min-width: 980px) {
  html:not(.in-shell) .sticky-side { top: calc(74px + var(--waiting-h, 0px)); max-height: calc(100vh - 90px - var(--waiting-h, 0px)); }
}
@media (min-width: 1024px) {
  html:not(.in-shell) .sidebar { top: calc(62px + var(--waiting-h, 0px)); height: calc(100vh - 62px - var(--waiting-h, 0px)); }
}
/* In the app the bar is not sticky: it sits at 0 (app.js waitingBarTop). */
html.in-shell body > .waiting-bar { top: 0; }
/* The ring on the amber bar is the bar's own ink: the brand blue vanished on it. */
.waiting-bar :focus-visible { outline-color: #1A1204; }
/* A range slider shows the one ring like everything else (A31). */
input[type='range']:focus-visible { outline: 2px solid var(--brand); outline-offset: 6px; }

/* ---- Colour that reads (1.4.3, 1.4.11; one token block) ----
   Text drawn on its own tint gets an ink token: the light theme's base
   colours stay for fills, lines, charts and figures on white; the dark
   theme's ink is its base colour except brand blue. */
:root {
  --warn-ink: var(--warn); --brand-ink: #8AA8FF; --brand-2-ink: var(--brand-2);
  --pos-ink: var(--pos); --neg-ink: var(--neg);
  --warn-fill: #D29922;
  --pos-fill: #238636; --neg-fill: #DA3633;
  --field-line: #6E7681;
}
[data-theme='light'] {
  --warn-ink: #7A5200; --brand-ink: #1D4ED8; --brand-2-ink: #0A6E6B;
  --pos-ink: #116329; --neg-ink: #A40E26;
  --warn-fill: #D29922;
  --pos-fill: var(--pos); --neg-fill: var(--neg);
  --violet: #0A6E6B;
  --field-line: #818B96;
}
.badge.amber, .badge.gold, .notice.warn, .act.down, #sidebar a.nav-urgent, #sidebar a.nav-urgent svg,
.sheet-status-held, .fx-chip, .fx-needed-k { color: var(--warn-ink); }
[data-theme='light'] .fx-chip, [data-theme='light'] .fx-needed-k, [data-theme='light'] .copyrow.is-paused .copyrow-sub { color: var(--warn-ink); }
.badge.blue, .step-n { color: var(--brand-ink); }
/* The verify and permission banners' link button on their warm tint (5.59:1 light, 6.29:1 dark). */
.verify-bar .linkish { color: var(--brand-ink); }
.feedcard .tag, .act.up { color: var(--brand-2-ink); }
.toast-act { color: var(--brand-2-ink, var(--brand)); }
.act.buy, .badge.green, .cal-day.pos, .step.done .step-mark { color: var(--pos-ink); }
.act.sell, .cal-day.neg, .btn-danger { color: var(--neg-ink); }
[data-theme='light'] .badge.orange { color: #9A3F00; }
/* Full amber, dark ink, in both themes (the toned-down bar "was invisible"). */
.waiting-bar { background: var(--warn-fill); }
html:not(.in-shell) .topbar-review { background: var(--warn-fill); }
@media (min-width: 1024px) { html.in-shell .topbar-review { background: var(--warn-fill); } }
.confirm-side.buy { background: var(--pos-fill); }
.confirm-side.sell { background: var(--neg-fill); }
.pw-tag { background: var(--brand-fill); }
/* Earlier-month calendar days: the tile fades, never the number (A23 A80). */
.cal-day.out { opacity: 1; background: color-mix(in srgb, var(--surface-3) 55%, transparent); }
.cal-day.out.pos { background: color-mix(in srgb, var(--pos-soft) 55%, transparent); border-color: color-mix(in srgb, var(--pos) 16%, transparent); }
.cal-day.out.neg { background: color-mix(in srgb, var(--neg-soft) 55%, transparent); border-color: color-mix(in srgb, var(--neg) 16%, transparent); }
.cal-day.out.flat { background: transparent; }
/* Fields have an edge you can see (1.4.11, 3:1). */
.input, select.input, textarea.input, .ob-input { border-color: var(--field-line); }
.input:focus, .ob-input:focus { border-color: var(--brand); }
.input.invalid, .input[aria-invalid="true"], .input.fx-flag { border-color: var(--neg); }
[data-theme='light'] select.input { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M3 4.5L6 8l3-3.5' stroke='%2357606A' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E"); }
/* The app's segmented control: the chosen white pill has an edge and a
   heavier label, not only a faint fill (A72). */
@media (max-width: 1023px) {
  .in-shell .tabbar button.on { box-shadow: 0 0 0 1.5px color-mix(in srgb, var(--text) 55%, transparent), 0 1px 3px rgba(0, 0, 0, .16); font-weight: 700; }
  .in-shell .tabbar button:not(.on) { font-weight: 500; }
}
/* The app's settings switches: the off state has an outline and its knob an edge (A98). */
html.in-shell[data-shell-route="profile"] #view :is(#copyupdates, #shariascreen, #emailupdates, #marketingemails):not(:checked) {
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--text) 60%, transparent);
}
html.in-shell[data-shell-route="profile"] #view :is(#copyupdates, #shariascreen, #emailupdates, #marketingemails):not(:checked)::after {
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--text) 60%, transparent), 0 3px 8px rgba(0, 0, 0, .15), 0 1px 1px rgba(0, 0, 0, .16);
}

/* ---- Links in running text are underlined, not colour alone (1.4.1) ---- */
/* Prose only: the paywall's fine print, the cookie notice, disclaimers, the
   footer's address line, a page's lead line under its heading (FAQ's "Ask
   us"), a card's paragraph (About), an email address in a sentence
   (Contact). Never nav lists, chips or feed rows. */
.pw-fine a, .consent-text a, .disclaimer a, .foot-contact a, .page > h1 + p a:not(.btn), .card > p.dim a:not(.btn), p a[href^="mailto:"] {
  text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px;
}

/* ---- The current tab or sidebar item has a shape, not only a colour (1.4.1, A81) ---- */
.bottomnav a { position: relative; }
.bottomnav a.active::before, html.in-shell .bottomnav a.shell-owner::before {
  content: ''; position: absolute; top: 0; inset-inline: calc(50% - 10px); height: 3px;
  border-radius: 0 0 3px 3px; background: var(--brand);
}
html.in-shell .bottomnav[data-shell-owned] a.active:not(.shell-owner)::before { content: none; }
@media (min-width: 1024px) {
  .sidebar a { position: relative; }
  .sidebar a.active::before {
    content: ''; position: absolute; inset-block: 8px; inset-inline-start: 0; width: 3px;
    border-radius: 3px; background: var(--brand);
  }
}

/* ---- Motion (pattern 13, A55) ---- */
/* Pseudo-elements too: the live dots' pulse ran on under reduced motion. */
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition: none !important; } }
/* A pulse that starts by itself stops by itself, within five seconds (2.2.2),
   on every platform. Passes from each loop's length (--slow is .45s): the
   1.8 s dots twice (3.6 s; the unread dot ends on its full-opacity frame
   and stays, its "Unread" is in words), the 1.35 s chat-app wait three
   times (4.05 s). The SOL guide's coin crosses the wire once and settles in
   the middle, where reduced motion keeps it (4.05 s); the app keeps its
   coin still (.in-shell .sol-coin above). */
.badge.green.live::before, .livedot::before, #livebalance .lbl::before, .pulse, .ops-dot { animation-iteration-count: 2; }
.chan-wait::before { animation-iteration-count: 3; }
.sol-coin { animation: sol-travel-once calc(var(--slow) * 9) var(--ease-soft) 1 forwards; }
@keyframes sol-travel-once {
  0% { left: 0; opacity: 0; }
  12% { opacity: 1; }
  70% { left: calc(100% - 10px); opacity: 1; }
  80% { left: calc(100% - 10px); opacity: 0; }
  81% { left: 50%; opacity: 0; }
  100% { left: 50%; opacity: 1; }
}
/* Live prices paused (pattern 8): the live dot stands still. */
html[data-live-prices="paused"] :is(.badge.green.live, .livedot, #livebalance .lbl)::before { animation: none; }

/* ---- Charts (2.5.7): Measure, two taps for a stretch ---- */
.chart-measure {
  background: none; border: 0; padding: 0 4px; margin-inline-start: 4px; font: inherit; font-weight: 600;
  color: var(--brand-ink); text-decoration: underline; text-underline-offset: 2px; cursor: pointer; border-radius: 6px;
}
.chart-measure[aria-pressed="true"] { background: var(--brand-soft); text-decoration: none; padding: 2px 8px; }
@media (pointer: coarse) { .chart-measure { min-height: 44px; padding: 0 8px; } }

/* ---- The cookie notice at 400% zoom (1.4.10, A82) ---- */
/* The bottom tab bar is there below 1024 px, so the notice sits above it at
   every one of those widths; it is never taller than the room left, and on a
   short screen its words scroll inside it while the two buttons stay. */
@media (max-width: 1023px) { .consent { bottom: calc(64px + env(safe-area-inset-bottom, 0px)); } }
.consent { max-height: calc(100vh - 32px); max-height: calc(100dvh - 32px); }
@media (max-width: 1023px) {
  .consent { max-height: calc(100vh - 72px - env(safe-area-inset-bottom, 0px)); max-height: calc(100dvh - 72px - env(safe-area-inset-bottom, 0px)); }
}
@media (max-height: 480px) {
  .consent { flex-direction: column; flex-wrap: nowrap; align-items: stretch; }
  .consent-text { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
  .consent-actions { flex: none; }
}
.consent:focus { outline: none; }

/* ---- Text follows the phone's text size in the app (1.4.4, pattern 12) ----
   --u is one pixel of type. On the website it is 1px, so nothing changes.
   In the app the root takes the iPhone's own body size (WebKit resolves
   -apple-system-body to the Dynamic Type setting, 17px at the default) and
   --u is that over 17, floored at 1px (macOS WebKit says 13px) and capped at
   2px (200%). The six sizes, every inline size in the page code and golive's
   are N * var(--u); the title and the hero grow more gently, the way iOS
   large titles do. The tab bar and the top bar cap their own words at 125%
   (Apple's own bars do not grow with the content). Fixed boxes keep their
   type: the 20px avatar's initials, a calendar cell's figure, the PRO mark. */
:root { --u: 1px; }
html.in-shell {
  font: -apple-system-body;
  font-family: var(--sans); font-weight: 400; font-style: normal; line-height: 1.55;
  --u: clamp(1px, calc(1rem / 17), 2px);
  --t-cap: calc(12 * var(--u)); --t-small: calc(13.5 * var(--u)); --t-body: calc(15 * var(--u)); --t-lead: calc(17 * var(--u));
  --t-title: calc(22px + (var(--u) - 1px) * 13); --t-hero: calc(34px + (var(--u) - 1px) * 6);
}
html.in-shell :is(.topbar, .bottomnav) {
  --u: clamp(1px, calc(1rem / 17), 1.25px);
  --t-cap: calc(12 * var(--u)); --t-small: calc(13.5 * var(--u)); --t-body: calc(15 * var(--u)); --t-lead: calc(17 * var(--u));
  --t-title: calc(22 * var(--u)); --t-hero: calc(34 * var(--u));
}
/* The onboarding team's Pro and go-live shapes keep their own sizes, scaled the same way in the app. */
.real-text b:where(html.in-shell *) { font-size: calc(14 * var(--u)); }
.real-text small:where(html.in-shell *) { font-size: calc(12.5 * var(--u)); }
.moment .real-text small:where(html.in-shell *) { font-size: calc(13 * var(--u)); }
.limit-sheet h3:where(html.in-shell *) { font-size: calc(21 * var(--u)); }
.limit-sheet p:where(html.in-shell *) { font-size: calc(14 * var(--u)); }
/* The date and Measure stay together; on a narrow card they go under the legend as one. */
.chartbox > .row.spread:has(> .chart-foot) { flex-wrap: wrap; row-gap: 4px; }
.chart-foot { white-space: nowrap; }

/* The logo's name is its word (no aria-label, 2.5.3): where the word gives
   way to the bar's controls it is hidden by clipping, never removed, so the
   link keeps "Meror" as its name. */
@media (max-width: 380px) { .logo-word { display: flex; position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; } }
@media (max-width: 560px) { .topbar-in:has(.topbar-review) .logo-word { display: flex; position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; } }
/* A trade row's toggle and the trader's name under it are each a 24 px target (2.5.8). */
.trade-toggle { min-height: 24px; }
.trade a.trade-trader { display: inline-block; min-height: 24px; line-height: 24px; }

/* At a large text size the Account screen's group headers and its Language
   row keep their room. */
html.in-shell[data-shell-route="profile"] #view .page > .card { margin-top: calc(26px + 12 * var(--u)); }
html.in-shell[data-shell-route="profile"] #view .page > .card > .card-title:first-child { top: calc(-12px - 12 * var(--u)); }
html.in-shell [data-lang-row] { flex-wrap: wrap; row-gap: 8px; }
html.in-shell.dt-large [data-lang-row] [data-lang-picker] { flex: 1 1 100%; min-width: 0; }
html.in-shell.dt-large [data-lang-row] .lang-picker { width: 100%; max-width: 100%; }
html.in-shell.dt-large .btn { white-space: normal; max-width: 100%; text-align: center; }
html.in-shell.dt-large #view .spread:has(> .tabbar) { flex-wrap: wrap; row-gap: 8px; }
html.in-shell.dt-large #view .tabbar[style*="180px"] { width: auto !important; }
html.in-shell.dt-large #view .tabbar { max-width: 100%; flex-wrap: wrap; }
.pro-line:where(html.in-shell *), .real-link:where(html.in-shell *) { font-size: calc(13.5 * var(--u)); }
.real-row > .btn:where(html.in-shell *), .moment > .btn:where(html.in-shell *) { font-size: calc(13.5 * var(--u)); }
.make-real:where(html.in-shell *) { font-size: calc(14 * var(--u)); }

/* ======================================================= aw-a11y pages-statement ==
   The accessibility statement package (A11Y-SPEC.md). The statement wears the
   Support page's look (.support-page); its lists read like its paragraphs. */
.accessibility-page .support-q ul { margin: 0 0 10px; padding-inline-start: 20px; }
.accessibility-page .support-q li { margin: 0 0 8px; line-height: 1.6; }
/* The guides' ranked traders, one link per row: each a 24 px target (2.5.8). */
.guide-traders a { display: inline-block; min-height: 24px; line-height: 24px; }
/* =================================================== end aw-a11y pages-statement == */

/* ==== aw-a11y pages-holdings ==== */
/* The phone holdings: a list of rows, each one native button (the whole
   iOS cell is the control). The row looks exactly as the div did. */
ul.holdrows { list-style: none; margin: 0; padding: 0; }
button.holdrow { position: relative; width: 100%; margin: 0; text-align: start; border-radius: 0; }
button.holdrow:focus-visible { border-radius: var(--radius-sm); }
.holdrows > li:last-child > .holdrow { border-bottom: 0; }
/* The app held its rows to 44 px through the [role="button"] rule; the
   native button keeps that height. */
.in-shell button.holdrow { min-height: 44px; }
/* At a large text size in the app (Dynamic Type) a holding stacks, the way
   an iOS cell does at the accessibility sizes: what it is and whose copy on
   top, the value and result under them, every line wrapping instead of
   ending in "...", so nothing is cut off (1.4.4). The default size keeps
   its two columns. */
html.in-shell.dt-large .holdrow.go { grid-template-columns: minmax(0, 1fr) 14px; row-gap: 4px; }
html.in-shell.dt-large .holdrow.go > span:nth-child(2) { grid-column: 1; grid-row: 2; align-items: flex-start; text-align: start; }
html.in-shell.dt-large .holdrow.go > svg { grid-column: 2; grid-row: 1 / span 2; }
html.in-shell.dt-large .holdrow > span > * { white-space: normal; overflow-wrap: anywhere; }
/* A table row's control: the symbol and name, as the bold text they were,
   a 24 px target at least (2.5.8), with the shared focus ring. The padding
   that makes the target is given back by the margin, so no line or row
   grows. */
.sec-btn { display: inline-block; min-height: 24px; padding: 2px 0; margin: -2px 0; text-align: start; color: inherit; background: none; border: 0; }
td > .sec-btn { display: block; }
/* "Copied from": a 24 px target, since the row around it is a pointer target
   too. The negative margin gives the padding back to the line, so the row
   keeps its height and only the link's box grows. */
.tbl.holdings a.dim { display: inline-block; padding-block: 4px; margin-block: -4px; min-height: 24px; }
/* /activity: the "what" line is the row's disclosure button; its ::after
   stretches over the row so a tap anywhere but the trader link toggles,
   and the focus ring is drawn around the whole row, as before. */
.feedrow { position: relative; }
.feedrow-toggle { display: block; max-width: 100%; padding: 0; margin: 0; font: inherit; font-weight: 600; color: inherit; background: none; border: 0; text-align: start; }
.feedrow-toggle::after { content: ''; position: absolute; inset: 0; }
.feedrow-toggle:focus-visible { outline: none; }
.feedrow:has(.feedrow-toggle:focus-visible) { outline: 2px solid var(--brand); outline-offset: 2px; border-radius: var(--radius-sm); }
.feedrow a { position: relative; z-index: 1; }
/* The press dims the whole row, as it did when the row was the control. */
.feedrow:has(.feedrow-toggle.press-dim.is-pressed) { opacity: .6; transition: opacity var(--press) var(--ease); }
.feedrow-toggle.press-dim.is-pressed { opacity: 1; }
/* In the app these rows stay chrome, not text to select, as they were when
   the whole row carried role="button". */
html.in-shell :is(.feedrow, .row-chart) { -webkit-user-select: none; user-select: none; }

/* ==== aw-a11y pages-browse ==== */
/* /styles: a style nobody supplies yet is a flat card with its words at full
   contrast (1.4.3, A24); only its decorative icon is faded. */
.style-empty .style-emoji { opacity: .6; }
/* /feed: Refresh while it waits keeps the faded look `disabled` gave it, and
   stays focusable and announced (ui.js busy: aria-busy, aria-disabled). */
#shuffle.is-busy { opacity: .5; }
/* ==== end aw-a11y pages-browse ==== */

/* ==== aw-a11y pages-account ==== */
/* Going live (A83, 1.4.12): a brokerage tile's caption is never cut to two
   lines. The French, Spanish, Arabic, Turkish, Hindi and pseudo-locale
   blocks above clamp it; golive.css now wraps every name and caption and
   lets the tile grow, so the clamp is lifted here for every language. */
html:root .gl-tiles .gl-tile .gl-tile-cap { display: block; -webkit-line-clamp: none; overflow: visible; }
/* /mode and /welcome (A41): each card's action is a real button where the
   span was. It sits where the span's text sat: the 16px above it is now the
   list's margin and the button's own 4px, and the button's 4px below hangs
   into the card's padding, so the focus ring hugs the words and the target
   is 28px tall without moving anything. The card dims while its choice is
   being saved, as the card-button did. */
.choice > .ticks:has(+ button.choice-cta) { margin-bottom: 12px; }
.choice > button.choice-cta { align-self: flex-start; padding: 4px 0; margin-bottom: -4px; text-align: start; border-radius: 6px; }
.choice.is-busy { opacity: .6; cursor: default; }
/* ==== end aw-a11y pages-account ==== */

/* ==== aw-a11y pages-auth-public ==== */
/* The app's sign-in boxes keep a caption above them once filled (A63, WCAG
   3.3.2): the "or with email" line's own size and ink, at the box's start,
   4px over the box (the form's 10px gap, less 6). Static: no floating label. */
.ob-label { display: block; font-size: var(--t-cap); color: var(--text-dim); text-align: start; line-height: 1.3; margin: 0 0 -6px; padding-inline-start: 4px; }
/* ==== end aw-a11y pages-auth-public ==== */

/* ==== aw-a11y reaudit fixes ==== */
/* Count badges on the brand in dark mode (1.4.3): white on the dark brand was
   3.74:1; the fill white buttons already use reads 4.69:1. The lit pill's own
   rule (.pill.on .pill-n) is more specific and keeps its colours. */
.pill-n, .sol-n { background: var(--brand-fill); }
/* The website's tab bar under 360px (1.4.12): with a reader's own letter and
   word spacing the one-line labels ran off the screen ("Start free") or into
   each other. English takes the rule the other languages already have (a
   label too long for its fifth wraps, the icons stay level), and the bar grows
   instead of clipping. The app's bar keeps its own fixed height and layout. */
@media (max-width: 359px) {
  html:not(.in-shell) .bottomnav { grid-template-columns: repeat(5, minmax(0, 1fr)); height: auto; min-height: calc(var(--nav-h) + env(safe-area-inset-bottom, 0px)); }
  html:not(.in-shell) .bottomnav a { justify-content: flex-start; min-width: 0; padding: 12px 0 0; }
  html:not(.in-shell) .bottomnav a > span { white-space: normal; text-align: center; line-height: 1.08; max-width: 100%; overflow-wrap: break-word; -webkit-hyphens: auto; hyphens: auto; }
}
/* The app at a large text size (dt-large: above the iPhone's default; 1.4.4,
   A11Y-SPEC pattern 12). Names, lines and labels wrap the way an iPhone list
   does instead of being cut to a few letters, a ranked row stacks (rank,
   avatar and name across the top, the figure and its buttons under them), a
   header's buttons wrap onto a line of their own, and a trade's word grows
   its pill. Nothing changes at the default size. Inside #view only: the top
   bar's title keeps its own 125% cap. */
html.in-shell.dt-large #view :is(.truncate, .lb-meta, .copyrow-name, .copyrow-sub, .tcard .name, .tcard .sub) { white-space: normal; overflow-wrap: anywhere; text-overflow: clip; }
html.in-shell.dt-large #view .lb-row,
html.in-shell.dt-large #view .lb-row:is(.norank, .withcopy, :has(> .card-more)) { grid-template-columns: auto auto minmax(0, 1fr) auto auto; row-gap: 6px; }
html.in-shell.dt-large #view .lb-row > .rank { grid-area: 1 / 1; }
html.in-shell.dt-large #view .lb-row > .avatar { grid-area: 1 / 2; }
html.in-shell.dt-large #view .lb-row > .grow { grid-area: 1 / 3 / auto / -1; }
html.in-shell.dt-large #view .lb-row.norank > .avatar { grid-area: 1 / 1; }
html.in-shell.dt-large #view .lb-row.norank > .grow { grid-area: 1 / 2 / auto / -1; }
html.in-shell.dt-large #view .lb-row > .lb-stats { grid-area: 2 / 1 / auto / 4; justify-self: start; text-align: start; min-width: 0; }
html.in-shell.dt-large #view .lb-row > :not(.rank, .avatar, .grow, .lb-stats, .card-more, .lb-note) { grid-area: 2 / 4; }
html.in-shell.dt-large #view .lb-row > .card-more { grid-area: 2 / 5; }
html.in-shell.dt-large #view .lb-row > .lb-note { grid-area: 3 / 1 / auto / -1; max-width: none; }
html.in-shell.dt-large #view .spread { flex-wrap: wrap; row-gap: 8px; }
html.in-shell.dt-large #view .spread > .row { flex-wrap: wrap; }
.trade .act:where(html.in-shell.dt-large *) { width: auto !important; min-width: 42px; height: auto !important; min-height: 42px; padding-inline: 6px; }
.trade .act[style*="width:36px"]:where(html.in-shell.dt-large *) { min-width: 36px; min-height: 36px; }
/* Initials repeat the name beside them, so they stop growing where the
   circle stops (the circles keep their size and leave the names their room);
   a rank number is content, so its medal grows around it. */
html.in-shell .avatar {
  --u: clamp(1px, calc(1rem / 17), 1.13px);
  --t-cap: calc(12 * var(--u)); --t-body: calc(15 * var(--u)); --t-title: calc(22 * var(--u)); --t-hero: calc(34 * var(--u));
}
html.in-shell.dt-large #view .badge { white-space: normal; max-width: 100%; height: auto; text-align: start; }
html.in-shell.dt-large #view .chart-foot { white-space: normal; }
html.in-shell.dt-large #view .tactions { grid-template-columns: minmax(0, 1fr); }
html.in-shell.dt-large #view .tactions-rest { flex-wrap: wrap; }
html.in-shell.dt-large #view .modeseg button { white-space: normal; min-width: 0; }
html.in-shell.dt-large .rank { width: auto; height: auto; min-width: 1.5em; min-height: 1.5em; padding: 0 .3em; line-height: 1.5; }
/* The bars' large content view (shell.js mountLargeContent): above their 125%
   cap, a touched and held tab, Back or balance shows here at the page's own
   size, in the sheets' own look. Never a target; a fade only with motion. The
   bars still scroll the page up and down; a slide along them is the finger's. */
html.in-shell.dt-xl :is(#bottomnav, #topbar) { touch-action: pan-y; }
.shell-lcv {
  position: fixed; z-index: 140; left: 50%; top: 50%; transform: translate(-50%, -50%); pointer-events: none;
  display: flex; flex-direction: column; align-items: center; gap: calc(8 * var(--u));
  width: max-content; min-width: 140px; max-width: calc(100vw - 48px); padding: calc(18 * var(--u)) calc(22 * var(--u));
  background: var(--bg-elev); color: var(--text); border: .5px solid var(--line); border-radius: 22px; box-shadow: var(--shadow);
  font-size: var(--t-lead); font-weight: 600; line-height: 1.2; text-align: center; overflow-wrap: break-word;
  animation: lcv-in var(--fast) var(--ease);
}
.shell-lcv[hidden] { display: none; }
.shell-lcv svg { width: calc(21 * var(--u)); height: calc(21 * var(--u)); flex: none; }
@keyframes lcv-in { from { opacity: 0; } }
/* ==== end aw-a11y reaudit fixes ==== */

/* ==== aw-safety: Your money and safety ==== */
/* The page (/safety) reads like Support and the accessibility statement;
   its lists read like its paragraphs. */
.safety-page .support-q ul, .safety-sheet .support-q ul { margin: 0 0 10px; padding-inline-start: 20px; }
.safety-page .support-q li, .safety-sheet .support-q li { margin: 0 0 8px; line-height: 1.6; }
/* The sheet the paywall opens: the legal sheet's body, sections at h4. */
.safety-sheet .support-q { margin-top: 18px; }
.safety-sheet .support-q h4 { margin: 0 0 6px; font-size: var(--t-body); }
.safety-sheet .support-q p, .safety-sheet .support-q li { font-size: var(--t-small); }
/* The short lines on the money screens. Every link to the page is underlined,
   so it is a link without colour (1.4.1), with a 24px target (2.5.8). */
.safe-line a, .safe-quiet a, .pw-safe a, .safe-link { text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px; display: inline-block; min-height: 24px; line-height: 24px; }
.safe-line a, .safe-quiet a, .safe-link { color: var(--text-dim); font-weight: 600; }
/* A trader page: under the actions, before the notices. */
.tactions + .safe-line { margin: 12px 0 0; line-height: 1.5; }
/* The real-money copy window: under the disclaimer. */
.disclaimer.safe-line { margin: 6px 0 0; }
/* The paywall: last, under Apple's lines and Restore, small and centred like them. */
.pw-safe { margin: 0; font-size: var(--t-cap); line-height: 1.45; color: var(--text-dim); text-align: center; text-wrap: balance; }
.pw-safe a { color: var(--text-dim); }
/* /confirm: one quiet line at the end. */
.safe-quiet { font-size: var(--t-small); }
/* Go live: "Before you connect" under Continue, and the points beside the tick. */
.safe-block { margin-top: 14px; text-align: start; font-size: var(--t-cap); line-height: 1.5; color: var(--text-dim); }
.safe-h { margin: 0 0 6px; font-size: var(--t-cap); font-weight: 600; color: var(--text); }
.safe-points { margin: 0; padding-inline-start: 18px; }
.safe-points li { margin: 0 0 4px; text-wrap: pretty; }
.safe-block .safe-link { margin-top: 2px; }
.gl-sign .safe-sign { flex: none; margin: 8px 0 0; font-size: var(--t-cap); line-height: 1.45; color: var(--text-dim); text-wrap: pretty; }
.safe-sign a { color: var(--text-dim); font-weight: 600; text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px; }
/* The sign screen is the phone's height with the document scrolling in its
   own pane. Where the words need more than that (a 320 px phone, large text
   in the app) the tick and Sign ran under the "Still needed" line; now the
   screen grows and the page scrolls instead. The pane keeps its basis of 0
   and its 160 px floor, so it still scrolls and the read-to-the-end gate
   still counts; where everything fits, nothing changes. golive.css loads
   after this file, hence :root for the weight. */
@media (max-width: 719px) { :root .gl.gl-sign { height: auto; min-height: max(100svh, 680px); } }
/* The money line beside the tick comes out of the reading pane, the document
   that must be read to the end to sign: on a 390 x 844 phone the pane fell
   from 241 px to its 160 px floor, four lines of the text. On a tall phone the
   floor follows the screen instead (253 px there), and the page scrolls the
   few lines more that costs; a short phone keeps 160 px. */
@media (max-width: 719px) { :root .gl-sign .gl-reader { min-height: max(160px, 30svh); } }
/* ==== end aw-safety ==== */

/* ==== aw-int (2026-10-18): confirmed-defect fixes ==== */
/* A placed bet slip's messages now sit under its stake/odds row (forms.js
   puts a row field's message after the row), on their own line. */
[data-placedslip] > .fx-msg { flex-basis: 100%; text-align: left; margin: 0; }
/* The app's onboarding boxes take the shared error state. After
   .ob-input:focus (same specificity), so a focused box that is wrong reads
   red, as a focused invalid .input does on the website. */
.ob-input[aria-invalid="true"], .ob-input.fx-flag { border-color: var(--neg); box-shadow: 0 0 0 3px var(--neg-soft); }
/* Explore's chip-row arrows sit inside the shell: at -6px the page's clip
   (main.shell overflow-x: clip, no padding at desktop) cut their outer edge. */
@media (hover: hover) and (pointer: fine) {
  .edge-btn.edge-prev { left: 0; }
  .edge-btn.edge-next { right: 0; }
}
/* At desktop the shell has no side padding, so the row's -16px bleed was
   clipped too and the fade lost its last 16 px. */
@media (min-width: 1024px) { .chipscroll { margin-inline: 0; padding-inline: 0; } }
/* A signed-out tab with a short label shows it under 360 px (app.js
   NAV_MAIN_PUBLIC): "How it works" ran into "Strategies" at 320-340 px. */
.bottomnav a > .short { display: none; }
@media (max-width: 359px) { .bottomnav a > .full { display: none; } .bottomnav a > .short { display: inline; } }
/* The paywall's loading shape holds the longer Apple wording's height under
   the shorter one (paywall.js fineHoldHtml), so the trial line landing moves
   nothing below it. */
.pw-fine-hold { display: grid; }
.pw-fine-hold > span { grid-area: 1 / 1; }
.pw-fine-ghost { visibility: hidden; }
/* Ranked rows on a 320-340 px phone (Explore's ranked list, a strategy's
   members): the card's own padding is the row's, the tracks give the name
   room, and the name takes two lines rather than "Omar A…". The meta line
   keeps its ellipsis. The 420 px gap rule never applied (a row cannot query
   itself as its own container), so the gap is set here by the screen. The
   watchlist's rows keep their own tracks (their rule is more specific). */
@media (max-width: 340px) {
  .lb-row { padding-inline: 4px; column-gap: 6px; grid-template-columns: 20px 30px minmax(56px, 1fr) auto; }
  .lb-row.withcopy { grid-template-columns: 20px 30px minmax(56px, 1fr) auto auto; }
  .lb-row.norank { grid-template-columns: 30px minmax(56px, 1fr) auto; }
  .lb-row.withcopy.norank { grid-template-columns: 30px minmax(56px, 1fr) auto auto; }
  .lb-row > .grow > .truncate { white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; }
}
/* A trader's holdings on a phone: three columns (components.js positionRow
   compact). The gain sits under what it was bought at, the held time under
   the name; the table fits the card rather than being cut mid-column with
   nothing to say it scrolls. */
.tbl-holdings .pos-held-m, .tbl-holdings .pos-since-m { display: none; }
@media (max-width: 559px) {
  .tbl-holdings .pos-since, .tbl-holdings .pos-held { display: none; }
  .tbl-holdings .pos-held-m { display: block; font-size: var(--t-cap); margin-top: 2px; }
  .tbl-holdings .pos-since-m { display: block; font-size: var(--t-cap); margin-top: 2px; }
  .tbl-holdings th, .tbl-holdings td { padding-left: 8px; padding-right: 8px; }
  .tbl-holdings th { white-space: normal; }
  .tbl-holdings .pos-name { white-space: nowrap !important; overflow: hidden; text-overflow: ellipsis; max-width: max(68px, calc(100vw - 240px)) !important; }
}
/* ==== end aw-int ==== */
/* ==== aw-int bf5 (2026-10-18): the bf4 hunt's confirmed defects ==== */
/* Back's cross-fade: the group (the overlay that takes taps while it runs)
   ends with its 140 ms fade instead of its 0.25 s default. A tap still skips
   it at once (app.js pageTransition). */
::view-transition-group(root) { animation-duration: var(--fast); }
/* The feed's Refresh keeps its place at the end of the row when the large
   title owns the page's "For you" heading (app.js mountShellTitle). */
html.in-shell .spread > h1.bar-owned + .btn { margin-left: auto; }
/* A button's icon keeps its size: a flex child SVG shrank to a speck (the
   feed's Follow lock was 2.7 px at 375 px and 0 px at 320). */
.btn svg { flex: none; }
/* Sheets in the app: a locked sheet (the legal gate) cannot be dismissed, so
   it shows no grabber (ui.js marks it data-locked). An unlocked one closes
   with a swipe down through shell.js dragToDismiss, whose .shell-dim fades
   with the drag. */
.in-shell .modal-bg[data-locked="1"] > .modal::before { display: none; }
/* The profile photo button drawn turned off (the app before a build with the
   camera usage string, profile.js photoPickerReady): the picture alone, no
   camera mark, nothing to press. */
.avatar-edit[disabled] { cursor: default; opacity: 1; }
.avatar-edit[disabled] .avatar-edit-mark { display: none; }
.avatar-edit[disabled]:active { transform: none; }
/* The terms reader on a short phone (320x568): the middle pane shrank to
   76 px while the reader inside kept 140 px, so the agreement row was drawn
   over the document and the "Scroll to the end" hint. The pane has a floor
   (the 96 px reader, its track and its hint) and, only on short screens, the
   sheet scrolls as a last resort. Never min-height:auto on the pane: that
   makes it as tall as the whole document. */
.reader-mid { min-height: 132px; }
.reader-mid .reader { min-height: 96px; }
@media (max-height: 640px) {
  .modal:has(.reader-modal) { overflow-y: auto; }
  .reader-modal { max-height: none; }
}
/* Explore's order, drawn as a grey word of its length until a filtered list
   says which period it really is (explore.js). */
.skel-words { color: transparent; border-radius: 6px; }
/* A ranked row's figure from another period (components.js leaderRow): its
   short tag shows where the long period word is hidden, on a narrow card,
   on its own line over the figure so the figure column is no wider (the
   name keeps its floor). */
.lb-row .lb-ptag { display: none; font-size: var(--t-cap); font-weight: 600; letter-spacing: .02em; line-height: 1.1; color: var(--text-faint); }
@container (max-width: 340px) { .lb-row .lb-ptag { display: block; } }
/* The search suggestions wrap inside their card on a 320-345 px phone
   ("Traders up more than 10% this month" ran 9 px past it); the loading
   shape's chips wrap the same way. Only this card: other chip rows scroll. */
.suggest-chips > .chip { white-space: normal; max-width: 100%; min-width: 0; text-align: left; }
/* Search's securities: one row that scrolls sideways, one height for one
   match or five (pages/search.js), held by the loading shape. */
.sec-row { margin-bottom: 12px; }
.sec-chip { display: inline-flex; align-items: baseline; gap: 6px; flex: none; min-height: 36px; padding: 8px 12px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); font-size: var(--t-small); white-space: nowrap; }
.sec-chip.skel { color: transparent; border-color: transparent; }
/* The paywall's billed price never breaks inside its digits (paywall.js
   priceParts): the run is one unit, and a long one steps down the type
   scale so it fits the narrowest card (116 px of content at 320 px) at
   0.58em a character (SF Mono, less the -0.02em letter-spacing): up to 9
   characters at the title size, 11 at --t-lead, 13 at --t-body, 14 at
   --t-small, 16 at --t-cap. Both plan cards take the longer price's size. */
.pw-price { overflow-wrap: normal; }
.pw-amt { white-space: nowrap; }
.pw-price-m { font-size: var(--t-lead); }
.pw-price-l { font-size: var(--t-body); }
.pw-price-xl { font-size: var(--t-small); }
.pw-price-xxl { font-size: var(--t-cap); }
/* ==== end aw-int bf5 ==== */
/* ==== aw-a11y reaudit 2 fixes ==== */
/* The scroll-to-accept reader (Billing Terms before checkout, the locked
   "We have updated our terms" gate) at 200% and 400% zoom and at the app's
   large text (1.4.10, 1.4.4). The frame stays the same full-height sheet, but
   its middle can no longer shrink below the pane's 140px floor plus the bar
   and the hint, so the agreement never lands on top of the document, and the
   sheet itself scrolls only when the three parts truly do not fit, so Accept
   stays reachable by touch, mouse and Tab. Overrides the reader's rules above
   (the block "the reader"); attachScrollGate and the look are unchanged. */
.modal:has(.reader-modal) { overflow-y: auto; height: 100%; }
.reader-mid { flex: 1 0 auto; }
.reader-mid .reader { flex: 1 1 140px; }
/* A sportsbook tile waiting on its save (sportsbookPicker.js): aria-disabled
   and aria-busy rather than disabled, so focus stays on the pressed book; the
   same greyed look as before, and a waiting tile neither lifts nor presses. */
.book-tile[aria-disabled="true"] { opacity: .55; cursor: default; }
.book-tile[aria-disabled="true"]:not(.on):hover { border-color: var(--line); }
.book-tile[aria-disabled="true"]:is(:hover, :active, .is-pressed) { transform: none; }
.book-check { display: inline-flex; flex: none; }
/* The feed's actions on the website (3.2.4 kept the Copy button's one name,
   "Paper copy" or "Copy with real money"): the in-app rule above (the feed
   card's row wraps, each button takes its share) on every width, so the long
   real-money label moves to its own line instead of running out of its
   button, and "Follow with Pro" fits at 320. */
.feedcard .row.mt { flex-wrap: wrap; }
.feedcard .row.mt .btn { flex: 1 1 auto; }
/* A source's name on /sources wraps under zoom and text spacing instead of
   being cut with an ellipsis (1.4.10, 1.4.12); its health badge stays beside
   it and the card grows a line. */
.src-name { white-space: normal; overflow-wrap: anywhere; min-width: 0; }
/* A chip in a wrapping row (Search's "Try searching for") at 200% text in the
   app and with a reader's text spacing at 320 px (1.4.4, 1.4.12): it moves to
   a line of its own first and breaks its words only when it is wider than the
   whole line, so the default look is unchanged. A .chiprow still scrolls
   sideways (the owner's "chips scroll, not wrap"). */
.row.wrap > .chip { white-space: normal; max-width: 100%; text-align: start; }
/* The go-live tiles at a large text size in the app: one per line, the way an
   iPhone list stacks at accessibility sizes, so the Recommended tag centred
   on its tile's top edge stays inside the screen and no neighbour covers it,
   and brokerage names stop breaking mid-word. Nothing changes at the default
   size. */
html.in-shell.dt-large .gl-tiles.gl-3, html.in-shell.dt-large .gl-tiles.gl-2 { grid-template-columns: minmax(0, 1fr); }
/* The long-press menu never runs below the screen at a large text size
   (1.4.4): shell.js places it from its measured height, and if even the whole
   screen is not enough it scrolls inside its own rounded box. */
html.in-shell .shell-ctx-menu {
  max-height: calc(100dvh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px) - 24px);
  overflow-y: auto; overscroll-behavior: contain;
}
/* The chosen sportsbook's name on its tint (1.4.3): the brand blue on the
   brand tint was 4.44:1; the ink token other blue words on tints use reads
   above 4.5:1 in light and dark. Found by the audit once a book was chosen. */
.book-tile.on { color: var(--brand-ink); }
/* ==== end aw-a11y reaudit 2 fixes ==== */

/* ==== aw-a11y reaudit 3 fixes ==== */
/* The chart's keyboard control (charts.js scrubHtml, 4.1.2): a slider laid
   over the drawing, transparent and deaf to the pointer, so the plot's
   pointer, drag and Measure work as before. It covers the drawing only (not
   the figures' gutter), so its focus ring is where the drawing's was. */
.chart-scrub { position: absolute; top: 0; bottom: 0; left: 0; right: 0; pointer-events: none; border-radius: 6px; outline: none; }
.chart-plot.has-y > .chart-scrub { right: 52px; }
/* Share (the share card) and "Switch to paper and show them" (/styles) wait
   busy rather than disabled, so they keep focus (2.4.3); the faded look the
   disabled button had stays. */
#share-card-go.is-busy, #topaper.is-busy { opacity: .5; }
/* A trade's action mark in another language (3.1.2: "ZUKAUF", "REDUZIERT",
   "СОКРАЩЕНИЕ" for ADD and TRIM): it grows to its word, as the French and
   Spanish rule above already let it, in every non-English locale, the
   copy sheet's 36px mark too. English keeps its fixed square. */
html:not(:lang(en)) .trade .act { width: auto; min-width: 42px; padding-inline: 6px; }
html:not(:lang(en)) .trade .act[style*="width:36px"] { width: auto !important; min-width: 36px; }
/* At the app's large text a long word's mark would leave the trade's details
   a sliver of a 320px screen, so the mark takes the first line and the
   details the full width under it (the way an iPhone list stacks). */
html.in-shell.dt-large:not(:lang(en)) #view .trade { flex-wrap: wrap; row-gap: 8px; }
html.in-shell.dt-large:not(:lang(en)) #view .trade > .act { align-self: flex-start; }
html.in-shell.dt-large:not(:lang(en)) #view .trade > .grow { flex: 1 1 100%; }
/* The weekday row's one-letter names (activity.js calendarCard) are for the
   app's large text only. */
.cal-week .cal-wd-narrow { display: none; }
/* The app at 200% text (1.4.4, pattern 12), at large sizes only, so the
   default size looks the same:
   - a word longer than its line breaks (hyphenated in the page's language)
     instead of running off the screen or under its card's edge: key-figure
     labels, long titles, an address link, the German large titles;
   - the calendar's weekday row keeps the day columns' tracks and gap and
     says each day in one letter (iOS Calendar's pattern), so "Sun" stays on
     screen over its own column;
   - the Pro step's perks wrap (they are not chips);
   - Home's style cards go one to a row, like the go-live tiles. */
html.in-shell.dt-large #view { overflow-wrap: break-word; -webkit-hyphens: auto; hyphens: auto; }
html.in-shell.dt-large .pcal-card .cal-week { grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px; }
html.in-shell.dt-large .cal-week .cal-wd { display: none; }
html.in-shell.dt-large .cal-week .cal-wd-narrow { display: inline; }
html.in-shell.dt-large .gl-perks { flex-wrap: wrap; row-gap: 12px; }
html.in-shell.dt-large .gl-perk { white-space: normal; text-align: center; min-width: 0; }
html.in-shell.dt-large .style-grid { grid-template-columns: minmax(0, 1fr); }
/* A ranked row in the app at 320px (1.4.10; the iPhone SE with Display Zoom):
   rank, avatar, name, figure and Copy no longer leave room for the More
   button's own track, which ran past the card and the screen (in Arabic off
   its left edge). The watchlist's narrow tracks (22px rank, a 56px name
   floor, 6px gaps), and More takes a second line in the Copy button's column
   at its full 32px. A row without Copy keeps More on its line, in the same
   narrower tracks. The large-text layout (dt-large, an id in its selectors)
   is untouched, and from 360px up nothing changes. */
@media (max-width: 359px) {
  html.in-shell .lb-row:has(> .card-more) { grid-template-columns: 22px 30px minmax(56px, 1fr) auto 32px; column-gap: 6px; }
  html.in-shell .lb-row.norank:has(> .card-more) { grid-template-columns: 30px minmax(56px, 1fr) auto 32px; }
  html.in-shell .lb-row.withcopy:has(> .card-more) { grid-template-columns: 22px 30px minmax(56px, 1fr) auto auto; column-gap: 6px; }
  html.in-shell .lb-row.withcopy.norank:has(> .card-more) { grid-template-columns: 30px minmax(56px, 1fr) auto auto; }
  html.in-shell .lb-row.withcopy:has(> .card-more) > :is(.lb-copy, .badge) { grid-area: 1 / -2; }
  html.in-shell .lb-row.withcopy > .card-more { grid-area: 2 / -2; justify-self: center; }
  html.in-shell .lb-row.withcopy:has(> .card-more) > .lb-note { grid-column: 3 / -2; }
  html.in-shell .lb-row.withcopy.norank:has(> .card-more) > .lb-note { grid-column: 2 / -2; }
}
/* A reader's own text spacing on the website at 320px (1.4.12): "Mark all
   read", the language pickers, the footer's legal row and a long button ran
   off the screen. The rules the German, Italian, Portuguese and Russian
   layouts already ship, for every language: the heading's button wraps
   under it, a language picker gives way to its label, the footer's legal
   links share two columns and break a long word, and under 360px a button's
   words wrap inside it (a board row's Copy keeps its line; its word hides on
   a narrow row instead). At normal spacing none of these moves; the app has
   its own large-text rules. */
.spread:has(> #readall) { flex-wrap: wrap; row-gap: 8px; }
[data-lang-row] > [data-lang-picker] { min-width: 0; flex: 0 1 auto; }
[data-lang-row] .lang-picker { max-width: min(220px, 100%); }
@media (max-width: 559px) { :is(.home-foot, .public-foot) .foot-legal { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
:is(.home-foot, .public-foot) .foot-legal a { min-width: 0; overflow-wrap: break-word; }
@media (max-width: 359px) {
  html:not(.in-shell) .btn { white-space: normal; max-width: 100%; text-align: center; overflow-wrap: break-word; }
  html:not(.in-shell) .lb-row .lb-copy { white-space: nowrap; }
}
/* The app's bar on a pushed screen with a trade waiting, in a longer
   language (1.4.10: "nicht verknüpft", "não vinculada" pushed the account
   avatar, and at 320px the bell, off the screen). The pill's word state
   wraps onto two short lines, as the website's 360px rule above already
   does; only the pill gives way, and a dollar figure (odometer columns) never
   wraps. Back, Search, the bell and Review keep their size. */
@media (max-width: 430px) {
  html.in-shell .shellbar.has-back:has(.topbar-review) .topbar-actions { min-width: 0; }
  html.in-shell .shellbar.has-back:has(.topbar-review) #livebalance { flex: 0 1 auto; }
  html.in-shell .shellbar.has-back:has(.topbar-review) #livebalance .odo:not(:has(.odo-col)) { white-space: normal; height: auto; text-align: center; min-width: min-content; }
  /* Where even that is not enough ("wird synchronisiert", "sincronizzazione"
     have one long word), the avatar goes, as it already does under 360px:
     the tab bar's Account is the same place. */
  :root:is(:lang(de), :lang(pt), :lang(it), :lang(ru)) .shellbar.has-back:has(.topbar-review) .topbar-actions > a[href="/profile"]:where(html.in-shell *) { display: none; }
}
/* At 360px and below in those languages, with the waiting banner and its own
   Review right under the bar (the same handler), the bar's Review gives way,
   as on the website. English keeps its bar as it was. */
@media (max-width: 360px) {
  :root:is(:lang(de), :lang(pt), :lang(it), :lang(ru)) body:has(> .waiting-bar) .shellbar.has-back .topbar-review:where(html.in-shell *) { display: none; }
}
/* ==== end aw-a11y reaudit 3 fixes ==== */
