/* THE CONFORMANT MEMBER SURFACE — MEMBER-SURFACE-FIDELITY-1.
 *
 * WHAT THIS FILE IS
 * -----------------
 * The first member-visible surface built against its governing boards:
 *
 *   design-source/project/MemberHome.dc.html   (1440)
 *   design-source/project/MobileHome.dc.html   (390)
 *
 * It is scoped to `body.member-surface`, which app.js sets only once the
 * server has answered that this principal is a member. The legacy operator
 * surface in styles.css is not touched by a single declaration here, because
 * the operator screens are governed by thirty Owner boards that no one has
 * reconciled yet and restyling them from a member board would be a guess.
 *
 * THERE IS NOT ONE LITERAL COLOUR, TYPE FAMILY OR RADIUS IN THIS FILE.
 * Every one is a `var(--board-*)` from product/src/board-tokens.css, which is
 * GENERATED by product/build_board_tokens.py out of the two boards above. A
 * hex typed here would be a frozen literal that agrees with the board on the
 * day it is typed and diverges silently afterwards, and that is the whole
 * defect class this lane exists to stop reproducing.
 * `tests/test_member_surface_fidelity.py` asserts that absence mechanically.
 *
 * WHY THE SHELL IS IN SCOPE AND NOT ONLY THE CONTENT
 * -------------------------------------------------
 * The board governs the whole screen. Restyling only the main column would
 * leave a member looking at a 232px rail on #0b0d10 with a cyan selected
 * state, and the surface would still diverge on rail width, ground, line and
 * accent — four of the named divergence classes — while a content-only diff
 * claimed to have fixed them. So the rail, the topbar and the phone tab bar
 * are governed here too.
 *
 * TYPE IS SELF-HOSTED, DELIBERATELY
 * ---------------------------------
 * The boards name Sora and IBM Plex Sans and fetch them from a font CDN.
 * styles.css refuses a font host on the stated ground that a product which
 * cannot render without reaching one has a network dependency in its first
 * paint, and that rule is right. Both hold at once: the families are the
 * board's, and the bytes are vendored under product/src/fonts/, so first paint
 * reaches nothing. @font-face is declared below rather than imported.
 */

@font-face {
  font-family: 'IBM Plex Sans';
  font-style: normal;
  font-weight: 400 600;
  font-display: block;
  src: url("./fonts/IBMPlexSans-var.woff2") format("woff2");
}
@font-face {
  font-family: 'Sora';
  font-style: normal;
  font-weight: 500 700;
  font-display: block;
  src: url("./fonts/Sora-var.woff2") format("woff2");
}

/* ======================================================================== */
/* THE GROUND, THE INK AND THE TYPE                                         */
/* ======================================================================== */

body.member-surface {
  background: var(--board-ground);
  color: var(--board-ink);
  font-family: var(--board-body);
  font-size: var(--board-d-text);
  line-height: var(--board-d-leading);
}

body.member-surface a { color: var(--board-link); }
body.member-surface a:hover { color: var(--board-link-hover); }

/* The display family carries every heading and every numeral the board sets
 * in Sora. Applied by element rather than by class so a view that adds a
 * heading cannot forget it. */
body.member-surface h1,
body.member-surface h2,
body.member-surface h3,
body.member-surface .ms-display { font-family: var(--board-display); }

/* ======================================================================== */
/* THE SHELL: A 212px RAIL WITH A RIGHT BORDER                              */
/* ======================================================================== */

/* THE BOARD'S SHELL IS TWO ROWS, NOT TWO COLUMNS.
 *
 * MemberHome.dc.html puts a full-width topbar ACROSS THE TOP -- wordmark and
 * `app.omnisx.ai` on the left, the Ask control and the avatar on the right --
 * and the 212px rail BELOW it. The legacy shell is two columns with a
 * full-height rail and a topbar that only spans the main column, which is a
 * different composition and reads as a different product.
 *
 * Corrected without touching the shared markup: `.main` becomes
 * `display: contents` so its two children place themselves directly on the
 * shell grid, the topbar spanning both columns on row 1 and the content
 * sitting beside the rail on row 2. The operator shell is unaffected because
 * every selector here is scoped to the member class. */
body.member-surface .shell {
  grid-template-columns: var(--board-d-rail) 1fr;
  grid-template-rows: var(--board-d-topbar) 1fr;
  background: var(--board-ground);
}
body.member-surface .main { display: contents; }
body.member-surface .topbar { grid-row: 1; grid-column: 1 / -1; }
body.member-surface .sidebar { grid-row: 2; grid-column: 1; }
body.member-surface .content { grid-row: 2; grid-column: 2; overflow-y: auto; }
body.member-surface .bottom-nav { grid-row: 3; grid-column: 1 / -1; }

body.member-surface .sidebar {
  background: var(--board-ground);
  border-right: 1px solid var(--board-line);
  padding: 0;
  /* The rail is its own scroll region. Without `min-height: 0` a grid item
   * refuses to shrink below its content, so a rail longer than the viewport
   * pushed its own footer off the bottom of the screen and the member lost
   * the control that ends their session. */
  min-height: 0;
  overflow-y: auto;
  /* MEASURED. styles.css gives the rail `height: 100vh` and `position:
   * sticky`, both correct for a full-height rail in a two-COLUMN shell. In the
   * board's two-ROW shell the rail starts BELOW the topbar, so a 100vh rail
   * made the grid 100vh + 60px tall and pushed its own footer off the bottom
   * of the window -- the member lost the control that ends their session, and
   * the screenshot showed a rail that merely looked slightly long.
   * The grid row already sizes the rail; `auto` lets it. */
  height: auto;
  position: static;
}
/* The rail's own brand block goes with it: the wordmark now lives in the
 * topbar, where the board puts it. */
body.member-surface .sidebar .brand { display: none; }

/* The board's rail is a flat list of destinations: no group labels, no
 * tallies, and no second wordmark — that lives in the topbar now, and the
 * rail's own brand block is hidden above. A member's screen set is decided by
 * the server, so a count beside a link would be an operator affordance on a
 * member rail. */
body.member-surface .nav { padding: 16px 12px; display: flex;
  flex-direction: column; gap: 2px; }
body.member-surface .nav-group-label { display: none; }
body.member-surface .nav .tally { display: none; }

body.member-surface .nav a {
  display: flex;
  align-items: center;
  height: 36px;
  padding: 0 12px;
  border-radius: var(--board-radius-control);
  font-size: 13.5px;
  color: var(--board-ink-dim);
  text-decoration: none;
  box-shadow: none;
}
body.member-surface .nav a:hover { background: var(--board-raised);
  color: var(--board-ink); }
/* The board marks the current destination with a raised GROUND and weight,
 * not with an accent bar. styles.css uses `inset 2px 0 0 var(--accent)`,
 * which is a different mechanism as well as a different colour, so it is
 * cancelled above rather than recoloured. */
body.member-surface .nav a.active {
  background: var(--board-raised);
  color: var(--board-ink);
  font-weight: 600;
}

body.member-surface .sidebar-foot {
  border-top: 1px solid var(--board-line);
  padding: 12px;
  font-size: 12px;
  color: var(--board-ink-faint);
}

/* ======================================================================== */
/* THE TOPBAR                                                               */
/* ======================================================================== */

body.member-surface .topbar {
  height: var(--board-d-topbar);
  padding: 0 26px;
  border-bottom: 1px solid var(--board-line);
  background: var(--board-ground);
}
/* The board's topbar names the PRODUCT, not the page. The page is named by
 * the screen's own h1, which is where the board puts it ("Your workforce"),
 * so a second title in the topbar would say it twice. */
body.member-surface[data-view="memberhome"] #topbar-title { display: none; }
/* AT <=860px THE RAIL IS A DRAWER, FOR MEMBERS TOO. styles.css makes the rail
 * an off-canvas drawer in a one-column shell at <=860px. The member block
 * above re-imposed the desktop board's two-column grid and a static rail, so
 * between 561 and 860px the closed drawer still held its 212px column and the
 * content was squeezed beside an empty strip (independent review STRIKEs at
 * 296a443 and aa253cf). Through 860px the member shell is one column, the
 * rail is the fixed drawer styles.css slides, and the row holding the menu
 * button is shown -- with no gap at fractional widths. The page title inside
 * that row stays hidden above the phone width, as the desktop board wants;
 * the phone block brings it back at its own width. */
@media (max-width: 860px) {
  /* [data-view] matches the Home hide's specificity, so this wins by order on
   * every screen, Home included: the menu row is never hidden at <=860px. */
  body.member-surface[data-view] #topbar-title { display: flex; }
}
/* Above the phone width only: the phone block below has its own composition
 * (bottom tab bar, two-row shell), measured at 390 and left exactly as it was.
 * The bound is the phone block's exact complement, so no width -- fractional
 * or not -- falls between the two. */
@media (max-width: 860px) and (not (max-width: 560px)) {
  body.member-surface .shell { grid-template-columns: 1fr; }
  body.member-surface .topbar,
  body.member-surface .content { grid-column: 1; }
  body.member-surface .sidebar {
    position: fixed; top: 0; left: 0; bottom: 0; height: 100vh;
    width: var(--board-d-rail); z-index: 40;
  }
  body.member-surface[data-view="memberhome"] #topbar-title #page-title,
  body.member-surface[data-view="memberhome"] #topbar-title #page-sub { display: none; }
  /* Every other member screen keeps its NAME on one line and drops the
   * subtitle, as the phone does: name and subtitle on two lines overflowed
   * the 60px topbar and pushed the menu button above the window (measured:
   * y = -41 on Labor market at 768). */
  body.member-surface[data-view] #topbar-title #page-sub { display: none; }
  body.member-surface[data-view] #topbar-title #page-title {
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* The row may be squeezed between the brand block and the actions (144px
   * on Labor market at 768): it never WRAPS -- a wrapped name pushed the menu
   * button above the window -- it shrinks, and the name truncates. */
  body.member-surface[data-view] #topbar-title { flex-wrap: nowrap; min-width: 0;
                                                 flex: 0 1 auto; }
  body.member-surface[data-view] #topbar-title > div { min-width: 0; }
}
/* The same block for engines without Media Queries Level 4 boolean syntax
 * (Safari <= 16.3, older WebViews), which drop the block above entirely and
 * would bring back the 212px rail column at tablet widths. Identical rules,
 * so on modern engines the two simply agree. */
@media (min-width: 560.02px) and (max-width: 860px) {
  body.member-surface .shell { grid-template-columns: 1fr; }
  body.member-surface .topbar,
  body.member-surface .content { grid-column: 1; }
  body.member-surface .sidebar {
    position: fixed; top: 0; left: 0; bottom: 0; height: 100vh;
    width: var(--board-d-rail); z-index: 40;
  }
  body.member-surface[data-view="memberhome"] #topbar-title #page-title,
  body.member-surface[data-view="memberhome"] #topbar-title #page-sub { display: none; }
  /* Every other member screen keeps its NAME on one line and drops the
   * subtitle, as the phone does: name and subtitle on two lines overflowed
   * the 60px topbar and pushed the menu button above the window (measured:
   * y = -41 on Labor market at 768). */
  body.member-surface[data-view] #topbar-title #page-sub { display: none; }
  body.member-surface[data-view] #topbar-title #page-title {
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  /* The row may be squeezed between the brand block and the actions (144px
   * on Labor market at 768): it never WRAPS -- a wrapped name pushed the menu
   * button above the window -- it shrinks, and the name truncates. */
  body.member-surface[data-view] #topbar-title { flex-wrap: nowrap; min-width: 0;
                                                 flex: 0 1 auto; }
  body.member-surface[data-view] #topbar-title > div { min-width: 0; }
}

/* --- the brand block ---------------------------------------------------- */
.ms-brand { display: none; }
body.member-surface .ms-brand {
  display: flex;
  align-items: center;
  gap: 12px;
  color: var(--board-ink);
}
/* ICON TREATMENT. The mark is a stroked, round-capped glyph in the primary
 * ink with ONE accent-filled dot at its centre: the accent marks the point of
 * convergence and nothing else in the mark. Stroke takes `currentColor` so the
 * mark inherits the ink rather than carrying a second copy of it. */
body.member-surface .ms-brand .ms-mark-dot { fill: var(--board-accent); }
body.member-surface .ms-wordmark {
  font-family: var(--board-display);
  font-weight: 700;
  font-size: 17px;
  letter-spacing: 0.02em;
}
/* The deployment host, divided from the wordmark by a left border on the
 * control line -- a divider, not a separator glyph, which is how the board
 * draws it. */
body.member-surface .ms-host {
  font-size: 13px;
  color: var(--board-ink-faint);
  padding-left: 12px;
  border-left: 1px solid var(--board-control-line);
}

/* --- the identity avatar ------------------------------------------------ */
.ms-avatar { display: none; }
body.member-surface .ms-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--board-line);
  font-family: var(--board-display);
  font-weight: 600;
  font-size: 12px;
  color: var(--board-ink);
}

body.member-surface .content {
  padding: var(--board-d-main-pad);
  display: flex;
  flex-direction: column;
  gap: 20px;
  max-width: none;
}

/* ======================================================================== */
/* CONTROLS                                                                 */
/* ======================================================================== */

body.member-surface button,
body.member-surface .ms-button {
  font-family: inherit;
  font-size: 13px;
  height: 36px;
  padding: 0 13px;
  border-radius: var(--board-radius-control);
  border: 1px solid var(--board-control-line);
  background: transparent;
  color: var(--board-ink);
  display: inline-flex;
  align-items: center;
  cursor: pointer;
  /* MEASURED ON THE FIRST RENDER OF THIS SURFACE. A `.ms-button` is an <a>,
   * so `body.member-surface a` gave every secondary control the LINK ink and
   * a text-decoration underline: the panels read as four lists of hyperlinks
   * where the board draws bordered buttons in the primary ink. Both are
   * overridden here rather than by narrowing the `a` rule, because the anchor
   * ink is correct for an actual link and wrong only for a control. */
  text-decoration: none;
  /* And the labels wrapped: "Get it done" broke across two lines inside a
   * 36px-high control. The board's buttons are single-line by construction
   * (fixed height, horizontal padding only), so the surface says so. */
  white-space: nowrap;
}
body.member-surface button:hover:not(:disabled),
body.member-surface .ms-button:hover { background: var(--board-raised);
  color: var(--board-ink); text-decoration: none; }

body.member-surface button.primary,
body.member-surface .ms-button.primary,
body.member-surface .ms-button.primary:hover {
  background: var(--board-accent);
  border-color: var(--board-accent);
  color: var(--board-ground);
  font-weight: 600;
  text-decoration: none;
}

body.member-surface input[type="text"],
body.member-surface input[type="password"],
body.member-surface textarea,
body.member-surface select {
  background: var(--board-sunken);
  border: 1px solid var(--board-control-line);
  border-radius: var(--board-radius-input);
  color: var(--board-ink);
  font-family: inherit;
}
body.member-surface input:focus,
body.member-surface textarea:focus,
body.member-surface select:focus {
  outline: none;
  border-color: var(--board-accent);
  box-shadow: none;
}

/* ======================================================================== */
/* THE MEMBER HOME COMPOSITION                                              */
/*                                                                          */
/* The board's hierarchy, which is not the legacy screen's. The legacy       */
/* memberhome was three equal cards of prose and a button — "What needs      */
/* doing?", "See who can do it", "Follow your work" — a MENU. The board is   */
/* an ANSWER: four panels of state the member already has, in a 2x2, each    */
/* ending in the links that go deeper, and only then an input for something  */
/* new. Grounds and accents could be corrected without this and the surface  */
/* would still not be the board, so composition is in scope.                */
/* ======================================================================== */

body.member-surface .ms-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 16px;
}
body.member-surface .ms-head h1 {
  margin: 0;
  font-weight: 600;
  font-size: var(--board-d-h1);
  letter-spacing: -0.01em;
}
body.member-surface .ms-head .ms-sub { color: var(--board-ink-faint); margin-top: 4px; }

/* The depth legend. Three pills; the one in force carries the accent as a
 * BORDER on a raised ground, never as a fill — the board reserves an accent
 * fill for the one primary action on the screen. */
body.member-surface .ms-depth { display: flex; gap: 6px; align-items: center;
  font-size: 11.5px; }
body.member-surface .ms-depth span {
  padding: 4px 10px;
  border-radius: var(--board-radius-pill);
  letter-spacing: 0.05em;
  border: 1px solid var(--board-control-line);
  color: var(--board-ink-faint);
}
body.member-surface .ms-depth span.on {
  background: var(--board-raised);
  border-color: var(--board-accent);
  color: var(--board-ink);
  font-weight: 600;
}
body.member-surface .ms-depth .ms-depth-note { border: 0; padding: 0;
  color: var(--board-ink-faint); margin-left: 6px; }

body.member-surface .ms-quad {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

/* ======================================================================== */
/* ACTIVE WORK — ActiveWork.dc.html (1440) and MobileWork.dc.html (390)      */
/*                                                                          */
/* The panel GRID is the board's own: two to a row at 1440, stacked at 390.  */
/* Its 18px gutter appears in no other board, so the generator has no token  */
/* for it and it is named here beside the board that draws it — the same     */
/* convention `.ms-teams`' column ratios are held under. Every colour below  */
/* is a token; this file may declare no literal colour.                      */
/* ======================================================================== */
body.member-surface .ms-work {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
}

/* ======================================================================== */
/* SETTINGS -- MemberSettings.dc.html (1440)                                 */
/*                                                                          */
/* The board lays its four sections out as TWO grids of two, each            */
/* `grid-template-columns:1fr 1fr;gap:18px`, and the view emits two          */
/* containers for exactly that reason: one grid of four would reflow the     */
/* pairs as the panels' own heights changed, and the board pairs them        */
/* deliberately (identity with services, privacy with account).             */
/*                                                                          */
/* The 18px gutter is the same figure `.ms-work` carries, and this is a       */
/* SEPARATE declaration rather than a shared class on purpose: the generator  */
/* has no token for either, so each is named beside the board that draws it,  */
/* which is the convention `.ms-teams` and `.ms-work` are already held        */
/* under. Collapsing them would make a later change to one board silently     */
/* move another board's surface. Every colour here is a token; this file may  */
/* declare no literal colour.                                                */
/*                                                                          */
/* NO PHONE RULE, BECAUSE THERE IS NO PHONE BOARD. The canvas draws no 390    */
/* counterpart for Settings, which derive_denominator.py records as an OWED   */
/* CANVAS ARTEFACT. The shared `.ms-panel` system and the single-column       */
/* fallback below the 560 breakpoint still apply, so a member reaching this   */
/* screen on a phone is not handed a broken two-column grid -- but the        */
/* surface is COUNTED at 1440 only, because 1440 is the only width a board    */
/* governs it at.                                                            */
/* ======================================================================== */
body.member-surface .ms-settings {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
}

/* COMMISSION WORK -- Commission.dc.html (1440) and MobileCommission.dc.html
 * (390).
 *
 * The desktop board draws `display:grid;grid-template-columns:1.2fr 1fr;
 * gap:18px` and lays its four sections out in DOM order, so the asymmetric
 * first column is the board's and not a choice: "Clarified" and "Cost" carry
 * the longer fact values and sit in the wider column by flowing there. */
body.member-surface .ms-commission {
  display: grid;
  grid-template-columns: 1.2fr 1fr;
  gap: 18px;
  min-height: 0;
}

/* The commissioning strip spans the whole width, as the board draws it: it is
 * a sibling ABOVE the grid there, and a full-width first row here, which is
 * the same composition without a second container. */
body.member-surface .ms-commission > .ms-stages {
  grid-column: 1 / -1;
}

/* A SECTION OR ROW ONE BOARD DRAWS AND THE OTHER DOES NOT.
 * MobileCommission.dc.html carries TWO sections where the desktop board
 * carries four -- no "Clarified" and no "Approve" -- and drops the
 * "Capabilities" and "Your plan" rows rather than wrapping them. This is the
 * complement of `.ms-short-only`, which already exists for the opposite case,
 * and it needs no rule above 560: the element keeps whatever display
 * `.ms-panel` or `.ms-row` gives it. */

/* The phone board's two trailing actions, drawn AFTER its two sections and
 * outside both. Deliberately NOT `.ms-actions`: that class is display:none at
 * <=560 because no phone board draws a control INSIDE a panel, and these two
 * are the controls MobileCommission.dc.html draws outside one. */
body.member-surface .ms-commission-actions {
  gap: 8px;
  flex-wrap: wrap;
}

/* THE STAGE STRIP. The board draws an ordered run of cells that share their
 * borders: each cell is pulled one pixel left over its neighbour so the seam
 * is a single line rather than two, and only the two ends are rounded. That
 * is why the negative margin is here and not a gap — a gap would draw six
 * separate chips, which is a different element.
 *
 * `overflow: hidden` is the board's, and it is load-bearing at 390: six cells
 * in 358px of panel cannot each keep their text, so the strip clips rather
 * than forcing the panel wider than the viewport. */
body.member-surface .ms-stages {
  display: flex;
  align-items: stretch;
  gap: 0;
  overflow: hidden;
}

body.member-surface .ms-stage {
  flex: 1;
  min-width: 0;
  padding: 9px 8px;
  border: 1px solid var(--board-control-line);
  background: var(--board-raised);
  color: var(--board-ink);
  /* The board's own cell type: small, letter-spaced, upper-case. 10.5px is the
   * same one-off size `.ms-state` carries and is not a token for the same
   * reason — it appears in one role. */
  font-size: 10.5px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin-left: -1px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
body.member-surface .ms-stage:first-child {
  margin-left: 0;
  border-radius: var(--board-radius-control) 0 0 var(--board-radius-control);
}
body.member-surface .ms-stage:last-child {
  border-radius: 0 var(--board-radius-control) var(--board-radius-control) 0;
}

/* THREE CONDITIONS, AND THE BOARD DRAWS EACH ONE DIFFERENTLY. A stage already
 * travelled keeps the control line and full ink; the stage the request is AT
 * takes the accent border; a stage still ahead drops to the sunken ground and
 * faint ink, so the strip reads as progress rather than as six equal chips. */
body.member-surface .ms-stage.on { border-color: var(--board-accent); }
body.member-surface .ms-stage.ahead {
  border-color: var(--board-line);
  background: var(--board-sunken);
  color: var(--board-ink-faint);
}
/* A request that left the path. The attention token is the board's own colour
 * for a fact that wants the member, which is exactly what a failed, refused or
 * non-actionable request is — all three are `needs_you` in MEMBER_WORK_STATE. */
body.member-surface .ms-stage.stopped {
  border-color: var(--board-attention);
  color: var(--board-attention);
}

body.member-surface .ms-panel {
  border-radius: var(--board-radius-panel);
  background: var(--board-panel);
  border: 1px solid var(--board-line);
  padding: var(--board-d-panel-pad);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
body.member-surface .ms-panel > h2 {
  margin: 0;
  font-size: var(--board-d-h2);
  font-weight: 600;
  text-transform: none;
  letter-spacing: normal;
  color: var(--board-ink);
}

/* A row of state: label left, fact right, divided from the row above by the
 * line token. The divider is a border-TOP on every row, which is how the
 * board draws it — the first row therefore carries a rule under the heading,
 * and that rule is part of the composition, not an off-by-one. */
body.member-surface .ms-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 0;
  border-top: 1px solid var(--board-line);
}
body.member-surface .ms-row > span:first-child { color: var(--board-ink-dim); }
body.member-surface .ms-row > span:last-child { text-align: right; }

body.member-surface .ms-actions { display: flex; gap: 8px; flex-wrap: wrap; }

/* TWO BOARDS, TWO WORDINGS. The view emits both; each viewport shows the one
 * its own board specifies. Defaults are the 1440 board's; the phone block
 * at the end of this file swaps them. */
body.member-surface .ms-short,
body.member-surface .ms-short-only { display: none; }
body.member-surface .ms-long { display: inline; }

/* The phone board's stretched link: above 560 the desktop board puts named
 * buttons in the panel footer instead, so the overlay is simply absent. */
body.member-surface .ms-panel { position: relative; }
body.member-surface .ms-panel-link { display: none; }

/* STATE WORDS. Small, heavy, letter-spaced, and coloured by what they mean. */
body.member-surface .ms-state {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
}
body.member-surface .ms-state.ok { color: var(--board-ok); }
body.member-surface .ms-state.you { color: var(--board-attention); }
body.member-surface .ms-state.none { color: var(--board-ink-faint); }

/* THE PLACEHOLDER UNDERLINE. The boards draw every un-sourced figure with a
 * dashed underline, which carries a meaning the surface must keep: this
 * number is not a measurement. A member who cannot tell a real figure from a
 * designed one has been misled by the surface, so the token is honoured. */
body.member-surface .ms-placeholder {
  color: var(--board-ink-faint);
  border-bottom: 1px dashed var(--board-placeholder-line);
}

/* THE COMPOSER. One accent-filled action on the screen, and it is this one. */
/* 1.2fr / 1fr, and the two columns STRETCH to a common height.
 *
 * The board's own grid carries no `align-items`, so its default stretch is
 * what draws the closed disclosure as a full-height dashed panel beside the
 * composer rather than as a compact strip above empty ground. Setting
 * `align-items: start` here -- which was the first thing this file did --
 * produced a composition that matched on every token and read differently,
 * because the dashed box is doing work at that height: it says there is more
 * under it. */
body.member-surface .ms-ask { display: grid; grid-template-columns: 1.2fr 1fr;
  gap: 18px; }
body.member-surface .ms-ask-row { display: flex; gap: 10px; }
body.member-surface .ms-ask input[type="text"] {
  flex-grow: 1;
  height: 50px;
  padding: 0 16px;
  font-size: 15px;
}
body.member-surface .ms-ask .ms-note { font-size: 12px; color: var(--board-ink-faint); }
/* THE HINT IS READABLE. The board leaves the field's placeholder at the
 * browser default, which measures 2.86:1 on this field -- under the 3:1 floor
 * every other word on Home clears. It takes the board's own faint ink, the
 * colour of the note beneath it (6.1:1). */
body.member-surface .ms-ask input[type="text"]::placeholder { color: var(--board-ink-faint); opacity: 1; }

/* THE OPERATOR DISCLOSURE. Dashed border on the sunken ground: the board
 * marks it as a thing you opened rather than a thing the screen asserted. */
body.member-surface .ms-operator {
  border: 1px dashed var(--board-placeholder-line);
  border-radius: var(--board-radius-panel);
  padding: 10px 16px;
  background: var(--board-sunken);
}
body.member-surface .ms-operator > summary {
  cursor: pointer;
  font-family: var(--board-display);
  font-weight: 600;
  font-size: 14px;
  color: var(--board-ink-dim);
}
body.member-surface .ms-operator .ms-operator-note { font-weight: 400;
  color: var(--board-ink-faint); font-size: 12px; }
body.member-surface .ms-operator > div { display: flex; flex-direction: column;
  gap: 12px; padding-top: 12px; }

/* ======================================================================== */
/* MY TEAMS — MyTeam.dc.html (1440)                                         */
/*                                                                          */
/* THE SECTION'S IDENTITY HERE IS ITS COLUMN HEADER, NOT AN <h2>. This       */
/* board's SIMPLE tranche is one section holding a five-column table —       */
/* Team / Doing for you / State / Cost so far / Needs you — with no heading  */
/* above it. All three of MyTeam.dc.html's <h2>s sit INSIDE its two          */
/* <details> disclosures, which the board draws CLOSED. probe.py reads the   */
/* identity through `.ms-grid-head`, cell by cell, for that reason.          */
/*                                                                          */
/* The column RATIOS are this board's own and appear in no other board, so   */
/* the generator has no token for them: they are named here beside the board */
/* that draws them. Every colour is a token — member-surface.css may declare */
/* no literal colour, and the guard that says so is mechanical.              */
/* ======================================================================== */
body.member-surface .ms-teams {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

body.member-surface .ms-grid-head,
body.member-surface .ms-grid-row {
  display: grid;
  grid-template-columns: 1fr 1.4fr 0.7fr 0.9fr 0.8fr;
  gap: 12px;
}
body.member-surface .ms-grid-head {
  font-size: 11px;
  color: var(--board-ink-faint);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 2px 0;
}
body.member-surface .ms-grid-row {
  align-items: center;
  padding: 9px 0;
  border-top: 1px solid var(--board-line);
}

/* The OPERATOR disclosure's timeline: when | what happened | the state it
 * reached. The board draws it at 96px | 1fr | 150px. */
body.member-surface .ms-trail-row {
  display: grid;
  grid-template-columns: 96px 1fr 150px;
  gap: 12px;
  align-items: center;
  padding: 8px 0;
}
body.member-surface .ms-trail-when {
  color: var(--board-ink-faint);
  font-size: 12px;
}

/* A version marker beside the thing it versions. The legacy sheet styles
 * `.ver` only inside `.entity-title`, so reusing that class here would have
 * shipped an UNSTYLED span on the member surface -- present, and in no
 * system. */
body.member-surface .ms-ver {
  color: var(--board-ink-faint);
  font-weight: 400;
}

/* The composition diagram's legend, under the board's own SVG. */
body.member-surface .ms-legend {
  font-size: 12px;
  color: var(--board-ink-faint);
}

/* ======================================================================== */
/* VERIFIED RESULTS — VerifiedResults.dc.html (1440)                        */
/*                                                                          */
/* THE SAME SHAPE AS MY TEAMS AND NOT THE SAME NUMBERS. This board's SIMPLE */
/* tranche is also one section holding a five-column table with no heading  */
/* above it, so it reuses `.ms-grid-head` / `.ms-grid-row` — but its column  */
/* RATIOS are its own (1.3fr 1fr 0.9fr 0.8fr 0.8fr against MyTeam's         */
/* 1fr 1.4fr 0.7fr 0.9fr 0.8fr), so they are overridden here, SCOPED to     */
/* `.ms-results`, rather than either board's being bent to the other's.     */
/* Writing one ratio for both would have made the second board's table a    */
/* near-miss of the first: right classes, wrong widths.                     */
/*                                                                          */
/* rule_f8cfe106c61c(5) measured this board as carrying clean column        */
/* headers, so it is graded by heading-text equality cell by cell and is    */
/* NOT declared instance-shaped.                                            */
/*                                                                          */
/* Every colour below is a token. member-surface.css may declare no literal */
/* colour and the guard that says so is mechanical.                         */
/* ======================================================================== */
body.member-surface .ms-results {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

body.member-surface .ms-results .ms-grid-head,
body.member-surface .ms-results .ms-grid-row {
  grid-template-columns: 1.3fr 1fr 0.9fr 0.8fr 0.8fr;
}

/* The OPERATOR disclosure's four-segment check strip — Spec | Evidence |
 * Sources | Verified. The board draws the segments flush, each overlapping
 * its neighbour's border by a pixel so the run reads as one control, with
 * the first and last corners rounded. That overlap is the whole form: four
 * separated boxes would be four facts, and this is one check in four parts.
 *
 * ONE CONDITION ONLY, DELIBERATELY. The board draws every segment at the
 * same raised ground because its example is a result that passed all four.
 * This deployment serves no member route for any of the four, so there is no
 * second condition to draw — and drawing a "lit" segment would assert a
 * check that was recorded. See viewVerifiedResults' own source. */
body.member-surface .ms-check-strip {
  display: flex;
  align-items: stretch;
  gap: 0;
  overflow: hidden;
}
body.member-surface .ms-check {
  flex: 1;
  min-width: 0;
  padding: 9px 8px;
  border: 1px solid var(--board-control-line);
  background: var(--board-raised);
  color: var(--board-ink);
  font-size: 10.5px;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  margin-left: -1px;
}
body.member-surface .ms-check:first-child {
  margin-left: 0;
  border-radius: 8px 0 0 8px;
}
body.member-surface .ms-check:last-child {
  border-radius: 0 8px 8px 0;
}

/* ======================================================================== */
/* 390 — MobileHome.dc.html                                                 */
/*                                                                          */
/* A second board, not a shrink of the first. The phone board sets a LARGER  */
/* body size (15px against 14px) because a phone is held closer, a smaller   */
/* h1, a shorter topbar, tighter panels, and it moves the rail to a bottom   */
/* tab bar. Scaling the desktop board down would have got every one of those */
/* backwards.                                                                */
/* ======================================================================== */

@media (max-width: 560px) {
  body.member-surface { font-size: var(--board-p-text); }

  /* One column, and the content must be told so.
   *
   * MEASURED. The desktop block places the content in `grid-column: 2`. At 390
   * the rail becomes an off-canvas drawer and the shell drops to one column,
   * but `grid-column: 2` still pointed at a second one -- so the grid created
   * an IMPLICIT column to satisfy it and split 390px into 264 and 126. Every
   * panel on the phone surface was rendering 126px wide inside a phantom
   * column. The screenshot looked like a narrow-content bug; it was a grid
   * placement left behind by the viewport change. */
  body.member-surface .shell {
    grid-template-columns: 1fr;
    grid-template-rows: var(--board-p-topbar) 1fr;
  }
  body.member-surface .content { grid-column: 1; grid-row: 2; }
  body.member-surface .topbar { grid-column: 1; }
  /* THE PHONE TOPBAR IS A DIFFERENT COMPOSITION, NOT A NARROWER ONE.
   *
   * MobileHome.dc.html pairs the mark with the SCREEN NAME in Sora 600 15px
   * ("Home") -- not with the OMNISX wordmark, and with no deployment host.
   * The desktop board does the opposite. So the wordmark and the host go, the
   * page title comes back, and the mark stays.
   *
   * The operator chrome goes too: the phone board's topbar right side holds
   * one compact control and the avatar, and `live-state` / `freshness` are
   * two lines of prose that forced the 54px topbar to wrap onto a second row.
   * Refresh is kept -- dropping it would cost a member the only way to pull
   * fresh state on a phone -- at the board's 30px phone control height. */
  body.member-surface .topbar {
    height: var(--board-p-topbar);
    padding: 0 16px;
    flex-wrap: nowrap;
    row-gap: 0;
  }
  body.member-surface .ms-wordmark,
  body.member-surface .ms-host { display: none; }
  body.member-surface .ms-brand { gap: 10px; }

  body.member-surface[data-view] #topbar-title { display: flex; flex: 0 1 auto;
    min-width: 0; }
  body.member-surface .topbar h1 {
    font-family: var(--board-display);
    font-weight: 600;
    font-size: var(--board-p-text);
    letter-spacing: normal;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* The phone board carries no subtitle. */
  body.member-surface .topbar .sub { display: none; }

  body.member-surface .topbar-actions { flex: 0 0 auto; gap: 8px; }
  body.member-surface #live-state,
  body.member-surface #freshness { display: none; }
  body.member-surface .topbar-actions button {
    height: 30px; padding: 0 10px; font-size: 12px;
  }
  body.member-surface .ms-avatar { width: 30px; height: 30px; font-size: 11px; }
  body.member-surface .content {
    padding: 16px 16px calc(var(--board-p-tabbar) + 24px);
    gap: 12px;
  }

  /* THE PHONE BOARD'S WORDING WINS HERE. */
  body.member-surface .ms-long { display: none; }
  body.member-surface .ms-short { display: inline; }
  body.member-surface .ms-short-only { display: flex; }

  body.member-surface .ms-head { flex-direction: column; align-items: flex-start;
    gap: 8px; }
  body.member-surface .ms-head h1 { font-size: var(--board-p-h1); }
  /* The phone board carries no subtitle under the heading: the depth legend
   * sits directly beneath it. One less line on a screen where vertical space
   * is the scarce thing. */
  body.member-surface .ms-head .ms-sub { display: none; }
  body.member-surface .ms-depth { font-size: 10.5px; }
  body.member-surface .ms-depth span { padding: 3px 8px; }
  body.member-surface .ms-depth .ms-depth-note { display: none; }

  /* One column. The board stacks the four panels in the order they are
   * asked about, which is why the markup order is the board's order. */
  body.member-surface .ms-quad { grid-template-columns: 1fr; gap: 12px; }
  /* MobileWork.dc.html stacks its panels in one column, like every
   * other phone board, and draws no grid at all. */
  body.member-surface .ms-work { grid-template-columns: 1fr; gap: 12px; }
  /* MobileCommission.dc.html stacks its two sections in one column with
   * `gap:12px`, like every other phone board. */
  body.member-surface .ms-commission { grid-template-columns: 1fr; gap: 12px; }
  /* SETTINGS HAS NO PHONE BOARD, SO THIS RULE IS NOT A BOARD CLAIM -- it is
   * the shared fallback, stated as such. The canvas draws no 390 counterpart
   * for MemberSettings.dc.html, so nothing here may be presented as that
   * board's specification; what this does is keep a member who reaches
   * Settings through the phone nav from meeting a two-column grid in a 390
   * viewport. The surface is COUNTED at 1440 only, and
   * derive_denominator.py records 390 as a board the canvas owes. */
  body.member-surface .ms-settings { grid-template-columns: 1fr; gap: 12px; }
  /* THE TWO SECTIONS AND TWO ROWS THE PHONE BOARD DOES NOT DRAW. Not a
   * narrowing of the desktop board: MobileCommission.dc.html draws "Proposed"
   * and "Cost" and nothing else, so showing Clarified or Approve here would
   * be this surface contradicting the board that governs this width. */
  body.member-surface .ms-long-only { display: none; }
  body.member-surface .ms-panel { padding: 13px 14px; gap: 7px; }
  body.member-surface .ms-row { font-size: var(--board-p-row); padding: 5px 0; }

  /* THE WHOLE PANEL IS THE TAP TARGET, AND THE BUTTONS GO.
   * The phone board draws no control inside a panel: the panel itself is a
   * link onward. Keeping the desktop board's named buttons here would have
   * been three 36px controls per panel on the narrowest screen, which is both
   * the wrong composition and the wrong density. Teams and Labor market stay
   * reachable through the rail drawer. */
  body.member-surface .ms-actions { display: none; }
  body.member-surface .ms-panel-link {
    display: block;
    position: absolute;
    inset: 0;
    border-radius: var(--board-radius-panel);
  }

  /* THE COMPOSER COLLAPSES TO ONE CONTROL.
   * The phone board has no field and no explanatory line under it -- just a
   * full-width accent button carrying the question. A field here would ask a
   * member to type a brief on a phone keyboard before they have been shown a
   * single price. */
  body.member-surface .ms-ask { grid-template-columns: 1fr; gap: 12px; }
  body.member-surface .ms-ask > .ms-panel {
    padding: 0;
    background: none;
    border: 0;
  }
  body.member-surface .ms-ask > .ms-panel > h2,
  body.member-surface .ms-ask .ms-note,
  body.member-surface .ms-ask input[type="text"],
  body.member-surface .ms-ask label { display: none; }
  body.member-surface .ms-ask-row { flex-direction: column; }
  body.member-surface .ms-ask .primary {
    height: var(--board-p-cta);
    width: 100%;
    justify-content: center;
    border-radius: var(--board-radius-panel);
    font-size: var(--board-p-text);
  }

  /* THE OPERATOR DISCLOSURE IS NOT ON THE PHONE BOARD.
   * MobileHome.dc.html ends at the accent CTA. The desktop board offers the
   * deeper read beside the composer because it has the horizontal room for a
   * second column; the phone board does not offer it at all. Nothing is lost:
   * every fact inside it is on a screen the bottom bar reaches. */
  body.member-surface .ms-operator { display: none; }

  body.member-surface .bottom-nav {
    height: var(--board-p-tabbar);
    padding: 6px 8px 10px;
    border-top: 1px solid var(--board-line);
    background: var(--board-ground);
    display: flex;
    align-items: center;
  }
  body.member-surface .bottom-nav a {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    text-decoration: none;
    font-size: 10.5px;
    letter-spacing: 0.02em;
    color: var(--board-ink-faint);
    border: 0;
    background: none;
  }
  /* `.glyph` is the span syncBottomNav already emits; the board's tab icon is
   * 17px and takes the accent only on the destination in force. */
  body.member-surface .bottom-nav a .glyph { font-size: 17px;
    color: var(--board-ink-faint); }
  body.member-surface .bottom-nav a.active { color: var(--board-ink); }
  body.member-surface .bottom-nav a.active .glyph { color: var(--board-accent); }
  /* A destination not built on this deployment is drawn as unreachable rather
   * than as reachable-and-broken. syncBottomNav marks it; the board has no
   * treatment for it, so it takes the faint ink and nothing else. */
  body.member-surface .bottom-nav a.unbuilt { color: var(--board-ink-faint);
    opacity: 0.55; }

  /* MY TEAMS AT 390 IS NOT A BOARD CLAIM, AND THIS BLOCK SAYS SO.
   *
   * The canvas draws My teams at ONE width. MobileTeam.dc.html is "Member ·
   * Team detail · 390px", which belongs to TeamDeploy.dc.html — a different
   * and future surface — so there is no phone board for this one. The phone
   * bottom nav still routes a member here, so the surface must not be broken
   * at 390; but a five-column table cannot be narrowed to 390px without
   * inventing a phone design, and inventing one is exactly what this lane may
   * not do (oact_00c5663bb32e). So the columns stack and the column header,
   * whose words only mean anything as columns, is not drawn.
   *
   * This is a FALLBACK, not fidelity. probe.py does not measure My teams at
   * 390 and derive_denominator.py records the missing board as an owed canvas
   * artefact, so nothing here is counted as having been built to a board. */
  body.member-surface .ms-grid-head { display: none; }
  body.member-surface .ms-grid-row {
    grid-template-columns: 1fr;
    gap: 4px;
    font-size: var(--board-p-row);
  }
  body.member-surface .ms-trail-row { grid-template-columns: 1fr; gap: 4px; }
}

/* The composer's label is for a screen reader only: the board draws the field
 * with a placeholder and no visible label, and dropping the label entirely
 * would leave the input unnamed. */
body.member-surface .ms-visually-hidden {
  position: absolute;
  left: -9999px;
}
