/* ============================================================================
   Client Portal - EXECUTIVE layout (Field Service, Consulting, Vendor)
   ----------------------------------------------------------------------------
   The consulting client is an owner, a CEO or a CFO reading about a business
   they run. This layer gives them the room that audience expects: a brand-ink
   side rail, a page header that names the section, one KPI band, and a
   two-column briefing grid with quiet hairlines instead of floating bubbles.

   Scoped to #app.portal-exec, which renderShell() sets for every signed-in
   shell. The screens before it (sign-in, the boot skeleton, the vendor picker)
   never carry the class and render as portal-skin.css + portal-v2.css draw
   them. The id in every selector is deliberate: it outranks the Daylight layer
   without a single !important.

   WHITE-LABEL. Color and type come from the tenant's own tokens, which
   applyBrand() writes inline on :root: --brand-ink / --brand-on-ink paint the
   rail, --brand-primary marks the active item, --teal is the on-light accent,
   and --f-head / --f-body / --f-num are the tenant's faces. The only literals
   below are the standard-tier canvas and neutral shadows.
   ========================================================================== */

/* ── Canvas ────────────────────────────────────────────────────────────────
   A lighter, neutral page than Daylight's cool gray, and no accent wash: the
   numbers are the color on this surface. A custom-tier pack keeps its own
   --paper. */
html[data-portal="on"]:not([data-brand-tier="custom"]):has(#app.portal-exec) { --paper: #f4f5f7; --card-2: #f7f8fa; --line: #e3e6eb; --line-soft: #edf0f3; }
html[data-portal="on"]:has(#app.portal-exec) body { background: var(--paper); }
html[data-portal="on"]:has(#app.portal-exec) body::before { display: none; }

/* ── Shell grid ────────────────────────────────────────────────────────── */
html[data-portal="on"] #app.portal-exec {
  --px-gut: 48px;
  --px-max: 1240px;
  --px-r: 14px;
  --px-rail-ink: var(--brand-ink, var(--ink, #0d1b20));
  --px-rail-on: var(--brand-on-ink, var(--on-ink, #f0f1f2));
  --px-mark: var(--brand-primary, var(--teal-bright, #12a6c0));
  --px-shadow: 0 1px 2px rgba(16, 24, 40, .04), 0 1px 3px rgba(16, 24, 40, .03);
  max-width: none; margin: 0; padding: 0; min-height: 100vh;
  display: grid;
  grid-template-columns: 264px minmax(0, 1fr);
  grid-template-rows: auto auto auto 1fr;
  grid-template-areas: "bar bar" "rail top" "rail main" "rail foot";
  font-family: var(--f-body);
}
/* The staff preview banner. It lives here rather than in a <style> tag emitted
   by whichever portal happened to render it: two surfaces show it, and the one
   that did not carry the inline rule showed an unstyled sentence across the top
   of the page. */
#app.portal-exec > .portal-preview-bar { grid-area: bar; }
.portal-preview-bar {
  background: linear-gradient(90deg, var(--teal-deep, #0a7285), var(--teal, #0b7e93));
  color: var(--on-teal); text-align: center; font-size: 12.5px; font-weight: 500;
  padding: 8px 14px; letter-spacing: .01em;
  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .18);
}
.portal-preview-bar b { font-weight: 700; }

/* ── Rail ──────────────────────────────────────────────────────────────── */
#app.portal-exec .px-rail {
  grid-area: rail; align-self: start;
  position: sticky; top: 0; height: 100vh; overflow-y: auto;
  display: flex; flex-direction: column;
  padding: 22px 14px 18px;
  background: var(--px-rail-ink); color: var(--px-rail-on);
}
#app.portal-exec .px-rail-brand {
  padding: 2px 10px 22px; margin-bottom: 14px;
  border-bottom: 1px solid color-mix(in srgb, var(--px-rail-on) 10%, transparent);
}
#app.portal-exec .px-rail-brand .brand-lockup { gap: 12px; }
#app.portal-exec .px-rail-brand .brand-mark {
  width: 36px; height: 36px; border-radius: 10px; font-size: 17px; font-weight: 700;
  background: var(--px-mark); color: var(--brand-on-primary, #0d1b20); box-shadow: none;
}
#app.portal-exec .px-rail-brand .brand-mark::after { display: none; }
#app.portal-exec .px-rail-brand .brand-wrap b { color: var(--px-rail-on); font-size: 16px; font-weight: 700; letter-spacing: -.01em; }
#app.portal-exec .px-rail-brand .brand-wrap small,
#app.portal-exec .px-rail-brand .brand-lockup-sub {
  font-family: var(--f-body); text-transform: none; letter-spacing: 0;
  font-size: 12px; font-weight: 500; margin-top: 2px;
  color: color-mix(in srgb, var(--px-rail-on) 58%, transparent);
}

#app.portal-exec .ptabs {
  display: flex; flex-direction: column; gap: 2px;
  margin: 0; padding: 0; overflow: visible;
  background: none; border: 0; border-radius: 0; box-shadow: none;
}
/* The "more tabs this way" cue (assets/portal-v2.css) belongs to a horizontal
   strip. Here the tabs are a vertical rail that never scrolls sideways, so the
   chevron and its backdrop would be a stray mark at the bottom of it. The
   mobile block below, where the rail DOES become a scrolling row, turns them
   back on. */
#app.portal-exec .ptabs::after,
#app.portal-exec .ptabs::before { display: none; }
#app.portal-exec .ptabs button {
  position: relative; width: 100%;
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px; border-radius: 9px;
  font-family: var(--f-body); font-size: 14px; font-weight: 500; text-align: left;
  color: color-mix(in srgb, var(--px-rail-on) 70%, transparent);
  background: transparent; box-shadow: none;
  transition: background .15s, color .15s;
}
#app.portal-exec .ptabs button:hover { color: var(--px-rail-on); background: color-mix(in srgb, var(--px-rail-on) 6%, transparent); }
#app.portal-exec .ptabs button:focus-visible { outline: 2px solid var(--px-mark); outline-offset: 1px; }
#app.portal-exec .ptabs button.on {
  color: var(--px-rail-on); font-weight: 600;
  background: color-mix(in srgb, var(--px-rail-on) 10%, transparent);
  box-shadow: none;
}
#app.portal-exec .ptabs button.on::before {
  content: ''; position: absolute; left: -14px; top: 9px; bottom: 9px; width: 3px;
  border-radius: 0 3px 3px 0; background: var(--px-mark);
}
#app.portal-exec .px-ic { display: grid; place-items: center; flex: none; opacity: .78; }
#app.portal-exec .ptabs button.on .px-ic { opacity: 1; color: var(--px-mark); }
#app.portal-exec .px-ic svg { width: 18px; height: 18px; }
#app.portal-exec .px-lbl { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#app.portal-exec .ptab-badge,
#app.portal-exec .ptabs button.on .ptab-badge {
  margin-left: auto; min-width: 20px; height: 20px; padding: 0 6px; border-radius: 10px;
  font-family: var(--f-num, var(--f-body)); font-size: 11px; font-weight: 700;
  background: var(--px-mark); color: var(--brand-on-primary, #0d1b20);
}
#app.portal-exec .px-rail-foot {
  margin-top: auto; padding: 16px 12px 0;
  display: flex; align-items: center; gap: 8px;
  font-size: 12px; line-height: 1.4;
  color: color-mix(in srgb, var(--px-rail-on) 50%, transparent);
  border-top: 1px solid color-mix(in srgb, var(--px-rail-on) 10%, transparent);
}
#app.portal-exec .px-lock {
  width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: var(--ok, #16a34a);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok, #16a34a) 22%, transparent);
}

/* ── Page header ───────────────────────────────────────────────────────── */
#app.portal-exec .portal-top {
  grid-area: top; position: static; z-index: auto;
  width: 100%; max-width: calc(var(--px-max) + 2 * var(--px-gut)); margin: 0 auto;
  padding: 40px var(--px-gut) 4px;
  display: flex; align-items: flex-start; gap: 24px;
}
#app.portal-exec .portal-top::before { display: none; }
/* The Daylight bar's accent thread, too. It bleeds 18px past each side to cover
   the column's gutter, and it is positioned against `.portal-top` because that
   bar is sticky; here the header is STATIC, so the same rule resolves against
   the viewport and put 18px of overflow on the page (check-visual h-scroll,
   1458px in a 1440px viewport). There is no gutter to cover here anyway. */
#app.portal-exec .portal-top::after { content: none; }
#app.portal-exec .px-head { flex: 1; min-width: 0; }
#app.portal-exec .px-eyebrow { font-size: 13px; font-weight: 500; color: var(--tx-mute); }
#app.portal-exec .px-title {
  margin: 6px 0 0; font-family: var(--f-head); font-size: 34px; line-height: 1.12;
  font-weight: 600; letter-spacing: -.025em; color: var(--head);
}
#app.portal-exec .px-lede { margin: 8px 0 0; font-size: 15px; line-height: 1.55; color: var(--tx-soft); max-width: 64ch; }
#app.portal-exec .portal-top .who { margin-left: auto; display: flex; align-items: center; gap: 10px; padding-top: 2px; flex: none; }
#app.portal-exec .portal-top .who .nm { font-size: 14px; font-weight: 600; color: var(--head); max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#app.portal-exec .px-av {
  width: 34px; height: 34px; border-radius: 50%; flex: none;
  display: grid; place-items: center;
  font-family: var(--f-body); font-size: 12.5px; font-weight: 700; letter-spacing: .02em;
  background: var(--px-rail-ink); color: var(--px-rail-on);
}

/* ── Content column ────────────────────────────────────────────────────── */
#app.portal-exec #panel {
  grid-area: main; min-width: 0;
  width: 100%; max-width: calc(var(--px-max) + 2 * var(--px-gut)); margin: 0 auto;
  padding: 24px var(--px-gut) 0;
}
#app.portal-exec .portal-foot {
  grid-area: foot; align-self: end;
  width: 100%; max-width: calc(var(--px-max) + 2 * var(--px-gut)); margin: 56px auto 0;
  padding: 22px var(--px-gut) 32px;
  border-top: 1px solid var(--line);
}

/* ── Surfaces, type and controls inside the content column ─────────────── */
#app.portal-exec #panel :is(.card, .px-card) {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--px-r); box-shadow: var(--px-shadow);
}
#app.portal-exec .px-card,
#app.portal-exec #panel .card-pad { padding: 22px 24px; }
#app.portal-exec #panel .px-flush { padding: 0; overflow: hidden; }
#app.portal-exec .px-stack { display: flex; flex-direction: column; gap: 24px; }
#app.portal-exec #panel .stack { gap: 18px; }

#app.portal-exec .px-card-h,
#app.portal-exec .ov-sec-h {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin: 0 0 12px;
}
#app.portal-exec .px-card-h h2,
#app.portal-exec #panel .section-label {
  margin: 0; font-family: var(--f-head); font-size: 17px; font-weight: 600;
  letter-spacing: -.01em; text-transform: none; color: var(--head);
}
#app.portal-exec .px-card-sub { font-size: 13px; color: var(--tx-mute); }
#app.portal-exec .ov-sec-link { font-size: 13px; font-weight: 600; color: var(--teal); }
#app.portal-exec .px-count {
  min-width: 24px; height: 24px; padding: 0 8px; border-radius: 12px;
  display: inline-grid; place-items: center;
  font-family: var(--f-num, var(--f-body)); font-size: 12px; font-weight: 700;
  background: var(--warn-soft); color: var(--warn-tx, var(--warn));
}
#app.portal-exec #panel .sub { font-size: 13px; color: var(--tx-mute); }

#app.portal-exec #panel .btn { border-radius: 9px; }
#app.portal-exec #panel .btn-primary,
#app.portal-exec .portal-top .btn-primary {
  background: var(--teal); color: var(--on-teal, #fff);
  box-shadow: 0 1px 2px rgba(16, 24, 40, .1);
}
#app.portal-exec #panel .btn-primary:hover { background: var(--teal-deep); box-shadow: 0 1px 2px rgba(16, 24, 40, .1); }
#app.portal-exec .btn-ghost { background: var(--card); border: 1px solid var(--line); color: var(--head); box-shadow: 0 1px 2px rgba(16, 24, 40, .05); }
#app.portal-exec .btn-ghost:hover { border-color: color-mix(in srgb, var(--tx) 22%, var(--line)); background: var(--card-2); color: var(--head); }

/* ── KPI band ──────────────────────────────────────────────────────────── */
#app.portal-exec .px-ov { display: flex; flex-direction: column; gap: 24px; }
#app.portal-exec .px-kpis,
#app.portal-exec .px-stats {
  display: grid; gap: 1px; overflow: hidden;
  background: var(--line-soft); border: 1px solid var(--line);
  border-radius: var(--px-r); box-shadow: var(--px-shadow);
}
#app.portal-exec .px-kpis { grid-template-columns: repeat(5, minmax(0, 1fr)); }
#app.portal-exec .px-stats { grid-template-columns: repeat(4, minmax(0, 1fr)); }
/* Three measures beside a control, rather than four across the column - the
   Vendor Portal's Hours tab, where the band shares its row with Submit Hours. */
#app.portal-exec .px-stats-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
#app.portal-exec .px-kpi {
  display: flex; flex-direction: column; align-items: stretch; gap: 6px;
  padding: 20px 22px 18px; margin: 0; border: 0; border-radius: 0;
  background: var(--card); color: var(--tx);
  font: inherit; text-align: left;
}
#app.portal-exec button.px-kpi { cursor: pointer; transition: background .15s; }
#app.portal-exec button.px-kpi:hover { background: var(--card-2); }
#app.portal-exec button.px-kpi:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }
#app.portal-exec .px-kpi-l,
#app.portal-exec .px-stat > span {
  display: flex; align-items: center; gap: 7px;
  font-size: 13px; font-weight: 500; color: var(--tx-soft); white-space: nowrap;
}
#app.portal-exec .px-kpi-dot,
#app.portal-exec .px-stat[class*="t-"] > span::before {
  width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--teal);
}
#app.portal-exec .px-stat[class*="t-"] > span::before { content: ''; }
#app.portal-exec .px-kpi.t-ok .px-kpi-dot, #app.portal-exec .px-stat.t-ok > span::before { background: var(--ok); }
#app.portal-exec .px-kpi.t-warn .px-kpi-dot, #app.portal-exec .px-stat.t-warn > span::before { background: var(--warn); }
#app.portal-exec .px-kpi.t-bad .px-kpi-dot, #app.portal-exec .px-stat.t-bad > span::before { background: var(--bad); }
#app.portal-exec .px-kpi.t-info .px-kpi-dot, #app.portal-exec .px-stat.t-info > span::before { background: var(--teal); }
#app.portal-exec .px-kpi-v,
#app.portal-exec .px-stat > b {
  font-family: var(--f-num, var(--f-head)); font-size: 28px; line-height: 1.1; font-weight: 600;
  letter-spacing: -.02em; color: var(--head); font-variant-numeric: tabular-nums;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
#app.portal-exec .px-kpi-v small { font-size: 15px; font-weight: 500; color: var(--tx-mute); margin-left: 2px; letter-spacing: 0; }
#app.portal-exec .px-kpi-s { font-size: 12.5px; color: var(--tx-mute); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#app.portal-exec .px-kpi-bar { display: block; height: 4px; border-radius: 4px; background: var(--line-soft); overflow: hidden; margin: 2px 0 1px; }
#app.portal-exec .px-kpi-bar > span { display: block; height: 100%; border-radius: 4px; background: var(--teal); }
#app.portal-exec .px-stat { display: flex; flex-direction: column; gap: 6px; padding: 18px 22px; background: var(--card); }
#app.portal-exec .px-stat > b { font-size: 26px; }

/* ── Briefing grid ─────────────────────────────────────────────────────── */
#app.portal-exec .px-grid { display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); gap: 24px; align-items: start; }
#app.portal-exec .px-col { display: flex; flex-direction: column; gap: 24px; min-width: 0; }

/* Waiting on You: numbered rows, full-bleed hairlines. */
#app.portal-exec .px-needs { overflow: hidden; padding-bottom: 0; }
#app.portal-exec .px-needs .ov-needs { display: flex; flex-direction: column; gap: 0; margin: 0 -24px; }
#app.portal-exec .ov-need {
  display: flex; align-items: center; gap: 14px;
  padding: 15px 24px; margin: 0; border: 0; border-top: 1px solid var(--line-soft); border-radius: 0;
  background: none; box-shadow: none; transform: none;
  font-family: var(--f-body); font-size: 14.5px; font-weight: 500; color: var(--head); text-align: left;
}
#app.portal-exec .ov-need:hover { background: var(--card-2); transform: none; box-shadow: none; border-color: var(--line-soft); }
#app.portal-exec .px-need-n {
  flex: none; width: 24px; font-family: var(--f-num, var(--f-body)); font-size: 12px; font-weight: 700;
  color: var(--teal); font-variant-numeric: tabular-nums;
}
#app.portal-exec .ov-need.warn .px-need-n { color: var(--warn-tx, var(--warn)); }
#app.portal-exec .ov-need-arrow { display: grid; place-items: center; color: var(--tx-mute); }
#app.portal-exec .ov-need-arrow svg { width: 16px; height: 16px; }
#app.portal-exec .ov-need:hover .ov-need-arrow { color: var(--teal); transform: translate(2px, -2px); }

#app.portal-exec .px-clear { display: flex; align-items: center; gap: 16px; }
#app.portal-exec .px-clear h2 { margin: 0; font-family: var(--f-head); font-size: 16px; font-weight: 600; color: var(--head); }
#app.portal-exec .px-clear p { margin: 3px 0 0; font-size: 14px; line-height: 1.5; color: var(--tx-soft); }
#app.portal-exec .px-clear-ic { width: 40px; height: 40px; flex: none; border-radius: 50%; display: grid; place-items: center; background: var(--ok-soft); color: var(--ok-tx, var(--ok)); }
#app.portal-exec .px-clear-ic svg { width: 18px; height: 18px; }

/* The proposal card: the one decision with money on it, marked by the accent
   edge rather than a colored wash. */
#app.portal-exec .pv-action {
  padding: 22px 24px; border-radius: var(--px-r);
  background: var(--card); border: 1px solid var(--line);
  box-shadow: inset 3px 0 0 var(--teal), var(--px-shadow);
}
#app.portal-exec .pv-action .ic { width: 42px; height: 42px; border-radius: 11px; background: color-mix(in srgb, var(--teal) 10%, var(--card)); color: var(--teal); }
#app.portal-exec .pv-action b { font-size: 17px; font-weight: 600; }
#app.portal-exec .pv-action p b { display: inline; font-family: inherit; font-size: inherit; font-weight: 600; letter-spacing: 0; }

/* Preview sections: full-bleed rows under the section title. */
#app.portal-exec #panel .card-pad:has(> .ov-mini-list) { overflow: hidden; padding-bottom: 0; }
#app.portal-exec .ov-mini-list { margin: 0 -24px; }
#app.portal-exec .ov-mini,
#app.portal-exec button.ov-mini:hover {
  padding: 13px 24px; border-radius: 0; border-bottom: 0; border-top: 1px solid var(--line-soft);
}
#app.portal-exec button.ov-mini:hover { background: var(--card-2); }
#app.portal-exec .ov-mini .mk { width: 7px; height: 7px; box-shadow: none; }
#app.portal-exec .ov-mini .bd b { font-size: 14px; }
#app.portal-exec .ov-mini .bd span { font-size: 12.5px; color: var(--tx-mute); }
#app.portal-exec .ov-mini .rt b { font-weight: 600; font-variant-numeric: tabular-nums; }

/* Current Engagement */
#app.portal-exec .px-spot-body { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 32px; align-items: center; }
#app.portal-exec .px-spot-name { font-family: var(--f-head); font-size: 22px; line-height: 1.2; font-weight: 600; letter-spacing: -.015em; color: var(--head); }
#app.portal-exec .px-spot-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 6px 0 20px; font-size: 13.5px; color: var(--tx-mute); }
#app.portal-exec .px-prog-h { display: flex; justify-content: space-between; gap: 12px; font-size: 13px; color: var(--tx-soft); }
#app.portal-exec .px-prog-h b { font-weight: 600; color: var(--head); font-variant-numeric: tabular-nums; }
#app.portal-exec .px-bar { height: 6px; border-radius: 6px; background: var(--line-soft); overflow: hidden; margin-top: 8px; }
#app.portal-exec .px-bar > span { display: block; height: 100%; border-radius: 6px; background: var(--teal); animation: pxGrow .9s cubic-bezier(.22, 1, .36, 1) both; }
@keyframes pxGrow { from { width: 0; } }
#app.portal-exec .px-next {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: baseline; gap: 2px 16px;
  margin-top: 18px; padding: 14px 16px; border-radius: 10px; background: var(--card-2); border: 1px solid var(--line-soft);
}
#app.portal-exec .px-next-l { grid-column: 1 / -1; font-size: 12px; font-weight: 500; color: var(--tx-mute); }
#app.portal-exec .px-next b { font-size: 14.5px; font-weight: 600; color: var(--head); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#app.portal-exec .px-next-d { font-size: 13px; color: var(--tx-soft); white-space: nowrap; }
#app.portal-exec .px-ring { position: relative; width: 136px; height: 136px; }
#app.portal-exec .px-ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
#app.portal-exec .px-ring circle { fill: none; stroke-width: 9; }
#app.portal-exec .px-ring .trk { stroke: var(--line-soft); }
/* The value arc sits IN the track rather than on top of it: a shadow tinted
   with the arc's own tone lifts it off, and the arc animates its dash so the
   score sweeps up on load instead of appearing fully drawn. currentColor plumbs
   the tone through from the .t-* modifier below, so one shadow rule covers all
   four states instead of repeating per tone. */
#app.portal-exec .px-ring .val {
  stroke: var(--teal); stroke-linecap: round; color: var(--teal);
  filter: drop-shadow(0 2px 5px color-mix(in srgb, currentColor 45%, transparent));
  animation: px-ring-sweep 1.1s cubic-bezier(.22,1,.36,1) both;
}
@keyframes px-ring-sweep { from { stroke-dasharray: 0 100; } }
/* `color` is set alongside `stroke` so the drop-shadow above, which reads
   currentColor, is tinted with the tone actually on screen. */
#app.portal-exec .px-ring.t-ok .val { stroke: var(--ok); color: var(--ok); }
#app.portal-exec .px-ring.t-warn .val { stroke: var(--warn); color: var(--warn); }
#app.portal-exec .px-ring.t-bad .val { stroke: var(--bad); color: var(--bad); }
@media (prefers-reduced-motion: reduce) { #app.portal-exec .px-ring .val { animation: none; } }
#app.portal-exec .px-ring-in { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; }
#app.portal-exec .px-ring-in b { font-family: var(--f-num, var(--f-head)); font-size: 34px; line-height: 1; font-weight: 600; letter-spacing: -.02em; color: var(--head); font-variant-numeric: tabular-nums; }
#app.portal-exec .px-ring-in span { font-size: 12px; font-weight: 500; color: var(--tx-mute); }

/* ── Rows (sessions, deliverables) ─────────────────────────────────────── */
#app.portal-exec .px-rows { display: flex; flex-direction: column; }
#app.portal-exec .px-card:not(.px-flush) > .px-rows { margin: 0 -24px -22px; }
#app.portal-exec .px-row {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 24px; border-top: 1px solid var(--line-soft);
}
#app.portal-exec .px-flush .px-row:first-child { border-top: 0; }
#app.portal-exec .px-row .grow { flex: 1; min-width: 0; }
#app.portal-exec .px-row .grow > b { display: block; font-size: 14.5px; font-weight: 600; color: var(--head); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#app.portal-exec .px-row .grow > .sub { display: block; margin-top: 2px; }
#app.portal-exec .px-row.is-past { opacity: .7; }
#app.portal-exec .px-date {
  flex: none; width: 46px; height: 50px; border-radius: 10px; border: 1px solid var(--line);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px; background: var(--card);
}
#app.portal-exec .px-date b { font-family: var(--f-num, var(--f-head)); font-size: 18px; line-height: 1; font-weight: 600; color: var(--head); }
#app.portal-exec .px-date span { font-size: 11px; font-weight: 600; color: var(--tx-mute); }

/* Deliverables reuse .cs-deliv's pieces as a flat row. */
#app.portal-exec .cs-deliv { border: 0; border-top: 1px solid var(--line-soft); border-radius: 0; box-shadow: none; background: none; padding: 18px 24px; transform: none; }
#app.portal-exec .px-flush .cs-deliv:first-child { border-top: 0; }
#app.portal-exec .cs-deliv:hover { transform: none; box-shadow: none; background: var(--card-2); border-color: var(--line-soft); }
#app.portal-exec .cs-deliv-ic { width: 40px; height: 40px; border-radius: 10px; border: 1px solid var(--line); background: var(--card-2); color: var(--tx-soft); }
#app.portal-exec .cs-deliv-act { flex-direction: row; align-items: center; gap: 12px; }
/* What the practice said when they handed it over, and the artifact itself. */
#app.portal-exec .cs-deliv-note {
  margin-top: 7px; padding-left: 10px; border-left: 2px solid var(--line);
  font-size: 13px; line-height: 1.55; color: var(--tx-soft); white-space: pre-wrap;
}
#app.portal-exec .cs-deliv-link {
  display: inline-flex; align-items: center; gap: 5px; margin-top: 7px;
  font-size: 12.5px; font-weight: 600; color: var(--teal-deep, var(--teal)); text-decoration: none;
}
#app.portal-exec .cs-deliv-link:hover { text-decoration: underline; }
#app.portal-exec .cs-deliv-link svg { width: 13px; height: 13px; }

/* ── Engagements ───────────────────────────────────────────────────────── */
#app.portal-exec .px-eng { display: grid; grid-template-columns: minmax(0, 1fr) 380px; grid-template-areas: "main side"; gap: 24px; align-items: start; }
#app.portal-exec .px-eng-main { grid-area: main; min-width: 0; }
#app.portal-exec .px-eng-side { grid-area: side; min-width: 0; position: sticky; top: 24px; }
#app.portal-exec .px-proj-h { display: flex; align-items: flex-start; gap: 16px; }
#app.portal-exec .px-proj-h .grow { flex: 1; min-width: 0; }
#app.portal-exec .px-proj-h h2 { margin: 0 0 4px; font-family: var(--f-head); font-size: 21px; font-weight: 600; letter-spacing: -.015em; color: var(--head); }
#app.portal-exec .px-proj-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 22px 0 16px; }
#app.portal-exec .px-proj-stats > div { display: flex; flex-direction: column; gap: 4px; padding: 0 18px; border-left: 1px solid var(--line-soft); min-width: 0; }
#app.portal-exec .px-proj-stats > div:first-child { padding-left: 0; border-left: 0; }
#app.portal-exec .px-proj-stats span { font-size: 12.5px; color: var(--tx-mute); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#app.portal-exec .px-proj-stats b { font-family: var(--f-num, var(--f-head)); font-size: 20px; font-weight: 600; color: var(--head); font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Published status updates on an engagement card. The practice ticks these one
   at a time (project_status_updates.client_visible); everything else it writes
   about the engagement stays internal. */
#app.portal-exec .px-updates { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--line-soft); }
#app.portal-exec .px-updates-h { font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: var(--tx-mute); margin-bottom: 10px; }
#app.portal-exec .px-update { display: flex; align-items: flex-start; gap: 12px; padding: 9px 0; }
#app.portal-exec .px-update + .px-update { border-top: 1px solid var(--line-soft); }
#app.portal-exec .px-update .grow { flex: 1; min-width: 0; }
#app.portal-exec .px-update b { display: block; font-size: 14px; font-weight: 500; color: var(--head); line-height: 1.5; }
#app.portal-exec .px-update .sub { display: block; margin-top: 3px; font-size: 12.5px; color: var(--tx-mute); }
#app.portal-exec .px-timeline { list-style: none; margin: 22px 0 -8px; padding: 0; }
#app.portal-exec .px-timeline li {
  display: grid; grid-template-columns: 22px minmax(0, 1fr) auto 104px; align-items: center; gap: 14px;
  padding: 12px 0; border-top: 1px solid var(--line-soft);
}
#app.portal-exec .px-timeline li .grow b { display: block; font-size: 14px; font-weight: 600; color: var(--head); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#app.portal-exec .px-timeline li.is-done .grow b { font-weight: 500; color: var(--tx-soft); }
#app.portal-exec .px-tl-mk {
  width: 20px; height: 20px; border-radius: 50%; display: grid; place-items: center;
  border: 2px solid var(--line); background: var(--card); color: #fff;
}
#app.portal-exec .px-tl-mk svg { width: 12px; height: 12px; stroke-width: 3; }
#app.portal-exec li.is-done .px-tl-mk { background: var(--ok); border-color: var(--ok); }
#app.portal-exec li.is-next .px-tl-mk { border-color: var(--teal); box-shadow: 0 0 0 4px color-mix(in srgb, var(--teal) 16%, transparent); }
#app.portal-exec li.t-bad:not(.is-done) .px-tl-mk { border-color: var(--bad); }
#app.portal-exec .px-tl-d { font-size: 13px; color: var(--tx-mute); text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }

/* ── The work under a milestone (published tasks) ──────────────────────────
   Only rendered when the practice has published something, so these rules are
   never dead weight on the ordinary milestone row. The rows sit INSIDE the
   milestone's own grid cell, so the milestone stops being centered against its
   pill the moment it grows a list. */
#app.portal-exec .px-timeline li:has(.px-tks) { align-items: start; }
#app.portal-exec .px-tks { display: block; margin-top: 8px; }
#app.portal-exec .px-tks-h {
  display: block; margin-bottom: 5px; font-size: 11px; font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase; color: var(--tx-mute);
}
#app.portal-exec .px-tk {
  display: grid; grid-template-columns: 15px minmax(0, 1fr) auto; align-items: center; gap: 8px;
  padding: 3px 0 3px calc(var(--tkd, 0) * 16px); font-size: 13px; color: var(--tx-soft);
}
#app.portal-exec .px-tk-mk {
  width: 13px; height: 13px; border-radius: 4px; display: grid; place-items: center;
  border: 1.5px solid var(--line); background: var(--card); color: #fff;
}
#app.portal-exec .px-tk-mk svg { width: 9px; height: 9px; stroke-width: 3.5; }
#app.portal-exec .px-tk.is-done .px-tk-mk { background: var(--ok); border-color: var(--ok); }
#app.portal-exec .px-tk-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#app.portal-exec .px-tk.is-done .px-tk-t { color: var(--tx-mute); text-decoration: line-through; }
#app.portal-exec .px-tk-d { font-size: 12px; color: var(--tx-mute); white-space: nowrap; font-variant-numeric: tabular-nums; }

/* ── Invoices ──────────────────────────────────────────────────────────── */
#app.portal-exec .px-inv-head,
#app.portal-exec .px-inv-row {
  display: grid; grid-template-columns: minmax(0, 2fr) 130px 150px 150px minmax(210px, auto);
  align-items: center; gap: 16px; padding: 0 24px;
}
#app.portal-exec .px-inv-head {
  padding-top: 11px; padding-bottom: 11px; background: var(--card-2); border-bottom: 1px solid var(--line);
  font-size: 12px; font-weight: 600; color: var(--tx-mute);
}
#app.portal-exec .px-inv-head .r { text-align: right; }
#app.portal-exec .px-inv + .px-inv { border-top: 1px solid var(--line-soft); }
#app.portal-exec .px-inv-row { padding-top: 18px; padding-bottom: 18px; }
#app.portal-exec .px-inv > :not(.px-inv-row) { margin: -6px 24px 18px; }
#app.portal-exec .px-inv-id { min-width: 0; }
#app.portal-exec .px-inv-id b { display: block; font-size: 14.5px; font-weight: 600; color: var(--head); }
#app.portal-exec .px-inv-id .sub { display: block; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#app.portal-exec .px-inv-due { font-size: 14px; color: var(--tx); white-space: nowrap; font-variant-numeric: tabular-nums; }
#app.portal-exec .px-inv-k { display: none; }
#app.portal-exec .px-inv-amt { text-align: right; }
#app.portal-exec .px-inv-amt b { display: block; font-family: var(--f-num, var(--f-head)); font-size: 16px; font-weight: 600; color: var(--head); font-variant-numeric: tabular-nums; }
#app.portal-exec .px-inv-amt .sub { display: block; margin-top: 2px; }
#app.portal-exec .px-inv-act { display: flex; justify-content: flex-end; align-items: center; gap: 6px; flex-wrap: wrap; }

/* ── Messages ──────────────────────────────────────────────────────────── */
#app.portal-exec .px-thread { padding: 0; overflow: hidden; }
#app.portal-exec .px-thread-h { display: flex; align-items: center; gap: 12px; padding: 16px 24px; border-bottom: 1px solid var(--line); }
#app.portal-exec .px-thread-h .grow { flex: 1; min-width: 0; }
#app.portal-exec .px-thread-h b { display: block; font-size: 15px; font-weight: 600; color: var(--head); }
#app.portal-exec .px-av-team { background: color-mix(in srgb, var(--teal) 12%, var(--card)); color: var(--teal); }
#app.portal-exec .p3-thread { padding: 24px; min-height: 340px; max-height: 60vh; gap: 14px; background: var(--card-2); }
#app.portal-exec .p3-bubble { max-width: min(72%, 620px); border-radius: 14px; padding: 11px 15px; font-size: 14px; line-height: 1.55; background: var(--card); border: 1px solid var(--line); }
#app.portal-exec .p3-row.them .p3-bubble { border-bottom-left-radius: 4px; }
#app.portal-exec .p3-row.me .p3-bubble { border-bottom-right-radius: 4px; background: var(--teal); color: var(--on-teal, #fff); border-color: transparent; }
#app.portal-exec .p3-meta { font-size: 11.5px; font-weight: 500; }
#app.portal-exec .p3-reply { margin: 0; padding: 16px 24px; border-top: 1px solid var(--line); background: var(--card); align-items: center; }

/* ── Shared tabs (Documents, Proposals, Request, My Info) ──────────────────
   These renderers are shared with Field Service, so they are retuned here
   rather than re-marked-up. */
#app.portal-exec #panel .stack.stagger:has(> .card > .listrow > .avatar) { gap: 12px; }
#app.portal-exec #panel .card > .listrow > .avatar {
  width: 40px; height: 40px; border-radius: 10px;
  background: var(--card-2); border: 1px solid var(--line); color: var(--tx-soft);
  font-family: var(--f-body); font-size: 14px; font-weight: 700;
}
#app.portal-exec #panel .card > .listrow .num { font-family: var(--f-body); font-size: 15px; font-weight: 600; letter-spacing: 0; color: var(--head); }
#app.portal-exec #panel .card > .listrow + .row { margin-left: 44px; }
#app.portal-exec #panel .card > .listrow + .row .btn-quiet { color: var(--teal); font-weight: 600; }
#app.portal-exec #panel .card > .listrow + .row .btn-quiet:hover { background: color-mix(in srgb, var(--teal) 8%, var(--card)); }
#app.portal-exec #panel > .card:has(form, .field) { max-width: 760px; }
#app.portal-exec #panel .field label { font-size: 13px; font-weight: 600; color: var(--tx); }
#app.portal-exec #panel :is(.input, .select, .textarea) { border-radius: 9px; border-color: var(--line); }
#app.portal-exec #panel :is(.input, .select, .textarea):focus { border-color: var(--teal); box-shadow: 0 0 0 3px color-mix(in srgb, var(--teal) 16%, transparent); }
#app.portal-exec .px-kpi-none { font-size: 22px; color: var(--tx-soft); }

/* ── Five across needs the room ────────────────────────────────────────────
   Beside the rail, a 1280-1439px window leaves each of five tiles about 140px
   of text, and a 28px figure does not fit: the client read "$48,000..." for an
   open proposal and "Lab Ope..." for their own engagement. Below 1440px the
   band goes three over two, the same split the tablet layout already uses. */
@media (max-width: 1439px) {
  #app.portal-exec .px-kpis { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  #app.portal-exec .px-kpis > .px-kpi { grid-column: span 2; }
  #app.portal-exec .px-kpis > .px-kpi:nth-child(n+4) { grid-column: span 3; }
}

/* ── Tablet: one column of content, the rail stays ─────────────────────── */
@media (max-width: 1279px) {
  html[data-portal="on"] #app.portal-exec { --px-gut: 36px; }
  #app.portal-exec .px-kpis { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  #app.portal-exec .px-kpis > .px-kpi { grid-column: span 2; }
  #app.portal-exec .px-kpis > .px-kpi:nth-child(n+4) { grid-column: span 3; }
  #app.portal-exec .px-grid { grid-template-columns: minmax(0, 1fr); }
  #app.portal-exec .px-eng { grid-template-columns: minmax(0, 1fr); grid-template-areas: "side" "main"; }
  #app.portal-exec .px-eng-side { position: static; }
  #app.portal-exec .px-inv-head { display: none; }
  #app.portal-exec .px-inv-row {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "id amt" "due st" "act act";
    gap: 8px 16px;
  }
  #app.portal-exec .px-inv-id { grid-area: id; }
  #app.portal-exec .px-inv-amt { grid-area: amt; }
  #app.portal-exec .px-inv-due { grid-area: due; font-size: 13px; color: var(--tx-soft); }
  #app.portal-exec .px-inv-k { display: inline; margin-right: 5px; color: var(--tx-mute); }
  #app.portal-exec .px-inv-st { grid-area: st; justify-self: end; }
  #app.portal-exec .px-inv-act { grid-area: act; justify-content: flex-start; margin-top: 4px; }
}

/* ── Phone and small tablet: the rail becomes a dark app bar ───────────────
   The rail and the header dissolve into the shell grid (display: contents) so
   the brand, the account and the tab strip share one ink band, and the page
   title sits on the canvas beneath it. */
@media (max-width: 959px) {
  html[data-portal="on"] #app.portal-exec {
    --px-gut: 16px;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: auto;
    grid-template-areas: "bar bar" "brand who" "nav nav" "head head" "main main" "foot foot";
  }
  #app.portal-exec .px-rail,
  #app.portal-exec .portal-top { display: contents; }
  #app.portal-exec .px-rail-brand,
  #app.portal-exec .portal-top .who,
  #app.portal-exec .ptabs { background: var(--px-rail-ink); color: var(--px-rail-on); }
  #app.portal-exec .px-rail-brand { grid-area: brand; min-width: 0; margin: 0; padding: 14px 16px 12px; border: 0; }
  #app.portal-exec .px-rail-brand .brand-mark { width: 32px; height: 32px; font-size: 15px; }
  #app.portal-exec .px-rail-brand .brand-wrap b { font-size: 15px; }
  #app.portal-exec .portal-top .who { grid-area: who; margin: 0; padding: 12px 16px 12px 8px; gap: 8px; }
  #app.portal-exec .portal-top .who .nm { color: var(--px-rail-on); font-size: 13px; max-width: 120px; }
  #app.portal-exec .px-av { display: none; }
  #app.portal-exec .portal-top .who .btn-ghost {
    background: transparent; color: var(--px-rail-on); box-shadow: none;
    border-color: color-mix(in srgb, var(--px-rail-on) 24%, transparent);
  }
  #app.portal-exec .ptabs {
    grid-area: nav; position: sticky; top: 0; z-index: 20;
    flex-direction: row; gap: 4px; overflow-x: auto; padding: 0 12px 10px;
    -webkit-overflow-scrolling: touch; scrollbar-width: none;
    border-bottom: 1px solid color-mix(in srgb, var(--px-rail-on) 8%, transparent);
  }
  /* A scrolling row again, so the cue is back on - and its backdrop has to be
     the rail's ink, not the card's, or the fade is a light smear on a dark bar
     (which is exactly what was reported). */
  #app.portal-exec .ptabs::after {
    display: block; background: linear-gradient(90deg, transparent, var(--px-rail-ink) 62%);
  }
  #app.portal-exec .ptabs::before { display: block; background: var(--px-rail-on); }
  #app.portal-exec .ptabs button { width: auto; flex: none; padding: 8px 12px; gap: 8px; font-size: 13.5px; }
  #app.portal-exec .ptabs button.on::before { left: 12px; right: 12px; top: auto; bottom: -10px; width: auto; height: 3px; border-radius: 3px 3px 0 0; }
  #app.portal-exec .px-ic svg { width: 16px; height: 16px; }
  #app.portal-exec .ptab-badge { margin-left: 2px; }
  #app.portal-exec .px-rail-foot { display: none; }
  #app.portal-exec .px-head { grid-area: head; padding: 24px var(--px-gut) 0; }
  #app.portal-exec .px-title { font-size: 26px; }
  #app.portal-exec .px-lede { font-size: 14px; }
  #app.portal-exec #panel { padding-top: 18px; }
  #app.portal-exec .px-stack, #app.portal-exec .px-ov, #app.portal-exec .px-col { gap: 16px; }
  #app.portal-exec .px-grid, #app.portal-exec .px-eng { gap: 16px; }
  #app.portal-exec .px-kpis > .px-kpi,
  #app.portal-exec .px-kpis > .px-kpi:nth-child(n+4) { grid-column: span 3; }
  #app.portal-exec .px-kpis > .px-kpi:last-child:nth-child(odd) { grid-column: 1 / -1; }
  #app.portal-exec .px-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  #app.portal-exec .px-kpi { padding: 16px; }
  #app.portal-exec .px-kpi-v { font-size: 23px; }
  #app.portal-exec .px-stat { padding: 14px 16px; }
  #app.portal-exec .px-stat > b { font-size: 21px; }
  #app.portal-exec .px-card, #app.portal-exec #panel .card-pad { padding: 18px; }
  #app.portal-exec #panel .px-thread { padding: 0; }
  #app.portal-exec .px-needs .ov-needs, #app.portal-exec .ov-mini-list { margin: 0 -18px; }
  #app.portal-exec .px-card:not(.px-flush) > .px-rows { margin: 0 -18px -18px; }
  #app.portal-exec .ov-need, #app.portal-exec .ov-mini, #app.portal-exec button.ov-mini:hover, #app.portal-exec .px-row, #app.portal-exec .cs-deliv { padding-left: 18px; padding-right: 18px; }
  /* THE APPROVAL ROW, ON A PHONE.
     `.cs-deliv-act` is a row of a status pill and two buttons. At this width
     the deliverable's name takes the line, the actions wrap under it, and the
     pill was left floating above the buttons at its own end of the row -
     "Ready for Approval" sitting nowhere near the thing it describes. Stack the
     whole row, left-align it, and let the pill lead the line it belongs to. */
  #app.portal-exec .cs-deliv { flex-wrap: wrap; }
  #app.portal-exec .cs-deliv > .grow { flex-basis: calc(100% - 52px); }
  #app.portal-exec .cs-deliv-act {
    flex-wrap: wrap; align-items: center; justify-content: flex-start;
    width: 100%; margin-top: 10px; gap: 10px;
  }
  #app.portal-exec .cs-deliv-act .row { flex: 1; min-width: 0; justify-content: flex-end; }
  #app.portal-exec .cs-deliv-act .btn { flex: 1; min-width: 0; justify-content: center; }
  #app.portal-exec .px-inv-row { padding-left: 18px; padding-right: 18px; }
  #app.portal-exec .px-inv > :not(.px-inv-row) { margin-left: 18px; margin-right: 18px; }
  #app.portal-exec .px-thread-h, #app.portal-exec .p3-reply { padding-left: 18px; padding-right: 18px; }
  #app.portal-exec .p3-thread { padding: 18px; min-height: 260px; }
  #app.portal-exec .p3-bubble { max-width: 86%; }
  #app.portal-exec .portal-foot { margin-top: 40px; }
}
@media (max-width: 560px) {
  #app.portal-exec .px-spot-body { grid-template-columns: minmax(0, 1fr); gap: 18px; }
  #app.portal-exec .px-ring { width: 104px; height: 104px; grid-row: 1; }
  #app.portal-exec .px-ring-in b { font-size: 26px; }
  #app.portal-exec .px-proj-stats > div { padding: 0 10px; }
  #app.portal-exec .px-proj-stats b { font-size: 16px; }
  #app.portal-exec .px-timeline li { grid-template-columns: 22px minmax(0, 1fr) auto; }
  #app.portal-exec .px-tl-d { grid-column: 2 / -1; text-align: left; margin-top: -8px; }
  #app.portal-exec .cs-deliv { flex-wrap: wrap; }
  #app.portal-exec .cs-deliv-act { width: 100%; justify-content: space-between; padding-left: 54px; }
  #app.portal-exec .px-rail-brand .brand-wrap small, #app.portal-exec .px-rail-brand .brand-lockup-sub { display: none; }
  /* Two figures side by side on a phone have about 120px each, and "$18,000.00"
     at 28px was cut to "$18,000...." - a money figure must never be clipped. */
  #app.portal-exec .px-kpi-v,
  #app.portal-exec .px-stat > b { font-size: 21px; }
  #app.portal-exec .px-kpi-none { font-size: 17px; }
  #app.portal-exec .px-kpi, #app.portal-exec .px-stat { padding-left: 16px; padding-right: 16px; }
}
/* The account name gives its room to the business's name on a phone. Clipped,
   not removed, so it is still announced beside Sign Out. */
@media (max-width: 480px) {
  #app.portal-exec .portal-top .who .nm { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  /* The composer takes the full width and Send sits beneath it. */
  #app.portal-exec .p3-reply { flex-wrap: wrap; }
  #app.portal-exec .p3-reply .textarea { flex: 1 1 100%; }
  #app.portal-exec .p3-reply .btn { flex: 1 1 100%; justify-content: center; }
}

@media (prefers-reduced-motion: reduce) {
  #app.portal-exec .px-bar > span { animation: none; }
}

/* Flat-shadow brand packs keep their structure from borders alone. */
html[data-portal="on"][data-shadow="flat"] #app.portal-exec :is(.px-card, .card, .px-kpis, .px-stats, .pv-action) { box-shadow: none; }
html[data-portal="on"][data-shadow="flat"] #app.portal-exec .pv-action { box-shadow: inset 3px 0 0 var(--teal); }

/* ============================================================================
   DEPTH, AND THE SECOND MODE
   ----------------------------------------------------------------------------
   Two changes land here together because they are the same change.

   1 - FIELD SERVICE WEARS THIS LAYOUT TOO. renderShell() now sets
       #app.portal-exec for both modes, so everything
       above applies to a homeowner's portal as well as a consulting client's.
       The Field Service overview brings four widgets this sheet had never seen
       - the live tracker, the gradient greeting hero, the stat tiles and the
       visit feed - and each one is given its place at the bottom of this
       section. The rest of that portal is already shared markup.

   2 - THE FLAT IS OVER. The canvas wash was switched off at the top of this
       file, the KPI band was one hairline-joined slab, and every card carried a
       4%-alpha shadow. That was a defensible choice and it is not the one the
       product makes: the console next door is a lit, grained, layered surface,
       and a client who has seen a screen of it and then opens their own portal
       should not be looking at a different company's software.

       So this section brings the console's recipe across - grain and grid on
       the canvas, a lit top edge and a real ambient shadow on every raised
       thing, an accent rail on every tile, and a rail that is a lit object
       rather than a block of ink. Nothing below changes a single measurement,
       a single word or a single control: it is depth only, and every value is
       either a token or neutral shadow ink, so a custom-tier pack still wins.
   ========================================================================== */

/* -- The canvas -------------------------------------------------------------
   `display: block` undoes this file's own `display: none` at the top. The wash
   is quieter than Daylight's because this paper is neutral by design and the
   numbers are meant to be the color on it: the grain and the grid do the work,
   and the accent is a hint at two corners rather than a tint across the page. */
html[data-portal="on"]:has(#app.portal-exec) body::before {
  display: block;
  background:
    var(--grain) 0 0 / 160px 160px,
    linear-gradient(var(--px-grid) 1px, transparent 1px) 0 0 / 64px 64px,
    linear-gradient(90deg, var(--px-grid) 1px, transparent 1px) 0 0 / 64px 64px,
    radial-gradient(85% 55% at 100% -6%, color-mix(in srgb, var(--teal) 10%, transparent), transparent 56%),
    radial-gradient(75% 48% at -6% 4%, color-mix(in srgb, var(--accent-2) 7%, transparent), transparent 58%);
}

/* -- The rail ---------------------------------------------------------------
   It was one flat fill of brand ink, 264px wide and 100vh tall, which is the
   largest single object on the page and had the least in it. Now it is lit: a
   vertical gradient so the top reads nearer than the foot, the white speckle
   over it, a wash of the brand mark behind the lockup, a hairline of light down
   its inner edge and a shadow onto the page it sits over. */
#app.portal-exec .px-rail {
  position: relative; isolation: isolate;
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--px-rail-ink) 88%, #ffffff) 0%,
      var(--px-rail-ink) 46%,
      color-mix(in srgb, var(--px-rail-ink) 86%, #000000) 100%);
  box-shadow:
    inset -1px 0 0 color-mix(in srgb, var(--px-rail-on) 8%, transparent),
    6px 0 28px -18px rgba(16, 24, 40, .7);
}
#app.portal-exec .px-rail::before {
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    var(--grain-lit) 0 0 / 160px 160px,
    radial-gradient(130% 38% at 0% 0%, color-mix(in srgb, var(--px-mark) 18%, transparent), transparent 64%);
}

/* The lockup gets the same treatment the console gives it: the mark carries the
   brand's own light, and the divider under it fades out rather than stopping. */
#app.portal-exec .px-rail-brand { border-bottom: 0; position: relative; }
#app.portal-exec .px-rail-brand::after {
  content: ''; position: absolute; left: 10px; right: 10px; bottom: 0; height: 1px;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--px-rail-on) 22%, transparent),
    color-mix(in srgb, var(--px-rail-on) 4%, transparent));
}
#app.portal-exec .px-rail-brand .brand-mark {
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, #ffffff 34%, transparent),
    0 6px 16px -8px color-mix(in srgb, var(--px-mark) 70%, transparent);
}

/* The active row is raised OUT of the rail rather than tinted inside it: a lit
   top edge, a soft fill and the accent rail this file already drew, now with a
   glow so the eye finds the current section without reading the labels. */
#app.portal-exec .ptabs button.on {
  background:
    linear-gradient(90deg,
      color-mix(in srgb, var(--px-mark) 22%, transparent),
      color-mix(in srgb, var(--px-rail-on) 7%, transparent) 62%);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--px-rail-on) 14%, transparent),
    0 6px 14px -10px rgba(0, 0, 0, .8);
}
#app.portal-exec .ptabs button.on::before {
  box-shadow: 0 0 12px 0 color-mix(in srgb, var(--px-mark) 70%, transparent);
}
/* The rail's own foot line, so the "private to" note sits on something. */
#app.portal-exec .px-rail-foot {
  border-top: 1px solid color-mix(in srgb, var(--px-rail-on) 9%, transparent);
}

/* -- Cards ------------------------------------------------------------------
   The lit edge plus the two-layer shadow, from portal-skin.css's --lift-* set.
   A card that only has a 1px hairline is a rectangle; these are objects. */
#app.portal-exec #panel :is(.card, .px-card),
#app.portal-exec .pv-action {
  border-color: color-mix(in srgb, var(--tx) 10%, var(--line));
  box-shadow: var(--lift-e), var(--lift-1), var(--lift-2), var(--lift-3);
}
#app.portal-exec #panel :is(.card, .px-card) > .card-head {
  background: linear-gradient(180deg, color-mix(in srgb, var(--teal) 5%, var(--card, #fff)), var(--card, #fff));
  border-bottom-color: color-mix(in srgb, var(--teal) 13%, var(--line-soft));
}
/* The section heading gets the same accent tick the Daylight column uses, so a
   column of cards reads as a spine of headings. */
#app.portal-exec #panel :is(.px-card-h h2, .section-label) {
  display: flex; align-items: center; gap: 9px;
}
#app.portal-exec #panel :is(.px-card-h h2, .section-label)::before {
  content: ''; flex: none; width: 3px; height: 13px; border-radius: 2px;
  background: linear-gradient(var(--teal-bright), var(--teal));
}

/* -- The measures band ------------------------------------------------------
   It was a single slab divided by 1px hairlines, which reads as a table header.
   The console puts its measures in separate lifted tiles with an accent rail
   each, and that is what makes a row of numbers read as a row of MEASURES. */
#app.portal-exec .px-kpis,
#app.portal-exec .px-stats {
  gap: 14px; overflow: visible;
  background: none; border: 0; border-radius: 0; box-shadow: none;
}
#app.portal-exec :is(.px-kpi, .px-stat) {
  position: relative; overflow: hidden;
  --tile-a: var(--teal);
  padding: 18px 20px 16px; border-radius: var(--px-r);
  border: 1px solid color-mix(in srgb, var(--tile-a) 22%, var(--line));
  background:
    radial-gradient(120% 130% at 100% 0%, color-mix(in srgb, var(--tile-a) 10%, transparent), transparent 62%),
    linear-gradient(158deg, var(--card, #fff), color-mix(in srgb, var(--tile-a) 6%, var(--card, #fff)));
  box-shadow: var(--lift-e), var(--lift-1), 0 14px 28px -20px rgba(16, 24, 40, .34);
  transition: transform .12s, box-shadow .16s, border-color .16s;
}
#app.portal-exec :is(.px-kpi, .px-stat)::after {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
  background: linear-gradient(color-mix(in srgb, var(--tile-a) 55%, var(--card, #fff)), var(--tile-a));
  opacity: .6;
}
#app.portal-exec .px-kpi.t-ok, #app.portal-exec .px-stat.t-ok { --tile-a: var(--ok); }
#app.portal-exec .px-kpi.t-warn, #app.portal-exec .px-stat.t-warn { --tile-a: var(--warn); }
#app.portal-exec .px-kpi.t-bad, #app.portal-exec .px-stat.t-bad { --tile-a: var(--bad); }
#app.portal-exec button.px-kpi:hover {
  background:
    radial-gradient(120% 130% at 100% 0%, color-mix(in srgb, var(--tile-a) 14%, transparent), transparent 62%),
    linear-gradient(158deg, var(--card, #fff), color-mix(in srgb, var(--tile-a) 8%, var(--card, #fff)));
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--tile-a) 45%, var(--line));
  box-shadow: var(--lift-e), 0 18px 34px -20px color-mix(in srgb, var(--tile-a) 45%, rgba(16, 24, 40, .5));
}
#app.portal-exec button.px-kpi:hover::after { opacity: 1; }

/* -- Buttons ----------------------------------------------------------------
   THIS BLOCK IS GONE (2026-09-20). It existed because the primary control was
   "a flat rectangle of accent" and needed a lit edge - which is true, and is
   now what theme.css does for every button in the product. Re-declaring it
   here only re-introduced the 10px/20px accent glow Jeremy asked to cut, and
   on the one canvas a client sees. The exec skin re-points colors; it does
   not own the shape of a button. */

/* -- FIELD SERVICE WIDGETS IN THE EXEC CANVAS ------------------------------
   Four widgets that had only ever rendered in a 760px column and now have a
   1240px one. Each needed a decision about what the extra width is FOR; none
   of them needed new markup. */

/* The greeting hero. It is the full width of the content column now, so the
   copy is held to a reading measure instead of running to 90 characters, and
   the glow is re-aimed at the corner it actually has. */
#app.portal-exec .ov-hero { border-radius: var(--px-r); padding: 30px 32px 28px; }
#app.portal-exec .ov-hero p { max-width: 58ch; }

/* The live tracker - the single best thing in this portal, and the one surface
   that was already doing what this whole section is about. It only needed the
   corner radius and the grain the rest of the page now carries. */
#app.portal-exec .pv-live {
  border-radius: var(--px-r);
  box-shadow: var(--lift-1), 0 26px 48px -26px rgba(16, 24, 40, .6);
}
#app.portal-exec .pv-live::after { background-size: 40px 40px; }

/* The four stat tiles. They are `.ov-tile`s carrying `.px-stats`, so they take
   the band's grid and keep their own Daylight skin - which is already the
   console's tile. Only the radius and the value size need to grow with the
   column. */
#app.portal-exec .ov-tiles { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; }
#app.portal-exec .ov-tile { border-radius: var(--px-r); padding: 18px 20px 16px; }
#app.portal-exec .ov-tile-l { font-size: 13px; font-weight: 500; }
#app.portal-exec .ov-tile-v { font-size: 26px; font-weight: 600; }
#app.portal-exec .ov-tile-s { font-size: 12.5px; }

/* "Waiting on You" is numbered here (the Consulting overview already numbers
   it), and the Field Service rows now carry the same `.px-need-n`. They sit in
   a full-bleed hairline list, so the Daylight row skin - which draws a raised
   box with its own left rail - has to stand down. The tone survives as the
   dot, which is what it was in the console. */
#app.portal-exec .ov-need {
  background: none; box-shadow: none; padding-left: 24px; border-radius: 0;
}
#app.portal-exec .ov-need::before { display: none; }
#app.portal-exec .ov-need:hover { background: var(--card-2); transform: none; box-shadow: none; }

/* The visit feed. The tick used to be a text glyph in a gray box; it is the
   product's own check mark now, in the color that means "done". */
#app.portal-exec .px-card .pv-feed,
#app.portal-exec #panel .card-pad > .pv-feed { margin: 0 -24px -22px; }
#app.portal-exec .pv-feed-row { padding: 13px 24px; grid-template-columns: 28px minmax(0, 1fr) auto; }
#app.portal-exec .pv-feed-row .dot {
  width: 28px; height: 28px; border-radius: 9px;
  background: var(--ok-soft, color-mix(in srgb, var(--ok) 13%, var(--card, #fff)));
  border-color: color-mix(in srgb, var(--ok) 26%, var(--line));
  color: var(--ok-tx, var(--ok));
}
#app.portal-exec .pv-feed-row .dot svg { width: 15px; height: 15px; }

/* The quote hero is an ASK, so it keeps the accent edge it has in the column
   and gains the lift every other raised thing here has. */
#app.portal-exec .pv-quote-hero { box-shadow: inset 3px 0 0 var(--teal), var(--lift-e), var(--lift-1), var(--lift-3); }

/* -- Tiers ------------------------------------------------------------------
   A pack that asked for flat shadows keeps its structure from borders and the
   lit edge; the rail and the tracker keep theirs, because both are dark objects
   on a light page and a border cannot hold them. */
html[data-portal="on"][data-shadow="flat"] #app.portal-exec
  :is(.px-card, .card, .px-kpi, .px-stat, .ov-tile, .pv-action, .btn-primary, .btn-ghost) {
  box-shadow: var(--lift-e);
}
html[data-portal="on"][data-shadow="flat"] #app.portal-exec .pv-action { box-shadow: inset 3px 0 0 var(--teal); }

@media (prefers-reduced-motion: reduce) {
  #app.portal-exec :is(.px-kpi, .px-stat, .ov-tile) { transition: none; }
  #app.portal-exec button.px-kpi:hover { transform: none; }
}

/* -- Narrow ----------------------------------------------------------------
   The tile row wraps to two before it wraps to one: four 26px numbers in a
   column is a list, not a band. */
@media (max-width: 1279px) {
  #app.portal-exec .ov-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 959px) {
  /* The rail becomes a dark app bar up top - `display: contents`, so it is no
     longer a box at all. Its vertical gradient, its inner edge, its drop shadow
     and its grain all describe a 264px column that is not there. */
  #app.portal-exec .px-rail { background: none; box-shadow: none; }
  #app.portal-exec .px-rail::before { display: none; }
  #app.portal-exec .px-rail-brand::after { display: none; }
  /* The active marker moves from the row's left edge to the strip's bottom
     edge, where the glow spills past the bar and reads as a second rule
     floating under it. The marker is enough on its own down here. */
  #app.portal-exec .ptabs button.on::before { box-shadow: none; }
  #app.portal-exec .ptabs button.on { box-shadow: none; }
  /* Two tiles to a row instead of four, so a label that fitted on one line at
     1240px no longer does. It WRAPS rather than truncating: "Approved, Not Yet
     Paid" clipped to "Approved, Not Yet Pa" is a different statement, and this
     one is about money. The value keeps its single line - a wrapped number is
     unreadable - and shrinks instead. */
  #app.portal-exec :is(.px-kpi-l, .px-stat > span) { white-space: normal; align-items: flex-start; }
  #app.portal-exec .px-kpi-dot, #app.portal-exec .px-stat[class*="t-"] > span::before { margin-top: 5px; }
  #app.portal-exec .px-kpi-s { white-space: normal; overflow: visible; }
  #app.portal-exec .px-kpi-v, #app.portal-exec .px-stat > b { font-size: clamp(18px, 6.2vw, 23px); }
  #app.portal-exec .ov-hero { padding: 24px 20px 22px; }
}
@media (max-width: 560px) {
  #app.portal-exec .ov-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  #app.portal-exec .ov-tile { padding: 14px 15px 13px; }
  #app.portal-exec .ov-tile-v { font-size: 21px; }
}

/* ── The preferred window on Request a Session ─────────────────────────────
   One date could only ever say "this day", so a client with a flexible week
   had to put it in the notes and hope. Two inputs on one line read as the
   range they are; the second is optional and the form works without it. */
#app.portal-exec .req-range { display: flex; align-items: center; gap: 10px; }
#app.portal-exec .req-range .input { flex: 1 1 0; min-width: 0; }
#app.portal-exec .req-range > span { flex: none; font-size: 13px; color: var(--tx-soft); }
@media (max-width: 480px) {
  #app.portal-exec .req-range { flex-wrap: wrap; }
  #app.portal-exec .req-range .input { flex: 1 1 100%; }
  #app.portal-exec .req-range > span { width: 100%; }
}
