/* =====================================================================
   EduMarkets — Portfolio Simulation, participant app.

   Two jobs. First, the shell rules the platform keeps in platform.css:
   this app is standalone, so it carries its own mount container and join
   screen instead of the platform's module loader. Second, the desk
   vocabulary, modelled on the platform's market-making.css and prefixed
   `pm-` so nothing collides with the copied component styles.

   Layout contract, copied from the market-making desk: while a session is
   live the Trading Desk fills ONE viewport with no page scrolling. Only
   the feeds and blotters scroll, inside their own boxes. The
   `pm-desk-active` class on the workspace switches the platform's
   scrolling content area into a fixed fill-the-height layout; every other
   page removes it and normal scrolling returns.
   ===================================================================== */

/* ── Shell ─────────────────────────────────────────────────────────── */

#module-mount {
  flex: 1;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* The header lockup deliberately carries no size preset (.md / .sm): the
   lockup stylesheet loads after components.css, so a preset would win the
   cascade over the header sizing in components.css. Same convention as the
   platform. */

/* ── One-viewport mode (Trading Desk) ──────────────────────────────── */

#pm-workspace.pm-desk-active .workspace-content {
  overflow-y: hidden;
}
#pm-workspace.pm-desk-active .tab-panel.active {
  flex: 1 1 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
#pm-workspace.pm-desk-active .tab-content-area {
  flex: 1 1 0;
  min-height: 0;
  max-width: 1560px;   /* cap on wide monitors; the desk must stay dense */
  padding: 10px 16px 12px;
  display: flex;
  flex-direction: column;
}
/* The desk page's content host is the flex column the .pm-desk inside it
   stretches into. Without this the desk grid collapses to zero height and
   the overflow:hidden left column clips away entirely. */
#pm-workspace.pm-desk-active #pm-desk-content {
  flex: 1 1 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
#pm-workspace .pm-desk {
  flex: 1 1 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* ── Placeholder used by pages that are not built yet ──────────────── */

#pm-workspace .pm-placeholder {
  color: var(--text-muted);
  font-size: 0.85rem;
  padding: 10px 12px;
}

/* ── Header, right-hand group ──────────────────────────────────────── */

/* ── Header, right-hand controls ───────────────────────────────────── */

/* Copied from the Bank ALM console header (css/console.css .header-controls,
   .theme-toggle and its glyph swap, .btn / .btn-secondary / .btn-sm), so the
   theme toggle and Leave sit exactly where they sit there: one group,
   centred on the header's middle line. Console tokens are substituted by
   their values where the platform has no equivalent: --radius-sm is --r-sm,
   --font-base is --font, and --bg-hover, which the platform does not
   define, is --bg-elevated. */

/* The console header pads itself (.app-header padding: 0 24px in
   console.css); the platform header pads nothing and each right-hand child
   carries its own inset instead. So the group takes the console's 24px on
   the right, which is what puts Leave the same distance from the edge. */
#pm-header-controls {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
  padding-right: 24px;
}
#pm-header-controls .theme-toggle {
  background: transparent;
  border: 1px solid transparent;   /* invisible by default, shows on hover */
  color: var(--text-secondary);
  width: 32px;
  height: 32px;
  border-radius: var(--r-sm);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16px;
  transition: var(--transition);
  /* Desaturate the colour emoji glyphs (yellow moon to grey). */
  filter: grayscale(1);
}
#pm-header-controls .theme-toggle:hover {
  background: var(--bg-elevated);
  border-color: var(--border);
  color: var(--text);
}
.icon-sun, .icon-moon { display: none; }
[data-theme="dark"] .icon-sun { display: inline; }
[data-theme="light"] .icon-moon { display: inline; }

#pm-header-controls .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px 16px;
  border-radius: var(--r-sm);
  font-size: 13px;
  font-weight: 600;
  font-family: var(--font);
  cursor: pointer;
  transition: var(--transition);
  border: none;
}
#pm-header-controls .btn-secondary {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-secondary);
}
#pm-header-controls .btn-secondary:hover {
  background: var(--bg-elevated);
  color: var(--text);
}
#pm-header-controls .btn-sm { padding: 5px 10px; font-size: 12px; }

/* ── Join screen ───────────────────────────────────────────────────── */

/* The same screen the trainer and admin consoles sign in on, copied from
   css/console.css (:735-1016) so all three look identical. The console
   stylesheet is not loaded here, so its tokens are substituted by the
   values it holds: --radius 8px is the platform's --r-md, --font-base is
   --font, and the teal focus ring is written out because the platform
   palette has no teal. Nothing else is changed. */

.join-screen {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  background: #111114;
  padding: 40px 20px;
  position: relative;
  overflow: hidden;
}
.join-screen::before {
  content: '';
  position: fixed;
  top: 0; left: 0; right: 0; bottom: 0;
  background-image:
    linear-gradient(rgba(255,255,255,0.02) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,0.02) 1px, transparent 1px);
  background-size: 60px 60px;
  z-index: 0;
}
.join-screen::after {
  content: '';
  position: fixed;
  top: -200px; left: -200px;
  width: 600px; height: 600px;
  background: radial-gradient(circle, rgba(20, 184, 166, 0.06) 0%, transparent 70%);
  z-index: 0;
}
[data-theme="light"] .join-screen { background: #f0f2f7; }
[data-theme="light"] .join-screen::before {
  background-image:
    linear-gradient(rgba(0,0,0,0.03) 1px, transparent 1px),
    linear-gradient(90deg, rgba(0,0,0,0.03) 1px, transparent 1px);
}
[data-theme="light"] .join-screen::after {
  background: radial-gradient(circle, rgba(13, 148, 136, 0.06) 0%, transparent 70%);
}

/* The login screens carry the sim's own module lockup at full size. The
   platform surface that does the same is the boot overlay, so both the name
   treatment and the centring come from there (platform.css :47-56). The
   centring is flex, never text-align, which would centre the wordmark over
   the module name instead of leaving the whole lockup left aligned against
   its bar. */
.join-logo {
  position: relative;
  z-index: 1;
  margin-bottom: 56px;
  display: flex;
  justify-content: center;
}
.join-logo .lockup-led .name {
  font-weight: 700;
  text-transform: none;
  line-height: 1.15;
  /* Two-value edge form: current Edge does not parse "trim-start cap". */
  text-box: trim-start cap alphabetic;
}

.join-card {
  position: relative;
  z-index: 1;
  background: linear-gradient(135deg, rgba(255,255,255,0.045) 0%, rgba(255,255,255,0.02) 100%);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 16px;
  padding: 44px 40px;
  backdrop-filter: blur(20px);
  -webkit-backdrop-filter: blur(20px);
  box-shadow:
    0 0 0 1px rgba(255,255,255,0.03),
    0 20px 60px -10px rgba(0,0,0,0.5);
  width: 100%;
  max-width: 440px;
}
[data-theme="light"] .join-card {
  background: linear-gradient(135deg, rgba(255,255,255,0.8) 0%, rgba(255,255,255,0.65) 100%);
  border: 1px solid rgba(0,0,0,0.1);
  box-shadow: 0 0 0 1px rgba(0,0,0,0.03), 0 20px 60px -10px rgba(0,0,0,0.12);
}

.join-title {
  font-size: 24px;
  font-weight: 500;
  text-align: center;
  margin-bottom: 6px;
  letter-spacing: -0.5px;
  color: #f0f0f0;
}
[data-theme="light"] .join-title { color: #1a1a1e; }

.join-subtitle {
  font-size: 14px;
  color: rgba(255,255,255,0.45);
  font-weight: 300;
  text-align: center;
  margin-bottom: 28px;
}
[data-theme="light"] .join-subtitle { color: rgba(0,0,0,0.45); }

.join-footer {
  position: relative;
  z-index: 1;
  margin-top: 28px;
  font-size: 12px;
  color: rgba(255,255,255,0.3);
  text-align: center;
}
[data-theme="light"] .join-footer { color: rgba(0,0,0,0.3); }

.footer-disclaimer {
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  text-decoration: underline;
  cursor: pointer;
}
.footer-disclaimer:hover { color: var(--color-accent); }
.footer-disclaimer:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

.auth-field {
  margin-bottom: 20px;
  text-align: left;
}
.auth-field label {
  display: block;
  font-size: 12px;
  font-weight: 500;
  color: rgba(255,255,255,0.5);
  margin-bottom: 8px;
}
[data-theme="light"] .auth-field label { color: rgba(0,0,0,0.5); }
.auth-field input {
  width: 100%;
  padding: 14px 18px;
  background: rgba(255,255,255,0.05);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: var(--r-md);
  color: #ffffff;
  font-family: var(--font);
  font-size: 15px;
  font-weight: 400;
  outline: none;
  transition: all 0.25s ease;
}
.auth-field input:focus {
  border-color: rgba(20, 184, 166, 0.45);
  background: rgba(255,255,255,0.06);
  box-shadow: 0 0 0 3px rgba(20, 184, 166, 0.08);
}
.auth-field input::placeholder { color: rgba(255,255,255,0.25); }
[data-theme="light"] .auth-field input {
  background: #f4f6fa;
  border: 1px solid #dde1ec;
  color: #1a1a1e;
}
[data-theme="light"] .auth-field input::placeholder { color: rgba(0,0,0,0.3); }
[data-theme="light"] .auth-field input:focus {
  border-color: #0d9488;
  background: #ffffff;
  box-shadow: 0 0 0 3px rgba(13, 148, 136, 0.08);
}

.auth-btn {
  width: 100%;
  padding: 14px;
  background: linear-gradient(135deg, #ffffff 0%, #e8e8e8 100%);
  color: #0B0B0C;
  border: none;
  border-radius: var(--r-md);
  font-family: var(--font);
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  margin-top: 4px;
  transition: all 0.25s ease;
  letter-spacing: -0.2px;
}
.auth-btn:hover { box-shadow: 0 8px 30px rgba(255,255,255,0.1); }
.auth-btn:disabled { opacity: 0.6; cursor: not-allowed; }
[data-theme="light"] .auth-btn {
  background: linear-gradient(135deg, #1a1a1e 0%, #333 100%);
  color: #ffffff;
}
[data-theme="light"] .auth-btn:hover { box-shadow: 0 8px 30px rgba(0,0,0,0.15); }

/* Second step of the join screen: the confirmed session, the quieter "back"
   action, and the small line under the join-code label. Same treatment the
   ALM sim uses, so the two join screens read the same. */
.auth-info-box {
  background: rgba(20, 184, 166, 0.06);
  border: 1px solid rgba(20, 184, 166, 0.15);
  border-radius: var(--r-md);
  padding: 14px 18px;
  margin-bottom: 24px;
}
.auth-info-box .auth-info-title {
  font-weight: 600;
  font-size: 15px;
  color: var(--text);
}
.auth-btn-secondary {
  width: 100%;
  padding: 12px;
  margin-top: 8px;
  background: transparent;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  font-family: var(--font);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.25s ease;
}
.auth-btn-secondary:hover { color: var(--text); border-color: var(--text-muted); }
.auth-field .auth-sublabel {
  display: block;
  font-size: 11px;
  color: var(--text-muted);
  margin-top: -4px;
  margin-bottom: 8px;
  font-weight: 400;
}
.auth-field input.mono {
  font-family: var(--font-mono);
  letter-spacing: 2px;
}

.pm-connect-error {
  max-width: 480px;
  margin: 80px auto;
  color: var(--negative);
  text-align: center;
}

/* ── Sidebar ───────────────────────────────────────────────────────── */

/* The platform pads sidebar content through a list of module content ids
   (components.css :914-923). This app's id is not in that list, so the
   same declaration is repeated here rather than forking the copied
   stylesheet. Values are the platform's, unchanged. */
#pm-params-content {
  padding: 20px 16px;
  min-width: var(--sidebar-width);   /* prevents reflow during animation */
}

/* Key-value rows and the sub-line, copied from the platform's
   market-making sidebar (css/market-making.css .mkm-sb-row). */

#pm-params-content .pm-sb-desk {
  font-size: 1rem;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 12px;
  line-height: 1.35;
}
/* The member names sit on the same line as the team name, after a plain
   hyphen: "Citadel - RvL". */
#pm-params-content .pm-sb-members {
  font-size: 0.8rem;
  font-weight: 400;
  color: var(--text-muted);
  margin-left: 4px;
}
/* The team name is the rename control: a click puts an input in its place.
   It is drawn as the name and nothing else, so the sidebar reads as it did;
   the hover colour and the block's own tooltip are what say it can be
   clicked. */
#pm-params-content .pm-sb-name {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
#pm-params-content .pm-sb-name:hover { color: var(--color-accent); }
#pm-params-content .pm-sb-name:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
  border-radius: 2px;
}
/* The editor takes the whole line, member names included, because the
   field needs the width of the sidebar to hold a team name. */
#pm-params-content .pm-sb-name-input { width: 100%; }
#pm-params-content .pm-sb-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  padding: 4px 0;
  font-size: 0.95rem;
  color: var(--text-secondary);
}
#pm-params-content .pm-sb-row .num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--text);
  text-align: right;
}
#pm-params-content .pm-sb-row .num.positive { color: var(--positive); }
#pm-params-content .pm-sb-row .num.negative { color: var(--negative); }
/* A value that reads as words: the session state, the pace, the day count.
   Same weight and colour as a figure, the body font instead of mono. */
#pm-params-content .pm-sb-row .pm-sb-word {
  font-weight: 600;
  color: var(--text);
  text-align: right;
}
#pm-params-content .pm-sb-row .pm-sb-word.negative { color: var(--negative); }
/* The sub-line sits inside the value's span, so it would inherit the mono
   face. It reads as a sentence ("30% of capacity", "limit 20%"), and mono
   is for figures alone, so it takes the body font back. */
#pm-params-content .pm-sb-sub {
  display: block;
  text-align: right;
  font-family: var(--font);
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--text-muted);
}
/* The session line: the day, the pace and the connection on one line under
   the team name, spread across the sidebar's width. It replaces four rows
   of key and value, so the figures a desk reads all session start that
   much higher up. Each reading keeps its own tooltip. */
#pm-params-content .pm-sb-status {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 4px;
  font-size: 0.95rem;
  color: var(--text-secondary);
}
#pm-params-content .pm-sb-stat {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  white-space: nowrap;
}
#pm-params-content .pm-sb-stat-value { font-weight: 600; color: var(--text); }

/* A row that belongs to no block above it: the session overview control
   stands clear of the last section rather than reading as one more
   activity figure. The space is a heading's, so the break reads the same
   as the one between two sections. */
#pm-params-content .pm-sb-row--apart { margin-top: 18px; }

/* The row the ones above it add up to. The rule over it is what says so,
   the way a total is ruled off on a statement.

   The rule is drawn brighter than --border, which at #252830 on the dark
   sidebar surface is a separator between rows and disappears when it has
   to carry meaning. The light theme takes the token back: #dde1ec on white
   reads at a glance already. */
#pm-params-content .pm-sb-row--sum {
  border-top: 1px solid rgba(255, 255, 255, 0.22);
  margin-top: 2px;
  padding-top: 6px;
}
[data-theme="light"] #pm-params-content .pm-sb-row--sum {
  border-top-color: var(--border);
}

#pm-params-content .pm-sb-conn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.85rem;
  color: var(--text-secondary);
}
/* The connection dot, copied from the Bank ALM console stylesheet
   (css/console.css .status-dot). The class names are the ones shared/ws.js
   writes, so the socket client is unchanged. */
.status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--text-muted);
}
.status-dot.online { background: var(--positive); }
.status-dot.offline { background: var(--negative); }
.status-dot.reconnecting {
  background: var(--warning);
  animation: pm-pulse-dot 1s ease-in-out infinite;
}
@keyframes pm-pulse-dot { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }

/* ── Trading Desk ──────────────────────────────────────────────────── */

#pm-workspace .pm-grid {
  flex: 1 1 0;
  min-height: 0;
  display: grid;
  /* The market grid is down to six columns, so the left side needs less
     than it did and the right side, which carries four cards of figures,
     needs more than it had. */
  grid-template-columns: minmax(600px, 44%) 1fr;
  gap: 12px;
}
/* Left: the market grid takes what is left after the ticket, and scrolls
   inside itself, so the ticket never moves down the screen. */
#pm-workspace .pm-col-market {
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  overflow: hidden;
}
#pm-workspace .pm-market-frame { flex: 1 1 0; }
#pm-workspace .pm-ticket-frame { flex: 0 0 auto; }
/* Right: the instrument panel, the newsfeed, positions, resting orders.
   The newsfeed sits under the panel, where the story that moves a name
   reads beside the name it moves. The blotter is not here: a fill confirms
   itself the moment the position moves, so the list of them is a record
   rather than something a desk trades from, and it reads on Portfolio
   Analytics.

   Height has one owner per card, and the grid distributes only what is
   genuinely elastic. Tuned fractions were tried for each card in turn and
   kept coming apart, because two things were sizing the same card at once:
   the card asking for its content's height and the grid handing it a share
   of the column regardless. So each card is sized by what it is.

   Fixed: the instrument panel shows one set of fields and one chart, so it
   is a known height and says so itself. That is also what stops it
   resizing when an instrument is picked.

   Content: resting orders is usually empty and never long, so it takes the
   height of what it holds, capped so a full book of orders scrolls rather
   than eating the column.

   Elastic: the newsfeed and positions are lists with no natural end, so
   they take equal shares of whatever the other two leave, down to nothing
   on a short screen. That is what keeps the column ending exactly where
   the ticket beside it does instead of running off the bottom. Any card
   too small to read on the day is opened with its own button. */
#pm-workspace .pm-col-right {
  min-height: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr) minmax(0, 1fr) auto;
  gap: 10px;
}
/* The fallback the enlarged card is positioned against on a page with no
   column of its own. The rules for that state live at the very bottom of
   this file, and the comment there says why they have to. */
#pm-workspace { position: relative; }

/* Each region is a card, copied from the Bank ALM console (.card and
   .card-title in css/console.css): rounded corners, the raised surface, a
   hairline border and a plain dark title. The market-making desk's square
   terminal frame suits a single-instrument desk; this one holds a page of
   tables and reads better as cards. Console tokens are substituted by their
   values: --radius-lg 12px is the platform's --r-lg.

   The padding differs from the console's 20px on purpose. These cards hold
   scrolling tables inside a fixed viewport, so the head carries the inset
   and the body runs to the card's edge. */
#pm-workspace .pm-frame {
  min-height: 0;
  display: flex;
  flex-direction: column;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
}
#pm-workspace .pm-frame-head {
  flex: 0 0 auto;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  padding: 10px 14px 8px;
}
/* The console's .card-title, verbatim: 15px, weight 600, plain text
   colour. Only table headers are set in capitals, which is what both
   stacks do. */
#pm-workspace .pm-frame-title {
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
}
#pm-workspace .pm-frame-note {
  font-size: 0.75rem;
  color: var(--text-muted);
}

/* ── Market grid ───────────────────────────────────────────────────── */

#pm-workspace .pm-market-wrap {
  flex: 1 1 0;
  min-height: 0;
  overflow-y: auto;          /* the grid is one of the regions that scroll */
  overflow-x: auto;          /* a narrow window scrolls rather than clips */
}

/* Fixed layout, so the columns are shared out rather than sized by their
   longest cell. Numbers keep their width and never truncate; the company and
   sector names give way with an ellipsis. Without this the table ran wider
   than its card and the last columns were cut off at the border. */
#pm-workspace .pm-market,
#pm-workspace .pm-table {
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
  font-size: 0.85rem;
}
#pm-workspace .pm-market th,
#pm-workspace .pm-market td,
#pm-workspace .pm-table th,
#pm-workspace .pm-table td {
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Market grid: symbol, beta, bid, ask, daily %, position. Every column is
   given a share, none left to take what is over: under `table-layout:
   fixed` an unsized column gets only the remainder, and that is how the
   company name came to be squeezed to two letters.

   Sector and the company name are not here. Neither prices the instrument,
   the panel beside the grid carries both, and the room they took was
   coming out of the six columns a desk reads on every glance. Beta stays:
   the mandate caps beta-adjusted net exposure, so it is read while
   scanning rather than looked up. */
#pm-workspace .pm-market th:nth-child(1) { width: 13%; }
#pm-workspace .pm-market th:nth-child(2) { width: 13%; }
#pm-workspace .pm-market th:nth-child(3),
#pm-workspace .pm-market th:nth-child(4) { width: 18.5%; }
#pm-workspace .pm-market th:nth-child(5) { width: 18.5%; }
#pm-workspace .pm-market th:nth-child(6) { width: 18.5%; }
/* A universe whose tickers are not the company leads on the name and drops
   the ticker (name, beta, bid, ask, daily %, position). Every column is
   redeclared rather than adjusted, because under `table-layout: fixed` an
   unsized column takes only the remainder. The name is the one column that
   may run out of room, and it takes the ellipsis the table already sets. */
#pm-workspace .pm-market--named th:nth-child(1) { width: 27%; }
#pm-workspace .pm-market--named th:nth-child(2) { width: 9%; }
#pm-workspace .pm-market--named th:nth-child(3),
#pm-workspace .pm-market--named th:nth-child(4) { width: 17%; }
#pm-workspace .pm-market--named th:nth-child(5) { width: 14%; }
#pm-workspace .pm-market--named th:nth-child(6) { width: 16%; }
/* The macro grid runs five columns: a universe without a market factor
   declares no beta. Redeclared in full, as above, because under fixed
   layout the five columns would otherwise take the first five of the six
   widths and the bid would sit in the beta column's 9%. */
#pm-workspace .pm-market--nobeta th:nth-child(1) { width: 30%; }
#pm-workspace .pm-market--nobeta th:nth-child(2),
#pm-workspace .pm-market--nobeta th:nth-child(3) { width: 18%; }
#pm-workspace .pm-market--nobeta th:nth-child(4) { width: 16%; }
#pm-workspace .pm-market--nobeta th:nth-child(5) { width: 18%; }
/* The name leads the grid where it is shown, so it reads as the identity
   column the ticker is on the other universes rather than as a gloss. */
#pm-workspace .pm-market .pm-name { font-weight: 500; }
/* The region heading between blocks of the grid. It is a row rather than a
   separate table so the columns stay in one fixed layout, and it sticks
   under the header the way the header sticks to the top, so the block a
   participant is scrolled into keeps saying which market it is. Weight and
   colour separate it from the rows, not a size of its own. */
#pm-workspace .pm-market .pm-market-block td {
  position: sticky;
  top: 27px;
  background: var(--bg-elevated);
  color: var(--text-secondary);
  font-weight: 600;
  padding-top: 7px;
  padding-bottom: 5px;
  border-bottom: 1px solid var(--border);
}
#pm-workspace .pm-market .pm-market-block:hover td { background: var(--bg-elevated); }
/* The blotter and the resting orders both lead with a symbol, which needs
   far less room than the columns of figures beside it. */
#pm-workspace .pm-table th:first-child { width: 84px; }
/* Both tables in the right column declare every column, so neither takes
   the pixel width above. */
#pm-workspace .pm-orders-table th:first-child,
#pm-workspace #pm-positions .pm-table th:first-child { width: auto; }
/* Positions: symbol, quantity, average cost, value, unrealised. Declared
   one by one for the same reason the market grid is. Left to share what
   was over, the columns came out uneven and cut the figures to -94... and
   -$8..., which is worse than no column at all. Realised profit and loss
   is a review figure and reads on Portfolio Analytics instead. */
/* Resting orders: side, quantity, instrument, type, price, time in force,
   cancel. Without a heading row this table had no sized columns at all, so
   `table-layout: fixed` split it into seven equal parts and cut the price
   to "Limit 22...". */
#pm-workspace .pm-orders-table th:nth-child(1) { width: 10%; }
#pm-workspace .pm-orders-table th:nth-child(2) { width: 15%; }
#pm-workspace .pm-orders-table th:nth-child(3) { width: 11%; }
#pm-workspace .pm-orders-table th:nth-child(4) { width: 10%; }
#pm-workspace .pm-orders-table th:nth-child(5) { width: 15%; }
#pm-workspace .pm-orders-table th:nth-child(6) { width: 22%; }
#pm-workspace .pm-orders-table th:nth-child(7) { width: 17%; }

#pm-workspace #pm-positions .pm-table th:nth-child(1) { width: 16%; }
#pm-workspace #pm-positions .pm-table th:nth-child(2) { width: 21%; }
#pm-workspace #pm-positions .pm-table th:nth-child(3) { width: 19%; }
#pm-workspace #pm-positions .pm-table th:nth-child(4) { width: 21%; }
#pm-workspace #pm-positions .pm-table th:nth-child(5) { width: 23%; }
#pm-workspace .pm-market th,
#pm-workspace .pm-table th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--bg-surface);
  padding: 5px 10px;
  text-align: left;
  font-size: 0.72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
#pm-workspace .pm-market th[data-sort] { cursor: pointer; }
#pm-workspace .pm-market td,
#pm-workspace .pm-table td {
  padding: 3px 10px;
  border-bottom: 1px solid var(--border-subtle);
  white-space: nowrap;
}
/* The blotter and the resting orders sit in the narrow right column and
   carry money figures, which need the room more than the gutters do. */
#pm-workspace .pm-col-right .pm-table th,
#pm-workspace .pm-col-right .pm-table td { padding-left: 7px; padding-right: 7px; }
/* A heading there is allowed a second line. Cut to fit, they were reading
   as AVE, UNR and REA, and a column nobody can name is a column nobody can
   use. The figures underneath still stay on one line. */
#pm-workspace .pm-col-right .pm-table th {
  white-space: normal;
  vertical-align: bottom;
  line-height: 1.15;
}
#pm-workspace .pm-market th.right,
#pm-workspace .pm-market td.right,
#pm-workspace .pm-table th.right,
#pm-workspace .pm-table td.right { text-align: right; }
#pm-workspace .pm-market .num,
#pm-workspace .pm-table .num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}
/* Two tiers down the identifying columns, carried by weight and colour
   rather than by size: the ticker is what the row is looked up by and the
   company name confirms it. */
#pm-workspace .pm-sym { font-weight: 600; color: var(--text); }
#pm-workspace .pm-company { color: var(--text-secondary); }
#pm-workspace .pm-market tbody tr { cursor: pointer; }
/* A region heading is not an instrument, so it is not clickable. */
#pm-workspace .pm-market tbody tr.pm-market-block { cursor: default; }
/* A halted name after a credit default: the row stays so the reader sees
   what happened, muted, with the quote cells replaced by the word. */
#pm-workspace .pm-market tbody tr.pm-row-halted td {
  color: var(--text-muted);
}
/* The instrument panel's halt line under the name tags. Inherits the
   tags' type scale; colour alone carries the state. */
#pm-workspace .pm-ip-halted {
  color: var(--negative);
  margin-top: 2px;
}
#pm-workspace #pm-positions .pm-table tbody tr { cursor: pointer; }
#pm-workspace .pm-market tbody tr:hover,
#pm-workspace #pm-positions .pm-table tbody tr:hover {
  background: var(--bg-elevated);
}
/* The instrument the desk is working on, held until another is picked. The
   tint has to read as chosen rather than as hovered, so it is the accent at
   low strength rather than another step of grey. Older engines that cannot
   mix colours fall back to the hover tint. */
#pm-workspace .pm-market tbody tr.pm-row-selected,
#pm-workspace #pm-positions .pm-table tbody tr.pm-row-selected {
  background: var(--bg-elevated);
  background: color-mix(in srgb, var(--color-accent) 14%, transparent);
}
#pm-workspace tr.pm-row-selected .pm-sym { color: var(--color-accent-text); }
#pm-workspace .positive { color: var(--positive); }
#pm-workspace .negative { color: var(--negative); }
/* A futures contract is worth multiplier times the index price, so its size
   sits next to the symbol. Without it a contract reads like a share. */
#pm-workspace .pm-mult {
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--text-muted);
}

/* ── Ticket ────────────────────────────────────────────────────────── */

#pm-workspace .pm-ticket { padding: 0 14px 12px; }
#pm-workspace .pm-ticket-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 8px;
  align-items: end;
}
#pm-workspace .pm-field { display: block; }
#pm-workspace .pm-field .sb-label { margin: 0 0 4px; }
/* What the typed size works out to. It sits on its own row under the size
   fields rather than taking a grid column of its own, so the fields keep
   their widths as the note appears and disappears. */
#pm-workspace .pm-ticket-size {
  grid-column: 1 / -1;
  min-height: 1.1em;
  margin-top: -2px;
  font-size: 0.8rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
#pm-workspace .pm-ticket-actions {
  display: flex;
  gap: 8px;
}
#pm-workspace .pm-ticket-actions .btn { flex: 1 1 0; }
/* A refused order stays here until the next ticket action. */
#pm-workspace .pm-ticket-message {
  margin-top: 8px;
  padding: 8px 10px;
  font-size: 0.85rem;
  color: var(--negative);
  background: var(--negative-bg);
  border: 1px solid color-mix(in srgb, var(--negative) 35%, transparent);
}
/* The preview is two side blocks of seven rows, plus one line for a
   futures contract. Reserving that height keeps the ticket the same size
   whichever instrument is picked, so the grid above it never jumps. */
#pm-workspace .pm-preview {
  margin-top: 8px;
  min-height: 208px;
}
#pm-workspace .pm-preview-unit {
  font-size: 0.78rem;
  color: var(--text-muted);
  margin-bottom: 6px;
}
#pm-workspace .pm-preview-sides {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 10px 28px;
}
/* No box around either side. The gap between the two columns and the
   heading over each one already separate them, and a drawn frame around
   a list of figures adds an edge for the eye to catch on without saying
   anything the layout has not said. */
#pm-workspace .pm-preview-side { padding: 2px 0; }
#pm-workspace .pm-preview-title {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 4px;
}
#pm-workspace .pm-preview-row {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: 0.8rem;
  color: var(--text-secondary);
  padding: 1px 0;
}
#pm-workspace .pm-preview-row .num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text);
}
#pm-workspace .pm-preview-blocked {
  font-size: 0.8rem;
  color: var(--negative);
}

/* ── Blotter, resting orders, feed ─────────────────────────────────── */

#pm-workspace .pm-scroll {
  flex: 1 1 0;
  min-height: 0;
  overflow-y: auto;
  overflow-x: auto;
}
/* The resting-orders box takes its natural height inside an `auto` grid row,
   so its scroller must not stretch: `flex: 1 1 0` there resolves to zero and
   the frame's overflow:hidden then clips the table away entirely. */
#pm-workspace .pm-orders-frame .pm-scroll {
  flex: 0 1 auto;
  max-height: 160px;
}
#pm-workspace .pm-order-side { font-weight: 600; }
#pm-workspace .pm-order-side--buy  { color: var(--positive); }
#pm-workspace .pm-order-side--sell { color: var(--negative); }

/* Feed, copied from the market-making desk (.mkm-feed). */
#pm-workspace .pm-feed {
  flex: 1 1 0;
  min-height: 0;
  overflow-y: auto;
  padding: 6px 0;
}
#pm-workspace .pm-feed-item {
  display: flex;
  gap: 10px;
  padding: 4px 14px;
  font-size: 0.88rem;
  line-height: 1.5;
}
#pm-workspace .pm-feed-day {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  flex-shrink: 0;
  min-width: 34px;
  text-align: right;
}
#pm-workspace .pm-feed-text { color: var(--text-secondary); }
#pm-workspace .pm-feed-item--news .pm-feed-text {
  color: var(--text);
  font-weight: 600;
}
#pm-workspace .pm-feed-item--fill .pm-feed-text { color: var(--text); }
#pm-workspace .pm-feed-item--risk .pm-feed-text {
  color: var(--warning);
  font-weight: 600;
}

/* ── Scrolling pages (everything except the Trading Desk) ──────────── */

#pm-workspace .pm-page {
  display: flex;
  flex-direction: column;
  gap: 16px;
  max-width: 1100px;
}
#pm-workspace .pm-page .card-title { margin-bottom: 10px; }

/* Chart and card headings on the participant pages are sentence case, by
   owner instruction: the platform's .card-title and .chart-title are both
   uppercase, and capitals on a wall of charts read as shouting. */
#pm-workspace .pm-card-title {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 10px;
}

/* Two charts to a row wherever one does not need the full width. */
#pm-workspace .pm-grid-2 {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(380px, 1fr));
  gap: 16px;
}
#pm-workspace .pm-muted {
  font-size: 0.85rem;
  color: var(--text-muted);
  margin: 8px 0;
}

/* The limit meter, copied from the platform's market-making desk
   (css/market-making.css .mkm-meter), which is where the platform shows a
   position against its limit. */
#pm-workspace .pm-meter {
  height: 5px;
  border-radius: 3px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  overflow: hidden;
  margin: 2px 0 6px;
}
#pm-workspace .pm-meter-fill {
  height: 100%;
  background: var(--color-accent);
}
/* Three bands, in the order a desk meets them: inside the cap, inside the
   last 15% of it, past it. The fill is the only thing that carries the
   colour, so a row at rest is as quiet as every other row. */
#pm-workspace .pm-meter--warn .pm-meter-fill { background: var(--warning); }
#pm-workspace .pm-meter--breach .pm-meter-fill { background: var(--negative); }
/* The same bar in the sidebar, sitting under the row it belongs to rather
   than in a cell of its own. */
#pm-params-content .pm-sb-meter { margin: 0 0 6px; }
/* The rung of the drawdown ladder the desk is standing on. A tint across
   the row rather than a rule down its side. */
#pm-workspace .pm-row-active > td {
  background: var(--bg-elevated);
  font-weight: 600;
}
/* The bar and its reading sit on one line, centred against the rest of the
   row. Stacked, the bar floated above the figures beside it and the row read
   as two rows. */
#pm-workspace .pm-risk-meter-cell {
  min-width: 150px;
  display: flex;
  align-items: center;
  gap: 10px;
}
#pm-workspace .pm-risk-meter-cell .pm-meter {
  flex: 1 1 auto;
  margin: 0;
}
#pm-workspace .pm-risk-meter-cell .num {
  flex: 0 0 auto;
  min-width: 34px;
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: 0.8rem;
  color: var(--text-secondary);
}

/* Inside a limit is the ordinary state of every row, so it is plain text.
   A bordered badge on it read as a button, and put a box on the answer that
   never needs one. Outside keeps its badge, which is the whole point of
   having one. */
#pm-workspace .pm-risk-inside {
  color: var(--text-muted);
  font-size: 0.8rem;
}

/* Risk figures beside the drawdown ladder. Both are short, and neither
   reads better for running the full width of the page. */
#pm-workspace .pm-risk-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  align-items: start;
}
/* A formula button belongs to the label it follows, so it never wraps to a
   line of its own. Half-width cards made the label column narrow enough for
   it to drop, which read as a stray button under the row. */
#pm-workspace .pm-risk-pair .data-table--kv2 td:first-child {
  white-space: nowrap;
}
@media (max-width: 1300px) {
  #pm-workspace .pm-risk-pair { grid-template-columns: 1fr; }
}

/* ── Risk-team messages ────────────────────────────────────────────── */

/* These arrive as pop-ups over the desk. The platform toast is a
   translucent tint, which was drawn to sit over a dark page; over the
   instrument panel the message and the panel underneath read through each
   other and neither could be made out. Here they take a solid card: the
   tint is mixed into the surface rather than laid over it, so nothing
   shows through, and the border carries which kind of message it is. */
.em-toast {
  background: var(--bg-surface);
  border: 1px solid var(--border);
  color: var(--text);
  font-weight: 500;
  line-height: 1.4;
}
.em-toast-error {
  background: color-mix(in srgb, var(--negative) 12%, var(--bg-surface));
  border-color: var(--negative);
  color: var(--text);
}
.em-toast-success {
  background: color-mix(in srgb, var(--positive) 12%, var(--bg-surface));
  border-color: var(--positive);
  color: var(--text);
}
.em-toast-info {
  background: color-mix(in srgb, var(--color-accent) 12%, var(--bg-surface));
  border-color: var(--color-accent);
  color: var(--text);
}
/* Light mode needs saying again. base.css carries its own light overrides
   for these three, written as `[data-theme="light"] .em-toast-error`, and
   two selectors beat the one above however the files are ordered. So the
   translucent tint came back in light mode only, and a risk message sat over
   the instrument panel with the panel reading through the text. Same
   selector shape here, and this file loads after base.css. */
[data-theme="light"] .em-toast-error {
  background: color-mix(in srgb, var(--negative) 12%, var(--bg-surface));
  color: var(--text);
}
[data-theme="light"] .em-toast-success {
  background: color-mix(in srgb, var(--positive) 12%, var(--bg-surface));
  color: var(--text);
}
[data-theme="light"] .em-toast-info {
  background: color-mix(in srgb, var(--color-accent) 12%, var(--bg-surface));
  color: var(--text);
}

/* ── Instrument panel ──────────────────────────────────────────────── */

/* The detail for whatever the desk has selected. A fixed region rather
   than something that floats over the grid: it is bound to the row's
   identity, not to where that row happens to sit, so a print or a re-sort
   leaves it alone. Capped and scrolling, so a name with three headlines
   never pushes the positions table off the screen. */
/* The panel takes the height of what it holds rather than a share of the
   column, and never more than the ceiling below. A stated height left dead
   space under the limit bar, because how much a name has to show varies:
   a stock carries a limit bar a future does not, and headlines are one line
   or two. Shrinking to the content hands that space to the newsfeed, which
   is the card underneath and the one with no natural end. The ceiling keeps
   a name with two long headlines from eating the column, and the overflow
   is the safety valve for that case rather than the way it is read. */
#pm-workspace .pm-instrument {
  flex: 0 0 auto;
  height: auto;
  max-height: 250px;
  overflow-y: auto;
  padding: 8px 14px 10px;
}
/* Nothing picked yet: the card holds its full height, so the one line sits
   in the middle of it rather than in a strip, and the column does not
   resize the moment a first name is clicked. */
#pm-workspace .pm-instrument--empty {
  min-height: 250px;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}
/* Two columns, so the panel is read at a glance rather than scrolled. */
#pm-workspace .pm-ip-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 18px;
  margin-top: 2px;
}
#pm-workspace .pm-ip-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}
/* A ticker is a name, not a number, so it is set in the body font like
   every other piece of text. Weight carries it instead. */
#pm-workspace .pm-ip-symbol {
  font-size: 1rem;
  font-weight: 700;
  color: var(--text);
}
#pm-workspace .pm-ip-name { color: var(--text-secondary); font-size: 0.9rem; }
#pm-workspace .pm-ip-tags {
  margin-top: 1px;
  font-size: 0.78rem;
  color: var(--text-muted);
}
/* The region rides beside the sector on a multi-region universe, separated
   by weight rather than by another size or another colour. */
#pm-workspace .pm-ip-region::before { content: '\00b7'; margin: 0 5px; }
/* The price history, with both axes drawn. The room for it came from
   taking bid, ask, the day's move and the position back out of the panel:
   the market grid already prints all four on the row that was clicked. */
#pm-workspace .pm-ip-chart { margin: 6px 0 4px; }
#pm-workspace .pm-ip-chart-svg {
  width: 100%;
  height: auto;
  max-height: 112px;
  display: block;
  font-family: var(--font);
  font-size: 10px;
}
/* Axis figures take the mono face, the way every other column of numbers
   on the desk does. The date labels beside them are words and do not. */
#pm-workspace .pm-ip-chart-svg .pm-ip-tick {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}
/* A value that reads as a word rather than a figure. */
#pm-workspace .pm-ip-row .pm-ip-word {
  font-weight: 600;
  color: var(--text);
  text-align: right;
}
#pm-workspace .pm-ip-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  padding: 2px 0;
  font-size: 0.85rem;
  color: var(--text-secondary);
}
#pm-workspace .pm-ip-row .num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  color: var(--text);
}
#pm-workspace .pm-ip-row .num.positive { color: var(--positive); }
#pm-workspace .pm-ip-row .num.negative { color: var(--negative); }
#pm-workspace .pm-ip-empty,
#pm-workspace .pm-ip-subhead {
  font-size: 0.78rem;
  color: var(--text-muted);
  padding: 4px 0 2px;
}
#pm-workspace .pm-ip-limit { margin-top: 4px; }
/* Why a future has no bar where every stock has one. */
#pm-workspace .pm-ip-exempt {
  margin-top: 6px;
  font-size: 0.78rem;
  line-height: 1.35;
  color: var(--text-muted);
}
#pm-workspace .pm-ip-news { margin-top: 4px; }
#pm-workspace .pm-ip-headline {
  display: flex;
  gap: 8px;
  padding: 2px 0;
  font-size: 0.8rem;
  color: var(--text-secondary);
}
#pm-workspace .pm-ip-day { color: var(--text-muted); flex: 0 0 auto; }

/* ── Charts ────────────────────────────────────────────────────────── */

#pm-workspace .pm-chart { width: 100%; }

/* The factor detail under the P&L decomposition: a group row carries the
   weight and the factors inside it are indented under it, the way the
   chart's one bar per group reads. Rank comes from weight and indent, not
   from colour or a rule between the rows. */
#pm-workspace .pm-attr-group td { font-weight: 600; }
#pm-workspace .pm-attr-factor td:first-child {
  padding-left: 36px;
  color: var(--text-secondary);
}

/* A group of more than one factor opens on a click. The chevron is the
   platform's expandable-row triangle (console.css .is-expand-toggle
   .is-arrow, live on the ALM income statement): drawn in CSS and rotated
   on open rather than swapped for another glyph. The blank keeps the width
   on the rows that do not open, so every group label starts level. */
#pm-workspace .pm-attr-toggle { cursor: pointer; user-select: none; }
#pm-workspace .pm-attr-arrow {
  display: inline-block; width: 0; height: 0; margin: 0 9px 0 1px;
  vertical-align: middle;
  border-style: solid; border-width: 3.5px 0 3.5px 6px;
  border-color: transparent transparent transparent var(--text-secondary);
  transition: transform 0.15s ease;
}
#pm-workspace .pm-attr-arrow--blank { visibility: hidden; }
#pm-workspace .pm-attr-toggle:hover .pm-attr-arrow {
  border-left-color: var(--color-accent);
}
#pm-workspace .pm-attr-open .pm-attr-arrow {
  transform: rotate(90deg);
  border-left-color: var(--color-accent);
}
#pm-workspace .pm-attr-total td {
  font-weight: 600;
  border-top: 1px solid var(--border);
}
#pm-workspace tr:has(+ .pm-attr-total) td { border-bottom: none; }

/* The wrapper class ends in -chart-wrap on purpose: that is the selector
   the platform's fullscreen rules key off (components.css :2695), so these
   charts inherit the whole fullscreen state and its button without a rule
   of their own. */
#pm-workspace .pm-chart-wrap { position: relative; }

/* The button hangs in the card's own top-right corner, level with the card
   title. The canonical rule anchors it top-left (components.css :2719),
   which lands it on the y-axis labels; overriding the anchor when a card
   carries a title is the platform's own precedent for that, in futures.css
   .fut-mtm-fs-btn and the lifecycle map. The corner is the card's rather
   than the chart's, because the chart area starts below the title and the
   button would otherwise float in the middle of the card. */
#pm-workspace .pm-page .card,
#pm-workspace .pm-chart-card { position: relative; }
#pm-workspace .pm-page .card > .chart-fullscreen-btn,
#pm-workspace .pm-chart-card > .chart-fullscreen-btn,
#pm-workspace .pm-frame > .chart-fullscreen-btn {
  top: 12px;
  right: 12px;
  left: auto;
}
/* The enlarge button on the positions card anchors to the card, so the
   card is what it is positioned against. */
#pm-workspace .pm-frame { position: relative; }
#pm-workspace .pm-frame > .chart-fullscreen-btn {
  width: 22px;
  height: 22px;
  top: 8px;
  right: 10px;
  z-index: 2;
}
/* The head keeps clear of the button rather than running under it. */
#pm-workspace .pm-positions-frame .pm-frame-head { padding-right: 40px; }

#pm-workspace .pm-chart-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 12px;
  margin-bottom: 8px;
}
#pm-workspace .pm-chart-controls .pm-field { flex: 0 1 260px; }
#pm-workspace .pm-chart-controls .sidebar-select { width: 100%; }

/* The curve view's mode and curve pickers (rates pack): the chart
   controls' row, sized for two short selects. */
#pm-workspace .pm-curve-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 12px;
  margin-bottom: 8px;
}
#pm-workspace .pm-curve-controls .pm-field { flex: 0 1 220px; }
#pm-workspace .pm-curve-controls .sidebar-select { width: 100%; }

/* Comparing adds one name at a time, and each addition becomes a chip that
   removes itself. The chip is the market-making desk's working-order chip,
   copied from css/market-making.css (.mkm-opt-working-chip and its
   .mkm-opt-working-x button). Console token --text-meta is written out as
   0.72rem, which is its value. */
#pm-workspace .pm-compare-add {
  display: flex;
  gap: 6px;
}
#pm-workspace .pm-compare-add .sidebar-select { flex: 1 1 auto; min-width: 0; }
#pm-workspace .pm-compare-add .btn { flex: 0 0 auto; }
#pm-workspace .pm-compare-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  flex: 1 1 100%;
}
#pm-workspace .pm-compare-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 4px 2px 8px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--bg-elevated);
  font-size: 0.72rem;
}
#pm-workspace .pm-compare-x {
  border: none;
  background: transparent;
  color: var(--text-muted);
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
  padding: 0 2px;
}
#pm-workspace .pm-compare-x:hover { color: var(--negative); }
#pm-workspace .pm-compare-swatch {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  flex-shrink: 0;
}

/* The grid of small charts: as many as fit, three or four to a row on a
   desk monitor. */
#pm-workspace .pm-chart-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 10px;
}
#pm-workspace .pm-chart-card {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: 6px 4px 4px;
  background: var(--bg-elevated);
}
#pm-workspace .pm-chart-pick {
  display: flex;
  align-items: baseline;
  gap: 6px;
  width: 100%;
  padding: 2px 8px 4px;
  background: none;
  border: 0;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
#pm-workspace .pm-chart-sym {
  font-weight: 700;
  font-size: 0.8rem;
  color: var(--text);
}
#pm-workspace .pm-chart-name {
  font-size: 0.72rem;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
#pm-workspace .pm-chart-pick:hover .pm-chart-sym { color: var(--color-accent); }
/* One size down in the corner of a small chart's card. */
#pm-workspace .pm-chart-card > .chart-fullscreen-btn {
  width: 20px;
  height: 20px;
  font-size: 0.7rem;
  top: 6px;
  right: 6px;
}

/* ── Briefing ──────────────────────────────────────────────────────── */

/* The same reading layout the trainer guide uses, copied from the console
   stylesheet (css/console.css :1055-1090 and the .doc-* block at :348-366):
   a sticky table of contents beside flowing prose. The two reference
   surfaces of this sim read the same way. */

/* The Briefing is a document, so it takes the handbook's own geometry: the
   nav rail and reading measure the platform's chapter pages use, from the
   tokens base.css already carries. The params sidebar is collapsed on this
   page (app.js sets the class), because the live book has nothing to do
   with reading the mechanics, and the document centres in what that
   leaves. The collapse reuses the platform's own sidebar-collapsed rule
   rather than a second mechanism. */
#pm-workspace.pm-briefing-active .params-sidebar.visible {
  width: 0;
  min-width: 0;
  overflow: hidden;
}
/* A document opens below the header rather than against it. The shared
   `.tab-content-area` pads 16px, which is right for a page of panels and
   too tight for the first line of prose and the top of the chapter index. */
#pm-workspace.pm-briefing-active .tab-content-area { padding-top: 48px; }
#pm-workspace .ref-mech-layout {
  display: grid;
  grid-template-columns: 200px minmax(0, 62rem);
  gap: 32px;
  align-items: start;
  justify-content: center;
  margin: 0 auto;
}
/* The reading measure caps the prose itself rather than the column, so a
   table that genuinely needs more room can opt out of it. Same split
   `.learn-chapter-body` uses on the platform. */
#pm-workspace .ref-mechanics > p,
#pm-workspace .ref-mechanics > .doc-part,
#pm-workspace .ref-mechanics > .ref-section,
#pm-workspace .doc-param-body p {
  max-width: var(--measure-prose, 46rem);
}
/* Tables read at the same measure by default. A table of three short
   columns stretched across the full column put yawning space between a
   label and its number. The global `thead th { white-space: nowrap }` is
   what forces a table past a cap, so headers wrap here and the columns
   shrink to their content, exactly as the handbook does it. Tables that
   need the room opt out with `data-table-wide`. */
/* A table is a block in the prose, so it gets the same air above and below
   that the handbook gives one. Neither `.data-table-wrap` nor a bare
   `.data-table` carries a margin of its own, so the paragraph after a table
   sat flush against it. */
#pm-workspace .ref-mechanics .data-table-wrap,
#pm-workspace .ref-mechanics .pm-limit-table {
  max-width: var(--measure-prose, 46rem);
  margin: var(--space-subsection, 32px) 0;
}
#pm-workspace .ref-mechanics .data-table-wrap.data-table-wide {
  max-width: none;
  width: 100%;
}
#pm-workspace .ref-mechanics .data-table { table-layout: auto; }
#pm-workspace .ref-mechanics .data-table thead th { white-space: normal; }
/* The chapter index, on the platform handbook's own `.learn-nav` model: a
   quiet group label per chapter, and rows that carry the reading position
   through weight and colour rather than a filled block. The grey pill this
   page used read as a UI control sitting in a document. */
#pm-workspace .ref-toc {
  position: sticky;
  top: 8px;
  align-self: start;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding-right: 8px;
  padding-bottom: 40px;
  max-height: calc(100vh - 140px);
  overflow-y: auto;
  /* Section numbering by CSS counter, so adding or removing a section
     renumbers the index without touching the markup. */
  counter-reset: ref-section;
}
#pm-workspace .ref-toc-group {
  font-size: 0.96rem;
  font-weight: 700;
  color: var(--text-secondary);
  padding: 6px 10px 4px 6px;
  margin-top: 14px;
}
#pm-workspace .ref-toc-group:first-child { margin-top: 0; padding-top: 2px; }
/* A number column and a title column, so a title that wraps hangs under
   itself rather than under its own number. */
#pm-workspace .ref-toc-link {
  display: grid;
  grid-template-columns: 2.2em 1fr;
  align-items: baseline;
  gap: 6px;
  padding: 7px 10px 7px 6px;
  border-radius: 4px;
  font-size: 0.92rem;
  font-weight: 500;
  line-height: 1.45;
  color: var(--text-muted);
  text-decoration: none;
  transition: color 0.15s ease, background 0.15s ease;
  counter-increment: ref-section;
}
#pm-workspace .ref-toc-link::before {
  content: counter(ref-section) ".";
  font-family: var(--font);
  font-size: 0.82rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  text-align: right;
  padding-right: 4px;
}
#pm-workspace .ref-toc-link:hover {
  color: var(--text-secondary);
  background: color-mix(in srgb, var(--text) 4%, transparent);
}
#pm-workspace .ref-toc-link:hover::before { color: var(--text-secondary); }
#pm-workspace .ref-toc-link.active {
  color: var(--text);
  font-weight: 600;
  background: transparent;
}
/* The number is the only ink marker on the active row. */
#pm-workspace .ref-toc-link.active::before {
  color: var(--color-accent);
  font-weight: 600;
}
#pm-workspace .ref-mech-layout .ref-mechanics .ref-section,
#pm-workspace .ref-mech-layout .ref-mechanics .doc-part { scroll-margin-top: 16px; }
@media (max-width: 900px) {
  #pm-workspace .ref-mech-layout { grid-template-columns: 1fr; }
  #pm-workspace .ref-toc { display: none; }
}
/* Reading type, not UI type: the same body size and leading the platform's
   handbook chapters use, from the tokens base.css already carries. */
#pm-workspace .ref-mechanics {
  font-size: var(--text-body);
  line-height: var(--line-body);
  color: var(--text);
}
#pm-workspace .ref-mechanics p { margin: 0 0 var(--space-block, 16px); }
#pm-workspace .ref-mechanics .ref-lead {
  margin: 0 0 24px;
  font-size: 16px;
  color: var(--text);
}
/* On the platform's typography scale, so a section heading sits above the
   18px body rather than under it. Chapter at h2, section at h3. */
#pm-workspace .ref-mechanics .ref-section {
  margin: 36px 0 10px;
  font-size: var(--text-h3);
  line-height: var(--line-tight);
  font-weight: 600;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: 6px;
}
#pm-workspace .doc-part {
  font-size: var(--text-h2);
  line-height: var(--line-tight);
  font-weight: 700;
  margin: 48px 0 4px;
  color: var(--text);
}
#pm-workspace .doc-part:first-child { margin-top: 0; }

/* The Reference button beside a formula button. It shares .formula-btn's
   chassis, and that class draws its glyph with ::before (the button's own
   text is hidden by font-size: 0), so without this override the "i"
   rendered as a second f. An upright i has no hook descender, so the
   f's optical-centring nudge comes off too. */
.briefing-btn::before {
    content: 'i';
    transform: none;
}

#pm-workspace .doc-param { padding: 14px 0; border-top: 1px solid var(--border); }
#pm-workspace .doc-param-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
/* No sizes of their own. A named block is prose on a page of prose, and
   three bespoke sizes here (14.5 for the name, 13.5 for the body, 12 for a
   gloss that no longer exists) made the order types read smaller than
   everything around them and the name read smaller than its own paragraph.
   Weight and colour separate the name from the body; the page decides how
   big any of it is. */
#pm-workspace .doc-param-name { font-weight: 600; color: var(--text); }
#pm-workspace .doc-param-body { margin-top: 6px; }
#pm-workspace .doc-param-body p {
  margin: 0 0 8px;
  color: var(--text-secondary);
}
#pm-workspace .doc-param-body p:last-child { margin-bottom: 0; }

/* ── Desktop-only notice ───────────────────────────────────────────── */

#pm-workspace .pm-desktop-notice { display: none; }
@media (max-width: 1199px) {
  #pm-workspace .pm-desk { display: none; }
  #pm-workspace .pm-desktop-notice {
    display: block;
    padding: 24px 4px;
    max-width: 520px;
  }
  #pm-workspace.pm-desk-active .workspace-content { overflow-y: auto; }
}

/* The read-only bar a trainer gets when they open a desk from Room Control.
   What sits below it is the desk's own screen, ticket and all, so without
   the bar it reads as a book that can be traded. Copied from the Bank ALM
   sim's observer banner, which is where the platform already answers this. */
#pm-observer-banner {
  flex: 0 0 auto;
  background: #f8d7da;
  color: #1a1a1a;
  font-size: 13px;
  font-weight: 600;
  text-align: center;
  padding: 7px 16px;
}
/* Lock what would otherwise be sent to the room, so the bar's statement
   holds rather than relying on the server dropping the message. Everything
   that only reads stays live: pages, sorting, selection, the enlarge
   buttons and the theme toggle. */
body.pm-observer #pm-t-buy,
body.pm-observer #pm-t-sell,
body.pm-observer .pm-orders-frame [data-cancel],
body.pm-observer #pm-rename { display: none; }
body.pm-observer .pm-ticket input,
body.pm-observer .pm-ticket select { pointer-events: none; opacity: 0.55; }

/* The Briefing's limits table. What each limit caps is a sentence, and a
   sentence in a fifth column pushed the table past the width of the page, so
   the figures could only be reached by scrolling sideways. The sentence sits
   under the name it belongs to instead. The name column takes what is left
   after three narrow ones, so the prose wraps and the figures stay put. */
#pm-workspace .pm-limit-table { width: 100%; table-layout: auto; }
#pm-workspace .pm-limit-table td:first-child { width: 100%; }
#pm-workspace .pm-limit-table th:not(:first-child),
#pm-workspace .pm-limit-table td:not(:first-child) { white-space: nowrap; }
/* No size of its own either: it takes the table's, which is the size every
   other cell on the page is already set in. */
#pm-workspace .pm-limit-note {
  display: block;
  margin-top: 3px;
  max-width: 60ch;
  color: var(--text-muted);
  white-space: normal;
}

/* The buttons under a revealed edge that put it on the chart. */
#pm-workspace .pm-evidence {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 8px;
}

/* ── Dialog shell ──────────────────────────────────────────────────────
   openDialog() in shared/dialog.js builds .iro-dialog-overlay markup, and
   this bundle carried only the two modifier rules for it, never the base
   ones. The dialog therefore rendered as unstyled text at the foot of the
   page. These rules are copied from the platform's platform.css, which the
   participant page does not load, so the sim now styles what it renders.
   The close button colour is the only change: the platform's is fixed white
   for a dark shell, and this sim runs light and dark. */
.iro-dialog-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--z-max);
  background: rgba(0,0,0,0.7);
  display: flex;
  align-items: center;
  justify-content: center;
  backdrop-filter: blur(var(--blur-sm));
}
.iro-dialog {
  background: var(--bg-surface, #151518);
  border: 1px solid var(--border, #333);
  border-radius: 12px;
  /* The platform sizes this for charts and tables at 960px. The only
     dialog here holds a paragraph, so it takes the width the trainer and
     admin shells give the same disclaimer, which is 460px in console.css.
     A dialog that needs the room can still pass wide. */
  width: min(460px, 92vw);
  max-height: 85vh;
  display: flex;
  flex-direction: column;
  box-shadow: var(--shadow-lg);
}
.iro-dialog.iro-dialog-wide { width: min(1100px, 94vw); }
.iro-dialog-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border, #333);
  flex-shrink: 0;
}
.iro-dialog-header h3 { margin: 0; font-size: 1.1rem; font-weight: 700; }
.iro-dialog-close {
  background: none;
  border: none;
  color: var(--text-muted);
  font-size: 1.4rem;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: 6px;
  transition: color 0.2s, background 0.2s;
}
.iro-dialog-close:hover { color: var(--text); background: var(--bg-elevated); }
.iro-dialog-body { flex: 1; overflow-y: auto; padding: 20px 24px; }

/* ── Session overview dialog ───────────────────────────────────────────
   Three sections in the shared dialog's wide shell, one at a time behind
   the platform's own subtab nav (.subtab-nav / .subtab-btn, components
   .css): the mandate summary, the market backdrop, the economic
   calendar. All three at once was too much to read at once, and the
   parts are wanted at different moments, so each gets the full width.

   The dialog is wide for the calendar's sake: four columns of dates read
   badly squeezed. The prose sections keep a readable line length of
   their own rather than running the full width. */
.iro-dialog-body p { margin: 0 0 12px; }
.iro-dialog-body p:last-child { margin-bottom: 0; }
.iro-dialog-body .quiet { color: var(--text-muted); }

/* ONE measure for everything in this dialog. The prose was capped at a
   readable line length while the tables ran the full width of a 1100px
   shell, so a paragraph stopped halfway across a table that carried on,
   and the dialog read as though it had been stretched. The width of the
   shell is now derived from the same measure the prose and the tables
   share, so the three can never disagree: change the one value below and
   all of them move together. */
.iro-dialog:has(.pm-backdrop-body) {
  --pm-bd-measure: 74ch;
  /* The dialog body's own left and right padding, from .iro-dialog-body. */
  width: min(calc(var(--pm-bd-measure) + 48px), 94vw);
}
/* The three sections differ in height, and the overlay centres the dialog
   vertically, so switching section re-centred it and the whole dialog
   jumped. This dialog is therefore anchored to a fixed top offset: 7.5vh
   is where the centred position puts the top at the 85vh max height, so a
   full-height section sits exactly where it did and a shorter one moves
   only its bottom edge. */
.iro-dialog-overlay:has(.pm-backdrop-body) { align-items: flex-start; }
.iro-dialog-overlay:has(.pm-backdrop-body) .iro-dialog { margin-top: 7.5vh; }
/* The section buttons and the reading-done line stay put and the panel
   between them is what scrolls, so the three sections are reachable from
   anywhere in a long one. `.iro-dialog-body` scrolls itself, so that is
   turned off here and handed to the panel. */
.pm-backdrop-body {
  display: flex;
  flex-direction: column;
  overflow-y: hidden;
}
.pm-backdrop-body .subtab-nav { flex: none; margin-bottom: 16px; }
.pm-backdrop-body #pm-bd-panel {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}
.pm-backdrop-body .pm-bd-section { flex: none; }
.pm-backdrop-body .pm-bd-section > p,
.pm-backdrop-body .pm-bd-section > .data-table-wrap {
  max-width: var(--pm-bd-measure);
}
/* The dialog's paragraphs space themselves with a 12px bottom margin
   (.iro-dialog-body p above); the table wrap carries none of its own, so
   the paragraph after the limits table sat flush against it. */
.pm-backdrop-body .pm-bd-section > .data-table-wrap { margin-bottom: 12px; }
.pm-bd-calendar { margin-top: 12px; }
/* The calendar runs to dozens of rows, so the table itself scrolls and
   keeps its column headings in view. `.data-table-wrap` is already a
   scrollport through its own `overflow-x: auto`, which is what the
   headings stick to, so the section hands it the height left over below
   the paragraph above it. */
.pm-backdrop-body .pm-bd-section--calendar {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.pm-backdrop-body .pm-bd-calendar {
  flex: 1;
  min-height: 0;
  overflow: auto;
}
.pm-backdrop-body .pm-bd-calendar thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  /* The dialog's own surface, so the rows read as passing under the
     headings rather than behind a fill band of their own. */
  background: var(--bg-surface);
  /* A collapsed border does not travel with a sticky cell, so the rule
     under the headings is drawn as an inset shadow instead. */
  border-bottom: 0;
  box-shadow: inset 0 -2px 0 var(--border);
}
/* A day already past. The calendar can be reopened at any point in the
   session, so what has happened stays on it, muted. */
.pm-backdrop-body .pm-bd-past td { color: var(--text-muted); }


/* ── A card holding a table or a feed, enlarged ─────────────────────────

   A card is laid over the column it already lives in, not over the page.
   On the desk that means the right-hand column: the four cards there are
   the ones with no natural end, and enlarging one gives it the room the
   other three were using while the market grid and the ticket beside it
   stay put and keep working. Covering the whole desk to read the book would
   take the prices away at the same time.

   What it fills is decided by the nearest positioned ancestor, so the
   column below is what makes it the column rather than the page. Portfolio
   Analytics has no such column, so its cards fall back to `#pm-workspace`
   and fill the page, which is right for a card that is already the width of
   the page.

   THIS BLOCK MUST STAY LAST IN THE FILE. Enlarging a card overrides the
   normal sizing of whichever card it is, and those rules are written per
   card further up with the same specificity as these. Three of them had
   overtaken this state by sitting later in the file. The fatal one was
   `#pm-workspace .pm-frame { position: relative }`, added to anchor the
   enlarge button to its card corner: same weight as the rule below, later
   in the file, so `position: absolute` lost, `inset: 0` did nothing, and
   the card stayed exactly where it was. The button appeared dead. Do not
   fix a future collision by adding an id or an !important here; keep this
   block at the bottom, where the state that overrides everything belongs. */
/* An enlarged card fills the column it lives in, never the page. Each
   column states `position: relative` so `inset: 0` below is measured
   against the column. A column that omits it sends the card to
   #pm-workspace instead, which is the sidebar and both columns, and the
   card reads as a full-screen takeover. That is what the market grid did
   until 2026-08-11. */
#pm-workspace .pm-col-right,
#pm-workspace .pm-col-market { position: relative; }
#pm-workspace .pm-card-max {
  position: absolute;
  inset: 0;
  z-index: 6;
  margin: 0;
  display: flex;
  flex-direction: column;
  box-shadow: var(--shadow-md);
}
/* Whatever height the card normally states gives way, and its body fills
   what it has been given. */
#pm-workspace .pm-card-max .pm-instrument,
#pm-workspace .pm-card-max .pm-scroll,
#pm-workspace .pm-card-max .pm-feed,
#pm-workspace .pm-card-max .pm-market-wrap,
#pm-workspace .pm-card-max > [id] {
  flex: 1 1 0;
  height: auto;
  min-height: 0;
  max-height: none;
  overflow-y: auto;
}

/* ── Pause overlay ─────────────────────────────────────────────────────────
   The full-screen hold shown to every desk while the trainer has the session
   paused. Ported from the ALM sim (css/alm.css) so the two sims give the same
   participant a single pause experience. */
.pause-overlay {
  position: fixed;
  inset: 0;
  z-index: 9999;
  background: rgba(11, 11, 12, 0.85);
  backdrop-filter: blur(6px);
  display: flex;
  align-items: center;
  justify-content: center;
  animation: pauseFadeIn 0.3s ease;
}
[data-theme="light"] .pause-overlay {
  background: rgba(245, 245, 247, 0.85);
}
@keyframes pauseFadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}
.pause-overlay-content {
  text-align: center;
  max-width: 520px;
  padding: 48px 40px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}
.pause-overlay-icon {
  font-size: 48px;
  margin-bottom: 16px;
  opacity: 0.7;
}
.pause-overlay-title {
  font-size: 24px;
  font-weight: 700;
  color: var(--warning);
  margin-bottom: 16px;
}
.pause-overlay-hint {
  font-size: 13px;
  color: var(--text-muted);
  animation: pausePulse 2s ease-in-out infinite;
}
@keyframes pausePulse {
  0%, 100% { opacity: 0.5; }
  50% { opacity: 1; }
}
/* ── Formula panel ─────────────────────────────────────────────────── */

/* Wider than the platform default of 560px. These mandates state identities
   with six and seven named terms, and at 518px of content the P&L
   decomposition and the financing cases both ran off the edge. The override
   lives here rather than in the copied platform stylesheet, whose paired
   dialog rules do their own arithmetic on the 560px number.

   The off-screen position is written on the hidden state, never on the
   panel itself: this file loads after components.css, so a bare
   `.formula-panel { right: -760px }` outranks `.formula-panel--visible`
   on source order alone and the panel opens where it cannot be seen. */
.formula-panel { width: 680px; }
.formula-panel:not(.formula-panel--visible) { right: -760px; }

/* A variable whose value is a list rather than a number: the borrow tiers,
   the margin haircuts, the largest beta contributions. One reading per
   line, because a comma-separated run in a narrow column reads as one
   long string. */
.formula-var-table .formula-var-item { display: block; }
