/* ============================================================================
 * sandbox-chrome.css — the ENVIRONMENT CHROME (Phase 6.22 Task 5, Ask 2).
 *
 * Board: docs/design/2026-09-09-sandbox/Sandbox - Chrome, Personas and Entry
 * V1.dc.html (w, 2a-2e, 4b, and the AMENDED 5a/5b/5d) + the Settings board's 4a
 * (the rebuilding bar).
 *
 * THREE HEIGHTS, AND ONE OF THEM IS NOT A WIDTH. 26px with a mouse (board 2a/2b);
 * 44px wherever the pointer is coarse (board 5a — "a capability and not a width",
 * so it fires at 800 on an iPad AND at 1440 on a touch monitor); 44px below 768
 * (board 5b — "The strip is 44px, 5.2% of a 390 × 844 screen"). (⚠ This sheet
 * drew 26 and a 22px phone strip until the 2026-09-22 build of the AMENDED
 * board — DEBT-101(2), paid by it, design-delta #165 — and the kebab overhung
 * that 22px strip upward — SBX-A57, now deleted.)
 * Markup: templates/partials/sandbox_chrome.html. Plan of record: §D Task 5 and
 * the SBX-A register of docs/superpowers/plans/2026-09-10-sandbox-surfaces-
 * stages-5-8.md — read those for what is decided.
 *
 * ITS OWN SHEET, LINKED BY ALL TEN DOCUMENTS — the rotate-gate.css precedent and
 * for the same measured reason: seven of the ten documents that must wear the
 * chrome (the six auth documents and /onboarding) link neither components.css nor
 * shell.css. So this
 * sheet needs tokens.css and nothing else, and it styles no shared `.tp-*` class
 * except the one spinner ring that the partial emits ONLY where base.html has
 * linked components.css (tests/test_sandbox_chrome.py holds both halves).
 *
 * INERT ON THE REAL PORTAL, BY CONSTRUCTION. Every rule below selects a `.sbx-env*`
 * class the partial emits only inside a Sandbox, and the two custom properties this
 * sheet sets are set only while that markup is in the document. tokens.css declares
 * `--tp-env-bar-h: 0px` and `--tp-env-safe-top: 0px`, so every shell consumer of
 * them adds or subtracts zero and nothing moves.
 *
 * THE TYPE SNAPS TO THE LADDER. The unamended boards (2a-2c) draw parts of the bar
 * at 10px and the database at 10.5px. §9.5's eight portal rungs start at 11, and a
 * new sheet may not add an off-ladder size
 * (tests/styleguide/test_cohesion_token_ladder.py), so with a mouse they all snap to
 * 11, which is what the 1440 bar draws anyway. The AMENDED boards draw ON the
 * ladder: on a coarse pointer, SANDBOX is 12 and the controls 13 (5a); on the phone
 * strip, SANDBOX is 12 (5b). The database stays 11 at every width. The rung for each
 * part is in its own rule below. (⚠ This paragraph ended "11 is the nearest rung
 * for all of them" until the pre-deploy review's L4-R5. That was true of the 26px
 * bar and the 22px strip. It stopped being true when 5a/5b were built.)
 *
 * The on-ink tiers are declared ONCE, on `.sbx-env` below. Read them there, never
 * in a list here. White on a solid ink fill is the documented #FFFFFF allowlist
 * exception. (⚠ This line listed the tiers as "(white · 72% · 25%)" until L4-R5.
 * The 5a/5b build added the 10% and 14% press washes beside them, and that list
 * went stale.)
 * ========================================================================= */

/* ---- the two tokens the shell shifts through -------------------------------
 * `:has()` rather than a class on <body>: the ten documents own ten <body> tags,
 * and a class stamped on each is ten places to forget. Child combinators keep the
 * invalidation scope to the body's own children.
 *
 * --tp-env-safe-top: THE BAR OWNS THE TOP INSET BAND (SBX-A56). The bar sits below
 * the status bar (`top: var(--tp-safe-top)`), so the shell starts below bar AND
 * inset, and the shell's first rows that reserve the inset on the real portal
 * subtract this instead of reserving it a second time (tokens.css lists them). It
 * does not change in the persona state below 768: the strip steps aside there, but
 * the violet bar that takes its slot reserves no inset of its own, so the shell
 * still has to. */
:root:has(> body > .sbx-env) {
  --tp-env-bar-h: 26px; /* board 2a · "26px desktop" */
  --tp-env-safe-top: var(--tp-safe-top);
}

.sbx-env {
  --sbx-on-ink: #FFFFFF;
  --sbx-on-ink-72: rgba(255, 255, 255, 0.72);
  --sbx-on-ink-25: rgba(255, 255, 255, 0.25);
  /* The two PRESS washes the AMENDED board draws on ink: 0.10 on a coarse-pointer
   * control (5a — "the wash is the only state a coarse pointer gets, because
   * there is no hover on a finger") and 0.14 on the phone strip's glyph while its
   * sheet is open (5b). */
  --sbx-on-ink-10: rgba(255, 255, 255, 0.10);
  --sbx-on-ink-14: rgba(255, 255, 255, 0.14);
}

/* ---- the FRAME -------------------------------------------------------------
 * Board 2c: "The frame's 3px runs inside both horizontal insets — including in
 * landscape — and its bottom edge stops above the home indicator", and nothing is
 * drawn behind a system inset. So each edge sits AT its inset (0 in a browser tab).
 *
 * THE RUNG IS --tp-z-env-frame (380). The frame has to out-paint every overlay that
 * can reach a viewport edge — the modal scrim (300) through the confirm (360) —
 * because the board draws the 1440 slide-over (3a) and the 390 full-screen picker
 * (3b) INSIDE the frame: an overlay is exactly when the environment must stay
 * stated. It sits under the toast region (400), which paints inside its own 12-16px
 * gutter and never meets a 3px edge, and far under the landscape gate (600).
 * `pointer-events: none`, so the 3px it covers can never eat a tap. */
.sbx-env-frame {
  position: fixed;
  top: var(--tp-safe-top);
  right: var(--tp-safe-right);
  bottom: var(--tp-safe-bottom);
  left: var(--tp-safe-left);
  z-index: 380;
  border: 3px solid var(--tp-ink);
  pointer-events: none;
}

/* ---- the BAR (>=768) and the STRIP (<768) ----------------------------------
 * Fixed to the viewport, below the status bar, above the shell — which is offset
 * by the bar's height plus the inset above it (shell.css `.tp-shell`, SBX-A56) —
 * and never resized on scroll (2d).
 *
 * THE RUNG IS THE SCRIM'S (300), AND THE PARTIAL'S PLACE IN THE DOCUMENT IS HALF
 * OF IT. Board 2d puts the bar "in the same z-band as the topbar": whatever covers
 * the topbar — a modal scrim, a slide-over, the drawer, the phone push — covers
 * this too, which is what 3a draws, and the FRAME keeps the environment stated
 * over them. So the bar must sit ABOVE every page layer and BENEATH every overlay.
 *   - It cannot declare nothing: that was the first build, and on /auth/login the
 *     hero's own layers (`.lg-hero` children at z 1 and 3) painted straight over
 *     it — measured in the browser, the whole bar except the database was hidden.
 *   - It cannot take a private low number: a fixed surface below the ladder's
 *     300 floor is off the ladder (tests/styleguide/test_overlays.py).
 *   - At 300 it is above every page layer, and every overlay that shares or beats
 *     that rung is LATER in the document than this bar — the partial is the first
 *     child of <body>, a fact tests/test_sandbox_chrome.py asserts from disk — so
 *     a scrim at 300 paints over it by source order and everything above 300 by
 *     rung. Move the include and that half of the argument goes with it.
 *
 * THE GAP IS ONE KNOB PER BAND, `--sbx-env-gap` (12 here · 10 below 1024 · 9 below
 * 768 — boards 2a, 2b and 5b), because it is read in TWO places: here as the flex
 * `gap`, and in the coarse-pointer block at the end of this sheet as the left
 * margin that replaces it everywhere except across the divider (board 5a draws
 * the controls meeting there). One declaration per band means the two can never
 * disagree. */
.sbx-env-bar {
  --sbx-env-gap: var(--tp-space-12);

  position: fixed;
  top: var(--tp-safe-top);
  left: 0;
  right: 0;
  z-index: 300;
  height: var(--tp-env-bar-h);
  display: flex;
  align-items: center;
  gap: var(--sbx-env-gap);
  padding: 0 max(14px, var(--tp-safe-right)) 0 max(14px, var(--tp-safe-left));
  background: var(--tp-ink);
  color: var(--sbx-on-ink);
  font-family: var(--tp-font-ui);
  font-size: 11px;
  font-weight: 400;
  line-height: 1;
  white-space: nowrap;
}

/* Board 2c: the strip sits BELOW the status bar, and the band above it is not
 * ink. Painted as the page's own top surface, the way the phone topbar's white
 * runs up to the glass on the real portal — here the shell starts below the bar,
 * so nothing else would paint it (SBX-A56). 0 tall in a browser tab. */
.sbx-env-bar::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  height: var(--tp-safe-top);
  background: var(--tp-surface);
}

/* "SANDBOX" — the EYEBROW role, which is the board's 11 / 700 / 0.06em / uppercase
 * exactly. The ink is on-ink, which no type role carries. */
.sbx-env-name {
  display: inline-flex;
  align-items: center;
  gap: var(--tp-space-6);
  flex-shrink: 0;
  font-size: var(--tp-type-eyebrow-size);
  font-weight: var(--tp-type-eyebrow-weight);
  letter-spacing: var(--tp-type-eyebrow-tracking);
  text-transform: var(--tp-type-eyebrow-transform);
  color: var(--sbx-on-ink);
}

.sbx-env-name i {
  font-size: 10px;
}

.sbx-env-sentence {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--sbx-on-ink-72);
}

/* Settings board 4a: while a factory reset runs, the sentence becomes the run —
 * full white, 600, beside a ring. "It is the only thing the bar ever says besides
 * its name." */
.sbx-env-status,
.sbx-env-status-short {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  font-weight: 600;
  color: var(--sbx-on-ink);
}

.sbx-env-status-short {
  display: none;
}

/* The shared ring (components.css `.tp-spinner`) at the board's 11px, on the
 * on-fill track the busy button already uses on an ink fill — the setup.css
 * `.su-pill--testing .tp-spinner` precedent for a ring sized by its context. */
.sbx-env-spinner {
  width: 11px;
  height: 11px;
  flex-shrink: 0;
  border-color: var(--tp-spinner-track-on-fill);
  border-top-color: currentColor;
}

/* The database is an IDENTIFIER, so it reads the mono-id role's family and weight
 * (§9.6: "always 500"); the ink is on-ink. */
.sbx-env-db {
  flex-shrink: 0;
  font-family: var(--tp-type-mono-id-family);
  font-weight: var(--tp-type-mono-id-weight);
  font-size: 11px;
  color: var(--sbx-on-ink-72);
}

.sbx-env-spacer {
  flex: 1;
  min-width: 0;
}

/* ---- the two controls (base.html only) -------------------------------------
 * Text controls on ink, the bar's full height as the hit box. "A control label
 * never ellipsizes; it moves" — below 1024 it shortens, below 768 it folds into
 * the kebab, unless script is off (the `(scripting: none)` block after the phone
 * band keeps it in the strip, shortened and text only; DEBT-259/CHR-13, drawn by the
 * Chrome board's 6a–6d). *(⚠ Until 2026-09-24
 * this ended "below 768 it folds into the kebab", with no exception.)* */
.sbx-env-control {
  display: inline-flex;
  align-items: center;
  gap: var(--tp-space-6);
  flex-shrink: 0;
  height: 100%;
  margin: 0;
  padding: 0 2px;
  border: 0;
  background: none;
  color: var(--sbx-on-ink);
  font: inherit;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}

/* `text-decoration: none` is the hover half of the reset above: the "Step into a
 * persona" control is an `<a>`, and the global `a:hover` underline
 * (components.css, (0,1,1)) beat the (0,1,0) rest rule (DEBT-259/ANCHOR/HOVER,
 * 2026-09-25). At (0,2,0) this rule wins on specificity. */
.sbx-env-control:hover {
  color: var(--sbx-on-ink);
  text-decoration: none;
}

.sbx-env-control:focus-visible,
.sbx-env-kebab:focus-visible {
  outline: 2px solid var(--sbx-on-ink);
  outline-offset: -2px;
}

.sbx-env-control i {
  font-size: 10px;
}

/* OPEN — board 3a draws "Step into a persona" as a white pill with ink text while
 * its slide-over is up: the environment names what it opened. Keyed on
 * `aria-expanded`, which drilldown.js writes on any overlay opener that DECLARES
 * the attribute (the tp.js popover rule, and the `.pd-trigger[aria-expanded]`
 * precedent in shell.css), so the visual state and the announced state can never
 * disagree. The pill is the board's own: 700, 3px 8px, 5px radius, 7px gap. The
 * hit box shrinks to the pill while open, which costs nothing — the page behind a
 * modal slide-over is inert. */
.sbx-env-control[aria-expanded="true"],
.sbx-env-control[aria-expanded="true"]:hover {
  height: auto;
  gap: 7px;
  padding: 3px 8px;
  border-radius: 5px;
  background: var(--sbx-on-ink);
  color: var(--tp-ink);
  font-weight: 700;
}

.sbx-env-label-short {
  display: none;
}

.sbx-env-leave {
  display: flex;
  height: 100%;
  margin: 0;
}

.sbx-env-divider {
  width: 1px;
  height: 12px;
  flex-shrink: 0;
  background: var(--sbx-on-ink-25);
}

.sbx-env-kebab {
  display: none;
}

/* ---- 940: the sentence drops, the controls shorten (board 2b) -------------- */
@media (max-width: 1023.98px) {
  .sbx-env-bar {
    --sbx-env-gap: 10px; /* board 2b's `gap: 10px` */
    padding: 0 max(var(--tp-space-12), var(--tp-safe-right)) 0
      max(var(--tp-space-12), var(--tp-safe-left));
  }

  .sbx-env-sentence,
  .sbx-env-status,
  .sbx-env-label {
    display: none;
  }

  .sbx-env-status-short,
  .sbx-env-label-short {
    display: inline;
  }

  /* Stepped in, the bar holds ONE control — the persona trigger is not offered
   * from inside a persona — and boards 2e and 3d both draw it at 940 in full:
   * "Leave the Sandbox". 2b's "Leave" is the shortening for two controls sharing
   * the bar; with one there is nothing to shorten for. */
  .sbx-env--persona .sbx-env-label {
    display: inline;
  }

  .sbx-env--persona .sbx-env-label-short {
    display: none;
  }
}

/* ---- a COARSE pointer: the bar is a touch target (board 5a) -----------------
 * "The bar goes to 44. Not because the viewport got narrower — because the
 * pointer got bigger." 26px stays the mouse value: "The 2b artboard is unchanged:
 * that is 940 with a mouse, and 26px is still right there." A capability, not a
 * breakpoint — 767.98 and 1023.98 are still the only two widths — so it fires on
 * an iPad at 800 and on a touch monitor at 1440, and never on a narrow mouse
 * window. `pointer: coarse` is the PRIMARY input: a hybrid laptop reports `fine`
 * (components.css's first coarse block says why that is the right question).
 *
 * ⚠ THIS BLOCK MUST STAY ABOVE THE PHONE BAND BELOW, and the reason is not style.
 * A phone is a coarse pointer too, so on a phone stepped into a persona this rule
 * and the band's `:root:has(> body > .sbx-env--persona) { … 0px }` BOTH match, at
 * equal specificity ((0,2,1) each) — and the LATER one wins. Written after the
 * band, a stepped-in phone would reserve 44px for a strip that is
 * `visibility: hidden` over the violet bar (board 2e: "phone merges").
 * tests/test_sandbox_chrome.py pins the order and a touch-context cell in
 * tests/functional/test_fn_sandbox.py measures it.
 *
 * The rest of 5a — the controls' 44-high boxes, the label's 12/13 — is in the
 * `(pointer: coarse) and (min-width: 768px)` block at the END of this sheet, so
 * none of it can reach the phone strip, which board 5b draws on its own. */
@media (pointer: coarse) {
  :root:has(> body > .sbx-env) {
    --tp-env-bar-h: 44px; /* board 5a · "the bar is 44" on a coarse pointer */
  }
}

/* ---- 390: the strip, and the kebab (board 2c, AMENDED by 5b) --------------- */
@media (max-width: 767.98px) {
  /* Board 5b: "2c costed the strip at 22px, 2.6% of the height. That number was
   * true, and it was the wrong number … The strip is 44px, 5.2% of a 390 × 844
   * screen." And "44 is the floor and the ceiling here: it is the touch minimum,
   * so there is no argument for 48 or 52". UNCONDITIONAL below 768 — "the 390
   * column is coarse by assumption, not by query" (5a). It read `22px` (board 2c)
   * until the 2026-09-22 build. */
  :root:has(> body > .sbx-env) {
    --tp-env-bar-h: 44px; /* board 5b · "The strip is 44px" */
  }

  /* Board 2e: "Desktop stacks; phone merges." Stepped in, the violet bar takes the
   * slot and carries the SANDBOX tag + database itself (impersonation.css), so the
   * strip goes and the shell reclaims its 44px. The frame never moves. The violet
   * bar is already 44 (board 5b's own note), so 5b changes nothing here. */
  :root:has(> body > .sbx-env--persona) {
    --tp-env-bar-h: 0px;
  }

  /* VISIBILITY, not `display: none`, and the reason is the band above it. The
   * strip's `::before` is what paints the status-bar band, and the shell still
   * starts below that band here (--tp-env-safe-top above). A hidden element's
   * children may be visible again, so the band stays painted while the strip, its
   * kebab and its landmark leave both the screen and the accessibility tree — and
   * its height is already 0 (the token above), so it takes no room. */
  .sbx-env--persona .sbx-env-bar {
    visibility: hidden;
  }

  .sbx-env--persona .sbx-env-bar::before {
    visibility: visible;
  }

  /* Board 5b's strip: `gap: 9px; padding: 0 4px 0 12px`. The right gutter is 4
   * because the 44×44 kebab box is the last thing in the strip and carries its
   * own air — the glyph lands 4 + 22 = 26px from the edge, where a thumb aims. */
  .sbx-env-bar {
    --sbx-env-gap: 9px;
    padding: 0 max(4px, var(--tp-safe-right)) 0
      max(var(--tp-space-12), var(--tp-safe-left));
  }

  /* 5b: SANDBOX at 12/700 with an 11px flask. The weight, tracking and case stay
   * the eyebrow role's (base rule); only the rung moves — 12 is on the ladder.
   * (The flask was 9px, sized for a 22px strip, until 2026-09-22.) */
  .sbx-env-name {
    font-size: 12px;
  }

  .sbx-env-name i {
    font-size: 11px;
  }

  /* Settings board 4a at 390: "SANDBOX ◌ Rebuilding" — the run takes the
   * database's place. */
  .sbx-env-status-short ~ .sbx-env-db {
    display: none;
  }

  /* THE RING TAKES THE FLASK'S SLOT while a factory reset runs — the close-out round
   * (2026-09-25): the Chrome board's 6b/6d with JavaScript off ("the spinner takes the
   * flask's place, so the status costs one word and not a glyph plus a word") and the
   * Settings board's 7b with it on ("The strip reads Rebuilding (the amended 44px strip,
   * spinner in the flask's slot)"). So it is written here, in the band, for both. Keyed
   * on the ring being IN the bar, which the partial emits only where base.html has linked
   * its sheet: a standalone document's bare bar has no ring and keeps its flask.
   *
   * `order`, never a markup move: the face's DOM order is what sandbox-settings.js's
   * `syncEnvFace` swaps in place and what the F-SET-9 functional cell pins, and the ring
   * is aria-hidden, so nothing is read in a different order.
   *
   * The drawn spacing is ring 6 · SANDBOX 9 · Rebuilding (7b: the strip's 9px gap with
   * SANDBOX pulled 3px left; 6b/6d: a 6px margin and a 9px one with no gap). Written as
   * a 6px gap with 3 given back to the status, never as that pull: a negative horizontal
   * margin is tests/test_device_chrome.py's guard. The 3px the smaller gap takes around
   * the spacer comes out of the spacer, which is flexible, so the kebab does not move.
   * "Rebuilding" is 12/600 at every phone width — 6b, 6d and 7b all draw 12, the rung
   * SANDBOX already stands on here. *(⚠ Until the close-out build the phone strip read
   * flask · SANDBOX · ring · Rebuilding, the status at the bar's 11px.)* */
  .sbx-env-bar:has(> .sbx-env-spinner) {
    --sbx-env-gap: 6px;
  }

  .sbx-env-name:has(~ .sbx-env-spinner) i {
    display: none;
  }

  .sbx-env-spinner {
    order: -1;
  }

  .sbx-env-status-short {
    font-size: 12px;
  }

  .sbx-env-spinner ~ .sbx-env-status-short {
    margin-left: 3px;
  }

  .sbx-env-control,
  .sbx-env-divider,
  .sbx-env-leave {
    display: none;
  }

  /* THE STRIP IS THE TARGET NOW, SO NOTHING OVERHANGS ANYTHING (board 5b). A flat
   * 44×44 box at the strip's right end, drawing `ellipsis-vertical` at 15px —
   * "drawn at a 44 × 44 box inside the 44px strip so the affordance sits where a
   * thumb aims, even though the row is live edge to edge." The bell's top edge is
   * safe by GEOMETRY now, not by the direction of an overhang.
   *
   * ⚠ This rule overhung a 22px strip UPWARD ONLY until 2026-09-22 (SBX-A57:
   * `margin: calc(22px - var(--tp-touch-min)) 0 0` with the matching padding), and
   * the first build before that split the overhang 11 above / 11 below, whose
   * lower 11 took the top 7.5px of the phone topbar's bell. Board 5b rejected
   * both by name: "an invisible target twice the size of the thing it belongs to,
   * sitting on top of a live control, is a bug that only shows up in somebody's
   * hands." Both overhangs are deleted, not tuned.
   *
   * `position` is deliberately NOT declared: the kebab stays static so that its
   * `::before` below takes the FIXED bar as its containing block. */
  .sbx-env-kebab {
    display: inline-grid;
    place-items: center;
    flex-shrink: 0;
    width: var(--tp-touch-min);
    height: var(--tp-touch-min);
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    color: var(--sbx-on-ink);
    font-size: 15px;
    cursor: pointer;
  }

  /* THE WHOLE STRIP IS THE HIT AREA — "tapping the label, the database name or
   * the glyph all open the environment sheet" (5b). The `.tp-bell::before` /
   * `.tp-zoombtn::after` idiom: the hit area grows and the drawing does not move.
   * Absolute inside the bar (the kebab is static, so the `position: fixed` bar is
   * the containing block), spanning it — the sides spelled one by one the way
   * `.sbx-env-bar::before` spells them, which is what the device-chrome side
   * sweep reads. It is painted nothing and it is the kebab's own box for
   * hit-testing, so the label and the database stay what the eye reads while a
   * tap anywhere on the strip is a tap on the one control it holds.
   *
   * `height: 100%`, NOT `bottom: 0`: tests/test_device_chrome.py's bottom-dock
   * sweep reads any absolute box pinned to `bottom: 0` inside a fixed root as a
   * surface docked to the VIEWPORT's bottom edge and demands the home-indicator
   * inset (measured red on the first draft of this rule). The bar's bottom is 44px
   * from its top, nowhere near the gesture bar, and `height: 100%` of the bar's
   * padding box says exactly that without claiming an edge. */
  .sbx-env-kebab::before {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    left: 0;
    height: 100%;
  }

  /* 5b: "The strip's glyph holds its press wash while the sheet is open, so it is
   * obvious what was tapped" — 0.14 white at radius 8. Keyed on `aria-expanded`,
   * which tp.js writes onto a popover opener that DECLARES the attribute (the
   * partial's kebab does), so the drawn state and the announced one agree. */
  .sbx-env-kebab[aria-expanded="true"] {
    background: var(--sbx-on-ink-14);
    border-radius: 8px;
  }
}

/* ---- 390 with JavaScript OFF: the controls stay in the strip (DEBT-259/CHR-13) --
 * The band above folds both controls into the kebab, and the kebab opens the shared
 * sheet, which only tp.js opens. With scripting off it was a dead glyph, and
 * neither "Step into a persona" nor "Leave the Sandbox" could be reached at 390.
 * Both work without script: the persona control is an ANCHOR to the picker's own
 * page, and Leave is a plain CSRF form whose answer hands off by `Refresh` (SBX-A55).
 * So where script cannot run, the strip keeps them, in the short words the 940 band
 * already gives them ("Persona", "Leave": `.sbx-env-label-short`, in the markup),
 * each the strip's full 44px tall; and the kebab, which would do nothing, is not
 * drawn. A capability query, not a breakpoint: 767.98 is still the only width here,
 * and `(scripting: none)` never matches a page that runs tp.js. Stepped into a
 * persona the strip is hidden over the violet bar (board 2e), and these rules
 * change nothing there.
 *
 * The DATABASE gives them its place, the way board 4a's rebuilding strip gives it to
 * the run: measured at 320 with the database shown, the strip needed 383px, and a
 * live project id is as long as its owner made it, so even 390 is not a width it
 * can be counted on to fit. Only in a strip that HAS the controls, which is the one
 * with a kebab after its database: a standalone document's bare bar (board 4b) ends
 * on the database and keeps it. Wherever script runs, the sheet's head names it.
 *
 * AFTER THE BAND ABOVE, and that is the mechanism: the rules that re-show the
 * controls and hide the kebab are bare class selectors, as the band's are, so the
 * later block wins where both apply.
 *
 * DRAWN SINCE THE CLOSE-OUT ROUND — the Chrome board's 6a–6d (Part 2, 2026-09-25):
 * "The sheet cannot open, so the strip carries the two controls itself. They are text
 * only at every phone width, and the status never truncates." The 940 strip's two
 * controls, "44 high, 12px padding, text only", at 13/600 (the coarse 940 frame's
 * rung), with the 1×16 divider between them and NO flex gap: the controls meet the
 * divider and each label sits its own 12px from it, as board 5a draws the 940 bar.
 * While a reset runs the ring stands in the flask's slot (the band above) — 6 to
 * SANDBOX, 9 to "Rebuilding" — and at 320 all of it fits whole with about 3px to
 * spare: "Spinner, SANDBOX, Rebuilding 166 · Persona · divider · Leave, text only 135
 * · strip padding 16 · needed 317". WHAT GIVES WAY IS THE CONTROL GLYPHS, "at every
 * phone width, in both states" — "the only thing on the strip that says nothing the
 * label does not" — never the divider (the only thing between two white words), never
 * an ellipsis ("Rebuild…" is the one word a trainee needs whole), and not only below
 * 346 ("that would be a third breakpoint, and the strip would change shape when a
 * reset starts"). The 940 strip keeps its glyphs: this block is the phone width alone.
 * *(⚠ Until the close-out build this block ended "No board draws this state; it is the
 * 940 strip's two controls at the 390 strip's height", and it kept the glyphs at the
 * bar's 11px with the band's 9px gaps — so below about 346px, while rebuilding, the
 * short status ellipsized to "Reb…" (SBX-A104's interim ellipsis, D-289(25)).)* */
@media (max-width: 767.98px) and (scripting: none) {
  .sbx-env-db:has(~ .sbx-env-kebab) {
    display: none;
  }

  /* 6a–6d draw the CONTROLS' strip with no gap at all: the spacer takes the slack, and
   * the one pair that is spaced — the ring and SANDBOX, then SANDBOX and the status — is
   * spaced by the two margins below.
   *
   * ALL THREE ARE KEYED ON THE KEBAB, the key the database rule above already uses,
   * because only the strip that has controls is the one 6a–6d draw. Every OTHER bar
   * below 768 keeps the phone band's spacing: a standalone document's bare bar (board
   * 4b: /auth/login, /offline, /onboarding) has no ring and no margin to stand in for
   * the gap, and neither does a base.html bar with no control to offer (an anonymous
   * error page) — with the gap taken away their items touched ("SANDBOXRebuilding",
   * "SANDBOXdemo-…"). The margins are keyed with it, not left bare, because on a
   * control-less base.html bar that DOES draw the ring the phone band's 6px gap would
   * stack on them (12 and 15 where 6 and 9 are drawn).
   * *(⚠ The close-out build wrote the three as bare `.sbx-env-bar`, `.sbx-env-spinner`
   * and `.sbx-env-spinner ~ .sbx-env-status-short` rules, which took the gap from every
   * phone bar with script off — the lane review's ESF-P2-1.)* */
  .sbx-env-bar:has(> .sbx-env-kebab) {
    gap: 0;
  }

  .sbx-env-bar:has(> .sbx-env-kebab) > .sbx-env-spinner {
    margin-right: 6px;
  }

  .sbx-env-bar:has(> .sbx-env-kebab) > .sbx-env-spinner ~ .sbx-env-status-short {
    margin-left: 9px;
  }

  .sbx-env-control {
    display: inline-flex;
    padding: 0 12px;
    font-size: var(--tp-type-body-dense-size);
  }

  .sbx-env-control i {
    display: none;
  }

  .sbx-env-leave {
    display: flex;
  }

  .sbx-env-divider {
    display: block;
    height: 16px;
  }

  .sbx-env-kebab {
    display: none;
  }
}

/* ---- the kebab while an overlay is up (board 5d, and where this departs) --
 * ⚠ A DELIBERATE DEPARTURE FROM THE BOARD. 5d says "Its X is the picker's X, and
 * the strip's kebab is still live". It is NOT built live, and D-050 is why: the
 * kebab opens a bottom SHEET (the shared `.tp-popover`, rung 340, EARLIER in the
 * document than the push) over a full-screen push (`.adm-drill`, also 340) — a
 * sheet over a push, which is the stacked-overlay shape D-050 bans ("no stacked
 * modals"). tp.js's background-inert pass (`setBackgroundInert`) already makes
 * `.sbx-env` inert behind EVERY drill-down — only `[data-tp-toast-region]` is
 * exempt (NEVER_INERT) — so the kebab is dead while the picker is up.
 *
 * An inert control that still LOOKS live is the worse defect (the reasoning of
 * shell.css's `.tp-overlay-open .pd-trigger`), so it wears the ONE unavailable
 * look (`.tp-btn[disabled]`, `.tp-offline-disabled`), keyed on the attribute
 * the seam actually writes — not on `.tp-overlay-open`, which
 * `syncOverlaySuppression` derives from tp.js's own two registries and which a
 * drill-down never stamps. The strip's TEXT stays at full ink: "the environment
 * never blinks" is the load-bearing half of 5d, and it is met.
 *
 * REVERSAL PATH, if the owner wants the live kebab: a D-050 clause admitting a
 * sheet over a push, plus a sheet LATER in the document than the push — not an
 * edit to this rule (SBX-A89). */
.sbx-env[inert] .sbx-env-kebab {
  opacity: 0.55;
}

/* ---- a COARSE pointer from 768 up: the controls are touch targets (5a) -------
 * Board 5a's 940 frame: "the bar is 44 and both controls are 44-high with 12px
 * of horizontal padding, so each is a real target with its label intact"; "The
 * label grows 11 → 12/13px with the bar: 11px was sized for a 26px band, and
 * arm's length on a break-room tablet is not desk distance." Also fires at 1440
 * on a touch monitor (5a says it should). `min-width: 768px` is the existing
 * tablet breakpoint's other side, so none of this reaches the phone strip.
 *
 * The sentence (≥1024 only) and the database stay 11: no coarse frame draws them
 * larger, and the AMENDED strip says "Nothing else already drawn moves"
 * (SBX-A86). */
@media (pointer: coarse) and (min-width: 768px) {
  /* 5a: SANDBOX 12/700/0.06em with an 11px flask, 7px apart. Only the rung moves;
   * weight, tracking and case stay the eyebrow role's. */
  .sbx-env-name {
    gap: 7px;
    font-size: 12px;
  }

  .sbx-env-name i {
    font-size: 11px;
  }

  /* 13/600 — the dense body rung at the control's own weight (600, base rule). */
  .sbx-env-control {
    gap: var(--tp-space-8);
    padding: 0 12px;
    font-size: var(--tp-type-body-dense-size);
  }

  /* The 940 frame draws masks-theater at 13 and the leave arrow at 12. */
  .sbx-env-control i {
    font-size: 13px;
  }

  .sbx-env-leave .sbx-env-control i {
    font-size: 12px;
  }

  /* "Leave is drawn pressed, at rgba(255,255,255,0.10) — the wash is the only
   * state a coarse pointer gets, because there is no hover on a finger and a
   * control that only responds to something the device cannot do reads as dead." */
  .sbx-env-control:active {
    background: var(--sbx-on-ink-10);
  }

  .sbx-env-divider {
    height: 16px;
  }

  /* THE TWO CONTROLS MEET AT THE DIVIDER. The 940 frame draws the bar with NO
   * flex gap, each control `padding: 0 12px` and the divider 1×16 with no margin,
   * so the 44-high targets touch it and each label sits 12px from it — its own
   * padding. A flex `gap` cannot be spent per item, so the bar's gap goes to 0
   * here and comes back as a left margin on every item EXCEPT the divider and the
   * item after it. The margin reads `--sbx-env-gap`, the width band's own gap, so
   * the head of the bar keeps exactly the mouse layout's spacing: the database
   * 10px after SANDBOX at 940 (the frame's `margin-left: 10px`), and 2a's 12 at
   * 1440, which no coarse frame redraws. The universal `*` is the bar's own
   * children and nothing deeper (`>`); a hidden child takes no box, so its margin
   * costs nothing.
   *
   * (⚠ The first build of this block left the band's 10px flex gap in place, so a
   * label sat 22px from the divider at 940 and 24px at 1440 — the pre-deploy
   * review's L4-R2. A negative margin on the divider would cancel the gap in fewer
   * lines and is refused: it is a horizontal pull that tests/test_device_chrome.py's
   * negative-pull guard would have to clear by hand.) */
  .sbx-env-bar {
    gap: 0;
  }

  .sbx-env-bar > * + * {
    margin-left: var(--sbx-env-gap);
  }

  .sbx-env-bar > .sbx-env-divider,
  .sbx-env-bar > .sbx-env-divider + * {
    margin-left: 0;
  }

  /* THE LAST CONTROL ABSORBS 6px INTO THE BAR'S GUTTER — the board draws the bar
   * at `padding: 0 6px 0 12px`, so the pressed box reaches 6px from the edge. The
   * absorb idiom (`.pd-trigger`, `.tp-profile-action`) rather than a second bar
   * padding, because only a CONTROL wants to sit there: a bare bar (the standalone
   * documents, board 4b) ends on the database, which keeps the full gutter.
   *
   * CLAMPED, never a flat -6px: the pull shrinks to 0 as the right inset eats the
   * 12px gutter, so it can never reach into a device reservation (the negative-pull
   * guard in tests/test_device_chrome.py, branch (a); `.tp-topbar-phone-menu` is
   * the same shape). 6px from the edge below 1024, 8px above it, where the bar's
   * gutter is 14. */
  .sbx-env-leave {
    margin-right: calc(0px - min(max(var(--tp-space-12) - var(--tp-safe-right), 0px), 6px));
  }
}
