/* launcher.css — the Location Launcher's OWN layer, on top of the athlete portal's
   design system (portal.css = a byte copy of shredfast-athlete-portal/app/globals.css,
   sha256 593cfb51fbae2bffdc31cfb66401b2e9741df7d9da3445529331ffaf8e6f7454).
   ===========================================================================
   Built to ScreenBrief v2 (md5 cb429b97…), §01 measured grammar throughout.

   THE RULE THIS FILE OBEYS. Nothing here invents a visual idea, and when the brief
   landed this file got SHORTER: five of the seven components v1 had to derive already
   ship in the portal, so they are consumed rather than re-drawn. What remains is the
   composition the brief specifies plus the layout glue no stylesheet can own for you.

   NAMING. Every class introduced here carries the `lx-` prefix. The D2006 lesson is
   bound: a bare vocabulary word ("gate", "phase", "item", "empty") is a namespace claim
   that collides with the donor, so none is used. The census arm in render2.mjs proves
   0 collisions between `lx-*` and portal.css's own selectors, in both directions.

   NOT INHERITED (§01.7): `--dim` never carries a word; the `.pfx` profile family is
   never used (its `.trackgrp` / `.trackctl` twins are deliberately kept apart from the
   base rules, and reaching across them is how the portal broke its own stage counter);
   no drop shadow outside a modal — a launcher panel with a shadow is a bento tile
   wearing the system's tokens, which is the defect this v2 exists to remove.

   FONTS. portal.css declares @font-face with ABSOLUTE `/fonts/*.woff2` URLs, which
   resolve against the Next public dir at runtime and against the filesystem root under
   file://. The faces are re-declared with paths relative to this sheet. Same family,
   same weights, later in the cascade — the last matching face wins. */

@font-face { font-family: "Inter"; src: url("/assets/inter.woff2") format("woff2"); font-weight: 100 900; font-display: block; }

/* THE next/font BRIDGE. portal.css:47 declares
     --font-display: "Bio Sans", var(--font-display-web), "Inter", sans-serif;
   where --font-display-web is supplied by next/font in app/layout.tsx (Exo 2, the
   self-hosted web fallback). In a static mock that variable does not exist, and an
   UNDEFINED var() with no fallback makes the whole declaration INVALID AT
   COMPUTED-VALUE TIME — font-family silently resolves to the inherited body face and
   every display heading renders in Inter while document.fonts.check("Bio Sans") still
   answers true. That is the defect the measured-advance arm caught: heading advance
   375.04 == Inter 374.46, against Bio Sans 346.95 (at 26px; the arm now measures at
   400px, where the faces actually separate). Declaring the variable restores the
   portal's own stack; Bio Sans is first in it and loads locally, so Exo 2 is never
   reached. */
:root { --font-display-web: "Exo 2"; }

/* GRID ITEMS DEFAULT TO min-width:auto, WHICH IS THE MIN-CONTENT WIDTH. `.main` is a
   grid item of `.app`; the all-locations table inside it carries min-width:900px, so at
   390 the column refused to shrink and the whole app grid measured 946px — a 556px body
   overflow. The portal hits this wherever it puts wide content in a grid cell and cures
   it the same way: `.trackgsec{min-width:0}` (:474), `.setmain .setsec, .setmain .panel
   {min-width:0}` (:1235). Same cure, same reason — the table owns its own scroller. */
.main, .frame, .gsec, .gcell { min-width: 0; }

/* ══════════════════════════════════════════════════════════════════════════════
   §04.2b RANK 1 — LAUNCH STATUS IS THE PAGE HEAD, NOT A BANNER.
   v1 rendered the §5.5 sentence in a flash-box: one bordered strip among several,
   under a panel of bordered strips. It read as having fallen out of that panel
   because structurally it had — same box, same weight, lower position. Making the
   box redder would have produced two alarms, not a hierarchy.
   The cure is to stop it being a box. It is the `.hero` (portal.css:340-347), the
   portal's own most emphatic non-modal element, and NOTHING here changes its
   geometry. Only the radial glow's mix colour is swapped by state, which is a
   one-token substitution inside a shipped rule. ══════════════════════════════ */
.hero.lx-amber { background: radial-gradient(120% 140% at 0% 0%, color-mix(in oklch, var(--amber) 8%, transparent), transparent 60%); }
.hero.lx-neutral { background: radial-gradient(120% 140% at 0% 0%, color-mix(in oklch, var(--fg3) 7%, transparent), transparent 60%); }
.hero.lx-amber .eyebrow .d { background: var(--amber); }
.hero.lx-neutral .eyebrow .d { background: var(--fg3); }
/* the hero's right slot: switcher above the primary action, per §04.2b and §04.3 */
.lx-heroact { display: flex; flex-direction: column; align-items: flex-end; gap: 10px; flex-shrink: 0; }
.lx-heroact .fhint { font-size: 12.5px; text-align: right; max-width: 250px; }
/* §04.4's second line on a lapsed clock, and §04.6's optional-items line */
.lx-subline { font-size: 12.5px; color: var(--fg3); margin-top: 8px; line-height: 1.5; }

/* ══════════════════════════════════════════════════════════════════════════════
   §04.2a THE TIMELINE. The bar is the portal's `.loadbar` / `.lbseg`
   (portal.css:1039-1052) consumed unmodified — this file adds only the three role
   fills, the threshold rule, and the caption's positioning context.
   ══════════════════════════════════════════════════════════════════════════════ */
.lx-track { position: relative; padding-top: 16px; }
.lx-phase .lx-track { padding-top: 0; margin-top: 10px; }
/* `.loadbar` carries `margin-top:auto` for its home at the foot of a stat cell; here
   it is not the last child of a flex column, so the auto margin is released. */
.lx-track .loadbar { margin-top: 0; overflow: visible; }

/* the three role fills. --green-press (not --green) on the approved segment is the
   LIGHT-ONLY substitution portal.css:1061-1066 already made for exactly this track:
   --green measures 3.02:1 against --c-surface-2 in light, a rounding error from
   failing WCAG 1.4.11 for a non-text meaningful graphic. Inherited, not rediscovered. */
.lbseg.lx-approved { background: var(--green); }
:root[data-theme="light"] .lbseg.lx-approved { background: var(--green-press); }
.lbseg.lx-awaiting { background: var(--amber); }
.lbseg.lx-optional { background: var(--green-dim); }

/* THE THRESHOLD MARK — 2px, --fg2, crossing the track 4px above to 4px below.
   `.gateline`'s dashed amber is deliberately NOT lifted: amber means *awaiting HQ*
   on this screen, and a threshold that borrows the alarm colour spends it. --fg2 is
   7.59:1 and is not a status colour anywhere in this brief: the boundary is
   structure, not alarm. */
.lx-thr { position: absolute; top: -5px; bottom: -5px; width: 2px; margin-left: -1px; background: var(--fg2); border-radius: 1px; z-index: 2; }

/* the caption, `.tgate` treatment verbatim (portal.css:519), rendered ONCE above the
   location bar's mark. Phase bars carry the mark without it. */
.lx-cap { position: absolute; top: 0; white-space: nowrap; }

/* the location bar's cell — `.trackcell` padding, and the `.statv` gate figure */
.lx-loccell { gap: 14px; }
.lx-loccell .statv .of { margin-left: 4px; }

/* the four-figure annotation row — `.detail .conv` grammar: a flex row of
   label-over-value pairs, `.cl` 10px/600/.1em uppercase over `.cv` 15.5px/600 display.
   The labels take --fg3, the small-label rung (portal.css:148), not --muted: at 10px
   they are below the AA-large threshold --muted is limited to (portal.css:149). */
.lx-convrow { display: flex; align-items: flex-start; gap: 30px; flex-wrap: wrap; border-top: 1px solid var(--line-soft); padding-top: 14px; }
.lx-conv .cl { font-size: 10px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: var(--fg3); }
.lx-conv .cv { font-family: var(--font-display); font-size: 15.5px; font-weight: 600; color: var(--fg); margin-top: 5px; letter-spacing: -.01em; }

/* the phase rows — `.nstep` consumed whole; these declarations are layout glue only */
.nstep.lx-phase { text-decoration: none; transition: .12s var(--ease); align-items: flex-start; }
.nstep.lx-phase:hover { background: var(--c-surface); }
.nstep.lx-phase:hover .rowarw { color: var(--green-quiet); transform: translateX(2px); }
.nstep.lx-phase .nsi { border-radius: 50%; font-size: 12px; font-weight: 700; font-family: var(--font-display); }
.nstep.lx-phase .nsn { gap: 14px; }
.lx-pcnt { font-family: var(--font-display); font-size: 14px; font-weight: 700; color: var(--fg2); letter-spacing: -.01em; margin-left: auto; }
.nstep.lx-phase .nsact { align-self: center; }
.nstep.lx-phase .rowarw svg { width: 15px; height: 15px; }
/* the panel foot carrying §04.8's signed sentence */
.lx-foot { padding: 12px 20px; border-top: 1px solid var(--line-soft); font-size: 11.5px; color: var(--fg3); }

/* ══════════════════════════════════════════════════════════════════════════════
   §01.6 STATUS COLOUR MAP — five roles, three boxes, one outline. Every box below
   is a PORTAL rule; this file adds only the role variants the map names.
   ══════════════════════════════════════════════════════════════════════════════ */
/* `.pill-stuck` with --red swapped for --amber (rejected row pill, deadline red and
   lapsed). The box is portal.css:1733 unchanged. */
.pill-stuck.lx-red { border-color: color-mix(in oklch, var(--red) 48%, transparent); background: color-mix(in oklch, var(--red) 10%, transparent); color: var(--red); }
/* dot colours on the outline badge come from the phase family already declared
   (portal.css:1729-1731). No fourth is minted. */
.badge-stage .d.lx-muted { background: var(--muted); }
.badge-stage .d.lx-amber { background: var(--amber); }
.badge-stage.lx-o3 { color: var(--fg3); }

/* MUST DO / OPTIONAL / LEGALLY REQUIRED markers. MUST DO and OPTIONAL take the
   `.badge-stage` outline (§01.6: colouring a badge that appears on the majority of rows
   spends the alarm budget on a constant); LEGALLY REQUIRED is the exception and takes
   the `.pill-stuck` box with --red. Sized to the 9.5px micro-label `.antag` already
   uses (portal.css:465). */
.lx-mark { display: inline-flex; align-items: center; gap: 5px; padding: 2px 9px; border: 1px solid var(--line-soft); border-radius: 60px; font-size: 9.5px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; white-space: nowrap; color: var(--fg2); }
.lx-mark.opt { color: var(--fg3); }
.lx-mark.legal { border-color: color-mix(in oklch, var(--red) 48%, transparent); background: color-mix(in oklch, var(--red) 10%, transparent); color: var(--red); }
.lx-mark svg { width: 11px; height: 11px; stroke: currentColor; fill: none; stroke-width: 2.2; }

/* ── the back link — §01.5 item 5: `.golink` (portal.css:378) with the chevron
      mirrored. NOT the `.pfx .backlink` twin (§01.7 item 2). ── */
.lx-back { display: inline-flex; align-items: center; gap: 5px; font-size: 12px; font-weight: 600; color: var(--green-quiet); text-decoration: none; transition: .14s var(--ease); }
.lx-back:hover { color: var(--on-green-tint); gap: 7px; }
.lx-back svg { width: 13px; height: 13px; stroke: currentColor; fill: none; stroke-width: 2; }

/* ── the back link INSIDE the kicker — decision 38, V-2/V-3. The chevron moved off the
      hero's right edge into the eyebrow, and `.lx-back` and `.eyebrow` disagree about
      type: 12px/none/normal against 11px/uppercase/.18em (portal.css:343). Left alone
      the kicker reads as two fragments at two sizes on one line. Inherited, not
      restated, so the eyebrow stays the single source of the kicker's type — and set
      per property rather than through the `font` shorthand, which would silently take
      family and line-height with it. The 13px chevron is left at 13px: measured against
      an 11px cap height it is the same optical weight the right-edge link had at 12px.
      `.eyebrow-sep` is the divider, a bar on phase pages and a middle dot on item pages
      (the glyph is the caller's, the ink is here); the 8px `.hero .eyebrow` gap
      (portal.css:342) does the spacing, so the glyph carries no padding of its own. ── */
.eyebrow .lx-back { font-size: inherit; font-weight: inherit; letter-spacing: inherit; text-transform: inherit; }
.eyebrow-sep { color: var(--fg3); font-weight: 400; }

/* ── rail count badge — `.chip-proofs` (portal.css:1625) in the nav row, exactly as
      `Proof review` carries its 3 in reference/admin-candidates@1440.png (§04.2b
      rank 4 item 1). ── */
.nav a .chip-proofs { margin-left: auto; }
.nav a.on .chip-proofs { border-color: color-mix(in oklch, var(--on-green-fill) 40%, transparent); background: color-mix(in oklch, var(--on-green-fill) 18%, transparent); color: var(--on-green-fill); }

/* ── the admin table. `.atable` (portal.css:1668-1720) consumed whole; the launcher's
      own columns are the only addition, and the two-line cell is `.aname`/`.an1`/`.an2`
      (portal.css:1721-1723), the portal's own name-over-description grammar. ── */
.atable th.num, .atable td.num { text-align: right; }
.lx-scroll { overflow-x: auto; }
.lx-figure { font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; color: var(--fg); }
.lx-figure .u { color: var(--fg3); font-weight: 500; font-size: 11.5px; margin-left: 1px; }
.lx-figure.zero { color: var(--fg3); font-weight: 500; }
/* PROGRESS AND RISK ARE DIFFERENT MEASURES, AND THE BAR ONLY CARRIES ONE.
   BEFORE: this bar was tinted by `deadline_risk`, so ShredFast Bolton at 97 of 104 —
   the most complete location on the table bar the three already at 104 — painted its
   near-full bar RED, and Stockport at 88/104 painted amber. A reader scanning the
   column read "failing" off a location that is 93% done. The risk is real and it is
   already said, in the deadline pill beside it, which is the one element §4.2 / OQ-I
   give the risk colour to.
   AFTER: the bar is always the progress tone — `--green-press`, the same fill the
   dashboard's location bar uses for must-do done, so the two surfaces agree. Risk
   colours the pill and nothing else. The `.amber`/`.red` modifiers are deleted rather
   than left unused, so nothing can reach for them again. */
.lx-mini { height: 5px; background: var(--c-surface-2); border-radius: 60px; overflow: hidden; position: relative; width: 76px; margin: 5px 0 0 auto; }
.lx-mini .f { position: absolute; inset: 0 auto 0 0; border-radius: 60px; background: var(--green-press); }
.atable td.arw { text-align: right; width: 40px; }
.atable td.arw svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 2; }
.atable tbody tr:hover .rowarw { color: var(--green-quiet); transform: translateX(2px); }

/* ── filter chips — `.chip` / `.chip.on` (portal.css:714-717) consumed whole; the
      launcher needs a count inside one, which `.ptitle .cnt` (portal.css:377) draws.
      `.chip.on` is amber in the portal (a toggled FILTER); the launcher's chips are a
      single-select segmented filter, so the selected one takes the green selection
      tone the sheet uses for a chosen row, not the amber "this filter is on" tone. ── */
.lx-chips { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.chip .c { font-variant-numeric: tabular-nums; font-size: 11px; font-weight: 700; opacity: .8; }
.chip.lx-on { border-color: color-mix(in oklch, var(--green) 55%, transparent); background: color-mix(in oklch, var(--green) 10%, transparent); color: var(--green-quiet); }
/* `.lx-toolrow` held the phase list's result-descriptor line. Decision 38 V-1 retired
   that line and moved its figure into the `.ptitle` `.cnt` chip, so nothing in the
   estate renders this class today. The rules are kept, unreferenced, because the
   toolrow is the launcher's declared slot for a filter-row control and the next one
   lands in it; they are inert until then. */
.lx-toolrow { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 10px 18px; }

/* ── form grammar — `.setsec` (portal.css:1236), `.fld`/`.fl` (portal.css:625-626),
      `.in` at 13.5px in a 44px box (portal.css:627). THE SHELL'S 16px INPUT RULE IS
      NOT IMPORTED (§01.3): it is not this system's, and a 16px input in a 44px box
      changes the measured density of every form on these screens. ── */
.lx-form { padding: 24px 26px; max-width: 760px; display: flex; flex-direction: column; gap: 24px; }
.lx-fieldset { display: flex; flex-direction: column; gap: 16px; }
.lx-2col { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.lx-err { font-size: 11.5px; color: var(--red); font-weight: 600; }
.in.lx-bad { border-color: var(--red); }
.lx-actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
/* form-level failure — `.merr` (portal.css:1618), the portal's red inline error panel */
.lx-merr { font-size: 12.5px; padding: 11px 14px; border: 1px solid color-mix(in oklch, var(--red) 30%, transparent); background: color-mix(in oklch, var(--red) 5%, transparent); color: var(--fg); display: flex; align-items: center; gap: 10px; }
.lx-merr svg { width: 15px; height: 15px; stroke: var(--red); fill: none; stroke-width: 2.2; flex-shrink: 0; }

/* ── item rows on the phase list — `.nstep` (portal.css:384-397) consumed whole ── */
.nstep.lx-item { text-decoration: none; transition: .12s var(--ease); }
.nstep.lx-item:hover { background: var(--c-surface); }
.lx-ref { font-size: 11.5px; font-weight: 700; color: var(--fg3); font-variant-numeric: tabular-nums; width: 32px; flex-shrink: 0; padding-top: 2px; }
.lx-meta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 7px; }
.lx-ev { font-size: 11.5px; color: var(--fg3); display: inline-flex; align-items: center; gap: 5px; }
.lx-ev svg { width: 12px; height: 12px; stroke: currentColor; fill: none; stroke-width: 2; }

/* ── item detail: definition rows — `.prow .pmain .n`/`.d` (portal.css:1244-1245) ── */
.lx-def { padding: 15px 20px; border-bottom: 1px solid color-mix(in oklch, var(--line-soft) 55%, transparent); }
.lx-def:last-child { border-bottom: none; }
.lx-defk { font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--fg3); font-weight: 600; }
.lx-defv { font-size: 13px; color: var(--fg); margin-top: 6px; line-height: 1.55; }
.lx-defv.quiet { color: var(--fg2); }

/* evidence rows — `.upi` compact dated row (portal.css:449) at file scale */
.lx-file { display: flex; align-items: center; gap: 13px; padding: 13px 20px; border-bottom: 1px solid color-mix(in oklch, var(--line-soft) 55%, transparent); }
.lx-file:last-child { border-bottom: none; }
.lx-thumb { width: 44px; height: 44px; flex-shrink: 0; background: var(--c-surface-2); border: 1px solid var(--line-soft); display: grid; place-items: center; color: var(--fg3); }
.lx-thumb svg { width: 19px; height: 19px; stroke: currentColor; fill: none; stroke-width: 1.6; }
.lx-thumb img { width: 100%; height: 100%; object-fit: cover; display: block }  /* K-1 road (c), decision 39 — the evidence thumbnail. BYTE-IDENTICAL in app/globals.css; A-21 is the arm. */
.lx-fname { font-size: 12.5px; font-weight: 600; color: var(--fg); word-break: break-all; }
.lx-fmeta { font-size: 11.5px; color: var(--fg3); margin-top: 3px; }

/* §01.5 item 4 — the upload is ONE control: a <label class="btn btn-ghost"> wrapping a
   .visually-hidden <input type=file>. The camera is offered by the operating system,
   so no separate "Take a photo" button is ever rendered. */
.lx-upload { padding: 14px 20px 16px; display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
.lx-upload .btn { cursor: pointer; }
.lx-uphint { font-size: 11.5px; color: var(--fg3); }

/* the locked notice */
.lx-locked { display: flex; align-items: center; gap: 11px; padding: 13px 20px; background: var(--c-surface); border-bottom: 1px solid var(--line-soft); font-size: 12.5px; color: var(--fg2); }
.lx-locked svg { width: 15px; height: 15px; stroke: var(--fg3); fill: none; stroke-width: 2; flex-shrink: 0; }

/* empty states — `.empty` + `.empty .big` (portal.css:379, 808) consumed whole. The
   reserve height is `.atable td.aempty`'s own 280px (portal.css:1781), which is also
   what stops `.frame` (flex:1, --line-soft ground) painting a band of bare hairline
   under a short page: MEASURED at 390 as a 128px and a 148px dead run before this. */
.lx-void { min-height: 280px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; padding: 26px 20px; text-align: center; }
.lx-voidi { width: 40px; height: 40px; border-radius: 50%; border: 1px solid var(--line-soft); display: grid; place-items: center; color: var(--fg3); margin-bottom: 4px; }
.lx-voidi svg { width: 18px; height: 18px; stroke: currentColor; fill: none; stroke-width: 1.8; }
.lx-voidd { font-size: 12.5px; color: var(--fg3); max-width: 400px; line-height: 1.5; }
.frame > .gsec:last-child { flex: 1; }
.frame > .gsec:last-child > .gcell { display: flex; flex-direction: column; }
.frame > .gsec:last-child > .gcell > .lx-void { flex: 1; }

/* the attention panel — `.rejitem` family (portal.css:1628-1635) consumed whole.
   NEVER A LEFT STRIPE (§01.6, §04.5): it is a full 1px red border at 30% over a 5%
   fill, the portal's own athlete-facing rejected-post treatment. */
.lx-rejwrap { padding: 14px 20px 16px; display: flex; flex-direction: column; gap: 9px; }
.lx-rejhint { font-size: 12.5px; color: var(--fg2); line-height: 1.5; }
.lx-kick { font-size: 11px; font-weight: 700; letter-spacing: .04em; color: var(--fg2); font-variant-numeric: tabular-nums; text-transform: uppercase; }
.rejitem .lx-rejmeta { font-size: 12.5px; color: var(--fg3); margin-top: 7px; }
.rejitem .lx-rejq { font-size: 13px; color: var(--fg); line-height: 1.5; margin-top: 5px; }

/* the mobile bar's surface title, in place of a duplicated wordmark: `.mobar` exists
   in the portal because the portal has no umbrella bar above it. Here the shell bar
   already carries the brand, so a second wordmark at 390 is pure duplication. */
.lx-mobt { font-family: var(--font-display); font-size: 14px; font-weight: 700; letter-spacing: -.01em; color: var(--fg); }

/* header action cluster — `.trackhead`/`.trackctl` (portal.css:495-497) */
.lx-hact { margin-left: auto; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* THE SHELL-SIDE .mtitle CURE IS GONE WITH THE BAR IT CURED. v2.1 removes the
   platform shell top bar entirely, so PlatformNav's untruncated mobile `.mtitle` is
   no longer rendered by these mocks. THE UPSTREAM DEFECT STILL EXISTS and the ticket
   is still owed: the shell's own zones (Floor Planner, Territory Map, Workouts) go on
   rendering that bar until the shell adopts this sidebar. Recorded, not cancelled. */

/* ══════════════════════════════════════════════════════════════════════════════
   RESPONSIVE — the PORTAL's three breakpoints (§01.3), which are NOT the shell's
   768px: 940 (rail off-canvas, .two and .statgrid to one column, hero title 26px),
   700 (the touch layer: controls reach 40-44px, step-row actions go full width),
   560 (.statgrid to two columns).
   ══════════════════════════════════════════════════════════════════════════════ */
@media (max-width: 940px) {
  .lx-2col { grid-template-columns: 1fr; }
  .lx-form { padding: 20px 18px; }
  .lx-heroact { align-items: stretch; width: 100%; }
  .lx-heroact .fhint { text-align: left; max-width: none; }
  .lx-convrow { gap: 20px 26px; }
  .lx-loccell { padding: 18px; }
  .lx-def, .lx-file { padding: 13px 18px; }
  .lx-upload, .lx-rejwrap { padding: 14px 18px 16px; }
  .lx-toolrow { padding: 10px 14px; }
}
@media (max-width: 700px) {
  /* the touch layer. The portal declares its floor per control (portal.css:2229-2242);
     these are the launcher's own controls reaching the same 40px. */
  .lx-actions .btn, .lx-heroact .btn { width: 100%; }
  .chip { min-height: 40px; }
  .lx-back { min-height: 40px; padding: 4px 0; }
  .rejitem { flex-wrap: wrap; }
  .rejitem .rejact, .rejitem .rejact .btn { width: 100%; }
  .nstep.lx-phase .nsn { flex-wrap: wrap; }
}
@media (max-width: 560px) {
  .lx-convrow { gap: 16px 24px; }
  .lx-conv { flex: 1 1 40%; }
}
@media (prefers-reduced-motion: reduce) {
  .nstep.lx-phase, .nstep.lx-item, .rowarw { transition: none; }
  .nstep.lx-phase:hover .rowarw, .atable tbody tr:hover .rowarw { transform: none; }
}

/* ══════════════════════════════════════════════════════════════════════════════
   THREE MEASURED CURES, each against a rule this build CONSUMES rather than owns.
   None edits portal.css; each is scoped to the launcher's own row.

   (1) `.nsd` is 11.5px --muted (portal.css:393) and §04.2a asks for the phase
   figures on it. But §01.1's ink rule is stated as not optional: "any string below
   16px that carries meaning takes --fg3 or better", and --muted is AA-large-only by
   the sheet's own comment (portal.css:149). MEASURED at 3.62:1 against the cell.
   The figures line is the most information-dense string on the screen, so the ink
   rule wins over the component default and the brief's two sentences are reconciled
   in favour of the one it calls binding.

   (2) `.nsi` carries the phase NUMBER, which is content, not decoration. The base
   rule inks it --muted and `.nstep.todo` inks it --amber (portal.css:387, 389) —
   MEASURED at 2.29:1 and 3.59:1 at 12px. The portal's own consumers put a GLYPH in
   that circle, where 1.4.11's 3:1 applies; a digit is text. The border colours that
   carry the met/todo state are untouched: only the digit's ink is raised.

   (3) `.hero` is flex with a 24px gap and `.hmain{min-width:260px}` (portal.css:341).
   At 390 that floor plus the action column overflowed the cell by 23px, and the
   `.selpill` select took its intrinsic width from its longest option on top. The
   portal never hit it because no portal hero has a right-slot control this wide. */
.nstep.lx-phase .nsd { color: var(--fg3); }
.nstep.lx-phase .nsi { color: var(--fg2); }
.nstep.lx-phase.met .nsi { color: var(--green-quiet); }

@media (max-width: 940px) {
  .hero .hmain { min-width: 0; }
  .lx-heroact { min-width: 0; max-width: 100%; }
  .lx-heroact .selpill, .lx-heroact .selpill select { width: 100%; max-width: 100%; min-width: 0; }
}

/* THE .chip-proofs BOX WITH --red SWAPPED IN, per §01.6's "same box, different role
   token" construction (portal.css:1625 unchanged). */
.chip-proofs.lx-red { border-color: color-mix(in oklch, var(--red) 34%, transparent); background: color-mix(in oklch, var(--red) 12%, transparent); color: var(--red); }

/* TWO MORE MEASURED CURES.

   (4) `.statv .of` is the denominator beside the gate figure — 15px --muted, MEASURED
   3.62:1. It is not decoration: `/ 104` is half of the fraction the whole screen turns
   on. Raised to --fg3 per §01.1's ink rule, the same rung the four `.cl` labels take.

   (5) A DEFECT I INTRODUCED AND THE ARM CAUGHT. `.nstep.met .nsi` fills the circle
   with --green-fill (#1F5763, a DEEP fill) and the portal inks it --green. Reaching
   for --green-quiet to "improve" it put #027770 on #1F5763 — MEASURED 1.49:1, worse
   than what I replaced. §01.1 states the matrix in terms: deep fills take LIGHT ink,
   mid accents take dark ink, and neither is derived from the other. --on-green-fill
   is the light ink for exactly this ground and measures 7.77:1. */
.lx-loccell .statv .of { color: var(--fg3); }
.nstep.lx-phase.met .nsi { color: var(--on-green-fill); }

/* the two zone captions on the timeline axis, and the phase column heads. Both carry
   §04.7's signed column vocabulary now that the table it headed IS the timeline.
   Grammar: `.atable thead th` (portal.css:1670) — 10.5px/600/.1em uppercase --fg3. */
/* the zone captions are CENTRED WITHIN THEIR OWN ZONE, not pinned to the track's
   outer edges: each is the name of the span it sits under, so it belongs at that
   span's midpoint. Widths come from the same threshold percentage the bar uses, so
   the caption row and the bar can never disagree. */
.lx-zones { display: flex; align-items: baseline; margin-top: -4px; }
.lx-zone { font-size: 10px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: var(--fg3); text-align: center; }
.lx-phhead { display: flex; align-items: center; padding: 10px 20px; border-top: 1px solid var(--line-soft); border-bottom: 1px solid var(--line-soft); font-size: 10.5px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: var(--fg3); }
.lx-phhead .r { margin-left: auto; }

/* (6) `.an2` is the table's row-secondary at 11.5px --muted (portal.css:1723) and
   §01.2 lists it as such. MEASURED 3.62:1 on "Opened 28 Jun 2026" — a date that says
   whether a location has launched. §01.1's ink rule is the binding one and it is
   explicit: any string below 16px that carries meaning takes --fg3 or better. Raised
   inside the launcher's table only; the portal's own tables are untouched. */
.atable .an2 { color: var(--fg3); }

/* (7) THE PHASE-ROW CHEVRON WRAPS AT 390. `.nstep` sends its action slot full-width
   below 700 (portal.css:2222) — correct for a step row whose action is a BUTTON, wrong
   for a chevron, which arrived on its own line under the bar as a stray glyph. The row
   is itself an anchor and the phase title is the affordance, so the chevron is dropped
   at the touch layer rather than re-laid-out: an affordance that has to be explained
   at 390 is not one. */
@media (max-width: 700px) {
  .nstep.lx-phase .nsact { display: none; }
}

/* ══════════════════════════════════════════════════════════════════════════════
   THE SIDEBAR ACCORDION — v2.1. The portal ships no accordion, so this is derived,
   and every part of the derivation is a rule that exists:
     the row        `.nav a` (portal.css:306-311) — used unchanged, including the
                    `.on` green-fill with its 2px left accent
     the chevron    `.shtoggle svg` (portal.css:528-529) — 12px, stroke 2, and the
                    180° rotation on open, which is the portal's own disclosure idiom
     the sub-row    `.nav a` again, indented to the parent's LABEL, not its icon, so
                    the two levels share one text column
   Nothing is invented. A zone with one destination renders as a plain `.nav a` with
   no chevron, because a disclosure that discloses nothing is a lie about depth.
   ══════════════════════════════════════════════════════════════════════════════ */
.lx-acc { display: contents; }
.lx-caret { width: 12px; height: 12px; stroke: currentColor; fill: none; stroke-width: 2; margin-left: auto; flex-shrink: 0; transition: .18s var(--ease); opacity: .75; }
.lx-acc.open > a > .lx-caret { transform: rotate(180deg); opacity: 1; }
/* a count chip and a caret can both ride the row; the chip keeps the auto margin */
.nav a .chip-proofs + .lx-caret { margin-left: 8px; }

.lx-sub { display: flex; flex-direction: column; gap: 1px; padding: 2px 0 6px; }
/* 18px row padding + 15px glyph + 11px gap = 44px, the parent's label column */
.lx-sub a {
  display: block; padding: 7px 14px 7px 44px; color: var(--fg3); font-size: 12.5px;
  font-weight: 500; text-decoration: none; transition: .14s var(--ease);
  border-left: 2px solid transparent;
}
.lx-sub a:hover { background: var(--c-surface); color: var(--fg2); }
/* the active SUB-item is the quieter half of the same treatment the active ZONE
   takes: the zone owns the green-fill, so a second green-fill under it would give
   the reader two "you are here"s. The sub-item takes the accent rail and the accent
   ink only — `.nav a.on`'s border-left with `--green-quiet` in place of the fill. */
.lx-sub a.on { border-left: 2px solid var(--green); color: var(--green-quiet); font-weight: 600; background: color-mix(in oklch, var(--green) 6%, transparent); }

/* the rail scrolls when the accordion is long; `.side` is 100dvh and sticky */
.side { overflow-y: auto; }
.side::-webkit-scrollbar { width: 0; }

@media (max-width: 940px) {
  /* the portal's own ≤940 behaviour, unchanged: `.side` off-canvas, `.mobar` shown,
     `.navscrim` over the page. The mocks carry no JS, so they render the DEFAULT
     (closed) state — which is exactly what reference/portal-home@390.png shows. */
  .lx-sub a { padding: 10px 14px 10px 44px; }
}

/* TWO DEFECTS THE RASTER SHOWED THAT NO ARM COULD.
   (a) The filter chips are anchors, and `.chip` (portal.css:714) is written for the
   portal's <button> consumers, so it never sets `text-decoration`. Every filter
   rendered underlined. The rule is scoped to the anchor form so the portal's own
   button chips are untouched.
   (b) `.brandsub` wraps "LOCATIONS PLATFORM" onto two lines in the 226px rail.
   MEASURED: at 10px/.16em uppercase the string is ~150px and the rail leaves ~148px
   beside the theme toggle — two pixels short. Forcing `nowrap` traded a wrap for a
   2px overflow, which is worse, so the wrap STANDS. The string is the operator's
   ruling and the type is the portal's; a two-line subtitle in a rail is a fit, not a
   defect. Recorded so nobody "fixes" it by shortening the words or the tracking.

   (c) `.fhint` is 11.5px `--muted` (portal.css:899) and I used it for field hints and
   the create-form's footnote — MEASURED 3.62:1. Those hints tell the reader what a
   field does; §01.1's ink rule applies. Raised in the launcher's own containers only. */
a.chip, a.chip:hover { text-decoration: none; }

/* (d) THE SAME DEFECT, ONE SELECTOR OVER — decision 38, V-4. `.btn` (portal.css:539) is
   written for the portal's <button> consumers and never sets `text-decoration` either,
   so wave 1's `<a class="btn btn-line btn-sm">Open item</a>` on the owner dashboard
   rendered underlined in a signed frame. Wave 2 cured its own anchors at
   launcher-w2.css:211 and could not reach back into a sheet it does not load; the
   operator has now ruled it estate-wide, so the cure belongs here, in the sheet every
   frame loads. Scoped to the anchor form: the portal's button consumers are untouched,
   and the six admin frames carry no anchor button at all, so their rendering does not
   move. */
a.btn, a.btn:hover { text-decoration: none; }
.lx-form .fhint, .lx-def .fhint, .lx-toolrow .fhint, .fld .fhint { color: var(--fg3); }

/* ══ APPEND-ONLY REGION · READ BEFORE EDITING THIS FILE ═══════════════════════════
   THE LAUNCHER REPOSITORY PINS THIS FILE'S RULES BY LINE NUMBER. Three instruments
   in `shredfast-launcher` hold an address of the form `launcher.css:<line>` together
   with the exact text expected on that line, and compare the two:

     · `scripts/assert-p1-fences.mjs` → T-3-source, against
       `scripts/supplement-enumeration.json` (45 rules; the check is
       `srcLines[rule.line - 1] !== rule.text`).
     · `scripts/assert-p1-fences.mjs` → T-3b-source, against
       `scripts/p3-supplement-enumeration.json` (102 rules, `line`..`endLine`).
     · `scripts/derive-p3-lift.mjs --check`, which keys every derived rule on
       `launcher.css:<line>` and reports ADDED / REMOVED / CHANGED against the
       committed enumeration.

   A pin therefore survives a TEXT-PRESERVING edit only if the rule keeps its LINE.
   Inserting anything mid-file shifts every rule below it and reds all three at once,
   which blocks `npm run build` on every launcher branch on any machine that can see
   this vault. It has happened twice: D3053 (re-pinned T-3-source after a "stale
   address") and D3197/D3212, whose 41 mid-file lines were relocated here by D3215.

   THE RULE: ADDITIONS TO THIS FILE ARE APPEND-ONLY, BELOW THIS NOTICE. Never insert,
   never reflow, never re-order above it. If a rule genuinely must change in place,
   that is a re-pin — run both branches' `scripts/assert-p1-fences.mjs` and
   `scripts/derive-p3-lift.mjs --check` before and after, and carry the enumeration
   change in the same ruling. Deleting a line shifts the file exactly as insertion
   does. After ANY edit here, run both branches' fence scripts.

   WHAT APPEND-ONLY CANNOT SAVE YOU FROM. `p3-supplement-enumeration.json` also carries
   a `sources` array holding this file's WHOLE-FILE sha256 and line count (pinned at
   `0a289b4a…`, 488 lines — the state before D3197). That is a content digest, not a
   line address, so ANY addition moves it and `derive:p3-lift --check` exits 1 even
   when all 102 rules regenerate byte-for-byte. Clearing it needs a `--write` re-derive
   ON the launcher branch, which is a ruling for that branch's owner. Append-only keeps
   the two fence limbs and the rule-level census green; it cannot keep a file digest
   still. Expect `--check` to stay red until the re-derive is ruled.

   This notice sits at the END and not the top because a top-of-file comment would
   itself shift every pinned line — the precise defect it exists to prevent. */

/* ── DECISIONS 50/52/54 · THE INVITATIONS LIST ────────────────────────────────
   Three rules, all on a class used nowhere else, and NOT ONE of them is authored:
   each adopts a treatment the estate has already ruled on somewhere else.

   POSITION: this block was authored mid-file by D3212 and RELOCATED here, unchanged,
   by D3215. Its rules are all descendants of `.lx-invlist`, which no other rule in
   this sheet names, and this sheet loads after `portal.css`; the move therefore
   cannot change which declaration wins on any element. Proven, not asserted: all 45
   frames in `frames.json` rendered at 390 and 1440 before and after the move,
   90 of 90 rasters byte-identical by sha256.

   (a) THE INLINE LOCATION LINK. `portal.css` has no bare anchor reset, so an
       unstyled <a> in a row would render browser-default blue with an underline.
       The estate's own inline-link treatment is portal.css:1955
       (`.pfx .proofmeta .pf1 a`), scoped to a surface these pages do not use. It is
       adopted here verbatim rather than rediscovered — the same move launcher-w2.css
       makes at :128 for the `--fg3` email ink.

   (b) THE ≤700 RESERVE. portal.css:2293 sets `.prow .pmain { padding-right: 92px }`
       inside the ≤700 block to clear a `.statuspill` that :2295 pins top-right. This
       surface pins none — every state chip rides `.pr`, which unpins at the same
       breakpoint (:2294) — so the reserve would take 92px out of a 342px text column
       for a pill that is not there. Wave 2 made exactly this cure for exactly this
       reason (launcher-w2.css:120-122); this is the same cure, scoped to this list.

   (c) THE PINNED `.statuspill`. An `Accepted 21 Aug 2026` chip is a `.statuspill`
       (§01.6's green role) and :2295 would absolutely position it over the location
       name at ≤700. Unpinning it is the completion of (b): if the reserve goes, the
       thing the reserve existed for must go with it, or the cure creates the overlap
       it was removing the space for.

   (d) THE EMAIL AND DATE INK. Wave-2 §05.3, ruled: "The date and the email both take
       --fg3, not --muted" — `.prow .pmain .d` ships at --muted (portal.css:1245),
       which that sheet records as 3.29:1, AA-large only. An email address on this
       page is the single most meaningful small text on it. Same rule, same reason.

   TOMBSTONE. D3197 also added a `.lx-invacts` rule here, for the owner-invitation
   action row that decision 51 put on the all-locations table. Decision 52 reversed
   that layout and D3212 deleted the rule but left its six-line note behind, where it
   read as a preamble to the pre-existing `.atable td.arw`. D3215 removed the orphan
   with the rest of the mid-file insertion. Nothing styles `.lx-invacts`; nothing
   emits it. */
.lx-invlist .prow .pmain .n a { color: var(--green-quiet); text-decoration: none; border-bottom: 1px solid color-mix(in oklch, var(--green) 30%, transparent); }
.lx-invlist .prow .pmain .d { color: var(--fg3); }
@media (max-width: 700px) {
  .lx-invlist .prow .pmain { padding-right: 0; }
  .lx-invlist .prow .pr .statuspill { position: static; }
}
