/* ===========================================================================
 * pro-system.css — the Cribfolio Pro dashboard visual system.
 *
 * 2026-08-31. Lux: "we helped make the agents page much more stylish looking,
 * fitting the app more… we need to make sure the LO and agency and Lender
 * Brokerages all have the new brighter, more stylish look like the agent page…
 * follow our color rules and take the lead from agent."
 *
 * WHAT THIS IS
 * ------------
 * dashboard.html grew a design system — zones, coloured section titles, a 4px
 * gradient rail down the left of every card, gradient step cards, and a sidebar
 * that collapses to icons. It lives in that file's inline <style>, gated behind
 * `html.v2`. team.html — the COMPANY dashboard, which serves the agency,
 * mortgage-lending and brokerage vocabularies — had NONE of it. Measured on
 * 2026-08-31: class="dash", class="side", class="navi", class="panel",
 * data-zone, html.v2 — zero hits, all of them.
 *
 * This file is that system, extracted, so a second dashboard page can use it.
 *
 * WHY IT IS NOT IN styles.css
 * ---------------------------
 * styles.css is shared with join.html, login.html, see-yourself.html and
 * get-started.html. Putting a dashboard shell there restyles four pages nobody
 * asked about. dashboard.html:1638-1641 says the same thing, and
 * test/proV2FlagIsReversible asserts styles.css stays clean.
 *
 * WHY IT IS A MIRROR AND NOT A MOVE
 * ---------------------------------
 * Deleting the inline block out of dashboard.html would restyle the one page
 * Lux is happy with, overnight, with nobody watching. So the values are
 * duplicated ONCE, deliberately, and
 * test/theTwoDashboardsShareOneSystem.test.js reads them out of dashboard.html
 * and fails if this file disagrees. Same discipline as the headerFit and
 * readableAccent ports.
 *
 * ⛔ NO `.v2` SELECTOR MAY APPEAR IN THIS FILE. proV2FlagIsReversible governs
 *    that flag and only dashboard.html is allowed to carry it.
 *
 * The law this implements is _CANON/PRO_PORTAL_DESIGN_SYSTEM.md.
 * ======================================================================== */

/* ── 1 · ZONES ─────────────────────────────────────────────────────────────
 * A section does not choose its own colour. It is stamped data-zone="<key>"
 * and inherits five variables. Assignments are MEANINGS, not decoration
 * (dashboard.html:1801-1806): cyan is the pro's own colour, purple is people,
 * teal is the company green, lime always means "this costs money", orange is
 * warm on purpose so it breaks the cool run while scrolling.
 *
 * ⛔ THE TRAP, PAID FOR ONCE (dashboard.html:1816-1825): a panel stamped with a
 *    key that has no var row renders a WHITE border and NO left bar — an
 *    undefined custom property makes border-color invalid at computed-value
 *    time and it falls back to currentColor. A new key and its row ship in the
 *    same commit. All five rows below are carried even though this page uses
 *    three, so a later section cannot trip it.
 */
/* The rail's two stop positions, declared once as the defaults every zone gets:
 * the ramp spans the whole bar. A tint that wants to HOLD its colour overrides
 * --zc-rail-from. Declared rather than living only inside a var() fallback — a
 * token nothing defines is a token nobody can find. Same specificity as the rows
 * below (0,1,0), so source order gives those the win. */
[data-zone] { --zc-rail-from: 72%; --zc-rail-stop: 100% }
[data-zone="today"]  { --zc:var(--cyan);   --zc-2:var(--purple); --zc-line:rgba(56,217,245,.40);  --zc-glow:rgba(56,217,245,.16);  --zc-wash:rgba(56,217,245,.055); --zc-edge:rgba(56,217,245,.62); --zc-chip:rgba(56,217,245,.20); --zc-wash2:rgba(56,217,245,.10); --zc-edge-b:rgba(176,144,255,.62); }
[data-zone="people"] { --zc:var(--purple); --zc-2:var(--cyan);   --zc-line:rgba(176,144,255,.40); --zc-glow:rgba(176,144,255,.16); --zc-wash:rgba(176,144,255,.055); --zc-edge:rgba(176,144,255,.62); --zc-chip:rgba(176,144,255,.20); --zc-wash2:rgba(176,144,255,.10); --zc-edge-b:rgba(56,217,245,.62); }
[data-zone="homes"]  { --zc:var(--orange); --zc-2:var(--pink);   --zc-line:rgba(255,136,64,.38);  --zc-glow:rgba(255,136,64,.15);  --zc-wash:rgba(255,136,64,.05); --zc-edge:rgba(255,136,64,.58); --zc-chip:rgba(255,136,64,.19); --zc-wash2:rgba(255,136,64,.095); --zc-edge-b:rgba(255,107,154,.58); }
[data-zone="gifting"]{ --zc:var(--lime);   --zc-2:var(--teal);   --zc-line:rgba(204,255,53,.34);  --zc-glow:rgba(204,255,53,.14);  --zc-wash:rgba(204,255,53,.045); --zc-edge:rgba(204,255,53,.54); --zc-chip:rgba(204,255,53,.17); --zc-wash2:rgba(204,255,53,.085); --zc-edge-b:rgba(46,232,160,.54); }
[data-zone="brand"]  { --zc:var(--teal);   --zc-2:var(--cyan);   --zc-line:rgba(46,232,160,.40);  --zc-glow:rgba(46,232,160,.16);  --zc-wash:rgba(46,232,160,.055); --zc-edge:rgba(46,232,160,.62); --zc-chip:rgba(46,232,160,.20); --zc-wash2:rgba(46,232,160,.10); --zc-edge-b:rgba(56,217,245,.62); }

/* ── 2 · THE RAIL ──────────────────────────────────────────────────────────
 * A BACKGROUND LAYER, never a pseudo-element, so border-radius clips it into
 * the corners with no overflow:hidden — and overflow:hidden is forbidden on a
 * card because it kills position:sticky on anything inside (the live preview
 * column on this page is sticky). dashboard.html:1857-1875.
 *
 * .card2 is this page's name for .panel. Both are listed so the file works on
 * either vocabulary rather than forcing a rename across 14 call sites.
 */
/* ── ⭐ 2026-08-31 (second pass) · COLOUR UP, NOT DOWN ──────────────────────
 * Lux, on the first pass: "why does it look like you stripped all of the color
 * out of the page when I said to add more!?! it looks super dull now… we wanted
 * to keep all of this existing color and ADD to it. adding color titles, color
 * outlines around the boxes with the bright side colors."
 *
 * The first pass copied dashboard.html's card exactly — a 4px rail, a .40 border
 * and a .055 wash. Faithful, and too quiet for THIS page, which opens on a band
 * of saturated KPI tiles: everything under them read as grey by comparison.
 * Three moves, all in the section's own colour so nothing new is invented:
 *   · the OUTLINE is the section colour at .62, not a hint of it at .40;
 *   · the wash is ~2x, so the card body carries the hue instead of the edge
 *     carrying it alone;
 *   · the rail is 5px and gets a glow, so it reads as the card's own edge.
 * Every existing colour on the page — the KPI band, the flame strip, the eight
 * stat cards, the leaderboard — is untouched. This is added, not swapped. */
.card2[data-zone], .panel[data-zone],
[data-zone] > .card2, [data-zone] > .panel {
  position: relative;
  /* ⭐ 2026-08-31 · THE OUTLINE AND THE RAIL NOW SHARE ONE AXIS.
   * Lux: "the side bar gradients and the outline need to line up. so the outline
   * needs to turn to cyan in the lower left corner to blend."
   * Wrong by construction until now: the rail was a GRADIENT and the outline a
   * FLAT colour, so on EVERY zoned card the bottom-left corner had the rail
   * arriving in one hue and the outline sitting in another.
   * border-color cannot hold a gradient, so the border goes transparent at 1px
   * and a border-box background layer paints the ring instead. It uses the SAME
   * angle and the SAME two stop positions as the rail, one step quieter (edge
   * alphas, not solid), so the two are co-linear and meet exactly at the corner.
   * THE OPAQUE --card LAYER IS LOAD-BEARING: the wash's first stop is
   * semi-transparent, and without it the ring bleeds across the whole card. */
  border-color: transparent;
  box-shadow: inset 1px 0 0 0 var(--zc-edge, var(--zc-line)), 0 2px 22px -12px var(--zc-glow);
  /* THE RAIL HAS ITS OWN TWO STOPS, and they default to the zone pair — so
   * every card that does not override them is unchanged, byte for byte.
   * They exist because the rail's FIRST stop and the card's title are the same
   * variable (--zc), and Lux asked for a rail that starts yellow above a title
   * that is green. One variable cannot be two colours. Fallback, not cycle:
   * --zc-rail-a naming --zc is a different property, not itself.
   * --zc-rail-stop is WHERE the second colour has fully arrived. At the default
   * 100% the ramp spans the whole rail, which is the two-stop gradient this rule
   * has always drawn. Lux: "go to yellow at the top but keep it majority green"
   * — that is a stop position, not a second colour. */
  background:
    linear-gradient(180deg,
      var(--zc-rail-a, var(--zc))   var(--zc-rail-from, 0%),
      var(--zc-rail-b, var(--zc-2)) var(--zc-rail-stop, 100%)) left center / 5px 100% no-repeat padding-box,
    linear-gradient(150deg, var(--zc-wash2, var(--zc-wash)), transparent 58%) padding-box,
    linear-gradient(var(--card), var(--card)) padding-box,
    linear-gradient(180deg,
      var(--zc-edge-a, var(--zc-edge, var(--zc-line))) var(--zc-rail-from, 0%),
      var(--zc-edge-b, var(--zc-edge, var(--zc-line))) var(--zc-rail-stop, 100%)) border-box;
  background-origin: border-box;
}
.card2[data-zone] { padding-left: 23px }          /* clear the 5px rail + its optical gap */
.card2[data-zone]:hover, .panel[data-zone]:hover {
  box-shadow: inset 1px 0 0 0 var(--zc-edge, var(--zc-line)), 0 14px 44px var(--zc-glow);
}

/* ── THE CARD'S OWN TITLE TAKES THE SECTION COLOUR ────────────────────────
 * Lux: "adding color titles… so it's not all white and black."
 * dashboard.html keeps these white because its zone <h2> is the only coloured
 * heading on that page. Here the card title IS the thing you read — this page
 * is nine cards and no charts — so it carries the colour, and the icon chip
 * beside it is filled and outlined in the same hue instead of sitting in grey.
 * Contrast on --card at these values: cyan 10.4:1, purple 6.9:1, lime 15.0:1,
 * teal 11.1:1, orange 8.2:1 — all clear of AA for large text, all clear of
 * 4.5:1 for body. */
[data-zone] > .card2 > .ch > h2,
.card2[data-zone] > .ch > h2 { color: var(--zc) }
[data-zone] > .card2 > .ch > .cicon,
.card2[data-zone] > .ch > .cicon {
  color: var(--zc);
  background: var(--zc-chip, var(--zc-glow));
  border-color: var(--zc-edge, var(--zc-line));
}
/* The meta on the right of a card header is a count or a caption — it stays
 * quiet on purpose, or the header becomes two competing colours. */
[data-zone] > .card2 > .ch > .cmeta,
.card2[data-zone] > .ch > .cmeta { color: var(--muted) }

/* ── ⭐ EVERY BOX ITS OWN COLOUR ────────────────────────────────────────────
 * Lux: "different box colors and side bars!! you made it so boring!!"
 *
 * A section's cards alternate between the zone's TWO colours instead of all
 * wearing the first one. The pair is already the section's own gradient — the
 * rail runs --zc to --zc-2 — so the group still reads as a group while no two
 * neighbouring boxes look alike. This is the same idea as the eight stat boxes
 * (cyan / purple / mint / lime), applied one level up.
 *
 * Swapping the four tokens in one place means every rule below — outline, rail,
 * wash, title, icon chip, inner .fgroup — follows automatically.
 */
/* ⛔ NOT A SWAP. The first attempt was
 *     --zc-swap: var(--zc); --zc: var(--zc-2); --zc-2: var(--zc-swap);
 * and it produced EMPTY values on every alternate card. Custom properties
 * resolve lazily, so --zc-swap pointing at a --zc that is being redefined in
 * the same block is a REFERENCE CYCLE, and the spec makes every property in
 * the cycle invalid-at-computed-value-time. Measured: the .duo came back with
 * `--zc: ""`. Each row is written out in full instead — eight literals beat
 * one clever line that silently resolves to nothing. */
[data-zone="today"].zcAlt   { --zc:var(--purple); --zc-2:var(--cyan);   --zc-edge:rgba(176,144,255,.62); --zc-chip:rgba(176,144,255,.20); --zc-wash2:rgba(176,144,255,.10); --zc-glow:rgba(176,144,255,.16); --zc-edge-b:rgba(56,217,245,.62); }
[data-zone="people"].zcAlt  { --zc:var(--cyan);   --zc-2:var(--purple); --zc-edge:rgba(56,217,245,.62);  --zc-chip:rgba(56,217,245,.20);  --zc-wash2:rgba(56,217,245,.10);  --zc-glow:rgba(56,217,245,.16); --zc-edge-b:rgba(176,144,255,.62); }
[data-zone="homes"].zcAlt   { --zc:var(--pink);   --zc-2:var(--orange); --zc-edge:rgba(255,107,154,.58); --zc-chip:rgba(255,107,154,.19); --zc-wash2:rgba(255,107,154,.095); --zc-glow:rgba(255,107,154,.15); --zc-edge-b:rgba(255,136,64,.58); }
[data-zone="gifting"].zcAlt { --zc:var(--teal);   --zc-2:var(--lime);   --zc-edge:rgba(46,232,160,.60);  --zc-chip:rgba(46,232,160,.20);  --zc-wash2:rgba(46,232,160,.10);  --zc-glow:rgba(46,232,160,.16); --zc-edge-b:rgba(204,255,53,.60); }
[data-zone="brand"].zcAlt   { --zc:var(--cyan);   --zc-2:var(--teal);   --zc-edge:rgba(56,217,245,.62);  --zc-chip:rgba(56,217,245,.20);  --zc-wash2:rgba(56,217,245,.10);  --zc-glow:rgba(56,217,245,.16); --zc-edge-b:rgba(46,232,160,.62); }

/* ── A CARD MAY STEP OUT OF THE ALTERNATION AND TAKE ITS OWN COLOUR ────────
 * Lux, 2026-08-31: "the side gradient bar can be yellow to green… with green
 * title."
 *
 * TWO STOPS, TWO JOBS. The title, icon chip, outline and glow all read --zc and
 * are TEAL — canon §2's "agency/company green", which is the right word on the
 * COMPANY dashboard. The rail is set separately, lime at the top running into
 * teal at the bottom, so it rhymes with the city bars' teal->lime ramp a few
 * pixels below it. Without --zc-rail-a the rail would have to start on the
 * title's colour and the two asks could not both be met.
 *
 * NOT LIME AS THE IDENTITY: canon §2 keeps lime for "this costs money". Lime is
 * a gradient STOP here, the way it already is on the city bars — decoration.
 * The colour a thing IS carries the meaning.
 *
 * Same specificity as the .zcAlt rows above (0,2,0), so this MUST stay below
 * them — a duplicate declared above an equal-specificity rule loses, which this
 * repo has already paid for once.
 * Eight literals plus the two rail stops, and no var() naming a property this
 * block redefines: that is the reference cycle documented above, and it
 * resolves to nothing. */
[data-zone].zcGreen  { --zc:var(--teal); --zc-2:var(--lime); --zc-rail-a:var(--teal); --zc-rail-b:var(--lime); --zc-rail-from:55%; --zc-edge-a:rgba(46,232,160,.62); --zc-edge-b:rgba(204,255,53,.62); --zc-line:rgba(46,232,160,.40); --zc-glow:rgba(46,232,160,.16); --zc-wash:rgba(46,232,160,.055); --zc-edge:rgba(46,232,160,.62); --zc-chip:rgba(46,232,160,.20); --zc-wash2:rgba(46,232,160,.10); }

/* ── THE TWO TINTS ARE MIRRORS ─────────────────────────────────────────────
 * .zcGreen holds TEAL to 55% then runs to lime; .zcYellow holds LIME to 55%
 * then runs to teal. Same hold, opposite ends, so a section reads as ONE colour
 * with the other only arriving at the foot of the rail — and whichever card
 * wears which, the pair always looks like one system.
 *
 * 2026-08-31, second pass. Lux moved them: "at a glance, market coverage and
 * top producers boxes YELLOW primarily and branding green primarily." Only the
 * AG_TINT assignment changed — the rows keep their honest names, so nothing is
 * called Green while drawing yellow.
 *
 * ⭐ AND THIS IS WHY LIME IS SAFE HERE. Canon §2 keeps lime for "this costs
 * money", and the money lives in the GIFTING zone — which exists on
 * dashboard.html and NOT on this page. dashboard.html's gifting stays lime and
 * untouched, so the rule still holds everywhere it applies. Lux chose this over
 * swapping the personal dashboard, which would have inverted it. */

/* PRIMARILY YELLOW, SOME GREEN — Lux, on the brand section.
 * The green may only arrive near the BOTTOM, and that needs a HELD first colour:
 * a two-stop ramp from 0% is half-mixed by halfway down and reads olive, not
 * yellow. Hence --zc-rail-from. Lime is the IDENTITY here, not just a stop —
 * the one place canon §2 says it may not go. It is allowed because team.html has
 * no gifting zone, so nothing on this page claims "costs money" in lime, and
 * because this is a per-card tint: the mirrored [data-zone="brand"] row is
 * untouched and dashboard.html keeps teal. */
[data-zone].zcYellow { --zc:var(--lime); --zc-2:var(--teal); --zc-rail-a:var(--lime); --zc-rail-b:var(--teal); --zc-rail-from:55%; --zc-edge-a:rgba(204,255,53,.62); --zc-edge-b:rgba(46,232,160,.62); --zc-line:rgba(204,255,53,.40); --zc-glow:rgba(204,255,53,.16); --zc-wash:rgba(204,255,53,.055); --zc-edge:rgba(204,255,53,.62); --zc-chip:rgba(204,255,53,.20); --zc-wash2:rgba(204,255,53,.10); }


/* ── 3 · ZONE HEADINGS — the only coloured heading level on the page ───────
 * The h2 takes --zc, which is the same colour as the 4px edge on every card
 * beneath it. That pairing is what makes the grouping readable at a glance.
 * Card <h2>/<h3> titles stay white. AA on --bg: cyan 11.0, purple 7.3,
 * lime 15.9, teal 11.7. (dashboard.html:1763-1786.)
 */
/* A grey hairline under a coloured title is the section changing colour and
 * then apologising for it. The rule is the section's colour, fading out. */
.zoneHead { margin: 42px 0 18px; padding: 0 0 14px 2px; border-bottom: 0; position: relative }
.zoneHead::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 2px; border-radius: 2px;
  background: linear-gradient(90deg, var(--zc, var(--line)), var(--zc-2, var(--line)) 34%, transparent 92%);
}
.zoneHead:first-child { margin-top: 8px }
.zh-t { font-size: 22px; font-weight: 900; letter-spacing: -.5px; line-height: 1.15; color: var(--txt); margin: 0 }
.zoneHead[data-zone] .zh-t { color: var(--zc) }
.zh-s { font-size: 13px; font-weight: 600; color: var(--muted); max-width: 660px; margin: 5px 0 0 }
@media (max-width: 700px) {
  .zoneHead { margin: 32px 0 14px }
  .zh-t { font-size: 19px }
}

/* ── 4 · THE SIDEBAR ───────────────────────────────────────────────────────
 * .dash / .side / .navi shells come from styles.css:285-291, which this page
 * already links. These are the dashboard.html refinements on top.
 */
/* ⭐ 2026-08-31 · 214 -> 192. Lux: "can we make the side bar a little thinner
 * when expanded."
 *
 * MEASURED, not guessed, because the floor here is a wrapped word and this repo
 * forbids both ellipsis and word-splitting (nothingTruncatesAndNoWordSplits).
 * The widest label on THIS page is "Administrators" at 108px; the link adds
 * 13px padding + a 17px icon + an 11px gap + 13px padding = 54px; the aside adds
 * 12px each side. Floor = 108 + 54 + 24 = 186px. 192 leaves headroom for the
 * active row, which is weight 900 and measures wider than the 800 it is at rest.
 *
 * The old 214 came from dashboard.html, where "Reached out" carries a count chip
 * beside it and wrapped at 196. That label does not exist on this page, and only
 * team.html links this file — so sizing it for the labels it actually has is
 * correct rather than a regression against dashboard. */
.dash { grid-template-columns: 192px 1fr }
.side { padding: 18px 12px }
.side .navi { gap: 2px; margin-top: 16px }
.side .navi a { padding: 9px 11px; font-size: 13.5px; gap: 9px; font-weight: 800 }
.side .navi a.on { font-weight: 900 }
.side .brandmark, .side .sideLogo { width: 116px; height: auto; display: block }
/* ⭐ CENTRED IN BOTH STATES, FROM ONE RULE. Lux: "center the logo uptop in
 * expanded and contracted view." `.side` is display:block, so the collapsed
 * rail's `align-items:center` never applied to it — centring has to come from
 * the wrap, which is the flex box. One declaration covers 192px and 72px, so
 * the two states cannot drift apart. */
.side .brandwrap { justify-content: center }
@media (min-width: 901px) {
  .side { position: sticky; top: 0; align-self: start; height: 100vh; min-height: 0; overflow-y: auto }
  .side .acctCard { margin-top: auto }
}
@media (max-width: 900px) {
  .dash { grid-template-columns: 1fr }
  .side { position: fixed; top: 0; left: 0; bottom: 0; width: min(284px,84vw); z-index: 60;
          transform: translateX(-100%); transition: transform .22s ease }
  body.drawer-open .side { transform: translateX(0) }
}

/* Group labels. Title-case in the data, uppercased here. */
.navi .navGroup { font-size: 9.5px; font-weight: 800; letter-spacing: 1.9px; text-transform: uppercase;
                  color: var(--muted); opacity: .72; margin: 15px 0 5px; padding-left: 12px }
.navi .navGroup:first-child { margin-top: 6px }
.navi a svg { flex-shrink: 0 }
/* ⚠️ A label MUST live in a span. A bare text node cannot be hidden by CSS,
 * which is the entire reason this class exists (dashboard.html:4052-4075). */
/* No ellipsis. test/nothingTruncatesAndNoWordSplits scans every stylesheet for
 * one and it is right to: "shrink it, wrap it, or say less — never hide a
 * character of the user's data". These labels are OURS and short, so the
 * honest fix is to not need the ellipsis rather than to waive the rule. */
.navLbl { min-width: 0; white-space: nowrap }
.navcount { margin-left: auto; font-size: 11px; font-weight: 900; background: rgba(255,255,255,.06);
            border: 1px solid var(--line); border-radius: 20px; padding: 1px 8px; min-width: 22px; text-align: center }
.navi a.on .navcount { background: rgba(56,217,245,.16); border-color: rgba(56,217,245,.4); color: var(--cyan) }

/* The collapse control. Desktop only — on a phone the sidebar is a drawer and
 * a rail would be a third state nobody asked for. */
.navMiniBtn { display: none }
@media (min-width: 901px) {
  .navMiniBtn { display: flex; align-items: center; justify-content: center; width: 38px; height: 38px;
                border-radius: 10px; border: 1px solid var(--line); background: transparent;
                color: var(--muted); cursor: pointer; margin: 10px auto 16px; transition: color .15s, border-color .15s }
  .navMiniBtn:hover { color: #fff; border-color: rgba(255,255,255,.22) }
}

/* ⚠️ CLASS ORDER IS LOAD-BEARING elsewhere in this codebase
 * (test/proV2FlagIsReversible reads selectors literally). Written
 * html.navmini here — there is no second class to order it against. */
html.navmini .dash { grid-template-columns: 72px 1fr }
html.navmini .side { padding: 18px 8px; align-items: center }
html.navmini .side .brandmark, html.navmini .side .sideLogo { width: 40px }
html.navmini .side .navi a { justify-content: center; padding: 10px 0 }
html.navmini .navGroup,
html.navmini .navLbl,
html.navmini .portalTag,
html.navmini .navcount { display: none }
html.navmini .navMiniBtn svg { transform: rotate(180deg) }
html.navmini .side .navi a { position: relative }
html.navmini .side .navi a[data-label]::after {
  content: attr(data-label); position: absolute; left: calc(100% + 10px); top: 50%; transform: translateY(-50%);
  background: var(--card-2,#141a2c); border: 1px solid var(--line); border-radius: 8px; padding: 5px 9px;
  font-size: 12.5px; font-weight: 700; color: #fff; white-space: nowrap; opacity: 0; pointer-events: none;
  transition: opacity .12s; z-index: 70;
}
html.navmini .side .navi a:hover[data-label]::after { opacity: 1 }

/* ── 5 · STEP CARDS — the gradient box + rounded number chip ────────────────
 * dashboard.html:2089-2131. The chip is a rounded SQUARE (radius 10), not a
 * disc — the disc belongs to .ss-n, the 4-up onboarding guide, and mixing the
 * two makes two different things look like one.
 */
.agStep { position: relative; border: 1px solid var(--line); border-radius: 16px;
          background: linear-gradient(180deg, rgba(255,255,255,.022), rgba(255,255,255,0));
          padding: 16px 17px 17px; margin: 0 0 12px; overflow: hidden }
.agStep::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px }
.agStep.s1 { border-color: rgba(56,217,245,.26) }
.agStep.s2 { border-color: rgba(176,144,255,.26) }
.agStep.s3 { border-color: rgba(46,232,160,.26) }
.agStep.s1::before { background: linear-gradient(180deg, var(--cyan), rgba(56,217,245,.12)) }
.agStep.s2::before { background: linear-gradient(180deg, var(--purple), rgba(176,144,255,.12)) }
.agStep.s3::before { background: linear-gradient(180deg, var(--teal), rgba(46,232,160,.12)) }
.agStepHd { display: flex; gap: 13px; align-items: flex-start }
.agStepN { flex-shrink: 0; width: 27px; height: 27px; border-radius: 10px; display: flex; align-items: center;
           justify-content: center; font-size: 13.5px; font-weight: 900; color: #0b0718;
           font-variant-numeric: tabular-nums; margin-top: 1px }
.agStep.s1 .agStepN { background: linear-gradient(135deg, var(--cyan), #7fe8ff) }
.agStep.s2 .agStepN { background: linear-gradient(135deg, var(--purple), #d3b8ff) }
.agStep.s3 .agStepN { background: linear-gradient(135deg, var(--teal), var(--lime)) }
.agStepT { font-size: 17px; font-weight: 900; letter-spacing: -.3px; color: #fff }
.agStepS { font-size: 12.5px; font-weight: 600; color: var(--muted); max-width: 52ch; margin-top: 3px }
.agStepAct { margin-top: 11px }
@media (max-width: 700px) {
  .agStep { padding: 14px 14px 15px; border-radius: 14px }
  .agStepT { font-size: 15.5px }
}

/* ── 6 · GROUPED FIELD BLOCK ───────────────────────────────────────────────
 * 2026-08-31. Lux, of the Client emails section: "feels like a block of type
 * and messy. we should have the 2 emails outlined in a box separating the 2
 * email groups."
 *
 * A .fgroup is an outlined box around the fields that belong to ONE thing, so
 * a stack of eight inputs reads as two decisions instead of eight chores.
 */
.fgroup { position: relative; border: 1px solid var(--line); border-radius: 14px; padding: 15px 16px 4px;
          margin: 0 0 12px; overflow: hidden }
.fgroup + .fgroup { margin-top: 12px }
/* A 3px edge in the zone's colour, INHERITED from the card this sits inside —
 * the same language as the card's own 4px rail, one step quieter because it is
 * one level down. A background layer for the same reason the card's is: it gets
 * clipped by the radius with no overflow:hidden on anything that scrolls. The
 * var() fallbacks matter — this class is also usable outside a zoned card, and
 * an undefined custom property in a gradient kills the whole declaration. */
.fgroup { background:
    linear-gradient(180deg, var(--zc, var(--cyan)), var(--zc-2, var(--purple))) left center / 3px 100% no-repeat,
    rgba(255,255,255,.028) }
.fgroup > .fgTitle { display: flex; align-items: center; gap: 9px; font-size: 15px; font-weight: 900;
                     color: #fff; letter-spacing: -.2px; margin: 0 0 3px }
/* The tag is the USEFUL half of the title — it is the bit that says which email
 * this is — so it carries the zone colour instead of sitting in grey beside it. */
.fgroup > .fgTitle .fgTag { font-size: 9.5px; font-weight: 900; letter-spacing: 1.2px; text-transform: uppercase;
                            padding: 3px 8px; border-radius: 20px; color: var(--zc, var(--cyan));
                            background: color-mix(in srgb, var(--zc, #38D9F5) 13%, transparent);
                            border: 1px solid color-mix(in srgb, var(--zc, #38D9F5) 38%, transparent) }
.fgroup > .fgSub { font-size: 12.5px; font-weight: 600; color: var(--muted); margin: 0 0 12px; max-width: 62ch }

/* ── 9 · A SUB-SECTION INSIDE A CARD ───────────────────────────────────────
 * Marked with the same bar the card carries, so the eye reads it as a DIVISION
 * of this card rather than as the title of another one. "Client emails" was an
 * unclassed inline-styled div — the only sub-heading on the page with no shell
 * vocabulary at all, and it read as a wall of type starting. */
.subHead { position: relative; font-size: 16px; font-weight: 900; letter-spacing: -.2px; color: #fff;
           padding-left: 12px; margin: 0 0 5px; line-height: 1.2 }
.subHead::before { content: ""; position: absolute; left: 0; top: 2px; bottom: 2px; width: 4px; border-radius: 3px;
                   background: linear-gradient(180deg, var(--zc, var(--cyan)), var(--zc-2, var(--purple))) }
.subSub { font-size: 12.5px; font-weight: 600; color: var(--muted); line-height: 1.55; margin: 0 0 13px; padding-left: 12px }

/* ── 7 · SHELL CHROME the sidebar needs ────────────────────────────────────*/
.portalTag { display: block; font-size: 10px; font-weight: 900; letter-spacing: 3px;
             text-transform: uppercase; color: var(--teal); margin: 6px 0 0; text-align: center }

/* The phone's way in. Hidden on desktop, where the sidebar is always present. */
.navDrawerBtn { display: none }
@media (max-width: 900px) {
  .navDrawerBtn { display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px;
                  border-radius: 10px; border: 1.5px solid var(--line); background: var(--card);
                  color: var(--txt-2); cursor: pointer }
  .navDrawerBtn:hover { color: var(--cyan); border-color: rgba(56,217,245,.5) }
  .scrim { position: fixed; inset: 0; background: rgba(0,0,0,.55); z-index: 55 }
  .scrim[hidden] { display: none }
}
@media (min-width: 901px) { .scrim { display: none } }

/* .main already pays the page's horizontal padding (styles.css:292). .tw is the
 * READING MEASURE and must not pay it twice, or the column drifts right and the
 * gutters stop matching the sidebar's. */
.main > .tw { padding-left: 0; padding-right: 0; padding-top: 0; margin: 0; max-width: 1080px }
@media (max-width: 900px) { .main { padding: 20px 18px 60px } }

/* A page with no sections — the join screen, the member screen, an error card —
 * gets no rail. A list of one link beside a single card is furniture.
 *
 * DEFECT 2026-08-31 — the phone's hamburger was NOT in this list, so on those
 * screens it opened a drawer containing a logo and nothing else. The line above
 * used to justify keeping it by saying the sidebar carries "the only sign-out on
 * the page" — true of dashboard.html and FALSE of team.html, where the account
 * menu mounts into #cfAcctSlot in the top bar. A control that opens an empty
 * panel is worse than no control: it reads as a broken menu.
 *
 * The sidebar itself stays. It carries the logo and the portal tag, and pulling
 * a whole column out mid-session is a bigger jolt than a quiet rail. */
.dash.noNav .navi,
.dash.noNav .navMiniBtn,
.dash.noNav .navDrawerBtn { display: none }
