/* =============================================================================
 * Boncom design system — components
 *
 * Reads `colors_and_type.css` for every value. Nothing here declares a colour
 * literal except the four places a token would be wrong: the empty-photo box
 * and its glyph, which are a photographic grey rather than a charcoal wash;
 * the camera housing, which has to be darker than any ink in the system; and
 * white on a coloured fill.
 *
 * There is no square button. Every button and every tag is a pill; radius is
 * reserved for surfaces that hold content.
 * ============================================================================= */

/* --- buttons -------------------------------------------------------------- */

.bs-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  min-height: 40px;
  padding: 0 18px;
  border-radius: var(--bs-radius-pill);
  border: 1px solid var(--bs-hairline);
  background: var(--bs-surface);
  color: var(--bs-ink);
  font-size: 14px;
  font-variation-settings: "wdth" 95, "wght" 600;
  font-weight: 400;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color var(--bs-fast) var(--bs-ease),
    border-color var(--bs-fast) var(--bs-ease),
    color var(--bs-fast) var(--bs-ease), opacity var(--bs-fast) var(--bs-ease);
}

.bs-btn:hover:not(:disabled) {
  background: var(--bs-wash);
}

.bs-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.bs-btn svg {
  width: 17px;
  height: 17px;
  flex: none;
}

.bs-btn--primary {
  background: var(--bs-ink);
  border-color: var(--bs-ink);
  color: var(--bs-cream);
}

.bs-btn--primary:hover:not(:disabled) {
  background: #000;
  border-color: #000;
}

.bs-btn--accent {
  background: var(--bs-maroon);
  border-color: var(--bs-maroon);
  color: #fff;
}

/* Ghost. Reads as available but secondary; carries no fill until hovered. */
.bs-btn--quiet {
  background: transparent;
  border-color: transparent;
}

/* A button that takes something away: Cancel, Remove, revoke. Resting red
   so it matches the warn colour, not the quiet ink of ordinary actions. */
.bs-btn--danger {
  background: transparent;
  border-color: var(--bs-red);
  color: var(--bs-red);
}

.bs-btn--danger:hover:not(:disabled) {
  background: var(--bs-red);
  border-color: var(--bs-red);
  color: #fff;
}

.bs-btn--lg {
  min-height: 52px;
  padding: 0 26px;
  font-size: 17px;
}

.bs-btn--block {
  width: 100%;
}

/* --- segmented control ---------------------------------------------------- */

.bs-seg {
  display: inline-flex;
  border: 1px solid var(--bs-hairline);
  border-radius: var(--bs-radius-pill);
  overflow: hidden;
  background: var(--bs-surface);
  flex: none;
}

/* Person drawer tabs sit under the name as two equal, separate pills. */
.bs-person-tabs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  width: 100%;
  align-items: start;
}

.bs-person-tabs__cell {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.bs-person-tabs .bs-btn {
  width: 100%;
  min-width: 0;
}

.bs-person-tabs__note {
  margin: 0;
  font-size: 12px;
  line-height: 1.3;
  color: var(--bs-red);
}

.bs-seg__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 38px;
  padding: 0 14px;
  border: 0;
  background: transparent;
  color: var(--bs-muted);
  font: inherit;
  font-size: 13px;
  font-variation-settings: "wdth" 90, "wght" 550;
  cursor: pointer;
  transition: background-color var(--bs-fast) var(--bs-ease),
    color var(--bs-fast) var(--bs-ease);
}

.bs-seg__btn:hover:not(:disabled):not(.bs-seg__btn--on) {
  background: var(--bs-wash);
}

.bs-seg__btn + .bs-seg__btn {
  border-left: 1px solid var(--bs-hairline);
}

.bs-seg__btn--on {
  background: var(--bs-ink);
  color: var(--bs-cream);
}

.bs-seg__btn svg {
  width: 16px;
  height: 16px;
}

/* The four-way density control on the Talent toolbar. Sits on the sunken
   ground so the inactive segments read as a track rather than as four buttons. */
.bs-seg--sunken {
  background: var(--bs-sunken);
  border-color: var(--bs-hairline-soft);
  padding: 3px;
  gap: 2px;
}

.bs-seg--sunken .bs-seg__btn {
  border-radius: var(--bs-radius-pill);
  border-left: 0;
  min-height: 34px;
}

/* Three rate choices in a half-width field. Shares the row instead of wrapping. */
.bs-seg--fill {
  display: flex;
  width: 100%;
}

.bs-seg--fill .bs-seg__btn {
  flex: 1;
  min-width: 0;
  min-height: 32px;
  padding: 0 8px;
  font-size: 12px;
}

/* --- fields --------------------------------------------------------------- */

.bs-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.bs-field > label,
.bs-field > span {
  font-size: 13px;
  color: var(--bs-muted);
  font-variation-settings: "wdth" 100, "wght" 550;
}

.bs-field input,
.bs-field select,
.bs-field textarea {
  min-height: 44px;
  padding: 10px 14px;
  border-radius: var(--bs-radius-input);
  border: 1px solid var(--bs-hairline);
  background: var(--bs-surface);
  color: var(--bs-ink);
  font-variation-settings: "wdth" 100, "wght" 450;
  transition: border-color var(--bs-fast) var(--bs-ease);
}

.bs-field textarea {
  line-height: 1.55;
  resize: vertical;
}

.bs-field input:focus,
.bs-field select:focus,
.bs-field textarea:focus {
  outline: 2px solid var(--bs-maroon);
  outline-offset: 1px;
}

.bs-field--lg input {
  min-height: 52px;
  font-size: 17px;
}

/* Toolbar controls are pills, and shorter than a form field. A row of them is
   chrome, not a form. */
.bs-field--pill input,
.bs-field--pill select {
  min-height: 40px;
  padding: 0 14px;
  border-radius: var(--bs-radius-pill);
  font-size: 14px;
}

/* Search, with its glyph inset. The padding is measured from the glyph, not
   guessed: 14px gutter, 17px mark, 7px gap. */
.bs-search {
  position: relative;
  display: flex;
  min-width: 0;
}

.bs-search input {
  width: 100%;
  padding-left: 38px;
}

.bs-search__glyph {
  position: absolute;
  left: 13px;
  top: 50%;
  transform: translateY(-50%);
  width: 17px;
  height: 17px;
  color: var(--bs-quiet);
  pointer-events: none;
}

/* --- pills and tags ------------------------------------------------------- */

.bs-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  border-radius: var(--bs-radius-pill);
  border: 1px solid var(--bs-hairline);
  font-size: 13px;
  font-variation-settings: "wdth" 95, "wght" 550;
  white-space: nowrap;
}

.bs-pill--signed {
  background: var(--bs-teal);
  border-color: transparent;
  color: var(--bs-teal-ink);
}

.bs-pill--waiting {
  background: transparent;
  color: var(--bs-quiet);
}

.bs-pill--alert {
  background: var(--bs-red);
  border-color: transparent;
  color: #fff;
}

/* Release stage, in the caps register because it is metadata about the product
   rather than a word in a sentence. Mustard says here what it says on a status
   dot: not final yet. The caps properties are restated rather than composed
   from `.bs-caps`, which would hand the colour to whichever rule the sheet
   happened to declare second. */
.bs-pill--stage {
  background: var(--bs-mustard);
  border-color: transparent;
  color: var(--bs-mustard-ink);
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-variation-settings: "wdth" 75, "wght" 700;
}

.bs-pill--version {
  color: var(--bs-muted);
}

/* --- status dot ----------------------------------------------------------
 *
 * The core of the design, and the thing a coordinator actually reads across a
 * board of thirty-four people. Three fills roll up seven exact statuses. In a
 * list row the Status column beside the dot says which one; on a card the dot
 * is the whole of it (D-257). A hollow ring for "no link yet" is
 * deliberate — an absence should look like an absence, not like a fourth
 * colour to learn. */

.bs-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  flex: none;
  background: transparent;
}

.bs-dot--signed {
  background: var(--bs-teal);
  box-shadow: inset 0 0 0 1px rgb(14 56 56 / 0.35);
}

.bs-dot--pending {
  background: var(--bs-mustard);
  box-shadow: inset 0 0 0 1px rgb(58 47 0 / 0.35);
}

.bs-dot--none {
  box-shadow: inset 0 0 0 1.5px rgb(26 26 26 / 0.45);
}

.bs-dot--expired {
  background: var(--bs-red);
}

/* --- banners -------------------------------------------------------------
 *
 * Every inline notice: a failed send, a refused submission, "sent 12 of 14".
 * Unlike a pill these wrap, because the messages are sentences. */

.bs-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  padding: 10px 16px;
  border-radius: var(--bs-radius-pill);
  border: 1px solid var(--bs-hairline);
  background: var(--bs-surface);
  font-size: 14px;
}

.bs-banner--warn {
  background: rgb(255 26 34 / 0.08);
  border-color: rgb(255 26 34 / 0.28);
  color: var(--bs-danger-ink);
}

/* --- cards ---------------------------------------------------------------- */

.bs-card {
  background: var(--bs-surface);
  border: 1px solid var(--bs-hairline);
  border-radius: var(--bs-radius-card);
  box-shadow: var(--bs-shadow-card);
  padding: 20px;
}

.bs-card--flush {
  padding: 0;
  overflow: hidden;
}

.bs-panel {
  background: var(--bs-surface);
  border: 1px solid var(--bs-hairline);
  border-radius: var(--bs-radius-panel);
  padding: 14px 16px;
}

/* A row of metadata on the sunken ground: the payment line on the reading
   screen, a Date/Payment pair on the signing screen. */
.bs-sunken-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 16px;
  border-radius: var(--bs-radius-panel);
  background: var(--bs-sunken);
}

/* --- table ---------------------------------------------------------------- */

.bs-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}

.bs-table th,
.bs-table td {
  text-align: left;
  padding: 11px 14px;
  border-bottom: 1px solid var(--bs-hairline-soft);
  vertical-align: top;
}

.bs-table th {
  color: var(--bs-muted);
  width: 38%;
  font-variation-settings: "wdth" 100, "wght" 550;
}

/* A data table's head is metadata, so it takes the caps register. The 38%
   width above is for the label/value tables and has to go. */
.bs-table--data th {
  width: auto;
  white-space: nowrap;
  font-size: 11px;
  line-height: 1.4;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-variation-settings: "wdth" 75, "wght" 700;
}

.bs-table tr:last-child th,
.bs-table tr:last-child td {
  border-bottom: none;
}

/* --- who ------------------------------------------------------------------ */

.bs-who {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.bs-who__avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--bs-maroon);
  color: #fff;
  font-size: 13px;
  letter-spacing: 0.02em;
  font-variation-settings: "wdth" 90, "wght" 700;
  flex: none;
}

.bs-who__avatar--sm {
  width: 28px;
  height: 28px;
  font-size: 11px;
}

.bs-who__name {
  font-size: 15px;
  font-variation-settings: "wdth" 100, "wght" 600;
}

.bs-who__meta {
  font-size: 13px;
  color: var(--bs-muted);
}

/* --- console ground ------------------------------------------------------
 *
 * The chrome is cream and the page behind the content is a shade sunk from it,
 * so a card reads as lifted off the page rather than as a border drawn on it.
 * Only the console does this; a phone screen is one surface and has nothing to
 * sink behind. */

.trc-app {
  background: var(--bs-sunken);
}

/* --- console top bar -----------------------------------------------------
 *
 * A horizontal bar, not a rail. Translucent cream over the sunken page, so
 * the board scrolling under it stays legible as motion without competing with
 * the nav. `saturate` compensates for the wash blur puts over the maroon
 * avatar. */

.bs-topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  min-height: 64px;
  display: flex;
  align-items: center;
  background: rgb(244 242 236 / 0.85);
  backdrop-filter: blur(12px) saturate(160%);
  -webkit-backdrop-filter: blur(12px) saturate(160%);
  border-bottom: 1px solid var(--bs-hairline);
}

.bs-topbar .trc-console-frame {
  gap: 12px 16px;
  flex-wrap: wrap;
}

/* The mark, the wordmark, its dot and the two badges are one lockup on one
   line, so they share a baseline rather than a centre. Centring them on a 34px
   line box floated the 11px caps about 3px above the letters it names. */
.bs-topbar__brand {
  display: flex;
  align-items: baseline;
  gap: 12px;
  min-width: 0;
}

/* The company mark: a bordered square holding the initial, set in type rather
   than shipped as an image. Type because it then inherits `currentColor` and
   the same variable cut as the wordmark beside it, and because a logo file is
   one more request in front of a first paint.

   The square is exactly as tall as the wordmark's capitals and sits on their
   baseline, so its top and bottom edges are the cap line and the baseline —
   the box frames the `B` beside it rather than being a shape near it. 16px
   because that is what the cap measures: `actualBoundingBoxAscent` for `B` in
   this face at 22px is 16.04, measured through a canvas rather than taken from
   a ratio, since a variable font's cap height is not a number you can guess
   from its size (D-286).

   That bottom edge is on the baseline because the letter is out of flow: a box
   with no in-flow baseline has one synthesised from its border box, so
   `align-items: baseline` on the row rests that edge on the line. The same
   mechanism D-255 uses for the dot, which has no text of its own either.

   D-282 removed exactly this and put the letter back in flow, which reads as a
   reversal and is not one — what changed is the size. A 30px box resting on
   the baseline cleared the cap line by 14px and read as an icon floating over
   the name, so straddling the line was the better of two wrong options. At cap
   height there is nothing to float: the box and the capitals occupy the same
   band. Keep the letter in flow at this size and the square drops half its
   height below the baseline instead. */
.bs-mark {
  position: relative;
  display: inline-flex;
  flex: none;
  /* 16px of cap height at 120%, with the border, the radius and the letter
     scaled by the same 1.2 so the mark is the same drawing larger rather than
     a different one (D-288). */
  width: 19.2px;
  height: 19.2px;
  border: 1.8px solid currentColor;
  border-radius: 4.2px;
  /* Half the 3.2px the box gained, so the centre stays where the cap-height
     version put it: 8px above the baseline. Growing a box whose bottom edge is
     pinned to the baseline lifts its centre by exactly this much, and the
     brief was to enlarge without moving the centre.

     `top` and not `margin-bottom`, which was tried first and does nothing
     here: a box with no in-flow baseline has one synthesised from its **border
     box**, not its margin box, so a negative bottom margin left the bottom
     edge on the baseline and the centre 1.6px high. Measured both ways.
     Relative offset is applied after layout, so the row's own baseline and the
     12px gap to the wordmark are untouched. */
  top: 1.6px;
}

.bs-mark__letter {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Sized so the `b` fills the interior without touching the border. */
  font-size: 13.2px;
  line-height: 1;
  font-variation-settings: "wdth" 100, "wght" 800;
}

/* The badges are metadata about the product, not part of the wordmark, so they
   are small enough to read as an aside beside it, and they sit on its bottom
   edge rather than on its baseline: these are boxes, and a box lines up with
   the name by its own outline, not by the type inside it.

   The empty `::before` is what does it. A pill left to itself hands the row the
   baseline of the text inside it, which leaves the box hanging below the
   wordmark by however much padding and descender that size of type happens to
   carry — 7.25px for the stage badge and 7.03px for the version, so the two
   boxes did not even agree with each other. A zero-width first flex item at
   `flex-end` has no baseline of its own, so the pill synthesises one from that
   item's bottom margin edge, which is the pill's own content-box bottom. Both
   boxes then land 1px under the wordmark's baseline — their bottom border,
   straddling the line the letters sit on — whatever type is inside them
   (D-282). `gap: 0` because the pseudo-element is otherwise a second flex item
   for the inherited 6px gap to open up in front of. */
.bs-topbar__brand .bs-pill {
  /* The wordmark's lowercase, not its capitals: these sit between the baseline
     and the x-line, so the badges are as tall as the `s` that ends the name
     (D-289). 11.8px because that is what the `s` measures —
     `actualBoundingBoxAscent` for it at 22px is 11.76, a little over the 11.54
     of a flat `x` because a round letter overshoots the x-line, and it is the
     `s` the eye actually compares these boxes to.

     `height` rather than `min-height`: a pill that can grow past the x-line is
     a pill whose top edge is wherever its type happened to land, and
     `line-height: 1` is what keeps a label inside a 9.8px interior rather than
     pushing the box open.

     Then 120% of that x-height box, grown about its centre the way the mark
     was (D-290), so the badges stay centred on the band the `s` occupies
     instead of climbing off it: 11.8 → 14.16px, with the label and the padding
     scaled by the same 1.2.

     The label's inline box runs a shade past the interior at this size — its
     content area is the font's em box, not the line box `line-height: 1` sets,
     and the two are not the same thing. Measured, not derived: an arithmetic
     rule of `font-size × 1.2` was tried and does not predict it, and
     `scrollHeight` flips either side of 9px because it is integer-rounded
     against a fractional interior. It is harmless because nothing clips it —
     `BETA` and `v1.0` have no descenders, so the glyphs sit well inside the
     box. **Do not add `overflow` to this rule**: the first thing it would take
     is the bottom of the type (D-290). */
  position: relative;
  height: 14.16px;
  padding: 0 7.2px;
  font-size: 9.6px;
  line-height: 1;
  gap: 0;
  /* Half the 2.36px gained, holding the centre. Same reason and same mechanism
     as the mark: a relative offset lands after layout, so the baseline this
     box hands the row is untouched and only the paint moves. */
  top: 1.18px;
}

.bs-topbar__brand .bs-pill::before {
  content: "";
  width: 0;
  align-self: flex-end;
  /* Cancels the pill's own bottom border, so what comes to rest on the
     baseline is the border box rather than the content box inside it. Without
     this the boxes sit 1px lower than the mark, whose margin edge is its
     border edge — which at 22px was invisible and at 16px is a sixteenth of
     the box. */
  margin-bottom: -1px;
}

/* The stage badge takes the shared 8px. It still reads a shade larger than the
   version beside it, being caps with letter-spacing, which is the difference
   the two font sizes used to be carrying. */

.bs-topbar__who {
  min-width: 0;
  justify-self: end;
}

.bs-topbar .bs-who__name {
  max-width: 16ch;
}

/* Narrow, the nav is what the bar is for. The version goes; the stage badge
   stays, because "this is not deployed yet" is the one thing in the lockup
   that changes what somebody does next. */
@media (max-width: 860px) {
  .bs-topbar .bs-pill--version {
    display: none;
  }
}

@media (max-width: 720px) {
  .bs-topbar .bs-who__name {
    display: none;
  }
}

/* Narrower still, the bar stops being a row and becomes two.

   Wrapping was already what the frame did, but wrapping three flex items
   whose widths nobody chose put the tabs wherever the lockup left room — on
   some widths beside the brand, on others alone, on others split across two
   lines with Activity by itself. So the nav is given its own full-width line
   underneath rather than allowed to find one, and the line is a strip that
   scrolls sideways instead of wrapping: three tabs that fit simply fit, and a
   fourth tab, or a locale that sets `Projects` longer, scrolls rather than
   silently stacking and pushing the board another 40px down the screen.

   The strip bleeds through the frame's gutter to both screen edges. A tab
   half-cut by the viewport edge is the affordance that says there is more to
   the right; the same strip inset from the gutter reads as a complete row that
   happens to be missing a tab. `scroll-padding-inline` keeps a tab scrolled to
   by keyboard from landing under that bleed.

   The bar also stops reserving 64px. Two rows of chrome pinned to the top of a
   phone is most of the room a roster has to be read in, and 64px was a
   single-row measure. */
@media (max-width: 720px) {
  .bs-topbar {
    min-height: 0;
    padding-top: calc(8px + env(safe-area-inset-top, 0px));
    padding-bottom: 6px;
  }

  .bs-topbar .trc-console-frame {
    row-gap: 6px;
  }

  .bs-topbar .bs-nav {
    order: 1;
    flex: 0 0 100%;
    flex-wrap: nowrap;
    justify-content: flex-start;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    margin-left: calc(-1 * max(var(--trc-console-pad), env(safe-area-inset-left, 0px)));
    margin-right: calc(-1 * max(var(--trc-console-pad), env(safe-area-inset-right, 0px)));
    padding-inline: max(var(--trc-console-pad), env(safe-area-inset-left, 0px));
    scroll-padding-inline: max(var(--trc-console-pad), env(safe-area-inset-left, 0px));
  }

  /* The strip is swiped, so the scrollbar is a line across the tabs rather
     than a control anybody uses. */
  .bs-topbar .bs-nav::-webkit-scrollbar {
    display: none;
  }

  .bs-topbar .bs-nav__link {
    flex: none;
  }
}

/* The lockup drops to the register the system already has for it.

   Two rows of chrome on a phone is the price of keeping the tabs; three is
   not, and three is what 22px was buying. The wordmark, the mark and the two
   badges came to 384px of a 362px track once the signed-in block was beside
   them — over by 22px, so the identity wrapped to a line of its own and the
   bar took 145px of an 844px screen.

   17px is `.bs-brand--sm`, which is what the drawer headings already use, so
   this is the lockup at a size the system drew rather than a size chosen to
   make a fold work. The mark and badges follow it down by the same ratio they
   were built at: both are pinned to the wordmark's cap and x-heights (D-286,
   D-289, D-290), and leaving them at 22px's measurements would float them off
   the letters they are measured against. */
@media (max-width: 520px) {
  .bs-topbar .bs-brand {
    font-size: 17px;
  }

  .bs-topbar .bs-mark {
    width: 14.8px;
    height: 14.8px;
    border-width: 1.4px;
    border-radius: 3.2px;
    top: 1.2px;
  }

  .bs-topbar .bs-mark__letter {
    font-size: 10.2px;
  }

  .bs-topbar__brand .bs-pill {
    height: 10.9px;
    padding: 0 5.6px;
    font-size: 8.4px;
    top: 0.9px;
  }

  .bs-topbar__brand {
    gap: 8px;
  }

  /* Admin and Sign out become their glyphs, and the stage badge goes.

     This is what two rows actually cost. `APP_NAME` is `Boncom Releases`, so
     the lockup measures 223px of a 362px track and the signed-in block beside
     it 169px — 392px of row for 362px of screen, and the identity wrapped to a
     third line. Dropping the word from both buttons takes the block to about
     110px and the badge takes the lockup to about 188px, which fits with room
     rather than by a pixel.

     The badge is the one to argue about, because the rule above keeps it at
     860px on the grounds that "this is not deployed yet" changes what somebody
     does next. It still does — and a phone is the one screen where the three
     things it competes with are the shoot's name, who is signed in, and the
     way out. It is back the moment there is room for it.

     Both buttons keep their accessible names on the control itself, so what is
     hidden here is the ink and not the name. Scoped to the bar: `SignOutButton`
     is shared with the deactivated-account card, where the word is the whole
     point and there is a full-width button to put it in. */
  .bs-topbar .bs-topbar__label {
    display: none;
  }

  .bs-topbar .bs-pill--stage {
    display: none;
  }

  .bs-topbar__who .bs-btn {
    padding: 0 12px;
  }
}

/* The tabs sit at the right end of their track, against the signed-in block,
   rather than centred in the bar. Centred, the row read as three competing
   groups and the tabs drifted as the lockup's width changed — the badges going
   in and out at the 860px fold moved them without anything about the tabs
   changing. Ended, the brand anchors the left and the tabs stay put (D-284).
   `flex: 1 1 auto` still holds the track open, so this pushes them right
   rather than collapsing the gap. */
.bs-nav {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex: 1 1 auto;
  min-width: 0;
  gap: 4px;
  flex-wrap: wrap;
}

.bs-nav__link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border-radius: var(--bs-radius-pill);
  background: transparent;
  text-decoration: none;
  font-size: 13px;
  font-variation-settings: "wdth" 90, "wght" 500;
  color: var(--bs-muted);
  white-space: nowrap;
  transition: background-color var(--bs-fast) var(--bs-ease),
    color var(--bs-fast) var(--bs-ease);
}

.bs-nav__link svg {
  width: 17px;
  height: 17px;
  flex: none;
}

.bs-nav__link:hover {
  background: var(--bs-wash);
}

.bs-nav__link--current,
.bs-nav__link--current:hover {
  background: var(--bs-ink);
  color: var(--bs-cream);
}

.trc-nav-divider {
  width: 1px;
  height: 22px;
  background: var(--bs-hairline);
  flex: none;
}

/* --- signature pad ------------------------------------------------------- */

.bs-signature-wrap {
  /* Gutters around the pad so a finger that has just signed can scroll the
     page. The pad itself is `touch-action: none`; these edges are not. */
  padding: 4px 8px 16px;
  touch-action: pan-y;
}

.bs-signature {
  position: relative;
  border: 1px dashed var(--bs-hairline);
  border-radius: var(--bs-radius-card);
  background: var(--bs-surface);
  overflow: hidden;
  /* The pad owns every pointer gesture inside it. Without this, a finger
     stroke scrolls the page instead of drawing. */
  touch-action: none;
}

.bs-signature__canvas {
  display: block;
  width: 100%;
  height: 190px;
  cursor: crosshair;
  /* Stated on the canvas as well as the container. The container's value
     already covers gestures starting on this element, but the page no longer
     disables zoom globally, so this is now the only thing standing between a
     two-finger slip and a ruined signature. */
  touch-action: none;
}

.bs-signature__baseline {
  position: absolute;
  left: 24px;
  right: 24px;
  bottom: 28px;
  border-bottom: 1px solid var(--bs-hairline);
  pointer-events: none;
}

.bs-signature__bar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 4px 0;
  touch-action: pan-y;
}

.bs-signature__hint {
  margin: 0;
  flex: 1;
  min-width: 0;
  font-size: 13px;
  line-height: 1.35;
  color: var(--bs-quiet);
  transition: opacity var(--bs-fast) var(--bs-ease);
}

.bs-signature-wrap[data-empty="false"] .bs-signature__hint {
  opacity: 0;
}

@media (max-width: 720px) {
  .bs-signature-wrap {
    /* Narrower pad plus side gutters so the page can still be scrolled after
       signing — a full-width `touch-action: none` box eats every swipe. */
    padding: 8px 32px 32px;
  }

  .bs-signature__canvas {
    height: 160px;
  }

  .bs-signature__baseline {
    left: 16px;
    right: 16px;
    bottom: 22px;
  }

  .bs-signature__bar {
    padding: 16px 8px 8px;
    gap: 16px;
  }

  .bs-signature__hint {
    font-size: 11px;
    line-height: 1.4;
  }
}

/* --- camera viewfinder ---------------------------------------------------- */

.bs-viewfinder {
  position: relative;
  border-radius: var(--bs-radius-card);
  /* clip + contain, not hidden: an overflowing face-frame shadow used
     to expand iOS Safari's layout viewport on the photo step, so the
     page opened already pinched in — the same overflow D-225 stopped
     on step 1 and the filed screen. */
  overflow: clip;
  contain: paint;
  clip-path: inset(0);
  background: #14130f;
  /* Same portrait the filed page uses (D-228). Width shrinks when the
     leftover viewport is shorter than a full-column 3:4, so the shutter
     stays on screen with the finder. */
  aspect-ratio: 3 / 4;
  width: min(100%, calc((100dvh - 24rem) * 3 / 4));
  max-width: 100%;
  min-width: 0;
  max-height: calc(100dvh - 24rem);
  margin-inline: auto;
}

.bs-viewfinder__video,
.bs-viewfinder__still {
  display: block;
  width: 100%;
  height: 100%;
  max-width: 100%;
  object-fit: cover;
}

.bs-viewfinder__caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 12px;
  text-align: center;
  color: #fff;
  font-size: 14px;
  text-shadow: 0 1px 3px rgb(0 0 0 / 0.6);
  pointer-events: none;
}

.bs-viewfinder--idle {
  display: grid;
  place-items: center;
  background: var(--bs-sunken);
}

.bs-viewfinder--idle .bs-viewfinder__caption {
  position: static;
  color: var(--bs-muted);
  text-shadow: none;
  padding: 16px;
}

.bs-photo-actions {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: 6px;
  padding-bottom: 0;
  min-width: 0;
  max-width: 100%;
}

.bs-photo-actions .bs-btn {
  flex: 1 1 0;
  width: auto;
  max-width: 100%;
  min-width: 0;
  min-height: 36px;
  padding: 0 14px;
  font-size: 14px;
  white-space: normal;
}

.trc-photo-capture {
  gap: 8px;
}

.trc-photo-capture__hint {
  margin: 0;
  text-align: center;
}

.trc-talent-body .trc-photo-step h1,
.trc-talent-body .trc-photo-step h2 {
  font-size: 20px;
  line-height: 1.2;
}

.trc-photo-step .bs-flowfoot {
  padding-top: 8px;
  padding-bottom: max(8px, env(safe-area-inset-bottom, 0px));
  margin-top: 8px;
}

.trc-photo-step .bs-flowfoot .bs-btn {
  min-height: 40px;
  font-size: 14px;
  padding: 0 14px;
}

.trc-talent-body--photo {
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

.trc-talent-body--photo .trc-langpicker {
  margin-bottom: 8px;
}

/* The tap has to hit the file input itself. iOS ignores `capture` on a
   hidden input opened with input.click() and shows the photo library —
   the same sheet as Upload a photo. */
.bs-photo-file-btn {
  position: relative;
  overflow: hidden;
}

.bs-photo-file-btn__input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
  font-size: 0;
}

@media (max-width: 720px) {
  .bs-photo-actions {
    padding-bottom: 4px;
  }
}

/* --- PIN pad -------------------------------------------------------------- */

.bs-pinpad {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  max-width: 300px;
  margin: 0 auto;
}

.bs-pinpad__key {
  min-height: 64px;
  border-radius: var(--bs-radius-pill);
  border: 1px solid var(--bs-hairline);
  background: var(--bs-surface);
  color: var(--bs-ink);
  font-size: 22px;
  font-variation-settings: "wdth" 100, "wght" 550;
  cursor: pointer;
}

.bs-pinpad__key:active {
  background: var(--bs-wash);
}

.bs-pinpad__key--quiet {
  background: transparent;
  border-color: transparent;
  font-size: 15px;
}

.bs-pindots {
  display: flex;
  justify-content: center;
  gap: 14px;
  margin: 20px 0 24px;
}

.bs-pindots__dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 1px solid var(--bs-ink);
}

.bs-pindots__dot[data-filled="true"] {
  background: var(--bs-ink);
}

/* --- bottom sheet --------------------------------------------------------- */

.bs-sheet__scrim {
  position: fixed;
  inset: 0;
  background: rgb(26 26 26 / 0.55);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  z-index: 40;
  animation: bs-fade var(--bs-slow) var(--bs-ease);
}

.bs-sheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 41;
  max-height: 92dvh;
  display: flex;
  flex-direction: column;
  background: var(--bs-surface);
  border-radius: var(--bs-radius-card) var(--bs-radius-card) 0 0;
  box-shadow: 0 -8px 40px rgb(26 26 26 / 0.18);
  animation: bs-sheet-rise var(--bs-slow) var(--bs-ease);
}

.bs-sheet__grip {
  width: 40px;
  height: 4px;
  border-radius: var(--bs-radius-pill);
  background: var(--bs-hairline);
  margin: 10px auto 0;
  flex: none;
}

.bs-sheet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 20px;
  border-bottom: 1px solid var(--bs-hairline);
  flex: none;
}

.bs-sheet__body {
  padding: 20px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

/* The coordinator filed-release viewer. Every filed page is stacked so the
   signed paper and the selfie are both on the sheet (D-228). Talent reading
   still uses `.trc-source-page { width: 100% }` (D-096). */
.bs-sheet--filed {
  inset: 0;
  height: auto;
  max-height: none;
  border-radius: 0;
}

.bs-sheet--filed .bs-sheet__body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  overflow-x: hidden;
  overflow-y: auto;
  padding: 8px 16px;
}

.trc-filed-page {
  flex: 1 1 auto;
  min-height: 0;
  margin: 0;
  position: relative;
}

.trc-filed-page--drawer {
  flex: none;
  border: 1px solid var(--bs-hairline);
  background: var(--bs-sunken);
}

.trc-filed-page--drawer .trc-source-pages {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.trc-filed-page--drawer .trc-source-page {
  width: 100%;
  height: auto;
  display: block;
}

.trc-filed-page__caption {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.bs-sheet--filed .trc-filed-page {
  flex: 0 0 auto;
}

.bs-sheet--filed .trc-filed-page .trc-source-pages {
  position: static;
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
  height: auto;
}

.bs-sheet--filed .trc-filed-page .trc-source-page {
  position: static;
  width: 100%;
  height: auto;
  object-fit: contain;
  object-position: center;
}

.bs-sheet--filed .bs-sheet__body > :not(.trc-filed-page) {
  flex: none;
}

.bs-sheet--filed .bs-sheet__actions {
  flex: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 10px 16px 16px;
  border-top: 1px solid var(--bs-hairline);
}

@keyframes bs-sheet-rise {
  from {
    transform: translateY(100%);
  }
  to {
    transform: translateY(0);
  }
}

@keyframes bs-fade {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes bs-drawer-slide {
  from {
    transform: translateX(100%);
  }
  to {
    transform: translateX(0);
  }
}

@keyframes bs-drawer-slide-out {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(100%);
  }
}

@keyframes bs-fade-out {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .bs-sheet,
  .bs-sheet__scrim,
  .bs-drawer,
  .bs-drawer--out,
  .bs-drawer--out::backdrop {
    animation: none;
  }
}

/* --- side drawer (person + project details) ------------------------------
 *
 * A drawer rather than a centred modal. Reading or editing is a side task
 * beside the board you are already on — the list stays visible at the left
 * edge, which a 720px box in the middle of the screen took away. */

.bs-drawer {
  position: fixed;
  inset: 0 0 0 auto;
  width: min(460px, 100%);
  max-width: 100%;
  max-height: 100dvh;
  height: 100dvh;
  margin: 0;
  padding: 0;
  border: none;
  border-radius: 0;
  background: var(--bs-surface);
  box-shadow: -16px 0 48px rgb(26 26 26 / 0.18);
  color: inherit;
  overflow: hidden;
  animation: bs-drawer-slide var(--bs-slow) var(--bs-ease);
}

.bs-drawer::backdrop {
  background: rgb(26 26 26 / 0.55);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  animation: bs-fade var(--bs-slow) var(--bs-ease);
}

.bs-drawer--out {
  animation: bs-drawer-slide-out var(--bs-slow) var(--bs-ease) forwards;
}

.bs-drawer--out::backdrop {
  animation: bs-fade-out var(--bs-slow) var(--bs-ease) forwards;
}

.bs-drawer__form {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

.bs-drawer__head {
  flex: none;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 18px 20px 14px;
  border-bottom: 1px solid var(--bs-hairline);
}

.bs-drawer__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 18px 20px 24px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* Pinned, and translucent, so a long form scrolling beneath it stays visibly
   continuous rather than looking cut off. */
.bs-drawer__foot {
  flex: none;
  display: flex;
  gap: 8px;
  padding: 12px 20px;
  border-top: 1px solid var(--bs-hairline);
  background: rgb(255 255 255 / 0.92);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.bs-drawer__close {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: none;
  padding: 0;
  border: 1px solid var(--bs-hairline);
  border-radius: 50%;
  background: transparent;
  color: var(--bs-muted);
  cursor: pointer;
  transition: background-color var(--bs-fast) var(--bs-ease);
}

.bs-drawer__close:hover {
  background: var(--bs-wash);
}

.trc-person-facts {
  display: grid;
  gap: 12px;
  margin: 0;
}

.trc-person-facts__row {
  display: grid;
  gap: 2px;
}

.trc-person-facts dd {
  margin: 0;
  overflow-wrap: anywhere;
}

/* A phone camera needs quiet space and contrast around the code, so it sits on
   white regardless of the surrounding surface. */
.trc-invite-qr {
  width: 220px;
  height: 220px;
  display: block;
  padding: 8px;
  background: #fff;
  border: 1px solid var(--bs-hairline);
  image-rendering: pixelated;
}

/* --- centered modal (import, add project) --------------------------------- */

.bs-modal {
  width: min(720px, calc(100vw - 32px));
  max-height: 92dvh;
  overflow: auto;
  padding: 0;
  border: none;
  border-radius: var(--bs-radius-card);
  background: var(--bs-surface);
  box-shadow: 0 16px 48px rgb(26 26 26 / 0.22);
  color: inherit;
}

.bs-modal::backdrop {
  background: rgb(26 26 26 / 0.55);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}

.bs-modal > form {
  padding: 20px 24px 24px;
}

.bs-modal--project {
  width: min(840px, calc(100vw - 32px));
}

/*
 * The compose queue is the one modal whose body is a list of unknown length,
 * so it cannot let the whole dialog scroll: the count and Stop here would go
 * off the bottom on a twenty-person queue, and the way out of the modal has to
 * stay on screen. The dialog holds the height, the list takes what is left.
 */
.bs-modal--queue {
  display: flex;
  overflow: hidden;
  width: min(680px, calc(100vw - 32px));
}

.trc-send-queue {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 20px;
  min-width: 0;
  /* `.bs-modal` pads a `> form`, and this body is not one. */
  padding: 28px 32px;
}

.trc-send-queue__list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  /* `min-height: 0` or the list refuses to shrink and the footer is pushed out. */
  min-height: 0;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  list-style: none;
}

.trc-send-queue__person {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 10px 2px;
  border-bottom: 1px solid var(--bs-hairline-soft);
}

.trc-send-queue__person:last-child {
  border-bottom: none;
}

/* The name wraps rather than shoving Compose off the right edge. */
.trc-send-queue__name {
  min-width: 0;
  overflow-wrap: anywhere;
}

/*
 * Compose / Opening… / Composed are three different widths on the same button,
 * so the column is held still rather than reflowing under the pointer as each
 * message goes out.
 */
.trc-send-queue__person .bs-btn {
  flex: none;
  min-width: 124px;
  white-space: nowrap;
}

.trc-send-queue__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding-top: 20px;
  border-top: 1px solid var(--bs-hairline);
}

.bs-modal .trc-photo-box {
  width: 160px;
  max-width: 100%;
}

.trc-project-create {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 288px;
  gap: 20px;
  align-items: start;
}

.trc-project-dates {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 6px;
}

.trc-project-dates li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  font-size: 13px;
}

@media (max-width: 720px) {
  .trc-project-create {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* --- progress ------------------------------------------------------------- */

.bs-progress {
  height: 8px;
  border-radius: var(--bs-radius-pill);
  background: rgb(26 26 26 / 0.07);
  overflow: hidden;
}

.bs-progress__fill {
  height: 100%;
  border-radius: var(--bs-radius-pill);
  background: var(--bs-maroon);
  transition: width 260ms var(--bs-ease);
}

/* A project's roster in one bar: signed, then pending, over the track that is
   everyone with no link yet. Stacked rather than three bars, because the
   question is what share of the whole is done. */
.bs-proportion {
  display: flex;
  height: 8px;
  border-radius: var(--bs-radius-pill);
  background: rgb(26 26 26 / 0.07);
  overflow: hidden;
}

.bs-proportion__part {
  height: 100%;
  transition: width 260ms var(--bs-ease);
}

.bs-proportion__part--signed {
  background: var(--bs-teal);
}

.bs-proportion__part--pending {
  background: var(--bs-mustard);
}

.trc-count-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  font-size: 13px;
  color: var(--bs-muted);
  font-variant-numeric: tabular-nums;
}

.trc-count-row > span {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.trc-roster-progress {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 12px;
}

.trc-roster-progress__label {
  font-size: 13px;
  color: var(--bs-muted);
}

/* --- step indicator (iPad walk-up flow) ---------------------------------- */

.bs-steps {
  display: flex;
  align-items: center;
  gap: 8px;
}

.bs-steps__step {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--bs-muted);
}

.bs-steps__dot {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  border: 1px solid var(--bs-hairline);
  font-size: 12px;
  font-variation-settings: "wdth" 90, "wght" 700;
}

.bs-steps__step[data-state="current"] {
  color: var(--bs-ink);
}

.bs-steps__step[data-state="current"] .bs-steps__dot {
  background: var(--bs-ink);
  border-color: var(--bs-ink);
  color: var(--bs-cream);
}

.bs-steps__step[data-state="done"] .bs-steps__dot {
  background: var(--bs-maroon);
  border-color: var(--bs-maroon);
  color: #fff;
}

.bs-steps__rule {
  flex: 1;
  height: 1px;
  background: var(--bs-hairline);
}

/* --- talent flow chrome --------------------------------------------------
 *
 * The performer's phone. A sticky header naming the shoot and the step, and a
 * sticky footer holding the one button that moves them on.
 *
 * The four-segment rule fills as steps *complete*. It is not a countdown and
 * carries no duration — invariant 16 forbids anything that adds time pressure
 * during consent, and a rule that says "you have finished two of these" is the
 * opposite of a clock. There is no timer anywhere in this flow. */

.bs-flowbar {
  position: sticky;
  top: 0;
  z-index: 20;
  background: rgb(244 242 236 / 0.92);
  backdrop-filter: blur(12px) saturate(160%);
  -webkit-backdrop-filter: blur(12px) saturate(160%);
  border-bottom: 1px solid var(--bs-hairline);
  padding: 12px 20px 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Same lockup as the console top bar: wordmark, dot and the caps step count
   share the wordmark's baseline rather than a centre. */
.bs-flowbar__top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.bs-flowbar__project {
  font-size: 17px;
  line-height: 1.25;
  letter-spacing: -0.01em;
  font-variation-settings: "wdth" 90, "wght" 700;
}

.bs-flowrule {
  display: flex;
  gap: 4px;
}

.bs-flowrule__seg {
  flex: 1;
  height: 3px;
  border-radius: var(--bs-radius-pill);
  background: rgb(26 26 26 / 0.11);
  transition: background-color var(--bs-slow) var(--bs-ease);
}

.bs-flowrule__seg--on {
  background: var(--bs-maroon);
}

/* Pinned to the bottom of the card, which on these screens means the bottom of
   the viewport. The negative margins take it flush to the card's edges and it
   picks the card's own bottom corners back up, so it reads as part of the sheet
   rather than as a bar floating over it. */
.bs-flowfoot {
  position: sticky;
  bottom: 0;
  z-index: 19;
  display: flex;
  gap: 8px;
  padding: 12px 20px;
  margin: 8px -20px -20px;
  border-top: 1px solid var(--bs-hairline);
  border-radius: 0 0 var(--bs-radius-card) var(--bs-radius-card);
  background: rgb(255 255 255 / 0.92);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}

.bs-flowfoot .bs-btn {
  flex: 1;
  min-width: 0;
  min-height: 52px;
  font-size: 17px;
  white-space: normal;
  text-align: center;
}

/* The step's own Back button never outweighs the one that moves them on. */
.bs-flowfoot .bs-btn:not(.bs-btn--primary) {
  flex: 0 1 auto;
  padding: 0 20px;
}

/* Same gutter the column uses, so the footer stays flush to the card on a
   320px phone and a 390px phone alike. Hard-coded -20px was how it slipped
   past the card when the padding shrank. */
.trc-talent-body .bs-card {
  max-width: 100%;
  min-width: 0;
  padding: var(--trc-talent-gutter);
  box-sizing: border-box;
}

/* Instruction box on the talent flow: the read-step note and the minor
   photo-step notice. Mustard, not red or teal — instruction, not an
   error and not a confirmation. Bold so it is not missed. */
.trc-talent-notice {
  margin: 0;
  padding: 15px 18px;
  border-radius: var(--bs-radius-panel);
  border: 1px solid rgb(229 193 0 / 0.5);
  background: rgb(229 193 0 / 0.22);
  color: var(--bs-mustard-ink);
  font-size: 18px;
  line-height: 1.5;
  font-variation-settings: "wdth" 100, "wght" 700;
}

.trc-talent-body .bs-flowfoot {
  padding-inline: var(--trc-talent-gutter);
  padding-bottom: max(12px, env(safe-area-inset-bottom, 0px));
  margin: 8px calc(-1 * var(--trc-talent-gutter)) calc(-1 * var(--trc-talent-gutter));
}

.trc-talent-body .bs-flowbar {
  margin-inline: calc(-1 * var(--trc-talent-gutter));
  padding-inline: var(--trc-talent-gutter);
  padding-top: max(12px, env(safe-area-inset-top, 0px));
}

.trc-talent-body .bs-field input {
  width: 100%;
  max-width: 100%;
  min-width: 0;
}

.trc-talent-body .bs-field select,
.trc-talent-body .bs-field textarea {
  width: 100%;
  max-width: 100%;
  min-width: 0;
}

.trc-talent-body .trc-row-between,
.trc-talent-body .bs-sunken-row {
  min-width: 0;
  flex-wrap: wrap;
}

.trc-talent-body .trc-row-between > *,
.trc-talent-body .bs-sunken-row > * {
  min-width: 0;
  overflow-wrap: anywhere;
}

/* Long names, emails, document titles, and non-English confirmation words
   (autorización, autorisation) must wrap inside the column. A single
   unbreakable 32px word is how iOS Safari widens the layout viewport and
   leaves the performer pinching below 100% after they file. */
.trc-talent-body h1,
.trc-talent-body h2,
.trc-talent-body p,
.trc-talent-body td,
.trc-talent-body th {
  max-width: 100%;
  overflow-wrap: anywhere;
}

.trc-talent-body h1 {
  font-size: clamp(26px, 8vw, 40px);
}

/* The filed confirmation. The one moment in the product that gets to look
   like a result rather than a form. */
.bs-filed-check {
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: var(--bs-teal);
  color: #fff;
  display: grid;
  place-items: center;
  flex: none;
}

.bs-filed-check svg {
  width: 30px;
  height: 30px;
}

.trc-talent-body .bs-filed-heading {
  font-size: clamp(22px, 7vw, 32px);
  line-height: 1.15;
  letter-spacing: -0.02em;
  font-variation-settings: "wdth" 95, "wght" 800;
  max-width: 100%;
  overflow-wrap: anywhere;
}

/* The line that closes counsel's document. Caps, because it is metadata about
   the document rather than part of it — nothing here may look like a clause. */
.trc-document__endline {
  margin: 4px 0 0;
  padding-top: 12px;
  border-top: 1px solid var(--bs-hairline-soft);
  text-align: center;
  color: var(--bs-quiet);
}

/* --- review summary ------------------------------------------------------
 *
 * The two-second version of what is about to be filed, above the assembled
 * pages rather than instead of them. The pages are the authority; this is so a
 * wrong name or a wrong amount is caught without reading five fields off a
 * raster.
 *
 * 3:4 on the photo, matching the aspect the camera captured. Any other ratio
 * would crop the frame they were asked to fill. */

.trc-review-summary {
  display: grid;
  grid-template-columns: 108px minmax(0, 1fr);
  gap: 16px;
  align-items: start;
  padding: 14px;
  border-radius: var(--bs-radius-panel);
  background: var(--bs-sunken);
}

.trc-review-photo {
  aspect-ratio: 3 / 4;
  border-radius: var(--bs-radius-input-sm);
  overflow: hidden;
  background: #e8e5e0;
}

.trc-review-photo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.trc-review-summary .bs-table {
  table-layout: fixed;
  width: 100%;
  font-size: 13px;
}

.trc-review-summary .bs-table th,
.trc-review-summary .bs-table td {
  padding: 5px 0;
  border-bottom-color: var(--bs-hairline-soft);
}

.trc-review-summary .bs-table td {
  padding-left: 10px;
  font-variation-settings: "wdth" 100, "wght" 600;
}

.trc-review-signed {
  display: flex;
  align-items: center;
  gap: 10px;
}

.trc-review-signed .bs-accent {
  font-size: 28px;
  line-height: 1;
  color: var(--bs-maroon);
}

@media (max-width: 420px) {
  .trc-review-summary {
    grid-template-columns: 84px minmax(0, 1fr);
    gap: 12px;
  }
}

/* --- legal body ----------------------------------------------------------- */

.bs-legal {
  max-height: 320px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 16px;
  border: 1px solid var(--bs-hairline);
  border-radius: var(--bs-radius-input);
  background: rgb(26 26 26 / 0.02);
  font-size: 14px;
  line-height: 1.6;
  white-space: pre-wrap;
}

/* --- talent board: cards -------------------------------------------------
 *
 * Card width is 132px; `auto-fill` then decides the column count, so the
 * same board is eight columns on a 27" display and two on a laptop without
 * a breakpoint being written for either. Browser zoom is the density
 * control. */

.trc-talent-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
  gap: 8px;
}

.trc-talent-card {
  border: 1px solid var(--bs-hairline);
  background: var(--bs-surface);
  border-radius: var(--bs-radius-panel);
  box-shadow: var(--bs-shadow-card);
  display: flex;
  flex-direction: column;
  cursor: default;
  overflow: hidden;
  /* Stacking order the close-up below can raise. Grid items otherwise paint in
     source order, which would slide the grown card under the one after it. */
  position: relative;
  transition: box-shadow var(--bs-slow) var(--bs-ease),
    transform var(--bs-slow) var(--bs-ease),
    border-color var(--bs-fast) var(--bs-ease);
}

/* The face under the pointer comes forward far enough to be read at a glance.
   A transform, never a wider grid track: `auto-fill` would recount the columns
   and reflow the whole board, moving the card the coordinator was reaching for.
   1.35 overhangs by about 23px a side, of which the 8px gap takes eight, so
   the close-up covers roughly 15px of each neighbour and 32px of the rows
   above and below on purpose — which is what the raised stacking order above
   is for. Growing the card only ever moves its edge away from the pointer, so
   there is no boundary for the hover to oscillate across. */
.trc-talent-card:hover {
  box-shadow: var(--bs-shadow-lift);
  transform: translateY(-2px) scale(1.35);
  z-index: 2;
}

/* A rubber-band selection asks each card for its `getBoundingClientRect`, and
   that rect includes the scale. A card growing into the band as the pointer
   crossed it would be selected by a band that never reached it, so the
   close-up stands down for as long as the band is out. */
.trc-talent-surface--marqueeing .trc-talent-card:hover {
  transform: none;
  z-index: auto;
}

/* No close-up for a finger.
 *
 * `:hover` on a touch screen is not a hover — there is no pointer resting
 * anywhere, so the browser fakes one on tap and leaves it applied until
 * something else is tapped. The effect is a card that stays blown up to 135%
 * over its neighbours after being touched, covering the two people either
 * side of it, and only releasing when the coordinator taps somewhere else to
 * get rid of it.
 *
 * The close-up is for reading a face under a pointer that is merely passing
 * over. A finger is not passing over; it has arrived, and the card it has
 * arrived at is the one already being looked at. So the whole effect stands
 * down — the shadow and the raised stacking order with it, since a lifted
 * shadow with nothing lifting is just a card that looks wrong. */
@media (pointer: coarse) {
  .trc-talent-card:hover {
    box-shadow: var(--bs-shadow-card);
    transform: none;
    z-index: auto;
  }
}

.trc-talent-card--selected {
  border-color: var(--bs-ink);
  box-shadow: inset 0 0 0 1px var(--bs-ink), var(--bs-shadow-card);
}

/* The card is focusable so arrow keys can walk the grid, which makes a visible
   focus ring mandatory rather than decorative: without it the keyboard path
   moves an invisible cursor. Maroon, not the selection charcoal, because focus
   and selection are different things and a coordinator arrowing across a
   selected card must still be able to tell which one is under the cursor. */
.trc-talent-card:focus-visible {
  outline: 2px solid var(--bs-maroon);
  outline-offset: 2px;
}

/* An expired link is the one status that colours the whole card. It is the
   only one where the coordinator has to do something before the shoot. */
.trc-talent-card--expired {
  border-color: rgb(255 26 34 / 0.45);
}

@media (prefers-reduced-motion: reduce) {
  .trc-talent-card,
  .trc-talent-card:hover {
    transform: none;
    transition: none;
  }
}

.trc-talent-board {
  display: flex;
  flex-direction: column;
  gap: 16px;
  flex: 1;
  min-height: 0;
}

.trc-talent-surface {
  position: relative;
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-bottom: 32px;
}

/* Safari treats a rubber-band across portraits as an image selection and
   paints a blue overlay on every photo the pointer crosses. Chrome already
   honours `user-select: none` on the surface. WebKit needs the prefix on
   the images themselves, and `pointer-events: none` so the drag never
   starts on the <img>. Clicks still land on the card. */
.trc-talent-surface img,
.trc-talent-surface svg {
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
  pointer-events: none;
}

.trc-talent-marquee {
  position: fixed;
  z-index: 40;
  pointer-events: none;
  border: 1px solid rgb(26 26 26 / 0.55);
  background: rgb(26 26 26 / 0.08);
}

.trc-talent-card__photo {
  position: relative;
}

/* The card's checkbox. Drawn only for a finger — a mouse selects a card by
   clicking it, and a control offering to do what the whole card already does
   is clutter. `display: none` rather than hidden, so it is out of the tab
   order and the accessibility tree too and there is nothing to find. It sits
   opposite the tick, in the corner the name does not reach. */
.trc-talent-card__photo .trc-talent-select {
  display: none;
}

@media (pointer: coarse) {
  .trc-talent-card__photo .trc-talent-select {
    position: absolute;
    /* Pinned to the corner rather than inset by it: the target is 44px and
       the box it sits in is the photo, so an inset plus a negative margin to
       win it back only hung the control off the top edge of the card. */
    top: 0;
    right: 0;
    z-index: 2;
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
  }

  /* Typed, and not only for precision: `.trc-talent-select input` is the list
     row's own rule, and a selector ending in those two words is indexed by the
     same search that finds it. Two rules one grep cannot tell apart is how the
     list's 24px box came to be read as this one's 26px. */
  .trc-talent-card__photo .trc-talent-select input[type="checkbox"] {
    /* A checkbox a thumb can hit without magnifying the tick beside it. */
    width: 26px;
    height: 26px;
    box-shadow: 0 0 0 2px var(--bs-surface);
    border-radius: 6px;
  }

  /* The tick goes where the checkbox is. It exists to show that a card is in
     the selection, which is precisely what a checked box already says, and two
     marks in two corners of the same photo saying one thing reads as two
     different states. The tick stays for a mouse, where there is no box. */
  .trc-talent-card__photo .trc-talent-check {
    display: none;
  }
}

/* The tick that says a card is in the selection. Sits in the photo corner
   rather than over the name, which is the one thing on the card that must
   never be covered. */
.trc-talent-check {
  position: absolute;
  top: 6px;
  left: 6px;
  z-index: 1;
  width: 20px;
  height: 20px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--bs-ink);
  color: var(--bs-cream);
  box-shadow: 0 0 0 2px var(--bs-surface);
  pointer-events: none;
}

.trc-talent-check svg {
  width: 13px;
  height: 13px;
}

.trc-talent-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.trc-talent-row--selected td {
  background: var(--bs-wash);
}

.trc-photo-box {
  aspect-ratio: 1 / 1;
  width: 100%;
  border-bottom: 1px solid var(--bs-hairline-soft);
  overflow: hidden;
}

/* A photographic grey, not a charcoal wash: this box stands in for a
   photograph, and everything else on the card is ink on paper. */
.trc-photo-blank {
  width: 100%;
  height: 100%;
  background: #e8e5e0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
}

.trc-photo-blank .bs-small {
  font-size: 11px;
  color: var(--bs-quiet);
}

.bs-photo {
  display: block;
  width: 100%;
  height: 100%;
}

.bs-photo img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  -webkit-user-drag: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

.bs-photo--round {
  width: 40px;
  height: 40px;
  flex: none;
}

.trc-talent-card__identity {
  padding: clamp(6px, calc(132px * 0.068), 10px)
    clamp(7px, calc(132px * 0.083), 11px)
    clamp(6px, calc(132px * 0.076), 10px);
  display: flex;
  flex-direction: column;
  gap: 3px;
  flex: 1;
  min-width: 0;
}

button.trc-talent-card__identity {
  appearance: none;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  width: 100%;
  cursor: pointer;
}

/* Dot, then name. In that order, because the dot is what the eye is scanning
   for and the name is what confirms it.

   `baseline`, so the dot sits on the name's own line rather than at the top of
   its line box, where it read 2.68px high. Not `center`: a name may clamp to
   two lines and centring would drift the dot into the gap between them.
   Baseline takes the first one. A 9px dot resting on the baseline of 12px type
   also lands within 0.2px of that type's optical centre, so this is the
   uncommon case where the consistent answer and the pretty one agree. */
.trc-talent-card__line {
  display: flex;
  align-items: baseline;
  gap: 7px;
  min-width: 0;
}

.trc-talent-card__name {
  flex: 1;
  min-width: 0;
  font-size: 12px;
  line-height: 1.2;
  font-variation-settings: "wdth" 100, "wght" 600;
  overflow: hidden;
  overflow-wrap: anywhere;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}

.trc-talent-card__flag {
  font-size: 10px;
  line-height: 1.3;
  color: var(--bs-mustard-ink, #3a2f00);
  overflow: hidden;
  overflow-wrap: anywhere;
}

/* Three buttons, or four where the person has a mobile and Text joins them.
   Counted by the grid rather than stated, so adding or removing one is a
   change in the markup alone. */
.trc-talent-card__footer {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  border-top: 1px solid var(--bs-hairline-soft);
}

.trc-talent-card__footer .bs-btn {
  border: none;
  border-radius: 0;
  background: transparent;
  min-height: 30px;
  width: 100%;
  padding: 0;
}

.trc-talent-card__footer .bs-btn + .bs-btn {
  border-left: 1px solid var(--bs-hairline-soft);
}

.trc-talent-card__footer .bs-btn svg {
  width: 15px;
  height: 15px;
}

/* --- talent board: selection bar ----------------------------------------
 *
 * Floats over the board rather than displacing the toolbar. Selecting people
 * must not move the cards you are selecting. */

.trc-selectbar {
  position: sticky;
  bottom: 16px;
  z-index: 30;
  align-self: center;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 8px 10px 8px 16px;
  border-radius: var(--bs-radius-pill);
  border: 1px solid var(--bs-hairline);
  background: rgb(255 255 255 / 0.92);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: var(--bs-shadow-lift);
}

.trc-selectbar__count {
  font-size: 14px;
  font-variation-settings: "wdth" 100, "wght" 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.trc-selectbar .bs-btn {
  min-height: 34px;
  padding: 0 14px;
  font-size: 13px;
}

.trc-selectbar__clear {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  flex: none;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: transparent;
  color: var(--bs-muted);
  cursor: pointer;
  transition: background-color var(--bs-fast) var(--bs-ease);
}

.trc-selectbar__clear:hover {
  background: var(--bs-wash);
}

.trc-selectbar__clear svg {
  width: 16px;
  height: 16px;
}

/* --- talent board: toolbar ----------------------------------------------
 *
 * Filters share `--trc-toolbar-columns` so they stay even rather than
 * sizing themselves. As five flex items they were 222 / 269 / 204 / 156 /
 * 118px at 1440 and stopped 263px short of a full-width row: five different
 * widths and a ragged tail. Search and Project take the wider tracks
 * because they hold the longest strings; the three short filters share the
 * rest evenly. The action row holds the roster counts on the left, Layout /
 * Import / Add person on the right, and the proportion bar on a full-width
 * row beneath both. */

.trc-talent-toolbar {
  /* Project takes the widest track: it is the only filter whose value is a
     name somebody typed, and each filter now pays 28–47px of its own width to
     the name in front of its value. Search gives up the most, because its
     value is a placeholder of known length. Measured at the 1320px frame:
     the longest fixture production needs 223px of the 239px left to it, and
     `All statuses (21)` 114px of 132px — enough that a three-digit roster
     count still fits. */
  --trc-toolbar-columns:
    minmax(0, 0.85fr) minmax(0, 1.55fr) repeat(3, minmax(0, 1.05fr));
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  width: 100%;
}

/* One row of 40px pills. The names moved inside the pills (D-280), so the
 * label row the subgrid existed to align is gone with it. */
.trc-talent-toolbar__tools {
  display: grid;
  grid-template-columns: var(--trc-toolbar-columns);
  grid-auto-rows: 40px;
  column-gap: 10px;
  row-gap: 8px;
  width: 100%;
}

.trc-talent-toolbar__row--actions {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "counts actions"
    "bar bar";
  align-items: center;
  column-gap: 16px;
  row-gap: 6px;
  width: 100%;
}

.trc-talent-toolbar__row--actions .trc-roster-progress {
  display: contents;
}

.trc-talent-toolbar__row--actions .trc-count-row {
  grid-area: counts;
  min-width: 0;
  min-height: 40px;
  align-items: center;
}

.trc-talent-toolbar__row--actions .bs-proportion {
  grid-area: bar;
}

.trc-talent-toolbar__actions {
  grid-area: actions;
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.trc-talent-toolbar__tools select,
.trc-talent-toolbar__tools input {
  max-width: 100%;
}

.trc-talent-toolbar__tools .bs-field {
  min-width: 0;
}

/* A filter that names itself inside its own pill: `Project: September Book`.
 * Not scoped to the Talent toolbar — Activity filters by project too, and a
 * console where one board prefixes its filter and the other labels it from
 * above is two conventions.
 *
 * The names sat on a row above the controls (D-250, D-251); inside the border
 * they are a row shorter and cannot come apart from the control they name.
 *
 * The border, the fill and the chevron move off the `select` and onto the
 * field, and the select becomes a transparent box inside it. That indirection
 * is the mechanism, not decoration: a native select's closed text is exactly
 * the selected `option`'s text, and nothing can precede it, so a prefix has
 * to sit beside the control rather than in it. Rebuilding the dropdown in
 * script would allow it and would cost the iPad its own picker — which is the
 * control a coordinator actually uses on set. */
.trc-filter {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0;
  height: 40px;
  min-width: 0;
  padding-inline-start: 14px;
  border: 1px solid var(--bs-hairline);
  border-radius: var(--bs-radius-pill);
  background-color: var(--bs-surface);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M2.5 4.25L6 7.75L9.5 4.25' stroke='%231a1a1a' stroke-opacity='0.55' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 14px center;
  background-size: 12px 12px;
}

/* The ring belongs to the pill, because the pill is what looks like the
   control. On the select alone it drew a rectangle inside a rounded box. */
.trc-filter:focus-within {
  outline: 2px solid var(--bs-maroon);
  outline-offset: 1px;
}

/* The field name, in the 13px secondary register against the 14px value, so
   the pill reads as one phrase with the answer emphasised. */
.trc-filter__name {
  flex: none;
  font-size: 13px;
  line-height: 40px;
  color: var(--bs-quiet);
  white-space: nowrap;
  font-variation-settings: "wdth" 100, "wght" 550;
}

/* A production nobody could have predicted the length of still has to fail
   gracefully at the narrow folds, so the value ellipsizes rather than being
   cut mid-letter. The right padding is the chevron's. */
.trc-filter select {
  flex: 1 1 auto;
  min-width: 0;
  appearance: none;
  height: 40px;
  min-height: 40px;
  line-height: 40px;
  padding: 0 34px 0 6px;
  border: 0;
  border-radius: 0;
  background: none;
  font-size: 14px;
  text-overflow: ellipsis;
}

.trc-filter select:focus {
  outline: none;
}

/*
 * Native search and select paint their text on different baselines even
 * when the boxes are both 40px. Stripping appearance puts every value on
 * the same line; the chevron is redrawn so the dropdowns do not go naked.
 */
.trc-talent-toolbar__tools .bs-field--pill input,
.trc-talent-toolbar__tools .bs-field--pill select {
  appearance: none;
  height: 40px;
  min-height: 40px;
  line-height: 40px;
}

.trc-talent-toolbar__tools .bs-search {
  height: 40px;
  align-items: center;
}

.trc-talent-toolbar__tools .bs-search input {
  padding-left: 38px;
}

.trc-talent-toolbar__tools input[type="search"]::-webkit-search-decoration,
.trc-talent-toolbar__tools input[type="search"]::-webkit-search-cancel-button,
.trc-talent-toolbar__tools input[type="search"]::-webkit-search-results-button,
.trc-talent-toolbar__tools input[type="search"]::-webkit-search-results-decoration {
  appearance: none;
}

/* Names the layout toggle beside it. Nothing on this row may shrink it. */
.trc-talent-toolbar__actions .bs-caps {
  flex: none;
  white-space: nowrap;
}

/*
 * Fewer columns, never a ragged reflow: the filters drop from five tracks to
 * three to two to one, each still naming itself. At one column the buttons may
 * wrap among themselves rather than run off the edge. The rows the folds
 * create are spaced by the grid's own `row-gap` now; they used to need a
 * `margin-top` on every field past the first row, because the gap had been
 * tuned to 3px for the label sitting above each pill.
 */
@media (max-width: 1180px) {
  .trc-talent-toolbar {
    --trc-toolbar-columns: repeat(3, minmax(0, 1fr));
  }
}

@media (max-width: 820px) {
  .trc-talent-toolbar {
    --trc-toolbar-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 520px) {
  .trc-talent-toolbar {
    --trc-toolbar-columns: minmax(0, 1fr);
  }

  .trc-talent-toolbar__actions {
    flex-wrap: wrap;
    justify-content: flex-end;
  }
}

/* --- talent board: list view --------------------------------------------
 *
 * A minimum width and a horizontal scroll rather than columns that collapse.
 * Status clipped to "Sent, not…" is worse than a scrollbar. Day lives in
 * Details / Edit, not in this table. */

.trc-talent-table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.trc-talent-table {
  min-width: 1100px;
  font-size: 14px;
}

.trc-talent-table th,
.trc-talent-table td {
  padding: 11px 14px;
}

.trc-talent-table.bs-table--data th {
  font-size: 11px;
}

/*
 * List actions are a six-column grid so every row keeps the same button in
 * the same place. Send link / reminder / send-again share one column and
 * swap with status. Text sits immediately after it, being the alternative to
 * it, and like Download PDF it occupies an empty slot when the person has no
 * mobile — so Edit / Remove are in the same place on every row whether or not
 * the row above had a number.
 */
.trc-talent-table__actions {
  display: grid;
  grid-template-columns:
    5.5rem
    8.8rem
    4.1rem
    7.6rem
    4.1rem
    5.3rem;
  gap: 4px;
  align-items: center;
  justify-content: start;
}

.trc-talent-table__slot {
  display: flex;
  min-width: 0;
}

.trc-talent-table__slot .bs-btn {
  width: 100%;
  min-height: 32px;
  padding: 0 10px;
  font-size: 13px;
  white-space: nowrap;
}

/* Each list button carries a glyph and a word, and at this width the word is
   the one that shows. The glyph is the phone's shape (see the 820px fold) and
   is hidden rather than not rendered, so one button serves both — two buttons
   for one action would be two entries in the accessibility tree and only ever
   one of them right to read.

   Hidden here rather than left visible because these five tracks are fixed rem
   widths measured against the labels alone. They do in fact still fit with a
   17px mark and its 8px gap added — measured, not assumed — but the row grew
   4px for it, and the desk is not what this change was for. */
.trc-talent-table__slot .bs-btn > svg {
  display: none;
}

/* --- activity feed --------------------------------------------------------
 *
 * The Activity tab (D-258). Read-only, so beyond `bs-table--data` it needs
 * only three things: a When column that never wraps, a verb pill that sits
 * beside its detail rather than above it, and a horizontal scroll so the
 * narrowest phone does not squeeze four columns into two characters each. */

.trc-activity-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

/* Sized to the longest client — title string, not stretched across the page:
   one control alone on a row looks like a mistake at full width. */
.trc-activity-toolbar .bs-field {
  flex: 1 1 280px;
  min-width: 0;
  max-width: 420px;
}

.trc-activity-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.trc-activity-table {
  min-width: 720px;
}

/* This is the column the eye runs down, and a timestamp that wraps mid-date
   cannot be scanned at all. `width: 1%` lets the rest of the row take the
   slack instead. */
.trc-activity-table__when {
  white-space: nowrap;
  width: 1%;
}

/* Baseline, for the same reason as the status dot (D-256): the pill and the
   clause after it are one line of type, and centring a padded box against
   plain text lifts the words inside it off the line they share. */
.trc-activity-what {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
}

.trc-activity-detail {
  font-size: 13px;
  color: var(--bs-quiet);
  overflow-wrap: anywhere;
}

.trc-talent-table td {
  vertical-align: middle;
}

.trc-talent-table__select {
  width: 36px;
}

.trc-talent-table__comment {
  min-width: 148px;
  max-width: 220px;
}

.trc-missing-note {
  font-size: 12px;
  line-height: 1.3;
  color: var(--bs-red);
  white-space: normal;
}

.trc-signed-diff-note {
  font-size: 12px;
  line-height: 1.3;
  color: var(--bs-mustard-ink, #3a2f00);
  white-space: normal;
}

.trc-signed-diff-banner {
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
  padding: 12px 16px;
  border-radius: var(--bs-radius-card);
}

.trc-signed-diff-banner__lead {
  margin: 0;
}

.trc-signed-diffs {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.trc-signed-diffs li {
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: 0.6em;
  align-items: baseline;
}

.trc-signed-diffs__values {
  min-width: 0;
}

.trc-signed-diff-banner .bs-btn {
  align-self: flex-start;
}

.trc-talent-select {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  font-size: 13px;
  line-height: 1.2;
  white-space: nowrap;
  user-select: none;
}

.trc-talent-select input {
  width: 16px;
  height: 16px;
  margin: 0;
  cursor: pointer;
  flex: none;
}

/* --- person drawer fields ------------------------------------------------- */

.trc-fields-two {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

@media (max-width: 520px) {
  .trc-fields-two {
    grid-template-columns: minmax(0, 1fr);
  }
}

.trc-person-photo {
  width: 120px;
  max-width: 100%;
  border: 1px solid var(--bs-hairline-soft);
  border-radius: var(--bs-radius-input);
  overflow: hidden;
}

.trc-person-photo .trc-photo-box {
  border-bottom: none;
}

/* --- talent list ---------------------------------------------------------
 *
 * The read-only roll of people: photo, name and meta, the payment, a status
 * pill. Shared by the project archive and the search results, and a row is a
 * link when the person has signed and a plain block when they have not.
 *
 * Stated here rather than inline on both so the row has one description and so
 * a fold can reach it — the phone-width section at the end of this file wraps
 * it, which a `style` attribute cannot be asked to do. */

.trc-talent-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.trc-talent-list > li + li {
  border-top: 1px solid var(--bs-hairline);
}

.trc-talent-list__row {
  min-height: 64px;
  padding: 10px 16px;
  color: inherit;
  text-decoration: none;
}

/* --- projects ------------------------------------------------------------- */

.trc-projects {
  display: grid;
  grid-template-columns: minmax(280px, 1fr) minmax(280px, 1fr);
  gap: 16px;
  align-items: start;
}

.trc-projects__pane {
  display: grid;
  gap: 12px;
  min-width: 0;
}

.trc-projects__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 12px;
}

.trc-projects__title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 18px;
  letter-spacing: -0.01em;
  font-variation-settings: "wdth" 90, "wght" 700;
}

.trc-project-swatch {
  width: 12px;
  height: 12px;
  border-radius: 3px;
  flex-shrink: 0;
}

.trc-project-actions {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  flex: none;
}

.trc-project-actions .bs-btn {
  min-height: 32px;
  padding: 0 10px;
  font-size: 13px;
}

.trc-project-facts {
  display: grid;
  margin-top: 14px;
}

.trc-project-facts__row {
  display: grid;
  grid-template-columns: 7.5em minmax(0, 1fr);
  gap: 12px;
  align-items: baseline;
  padding: 8px 0;
  border-top: 1px solid var(--bs-hairline, rgb(26 26 26 / 0.12));
  font-size: 13px;
  line-height: 1.45;
}

.trc-project-facts__row:first-child {
  border-top: 0;
  padding-top: 0;
}

.trc-project-facts__label {
  color: var(--bs-muted);
  font-size: 11px;
  line-height: 1.4;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  font-variation-settings: "wdth" 75, "wght" 700;
}

.trc-project-facts__edit,
.trc-project-facts__editor {
  grid-column: 1 / -1;
}

.trc-project-facts__edit {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
}

.trc-project-facts__editor .trc-cal {
  width: 100%;
  max-width: 288px;
}

@media (max-width: 720px) {
  .trc-projects {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* --- calendar ------------------------------------------------------------- */

.trc-cal {
  width: 288px;
  max-width: 100%;
}

.trc-cal--wide {
  width: min(520px, 100%);
}

.trc-cal__nav .bs-btn {
  min-height: 32px;
  min-width: 32px;
  padding: 0 8px;
}

.trc-projects .trc-cal--wide {
  width: 100%;
}

.trc-cal__weekdays,
.trc-cal__grid {
  display: grid;
  grid-template-columns: repeat(7, 28px);
  justify-content: space-between;
  gap: 4px;
}

.trc-cal--wide .trc-cal__weekdays,
.trc-cal--wide .trc-cal__grid {
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 6px;
}

.trc-cal__weekdays {
  margin-bottom: 6px;
  text-align: center;
}

.trc-cal__cell {
  width: 28px;
  height: 28px;
}

.trc-cal--wide .trc-cal__cell {
  width: auto;
  height: 44px;
  min-height: 44px;
}

.trc-cal--wide .trc-cal__day {
  width: 100%;
  height: 100%;
  border-radius: var(--bs-radius-input-sm);
  font-size: 15px;
}

.trc-cal__day {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  position: relative;
  padding: 0 0 2px;
  border: 0;
  border-radius: 6px;
  background: transparent;
  font: inherit;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  color: var(--bs-ink);
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
  transition: background-color var(--bs-fast) var(--bs-ease);
}

/* Add-project still fills a picked day. The Projects month uses dots. */
.trc-cal__day--covered {
  background: var(--bs-maroon);
  color: var(--bs-cream);
}

.trc-cal__day--range:not(.trc-cal__day--covered) {
  background: rgb(26 26 26 / 0.08);
}

.trc-cal__day--picked {
  background: rgb(26 26 26 / 0.08);
  box-shadow: inset 0 0 0 2px var(--bs-maroon);
}

.trc-cal__day--covered.trc-cal__day--picked,
.trc-cal__day--covered.trc-cal__day--range {
  box-shadow: inset 0 0 0 2px var(--bs-cream);
}

.trc-cal__dots {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-height: 8px;
  line-height: 0;
}

.trc-cal__dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
  box-shadow: 0 0 0 1px rgb(255 255 255 / 0.85);
}

.trc-cal__grid {
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  -webkit-touch-callout: none;
}

.trc-cal__grid--marqueeing,
.trc-cal__grid--marqueeing .trc-cal__day {
  cursor: default;
}

.trc-cal__marquee {
  position: fixed;
  z-index: 40;
  pointer-events: none;
  border: 1px solid rgb(26 26 26 / 0.55);
  background: rgb(26 26 26 / 0.08);
}

.trc-cal__footnote {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  margin-top: 12px;
}

.trc-cal__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}


/* =============================================================================
 * Touch targets
 *
 * Last in the file on purpose. Everything below raises a control the system
 * draws smaller than a finger, and every selector it raises already exists
 * above with a smaller value — so these rules win on source order rather than
 * on a specificity bump, and each one can be read against the rule it
 * overrides instead of hunting for an `!important`.
 *
 * `pointer: coarse` rather than a width. The console's main device is an iPad
 * in landscape, which is 1180px wide and driven with a finger: a 28px calendar
 * day is as hard to hit there as it is on a phone, and a width query would have
 * called it a desktop and left it alone. 44px is not an outside number either —
 * `.bs-field input` has been 44px since the system was written, so this is the
 * rest of the controls agreeing with the one that already had it right.
 *
 * Two controls are deliberately left below 44px and must stay that way. The
 * photo-step buttons are 36px so the viewfinder and the shutter fit one phone
 * screen, and the signing column's language pills are 40px so four endonyms
 * stay on one row. Both are asserted by `tests/talent/mobile-fit.test.ts`.
 * Neither is reachable from the selectors below; keep it so.
 * ============================================================================= */

@media (pointer: coarse) {
  /* Console nav, and the segmented controls: Layout on the talent toolbar,
     the rate picker in the person drawer, the drawer's own tabs. */
  .bs-nav__link {
    min-height: 44px;
  }

  .bs-seg__btn,
  .bs-seg--sunken .bs-seg__btn,
  .bs-seg--fill .bs-seg__btn {
    min-height: 44px;
  }

  /* Row actions. The talent list's five slots and a project card's five
     buttons were 32px, and Remove is among them in both — the one button on
     either row where a mistap costs somebody their place on the call sheet. */
  .trc-talent-table__slot .bs-btn,
  .trc-project-actions .bs-btn,
  .trc-talent-card__footer .bs-btn,
  .trc-selectbar .bs-btn {
    min-height: 44px;
  }

  .trc-selectbar__clear {
    width: 44px;
    height: 44px;
  }

  .bs-drawer__close {
    width: 44px;
    height: 44px;
  }

  /* The checkbox that selects a person for a bulk send. At 16px it was the
     smallest target in the console by a factor of two, sitting immediately
     beside the row it selects — the two are easy to confuse for each other. */
  .trc-talent-select input {
    width: 24px;
    height: 24px;
  }

  /* The narrow calendar becomes fluid, which is what `--wide` already does:
     seven 44px cells do not fit the 288px the fixed grid was built around, so
     the answer is to stop fixing the cell and let the month take the column.
     Add-project and the shoot-day picker both draw this one. */
  .trc-cal {
    width: 100%;
  }

  .trc-cal__weekdays,
  .trc-cal__grid {
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 6px;
  }

  .trc-cal__cell {
    width: auto;
    height: 44px;
    min-height: 44px;
  }

  .trc-cal__day {
    width: 100%;
    height: 100%;
  }

  .trc-cal__nav .bs-btn {
    min-height: 44px;
    min-width: 44px;
  }
}

/* =============================================================================
 * Phone widths
 *
 * The console was laid out for a desk and for an iPad in landscape, and the
 * folds it already had stop at 520px. These are the ones below that, where a
 * coordinator is holding a phone: the rows that refused to wrap, the tables
 * that could not compress, the overlays that were still drawn as panels
 * floating on a page they now cover completely.
 *
 * Same placement argument as the touch-target section above — last in the
 * file, so each rule wins on order against the one it overrides.
 * ============================================================================= */

/* --- form controls: the iOS focus zoom -----------------------------------
 *
 * Mobile Safari zooms the page when a text field smaller than 16px takes
 * focus, and it does not zoom back out. The field is then legible and the rest
 * of the layout is off the right edge, which reads as the page having broken
 * on touch — and on the signing flow it lands a performer in a horizontally
 * panned contract, which D-226 and `mobile-fit.test.ts` went to some trouble
 * to prevent for exactly this reason.
 *
 * So 16px is a floor rather than a preference, and it is the interface's own
 * 15px body that trips it. Width-gated, not `pointer: coarse`: iPad Safari
 * does not do this, and the console's type scale on its main device is not
 * something to change over a phone bug. */
@media (max-width: 720px) {
  .bs-field input,
  .bs-field select,
  .bs-field textarea,
  .bs-field--pill input,
  .bs-field--pill select,
  .trc-filter select,
  .trc-talent-toolbar__tools .bs-field--pill input,
  .trc-talent-toolbar__tools .bs-field--pill select {
    font-size: 16px;
  }

  /* Restated because the rule above ties with it on specificity and now comes
     after it. The name field on the signing flow stays the largest thing on
     the screen; it is not an exception to the floor, it is well over it. */
  .bs-field--lg input {
    font-size: 17px;
  }
}

/* --- the talent list on a phone ------------------------------------------
 *
 * The list stays a table and keeps scrolling sideways; what changes is how far
 * it has to scroll. 1100px was five action columns on fixed rem tracks plus
 * four data columns, and it asked a 390px screen to travel nearly three
 * screens to reach Remove.
 *
 * So the five action slots fold into two columns and the row grows downward
 * instead of rightward, which is the trade worth making on a phone: vertical
 * is the direction the board already scrolls. The columns keep their order, so
 * a button is still in the same place on every row — that was the point of the
 * fixed grid and folding it does not cost it.
 *
 * Nothing is hidden. Dropping Status or the comment column would have taken
 * the width off faster, but the comment is where "no email on file" is said,
 * and a warning removed on the narrowest screen is a warning removed from the
 * person most likely to be reading the board one-handed on set. The card view
 * is what a phone opens to now (D-304); this is the list still being usable
 * for somebody who asked for it. */
@media (max-width: 940px) {
  /* The floor goes. `min-width: 1100px` is what makes the element demand more
     room than the screen has, and the block layout below cannot take the width
     back while it is set — a floor is a floor whatever `display` says.

     Stated as `0` rather than removed because there is a floor above this fold
     and it is deliberate there: the wide table would rather scroll than let a
     status clip to `Sent, not…`. This is the other side of that decision, for
     the widths where there is no amount of scrolling that helps. */
  .trc-talent-table {
    min-width: 0;
  }

  .trc-talent-table th,
  .trc-talent-table td {
    padding: 9px 10px;
  }

  /* The verbs go and the glyphs stay. Five words came to about 424px of every
     row; five glyphs come to about 196px, and they fit one line again instead
     of folding into two and doubling the row. Each button keeps its name on
     `aria-label`, with the person's name in it, so a row of glyphs reads as
     `Remove Farrah Haddad` rather than as five unlabelled squares. */
  .trc-talent-table__label {
    display: none;
  }

  .trc-talent-table__slot .bs-btn > svg {
    display: block;
  }

  /* 52px, stated rather than derived. The slot carries `min-width: 0` so a
     table row can compress, which leaves any content-sized track free to size
     below the button inside it — `auto` was tried and collapsed every action
     column to 6px.

     52 and not 44: the glyph is the whole target now that the word is gone,
     and five of them in a row are five things a thumb can confuse for each
     other. The row has the width to spare — the block is full-bleed and the
     five come to 276px of a 292px line at the narrowest — so the height floor
     that applies to a control in a crowd is the wrong number for one that can
     simply be bigger. */
  .trc-talent-table__slot {
    min-width: 52px;
  }

  .trc-talent-table__slot .bs-btn {
    width: 100%;
    padding: 0;
  }

  /* The three columns that were breaking mid-word. A name, a status and a role
     are each one thing and read as one line; the comment column is left
     wrapping on purpose, because it holds sentences. */
  .trc-talent-table__name,
  .trc-talent-table__status,
  .trc-talent-table__role {
    white-space: nowrap;
  }
}

/* --- the talent toolbar's filters ----------------------------------------
 *
 * Five controls — search, project, day, status, sort — one per line below
 * 520px, at 40px each with the gaps. Together with the page title, the counts,
 * the buttons and the proportion bar that came to roughly 420px of a 390×844
 * screen, so a coordinator opening the board on a phone saw **no people at
 * all** until they scrolled. The toolbar is how you find somebody on a roster;
 * it is not what you came to look at.
 *
 * Two to a line for the four filters, and search spans both because it is the
 * one control whose value is typed rather than chosen from a list it can be
 * truncated back to. Three rows instead of five, and the first card is on the
 * screen the board opens on.
 *
 * The values do truncate at ~176px, which is what `text-overflow: ellipsis` on
 * `.trc-filter select` was already there for — D-280 put each filter's name
 * inside its own pill and tuned the tracks at the 1320px frame, and a phone is
 * not that frame. `Project: Giseok Stu…` is a chosen value the select still
 * shows in full when opened; five rows of chrome before the first person is
 * not recoverable by tapping anything.
 *
 * Search is identified as the child that is not a `.trc-filter`, which is what
 * distinguishes it in the markup: it carries no prefix label, because the glyph
 * and the placeholder already name it. */
/* The disclosure exists only below this fold. Above it the five controls are a
   five-track grid that fits its row, and a button to reveal what is already
   revealed would be a control that does nothing. */
.trc-talent-toolbar__fold {
  display: none;
}

@media (max-width: 720px) {
  /* A row, not a track list. The grid is written for five equal-ish columns and
     what a phone needs is two different things on one line: a search field that
     takes what is left, and a button the width of its own label. Flex gives
     that without a track list per state, and the folded and open layouts are
     then the same row with the filters shown or not. */
  .trc-talent-toolbar__tools {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
  }

  /* Search is the child with no `.trc-filter` prefix label. It grows. */
  .trc-talent-toolbar__tools > .bs-field:not(.trc-filter) {
    flex: 1 1 12ch;
    min-width: 0;
  }

  .trc-talent-toolbar__fold {
    display: inline-flex;
    flex: none;
    min-height: 40px;
  }

  /* The count rides the button. `--stage` because it is the mustard the system
     already uses for "there is something you should know here", and the number
     is exactly that: the board you are looking at is not the whole roster. */
  .trc-talent-toolbar__fold .bs-pill--stage {
    margin-left: 2px;
  }

  /* Two to a line once opened. Project keeps its own line above them: it is the
     longest value in the toolbar by far, it is the shoot everything else is
     read against, and truncating it to `Giseok Stu…` in half a row is the one
     truncation here that costs real context. */
  .trc-talent-toolbar__tools .trc-filter {
    flex: 1 1 calc(50% - 4px);
    min-width: 0;
  }

  .trc-talent-toolbar__tools .trc-filter--project {
    flex: 1 1 100%;
  }

  /* Folded: the first line is the whole toolbar. */
  .trc-talent-toolbar__tools--folded .trc-filter {
    display: none;
  }
}

/* --- the talent toolbar's count row --------------------------------------
 *
 * `minmax(0, 1fr) auto` put the roster counts beside Layout / Import / Add
 * person, and `auto` is the half that breaks: it resolves to the buttons'
 * max-content width, and a track that may shrink to zero is exactly what
 * `minmax(0, 1fr)` promises it will do. At 390px the buttons took all 346px
 * and the counts were measured at **zero** wide and 153 tall — `12 signed`
 * broken after the number, `6 not sent` over three lines, a column of
 * fragments where a summary of the roster should be.
 *
 * So below 720px the row stops being two columns. Counts first, on their own
 * full-width line where the three of them fit side by side; the buttons under
 * them; the proportion bar under that, where it already was. Started rather
 * than ended, because a left-aligned block under a left-aligned summary reads
 * as belonging to it, and `flex-end` on its own row left Add person alone
 * against the right margin with a hole beside it. */
@media (max-width: 720px) {
  /* Counts go, the bar stays.
   *
   * `12 signed  3 pending  6 not sent` and the proportion bar under it are the
   * same three numbers twice, and the bar is the one that survives being
   * glanced at — it is the shape of the shoot, where the words are an
   * inventory. The Status filter carries the exact figures for anybody who
   * wants them, with its own counts in the options. So the row goes and the
   * bar keeps its full width, which is a line of chrome back on a screen that
   * was spending four of them before the first person. */
  .trc-talent-toolbar__row--actions {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "actions"
      "bar";
  }

  .trc-talent-toolbar__row--actions .trc-count-row {
    display: none;
  }

  /* Layout, Import list and Add person on one line.
   *
   * They wrapped, which put Add person alone on a row with a hole beside it.
   * Fitting all three in 292px at the narrowest takes the `LAYOUT` caps label
   * — the two icons are a segmented control and say what they are by being
   * one — and a tighter pill. The buttons may shrink before they overflow,
   * because a clipped `Add person` is worse than a narrow one. */
  .trc-talent-toolbar__actions {
    flex-wrap: nowrap;
    justify-content: flex-start;
    gap: 8px;
    min-width: 0;
  }

  .trc-talent-toolbar__actions .bs-caps {
    display: none;
  }

  .trc-talent-toolbar__actions .bs-btn {
    flex: 0 1 auto;
    min-width: 0;
    padding: 0 12px;
  }
}

/* --- the talent list stops being a table --------------------------------
 *
 * Everything above narrowed the row. This stops pretending the row can fit.
 *
 * Six columns and five per-row actions come to 835px at their tightest — with
 * the verbs already off the buttons and three columns refusing to break. A
 * 320px phone has 292px. There is no arrangement of six columns that fits
 * that, so the honest options were a table that scrolls sideways for ever or a
 * row that stops being one line, and a board whose job is "who has not signed"
 * is not worth swiping to read.
 *
 * So each person becomes a block: the checkbox and the name on the first line
 * with the status ended against the right margin, the role under it, the
 * comment under that when there is one, and the five glyphs along the bottom.
 * Nothing is dropped and nothing scrolls. It fits 320px and it fits 430px, and
 * it gets better rather than worse as the screen grows, because the only thing
 * that changes with width is how much room the name has.
 *
 * On the semantics of doing this to a `<table>`: `display: grid` on a `tr`
 * takes the table roles with it, and that is the right outcome here rather
 * than a cost to be repaired. There is no visible table at this width — the
 * column headers are gone, because a header labels a column and there are no
 * columns — so a screen reader announcing "row 4, column 2" would be
 * describing a grid nobody can see. What it reads instead is the block in DOM
 * order: the name, the status, the role, then five buttons that each name the
 * person they act on (D-308). The `<thead>` keeps its select-all box, which is
 * the one thing in it that was a control rather than a label.
 *
 * The fold is 940px, and it is a phone-shaped number rather than a taste: it
 * is above the widest phone there is in either orientation. 430px is the
 * widest in portrait and 932px the widest in landscape, and a phone turned
 * sideways is still a phone — at 932px this was drawing a 1110px table and
 * asking for 236px of sideways scroll, which is the same defect as at 320px
 * wearing a different number. An iPad in portrait, 768 or 820, falls under it
 * too and stops scrolling by 90 to 190px.
 *
 * Above 940px nothing here applies and the table is exactly as it was drawn:
 * an iPad in landscape at 1180px, which is the device the console was built
 * for, keeps its words and its columns and has room for both. */
@media (max-width: 940px) {
  .trc-talent-table,
  .trc-talent-table tbody,
  .trc-talent-table thead,
  .trc-talent-table tr {
    display: block;
    width: 100%;
  }

  /* The head is now one control. The column labels have nothing left to
     label, and `Select all` keeps its name on the input's `aria-label`. */
  .trc-talent-table thead tr {
    padding: 8px 12px;
    border-bottom: 1px solid var(--bs-hairline);
  }

  .trc-talent-table thead th:not(.trc-talent-table__select) {
    display: none;
  }

  .trc-talent-table thead th.trc-talent-table__select {
    display: block;
    padding: 0;
  }

  /* One person, and the name gets the whole first line.
   *
   * The status started up there beside it, ended against the right margin,
   * which is the arrangement a table suggests. It cost the name about a third
   * of its width and at 320px `Farrah Haddad` truncated — a middling name on
   * a common phone, so most of the roster would have. The name is the thing
   * being looked for; the status is what is checked once it is found.
   *
   * Moving the status down to share a line with the role is free, which is why
   * it is the answer rather than a compromise: the row's height is set by the
   * 44px action line and the 36px face beside the name, and status is short
   * enough to sit on the role's line without adding one. The name went from
   * 96px to the full column and nothing grew.
   *
   * The first column belongs to the checkbox alone, so the name and everything
   * under it share a left edge. */
  .trc-talent-table tbody tr {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
      "select name"
      ".      comment"
      ".      actions";
    align-items: center;
    column-gap: 10px;
    row-gap: 2px;
    padding: 10px 12px;
    border-bottom: 1px solid var(--bs-hairline-soft);
  }

  .trc-talent-table tbody tr:last-child {
    border-bottom: 0;
  }

  /* The cells stop being cells: no borders of their own, and no padding, since
     the block above now owns the spacing. */
  .trc-talent-table tbody td {
    display: block;
    padding: 0;
    border: 0;
  }

  .trc-talent-table__select {
    grid-area: select;
  }

  .trc-talent-table__name {
    grid-area: name;
    overflow: hidden;
  }

  /* The name is the only thing here that can be any length, so it ellipsizes
     rather than running under the status ended against the right margin.
     `Farrah Haddad` at 320px is the case.
   *
   * It takes this many rules because the ellipsis has to land on a box that
   * can actually carry one. The name sits four elements deep inside two nested
   * flex rows, `text-overflow` does nothing on an inline span, and a flex item
   * will not shrink below its content unless it is told `min-width: 0` — so
   * the chain from the cell down to the word has to give permission at every
   * step, and the last one is blockified by being a flex item and can finally
   * be truncated. */
  .trc-talent-table__name .trc-row,
  .trc-talent-table__name .trc-talent-name,
  .trc-talent-table__name .trc-talent-card__line {
    min-width: 0;
    overflow: hidden;
  }

  .trc-talent-table__who {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* The status is the dot beside the name and nothing else.
   *
   * The word said the same thing the dot already showed, on a line of its own,
   * for every person on the roster — three lines of a block spent on two
   * values the eye does not read one at a time. Scanning a roster is looking
   * for the colour that is not the others.
   *
   * Hidden from the eye, not from the machine. The dot is `aria-hidden`, so
   * deleting this cell would take the status out of the accessibility tree
   * altogether and leave a screen reader a name and five buttons — the one
   * reader that cannot see the colour losing the only other copy of it. So it
   * keeps the visually-hidden treatment and is still read in row order:
   * `Ada Palens, Signed`.
   *
   * What this does cost is WCAG 1.4.1 for a sighted reader who cannot
   * separate the hues. `--none` is an outline and tells itself apart by shape,
   * but signed, pending and expired are three filled circles that differ only
   * in colour. The Status filter still sorts the board by any one of them and
   * Details still says the word, so nothing is unreachable — but the row alone
   * no longer distinguishes them without colour, and that is a real trade
   * rather than a free one. Written down so it is reversed knowingly. */
  .trc-talent-table__status {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
  }

  /* The role is genuinely dropped. Unlike the status it has nothing standing
     in for it, and unlike the status it is not what the board is for — it is
     a fact about a person, in Details and in the drawer, rather than the
     thing being scanned down the list.

     Qualified by `tbody td` to outrank the rule above that turns every cell
     into a block: a lone class loses to `.trc-talent-table tbody td` on
     specificity, and the role came back — auto-placed into the checkbox's
     column, the grid no longer naming an area for it. */
  .trc-talent-table tbody td.trc-talent-table__role {
    display: none;
  }

  .trc-talent-table__comment {
    grid-area: comment;
  }

  /* A person with nothing wrong with them has an empty comment cell, and an
     empty block in a grid is still a row of the grid. */
  .trc-talent-table__comment:empty {
    display: none;
  }

  .trc-talent-table__act {
    grid-area: actions;
    margin-top: 4px;
  }

  /* The reserved slot is given up here, and only here.
   *
   * Five fixed tracks exist so a button is in the same place on every row —
   * Download occupies an empty slot until somebody has signed, so Edit and
   * Remove do not shift. That is worth a hole in a table, where the eye runs
   * down a column and a moving target is the expensive thing. In a stacked
   * block there is no column to run down: each person is read on their own,
   * and the empty track just reads as a gap where a control failed to draw.
   * So the row flows and the empty slots collapse. */
  /* The glyphs take the whole line and divide it between them.
   *
   * `flex: 1 1 0` rather than a width, so the row is as wide as the block and
   * the buttons are as wide as the row allows — four actions on an unsigned
   * person are wider than five on a signed one, which is the right way round:
   * the width is whatever is going spare. At 320px that is about 55px each and
   * at 390px about 69px, against the 52px they were fixed at.
   *
   * `nowrap`, because dividing a line is only meaningful while there is one
   * line; wrapping would give the last button a row of its own at full width
   * and make the set look like two groups. */
  .trc-talent-table__actions {
    display: flex;
    flex-wrap: nowrap;
    gap: 6px;
  }

  .trc-talent-table__slot {
    flex: 1 1 0;
  }

  .trc-talent-table__slot:empty {
    display: none;
  }

  /* Each one gets an edge.
   *
   * `--quiet` is a pill with a transparent border, which is legible in a
   * toolbar where the buttons sit against each other and reads as four loose
   * marks on a line when they are spread across a block. Remove keeps its red
   * and the send action keeps its fill, so this names the quiet ones only —
   * `.bs-btn--danger` sets its border in a single-class rule, and a
   * two-class rule here would otherwise take the red off the one button on
   * the row that has to keep it. */
  .trc-talent-table__slot .bs-btn--quiet {
    border-color: var(--bs-hairline);
  }
}

/* --- rows that would not wrap -------------------------------------------- */

/* Five buttons held on one line by `flex-wrap: nowrap`, which is right beside
   a project name that can be any length. Ended rather than centred when they
   do wrap, so the block still reads as belonging to the card's right side. */
@media (max-width: 720px) {
  .trc-project-actions {
    flex-wrap: wrap;
    justify-content: flex-end;
  }
}

/* Narrower than that, they pair up: two to a line, each half the card.

   Not one per line, which was tried first and is worse. Three of these five
   are `--quiet`, and a quiet button is a pill with a transparent border — at
   full width, stacked, they stop reading as buttons at all and become four
   lines of centred text with a red box under them. Two to a line keeps them
   the shape of buttons and keeps Open's dark fill and Remove's red outline
   doing the work of telling the group apart.

   `flex-grow: 1` on a wrapped line means the fifth button, alone on the last
   one, takes the full width by itself — which is where Remove lands, and a
   destructive action on its own line is the right accident. */
@media (max-width: 520px) {
  .trc-project-actions {
    width: 100%;
  }

  .trc-project-actions > * {
    flex: 1 1 calc(50% - 3px);
  }

  .trc-project-actions .bs-btn {
    width: 100%;
  }
}

/* A read-only talent row: photo, name, the payment, the status pill. The
   payment is `nowrap` so an amount never breaks across lines, which is correct
   and is also what stopped the row compressing. Letting the row wrap gives the
   amount and the pill a second line to share instead of squeezing the name. */
@media (max-width: 520px) {
  .trc-talent-list__row {
    flex-wrap: wrap;
  }
}

/* --- the other two wide tables on a phone --------------------------------
 *
 * Activity and the admin People list, folded into blocks the way the talent
 * list already is (D-309) and at the same 940px. One number, because a
 * coordinator moving between three tabs should not meet three different
 * answers to "this table is wider than my screen", and because the reasoning
 * behind 940 is a fact about phones rather than about the talent list: it is
 * above the widest there is in either orientation, 430 portrait and 932
 * landscape, and a phone turned sideways is still a phone.
 *
 * Both heads go entirely. The talent list keeps one cell of its own because
 * that cell holds the select-all box; there is no control in either of these,
 * and a header labels a column that no longer exists.
 *
 * This supersedes the `white-space: normal` these tables' caps headers used to
 * get at 520px. That rule let three unbreakable headers wrap so they stopped
 * setting the People table's minimum width; with no header drawn at all below
 * 940px it had nothing left to act on, and it was the last `.bs-table--data`
 * rule of its kind, so it is gone rather than left to read as live. */

/* --- activity ------------------------------------------------------------
 *
 * `min-width: 720px` is what made this scroll. At 390px the column had 362px
 * to give it, so a third of every row sat off the right edge — and the feed is
 * read by running down it, which is the one gesture a sideways scroll makes
 * expensive. The floor goes with the columns; a floor is a floor whatever
 * `display` says, and the block below could not take the width back while it
 * was set. */
@media (max-width: 940px) {
  .trc-activity-table,
  .trc-activity-table tbody,
  .trc-activity-table thead,
  .trc-activity-table tr {
    display: block;
    width: 100%;
  }

  .trc-activity-table {
    min-width: 0;
  }

  .trc-activity-table thead {
    display: none;
  }

  .trc-activity-table tbody tr {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: 8px;
    row-gap: 2px;
    padding: 10px 12px;
    border-bottom: 1px solid var(--bs-hairline-soft);
  }

  .trc-activity-table tbody tr:last-child {
    border-bottom: 0;
  }

  .trc-activity-table tbody td {
    display: block;
    padding: 0;
    border: 0;
  }

  /* The markup stays in column order — it has to, there is a header row above
     this fold that it must keep matching — and `order` says how a block reads
     instead. A chronology wants the moment first, then who it was about, then
     what happened; the two muted facts share the last line rather than taking
     one each, which is the difference between a four-line block and a
     six-line one on a feed whose whole job is to be scanned.

     `width: auto` undoes the `width: 1%` that keeps the When column from
     taking slack off the others. There are no others here. */
  .trc-activity-table__when {
    order: 1;
    flex: 0 0 100%;
    width: auto;
    font-size: 12px;
    color: var(--bs-quiet);
  }

  .trc-activity-table__person {
    order: 2;
    flex: 0 0 100%;
    font-variation-settings: "wdth" 100, "wght" 550;
    overflow-wrap: anywhere;
  }

  .trc-activity-table__what {
    order: 3;
    flex: 0 0 100%;
  }

  .trc-activity-table__project {
    order: 4;
    font-size: 12px;
    min-width: 0;
    overflow-wrap: anywhere;
  }

  /* A name on a line by itself, with no column header left to say what it is
     doing there, reads as a second person the event was about. Two characters
     fix that, and they are only needed at this width. The dash an unattributed
     event carries becomes `by —`, which is the same admission the column made.

     Only when the feed is one project does this cell begin its line; the
     `::before` is on the value either way, so it does not depend on which. */
  .trc-activity-table__by {
    order: 5;
    font-size: 12px;
  }

  .trc-activity-table__by::before {
    content: "by ";
  }
}

/* --- the admin People list -----------------------------------------------
 *
 * Name, email, added, remove. No minimum width, so nothing scrolled — it
 * compressed instead, which D-308 already found is the worse of the two: an
 * email address is one unbreakable token, so the column it is in cannot give
 * way, and the name column pays for it. `rishi.ramesh@boncom.com` against a
 * 362px line leaves the name a few characters and the date none.
 *
 * Remove spans the block rather than sitting under it. It is one 44px target
 * against three short lines, the row is about that tall anyway, and a
 * destructive button that moves down the block as an address gets longer is a
 * button in a different place on every row. */
@media (max-width: 940px) {
  .trc-people-table,
  .trc-people-table tbody,
  .trc-people-table thead,
  .trc-people-table tr {
    display: block;
    width: 100%;
  }

  .trc-people-table thead {
    display: none;
  }

  .trc-people-table tbody tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "name  remove"
      "email remove"
      "added remove";
    align-items: center;
    column-gap: 10px;
    row-gap: 2px;
    padding: 10px 12px;
    border-bottom: 1px solid var(--bs-hairline-soft);
  }

  .trc-people-table tbody tr:last-child {
    border-bottom: 0;
  }

  .trc-people-table tbody td {
    display: block;
    padding: 0;
    border: 0;
  }

  .trc-people-table__name {
    grid-area: name;
    min-width: 0;
  }

  /* The address wraps rather than ellipsizing, because it is the thing being
     checked. A truncated name is still a person you recognise; a truncated
     address is not one you can confirm you meant to give access to. */
  .trc-people-table__email {
    grid-area: email;
    min-width: 0;
    overflow-wrap: anywhere;
  }

  /* `nowrap` is set inline on this cell and stays right — a date that breaks
     mid-month is unreadable — but the label has to come from here, the column
     header that used to carry it being gone. */
  .trc-people-table__added {
    grid-area: added;
    font-size: 12px;
  }

  .trc-people-table__added::before {
    content: "Added ";
  }

  .trc-people-table__act {
    grid-area: remove;
  }
}

/* --- overlays on a screen they cover completely -------------------------- */

/* The drawer is already `min(460px, 100%)`, so on a phone it is the screen.
   What it was missing is that the screen has a home indicator across the
   bottom of it, and the drawer's footer is where Save is. */
@media (max-width: 720px) {
  .bs-drawer__foot {
    padding-bottom: calc(14px + env(safe-area-inset-bottom, 0px));
  }

  .bs-sheet--filed .bs-sheet__actions {
    padding-bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  }
}

/* The filed sheet's action row is Download, Correct, Void, Resend, Close. On a
   phone that wraps into a ragged block of five differently-sized pills in
   which Void sits wherever the reflow put it. A column puts every one of them
   in a predictable place and gives the destructive one its own line. */
@media (max-width: 520px) {
  .bs-sheet--filed .bs-sheet__actions {
    flex-direction: column;
    align-items: stretch;
  }

  .bs-sheet--filed .bs-sheet__actions .bs-btn {
    width: 100%;
  }
}

/* --- step indicator ------------------------------------------------------ */

/* Three steps, a dot and a word each, on one unwrapping row. In English they
   fit; `Autorisation` and `Archivada` are what the row was not measured for.
   Wrapping beats a fourth step's label being pushed off the edge of the
   screen, since the label is the only thing that says which step it is. */
@media (max-width: 520px) {
  .bs-steps {
    flex-wrap: wrap;
    row-gap: 6px;
  }
}
