/* Pace hero — marketing landing-page replica of the Pace app UI.
   Everything is scoped under .pace-hero; all custom properties and
   @keyframes are ph- prefixed so the host page can never collide.
   All units are px (no rem/em). */

.pace-hero {
  /* The page ground is pure white: the app has no grey page background and no
     outer frame. Its inset-panel reading is 1px borders plus shadow-sm on
     white-on-white panels, nothing else. */
  --ph-page-bg: #FFFFFF;
  --ph-bg: #FFFFFF;
  --ph-fg: #09090B;
  --ph-muted: #F4F4F5;
  --ph-muted-fg: #71717A;
  /* hsl(240 14% 95%), measured exactly on every panel edge, pill edge and row
     separator — the highest-instance-count token on the screen. */
  --ph-border: #F1F1F4;
  /* `border-border/50`, which §5.3's Changes surfaces use everywhere the rest of
     the app uses the full-strength border: the actor pill, the date pill, the
     group badge's under-rule and the rail's chips — four consumers, which is the
     condition R3 attached to re-adding this token after deleting it unused.
     Kept as the source's own alpha rather than R3's flattened #F8F8FA, because
     the two are not interchangeable: over the white row it composites to #f8f8fa,
     but over the rail chip's own translucent #f9f9fa fill it composites to
     #f5f5f7 — and both were measured in ref-6. */
  --ph-border-50: rgba(241, 241, 244, 0.5);
  /* The one dark line the app draws: its focus ring. */
  --ph-ink: #484851;
  --ph-red: #EF4444;
  /* The app remaps Tailwind's radius scale off --radius: 0.8rem, so rounded-lg
     is 12.8px rather than 8px. tailwind.config.ts extends `lg`/`md`/`sm` ONLY.
     --ph-radius-xl is NOT a Tailwind step: 16px is the header panel's own inline
     `borderRadius` (header.tsx:682), which is the only 16px corner in the app. */
  --ph-radius-xl: 16px;
  --ph-radius-lg: 12.8px;
  --ph-radius-md: 10.8px;
  --ph-radius-sm: 8.8px;
  /* Tailwind's own untouched `rounded-xl` = 0.75rem = 12px, which every Home
     card, every pacing tile and every chart metric slot carries. Measured on
     ref-1's band-3 panel: the outer edge's inset reaches 0 at 9px below the
     corner, which fits r=12 (0.38px predicted) and rules out r=16 (1.61px) —
     so fidelity-structural-spec §5.4's "every Home card is rounded-xl = 16px"
     is wrong twice over, in the config and in the capture. */
  --ph-radius-home: 12px;
  /* Tailwind's own shadow-sm, which is what .glass and every Card apply and what
     the app therefore renders: ~3px below a panel and ~2px laterally. Measured
     against ref-5 — under the panel border this paints #f6f6f6 / #fafafa /
     #fefefe where the reference reads #f5f5f5 / #fafafa / #fefefe. The spec's
     suggested two-layer equivalent measures 2 units darker on all three rows. */
  --ph-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
  /* Tailwind's shadow-md, which is what ui/popover.tsx puts on PopoverContent and
     nothing else in the hero uses. Three times shadow-sm's reach and much darker:
     measured under ref-3's bottom border it starts at #dfdfdf and ramps to white
     over ~9px, against shadow-sm's #f5f5f5 over 3. */
  --ph-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
  /* The header panel's own height, and the band it occupies: 16px of white
     above it (header.tsx `p-4 pb-0`) plus the 64px panel. The views start where
     that band ends, so the two must be read together. */
  --ph-header-h: 64px;
  --ph-header-band: 80px;
  /* The content region's own frame: 32px left/right, 24px top (SharedLayout's
     `md:p-8 md:pt-6`), and 24px between stacked panels (`md:space-y-6`). */
  --ph-gutter: 32px;
  --ph-gap: 24px;
  /* The app's actual stack. layout.tsx defines --font-inter and nothing
     consumes it, so `font-sans` resolves to Tailwind's default — SF Pro on
     macOS, which is what every reference capture measures. No webfont. */
  font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif;
  /* The app's root size. Line-heights come from the utility that set each font
     size, so nothing is meant to inherit a ratio; 1.5 is Tailwind preflight's
     root value, which is what an un-sized text node gets in the app too. */
  font-size: 16px;
  color: var(--ph-fg);
  text-align: left;
  line-height: 1.5;
  /* Insulation against the host page. Every property below is INHERITED, so a
     host `body { … }` rule reaches every text node the hero owns unless the root
     answers it here — and the stylesheet only declares letter-spacing on 7 of
     the 93 rules that set a font size, so the other 86 would take whatever the
     page has. Measured before this block existed: `body { letter-spacing: 0.6px }`
     alone moved 163-352 elements per view and repainted up to 4.7% of the frame,
     widening chips by 34px and re-wrapping a Home card 23px taller.
     `font-size`, `line-height`, `text-align`, `color` and `font-family` above are
     the same defence; these seven finish it. Longhands on descendants (a
     `font-weight: 600` on a card title, a `font-variant-numeric` anywhere) still
     win — this only stops inheritance from OUTSIDE. */
  letter-spacing: normal;
  word-spacing: normal;
  font-weight: 400;
  font-style: normal;
  font-variant: normal;
  text-transform: none;
  direction: ltr;
  /* Six more of exactly the same bug, found by sweeping every inherited text
     property rather than only the ones a host is likely to set. Each of these on
     `body` changed the hero's layout before this block: text-indent and
     font-stretch and font-kerning on all four views, word-break and line-break on
     Home. Every value here is the property's own initial value, so none of them
     changes anything the hero renders — the whole point is that the root answers
     the question before the page can. */
  text-indent: 0;
  word-break: normal;
  line-break: auto;
  font-stretch: normal;
  font-kerning: auto;
  /* Leaked its computed value without moving anything, because the emitted markup
     has no collapsible whitespace to be affected. Declared anyway: that is a
     property of today's copy, not of the CSS. */
  white-space: normal;
  /* The height reservation, and it is on THIS element rather than only on
     .ph-wrapper because .ph-wrapper does not exist until buildDom runs. The host
     writes an empty <div class="pace-hero">, whose height is 0 until then, so
     reserving the box on the wrapper alone reserves it one DOMContentLoaded too
     late: everything below the hero starts high and drops when it mounts.
     Measured with the reservation only on the wrapper, 1280x900 with content
     below: CLS 0.471, one 775px shift at 46ms. With this line: CLS 0.
     Both boxes carry the ratio deliberately — this one so the space exists
     before the script does, the wrapper's so the scaled canvas has something to
     be clipped by. aspect-ratio only sizes an AUTO dimension, so a host that
     gives the div an explicit height still wins — measured: `height: 400px` on
     the host gives a 400px root AND a 400px wrapper, where before this line the
     wrapper stayed 775 and overflowed. Declaring a height here would take that
     away, which is why shell.test.ts asserts there is none. */
  aspect-ratio: 1920 / 1240;
}
.pace-hero, .pace-hero *, .pace-hero *::before, .pace-hero *::after { box-sizing: border-box; }
.pace-hero h1, .pace-hero h2, .pace-hero h3, .pace-hero h4, .pace-hero p,
.pace-hero ul, .pace-hero ol, .pace-hero figure { margin: 0; padding: 0; }
.pace-hero img, .pace-hero svg { max-width: none; display: block; }
.pace-hero .ph-btn { all: unset; box-sizing: border-box; cursor: pointer; }

/* No radius, no border, no elevation on either box: the app has no page-level
   chrome, so the hero is the app's own white surface sitting on the host page.
   The wrapper exists only to hold the aspect ratio and clip the scaled canvas. */
.pace-hero .ph-wrapper {
  width: 100%; aspect-ratio: 1920 / 1240; overflow: hidden; position: relative;
}
.pace-hero .ph-canvas {
  position: absolute; top: 0; left: 0;
  width: 1920px; height: 1240px;
  transform-origin: 0 0;
  background: var(--ph-page-bg);
  overflow: hidden;
}
.pace-hero .ph-fade {
  position: absolute; left: 0; right: 0; bottom: 0; height: 80px;
  background: linear-gradient(to bottom, transparent, var(--ph-page-bg));
  pointer-events: none; z-index: 30;
}
.pace-hero.ph-noninteractive { pointer-events: none; }
/* Ready gate: until the first scale lands the canvas would paint 1:1, showing a
   top-left crop of the 1920px design inside the clipped wrapper. The script adds
   .ph-ready on the first successful scale — synchronously during init in the
   normal case, so there is no fade to stage; visibility (not opacity) so nothing
   in the unrevealed canvas is focusable either. */
.pace-hero .ph-canvas { visibility: hidden; }
.pace-hero.ph-ready .ph-canvas { visibility: visible; }
/* The app's <main>: it starts where the header band ends and carries the content
   region's own padding, which is what puts the toolbar panel's top edge at
   y=104 (80 + 24) and both stacked panels' left edge at x=32. No bottom
   padding — the last row is meant to run off the canvas. */
.pace-hero .ph-view {
  position: absolute; inset: var(--ph-header-band) 0 0 0;
  padding: var(--ph-gap) var(--ph-gutter) 0;
  overflow: hidden;
}
.pace-hero .ph-view[hidden] { display: none; }
/* No fill mode on purpose: once the cross-fade ends the panel goes back to its
   own styles, so the class can stay on the element without its final keyframe
   overriding anything Tasks 4-9 set on the panel. */
.pace-hero .ph-view-enter { animation: ph-view-in 220ms ease-out; }
@keyframes ph-view-in {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: none; }
}

/* ── Header ──────────────────────────────────────────────────────────────── */

/* The header is its own floating panel, not a full-bleed bar: a 16px white
   inset on three sides and none at the bottom, so the band is 16 + 64 = 80px
   and the 24px gap below it comes from the views' own top padding. .ph-view is
   inset by --ph-header-band, so any drift here either overlaps the views or
   opens a gap. */
/* Above .ph-fade's 30, because the dropdowns hang off this panel and the app
   portals its popovers at z-50 — over everything. The header and the fade never
   overlap (one is the top 80px, the other the bottom 80), so the only thing this
   changes is which of the two an open dropdown paints over. */
.pace-hero .ph-header {
  position: relative; z-index: 40;
  height: var(--ph-header-band);
  padding: 16px 16px 0;
}
.pace-hero .ph-header-panel {
  position: relative;
  height: var(--ph-header-h);
  background: var(--ph-bg);
  border: 1px solid var(--ph-border);
  border-radius: var(--ph-radius-xl);
  box-shadow: var(--ph-shadow-sm);
}
/* The app's trigger row carries an inline height: 64px inside this 64px
   border-box panel, so it starts at the 62px content box's top (y17) and
   overflows 2px past the bottom border — which centres everything it holds on
   y49, not the panel's own y48. Measured in all four references: the nav track,
   both trigger buttons and the avatar disc all span y31→67. Dropping the row and
   centring in the panel puts the whole header 1px high. */
.pace-hero .ph-header-row {
  position: relative;
  height: var(--ph-header-h);
  display: flex; align-items: center;
  padding: 0 16px;
}
/* Fixed 78px, which is what puts the nav track's left edge at x151 (33 + 78 +
   40). The wordmark's own advance is narrower than the slot. */
.pace-hero .ph-logo-slot { flex: none; width: 78px; }
.pace-hero .ph-logo {
  /* text-3xl / font-bold / leading-tight / tracking-tighter (-0.05em at 30px),
     plus pr-1 and relative bottom-0.5 — the wordmark sits low in the row, ink
     from y42 to the p's descender at y64, and that low sit is a signature. */
  font-size: 30px; font-weight: 700; line-height: 37.5px; letter-spacing: -1.5px;
  padding-right: 4px;
  position: relative; bottom: 2px;
  /* bg-gradient-to-br from-foreground via-foreground to-foreground/40: a corner
     direction, with the via stop at 50%. */
  background: linear-gradient(to bottom right, #09090B 0%, #09090B 50%, rgba(9, 9, 11, 0.4) 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* ── Nav tablist ─────────────────────────────────────────────────────────── */

/* Anchored to the logo, not centred on the canvas: 40px after the 78px logo
   slot, which lands the track at x151 → 529.5. It stays positioned so it is the
   indicator's offsetParent, which is what makes offsetLeft nav-relative.
   No gap: the four items abut, and the track reads as one continuous band
   straight across every boundary. bg-muted/60 over white measures #f9f9f9 in
   every reference (headless Chromium rounds the same blend to #f8f8f9, which is
   a capture artefact, not a drift — keep the source declaration). */
.pace-hero .ph-nav {
  position: relative;
  flex: none;
  margin-left: 40px;
  display: flex; padding: 4px;
  background: rgba(244, 244, 245, 0.6);
  border-radius: var(--ph-radius-lg);
}
.pace-hero .ph-nav-indicator {
  position: absolute; top: 4px; bottom: 4px; left: 0; width: 0;
  border-radius: var(--ph-radius-md);
  /* shadow-sm, and no border: the app's pill is a gradient with a soft edge,
     measured 2.5px of ramp under it whose darkest row is #efefef over the
     track. */
  box-shadow: var(--ph-shadow-sm);
}
.pace-hero .ph-nav.ph-nav-measured .ph-nav-indicator {
  transition: left 0.25s ease, width 0.25s ease;
}
/* Same specificity as the rule above and declared after it, so it wins: the
   script adds .ph-nav-snap for writes that must land instantly (first
   measurement, resize ticks, font swaps) and drops it for a tab change. */
.pace-hero .ph-nav.ph-nav-snap .ph-nav-indicator { transition: none; }
.pace-hero .ph-nav-tab {
  position: relative; z-index: 1;
  display: flex; align-items: center; gap: 6px;
  margin: 0; padding: 6px 12px;
  border: 0; background: none; cursor: pointer;
  font: inherit; font-size: 12px; font-weight: 500; line-height: 16px;
  text-align: left; color: var(--ph-muted-fg);
  border-radius: var(--ph-radius-md);
  transition: color 0.2s ease;
}
.pace-hero .ph-nav-tab svg { width: 14px; height: 14px; }
.pace-hero .ph-nav-tab:hover { color: var(--ph-fg); }
.pace-hero .ph-nav-tab:focus-visible { outline: 1px solid var(--ph-ink); outline-offset: 1px; }

/* Per-view indicator fill + accent, driven off the nav's data-active-view so
   both sides of the pair can never disagree. The accent is one `color` on the
   link, so it reaches the label and the glyph together — the icons stroke with
   currentColor. Home is pure black (text-black), not slate. Each fill is that
   view's own `from-<hue>-50 to-white` corner gradient. */
.pace-hero .ph-nav[data-active-view="home"] .ph-nav-indicator { background: linear-gradient(to bottom right, #F8FAFC, #FFFFFF); }
.pace-hero .ph-nav[data-active-view="home"] .ph-nav-tab[data-view="home"] { color: #000000; }
.pace-hero .ph-nav[data-active-view="portfolios"] .ph-nav-indicator { background: linear-gradient(to bottom right, #FAF5FF, #FFFFFF); }
.pace-hero .ph-nav[data-active-view="portfolios"] .ph-nav-tab[data-view="portfolios"] { color: #7E22CE; }
.pace-hero .ph-nav[data-active-view="accounts"] .ph-nav-indicator { background: linear-gradient(to bottom right, #ECFDF5, #FFFFFF); }
.pace-hero .ph-nav[data-active-view="accounts"] .ph-nav-tab[data-view="accounts"] { color: #047857; }
.pace-hero .ph-nav[data-active-view="changes"] .ph-nav-indicator { background: linear-gradient(to bottom right, #EFF6FF, #FFFFFF); }
.pace-hero .ph-nav[data-active-view="changes"] .ph-nav-tab[data-view="changes"] { color: #1D4ED8; }

/* ── Header right cluster ────────────────────────────────────────────────── */

/* The app absolutely positions this cluster at `inset-y-0 right-0 pr-4`; in
   flow with margin-left: auto it lands on the same right edge — the row's own
   16px padding — which is what puts the avatar's disc at x1853→1889. space-x-4
   is the 16px gap. */
.pace-hero .ph-header-right {
  margin-left: auto; display: flex; align-items: center; gap: 16px;
}
/* The §4.12 icon button as a real control. Its box, border, radius, fill,
   shadow and glyph size are the shared recipe further down (one rule for
   .ph-tool and .ph-icon-btn together); only what makes this one interactive
   lives here, so an edit to the header cluster cannot drift the two apart. */
.pace-hero .ph-icon-btn {
  position: relative;
  padding: 0; margin: 0; cursor: pointer;
  transition: background-color 0.2s ease, color 0.2s ease;
}
.pace-hero .ph-icon-btn:hover { background: var(--ph-muted); color: var(--ph-fg); }
.pace-hero .ph-icon-btn:focus-visible { outline: 1px solid var(--ph-ink); outline-offset: 1px; }
/* The Sparks trigger's open state, from ref-3: yellow-50 fill, yellow-200
   border, yellow-600 glyph. Its (0,3,0) specificity outranks :hover, so an open
   trigger stays yellow under the pointer. The Bell has no open state — ref-2
   shows it white with a #f1f1f4 border while its own panel is open. */
.pace-hero .ph-icon-btn[data-dropdown="sparks"][aria-expanded="true"] {
  background: #FEFCE8; border-color: #FEF08A; color: #CA8A04;
}

/* Unread dots. The app's structure exactly (header.tsx:816-819,
   InsightsPopover.tsx:254-257): a 10px WRAPPER at top-1/right-1, an absolutely
   positioned 10px ring at the wrapper's top-left, and the 8px dot in flow after
   it. So the dot's own right inset is 6px — 36 - 4 - 10 puts the wrapper's left
   edge at x22, and the dot runs x22→30 against the button's 36px box.

   R2 anchored the 8px dot itself at right-1 and centred the ring on it, which
   put the dot at x24→32 and the ring at x23→33. Two px right and a ring that
   painted OVER the dot, tinting the solid 500 shade with the pulsing 400 one for
   the whole cycle. Neither was visible then, because nothing revealed the dots
   and no reference shows one; R10 reveals them, so both are corrected here.

   R10 owns when they appear; the shell ships them hidden. */
.pace-hero .ph-ping {
  position: absolute; top: 4px; right: 4px;
  display: flex; width: 10px; height: 10px;
}
.pace-hero .ph-ping[hidden] { display: none; }
/* `animate-ping` verbatim: Tailwind's own `ping 1s cubic-bezier(0, 0, 0.2, 1)
   infinite` over a static `opacity-75`, which is the implicit 0% the keyframes
   below start from. */
.pace-hero .ph-ping-ring {
  position: absolute; top: 0; left: 0;
  width: 100%; height: 100%; border-radius: 50%; opacity: 0.75;
  animation: ph-ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
}
.pace-hero .ph-ping-dot {
  position: relative; flex: none;
  width: 8px; height: 8px; border-radius: 50%;
}
/* Once its popover has been opened the ping has said what it had to say. The
   ring goes and the solid dot stays — the app's plain unread marker. */
.pace-hero .ph-ping-seen .ph-ping-ring { display: none; }
.pace-hero .ph-ping-sparks .ph-ping-dot { background: #EAB308; }
.pace-hero .ph-ping-sparks .ph-ping-ring { background: #FACC15; }
.pace-hero .ph-ping-alerts .ph-ping-dot { background: var(--ph-red); }
.pace-hero .ph-ping-alerts .ph-ping-ring { background: #F87171; }
@keyframes ph-ping {
  75%, 100% { transform: scale(2); opacity: 0; }
}

/* The app hangs a 36px Avatar inside a 32px ghost button, so the disc overflows
   its button by 2px on each side and is the widest thing in the cluster: the
   visible gap before it measures 14px, and the disc's right edge sits 2px past
   the row's content edge, at x1889. */
.pace-hero .ph-avatar-btn {
  flex: none;
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
}
.pace-hero .ph-avatar {
  flex: none;
  display: flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: 50%;
  /* A flat orange disc: the signed-in user's generated image, not a design
     token. The literal bytes of all four references read #ec6f24, but those
     files carry one monitor's iCCP (DELL S2722QC) and literal samples in that
     space are not sRGB — the same captures hold #157859 / #7c26c8 / #1e4ed2 for
     the three nav accents whose sRGB tokens are known from source to be
     #047857 / #7e22ce / #1d4ed8. Converting through the embedded profile
     recovers all three to within a unit, and recovers this disc as #ef6c00,
     which is therefore the sRGB value to declare. R2 fix report §1. */
  background: #EF6C00; color: #FFFFFF;
  /* 18px, not the 16px §2.6 estimates: the reference's initial inks 8 x 13.5 in
     all four captures (ink geometry is profile-independent), which 16px
     undershoots by 1 x 2 while 18px matches the width and leaves 1px on height.
     That 1px is the floor, not a defect: in the app the initial is part of a
     generated image rather than a text node, its stroke measures 1.5px against
     2.5px for this font at 18px, and no font-size closes both. */
  font-size: 18px; font-weight: 600; line-height: 1;
}

/* ── Shared view surfaces ────────────────────────────────────────────────── */

/* Paint-server store for the Meta logo (see svgDefs). 0x0 and outside the
   canvas, so it never lays anything out. */
.pace-hero .ph-defs { position: absolute; width: 0; height: 0; }

.pace-hero .ph-card {
  background: var(--ph-bg);
  border: 1px solid var(--ph-border);
  border-radius: var(--ph-radius-lg);
  box-shadow: var(--ph-shadow-sm);
}
/* Everything below the top-level nav is a replica, not a control: no element
   inside a view is focusable or clickable, so nothing here may offer a text
   caret or a drag selection either. */
.pace-hero .ph-inert { cursor: default; -webkit-user-select: none; user-select: none; }

/* Icon wrapper: a flex box so a glyph never inherits its line box's extra
   leading and shifts the text it sits beside off-centre. Each surface sizes its
   own glyph — the card heads at 20px, every button at 16px (the Button forces
   [&_svg]:size-4), the §4 pills at 12px. */
.pace-hero .ph-glyph { display: flex; align-items: center; flex-shrink: 0; }
.pace-hero .ph-hue-muted { color: var(--ph-muted-fg); }
.pace-hero .ph-hue-amber { color: #F59E0B; }
.pace-hero .ph-hue-blue { color: #3B82F6; }
.pace-hero .ph-hue-blue-700 { color: #1D4ED8; }
.pace-hero .ph-hue-violet { color: #8B5CF6; }
.pace-hero .ph-hue-yellow { color: #EAB308; }

/* Tinted icon plates. One hue means one thing everywhere: the summary tiles'
   metric pills and the Quick Actions rail already share these, and the
   remaining views take them from here rather than restating the palette. */
.pace-hero .ph-ico-amber { background: #FFFBEB; color: #D97706; }
.pace-hero .ph-ico-emerald { background: #ECFDF5; color: #059669; }
.pace-hero .ph-ico-blue { background: #EFF6FF; color: #2563EB; }
.pace-hero .ph-ico-violet { background: #F5F3FF; color: #7C3AED; }
.pace-hero .ph-ico-orange { background: #FFF7ED; color: #EA580C; }
.pace-hero .ph-ico-muted { background: var(--ph-muted); color: var(--ph-muted-fg); }

/* A card that fills a fixed-height band and crops what does not fit — which is
   what production's own scroll regions look like. `p-6` = 24px, which is what
   home-page-content.tsx puts on Recent Changes, Top Performers and the chart
   card; the first build's 16px cost 8px on every edge of four cards. Sparks and
   Quick Actions split their padding between a header and a body block instead,
   so they pad their own children and are not .ph-pane. */
.pace-hero .ph-pane {
  display: flex; flex-direction: column; overflow: hidden; padding: 24px;
}
.pace-hero .ph-head-row { display: flex; align-items: center; justify-content: space-between; }
.pace-hero .ph-card-head { display: flex; align-items: center; gap: 8px; }
.pace-hero .ph-card-head svg { width: 20px; height: 20px; }
/* text-lg = 18px over an explicit 28px line box, tracking-tight = -0.025em =
   -0.45px at 18px. The first build ran 26.1px/-0.36px, which is a -0.02em
   tracking and the root ratio rather than the utility's own line-height. */
.pace-hero .ph-card-title {
  font-size: 18px; font-weight: 600; line-height: 28px; letter-spacing: -0.45px;
}
/* The 4px is the icon+title row's own `mb-1`; the bottom margin belongs to each
   card (Recent Changes `mb-4`, Top Performers `mb-3`, Sparks and the chart card
   get their gap from a padding split instead) and is declared per card below. */
.pace-hero .ph-card-sub {
  margin-top: 4px; font-size: 14px; line-height: 20px; color: var(--ph-muted-fg);
}
/* The Badge `secondary` base, un-overridden except for the caller's font-normal:
   `rounded-full border px-2.5 py-0.5 text-xs` with a transparent border and
   secondary-foreground, and `ml-2 align-middle` from the call site. 400, not the
   500 the first build painted — the caller writes font-normal explicitly. */
.pace-hero .ph-tag {
  display: inline-flex; align-items: center; vertical-align: middle;
  margin-left: 8px; padding: 2px 10px;
  border: 1px solid transparent; border-radius: 999px;
  background: var(--ph-muted); color: #18181B;
  font-size: 12px; font-weight: 400; line-height: 16px;
}
/* The inert select production puts at the right of a card head: a SelectTrigger
   stripped to `h-7 w-auto gap-1 border-none shadow-none text-xs
   text-muted-foreground px-2`, so it keeps the primitive's 10.8px radius and its
   `ChevronDown h-4 w-4 opacity-50` — a 28px control, not bare text. */
.pace-hero .ph-rankby {
  display: inline-flex; align-items: center; justify-content: space-between;
  gap: 4px; height: 28px; padding: 0 8px;
  border-radius: var(--ph-radius-md);
  font-size: 12px; line-height: 16px; color: var(--ph-muted-fg);
  white-space: nowrap;
}
.pace-hero .ph-rankby-chev { display: flex; align-items: center; flex-shrink: 0; opacity: 0.5; }
.pace-hero .ph-rankby-chev svg { width: 16px; height: 16px; }

/* The inset list production wraps its feeds in: `border rounded-lg p-2
   bg-muted/20 flex-grow min-h-0`. No margin of its own — the gap above it is the
   subtitle's `mb-4` on Recent Changes and a header/body padding split on Sparks,
   and baking one in here put both 4px short. */
.pace-hero .ph-panel {
  flex: 1; min-height: 0; padding: 8px; overflow: hidden;
  border: 1px solid var(--ph-border); border-radius: var(--ph-radius-lg);
  background: rgba(244, 244, 245, 0.2);
}
/* The `h-full overflow-y-auto pr-2` inside every inset frame: an 8px right
   gutter, so a row's right edge sits 16px inside the frame's border rather than
   8. Static frame, so the scroll is a clip. */
.pace-hero .ph-scroller {
  height: 100%; overflow: hidden; padding-right: 8px;
}

/* ── View toolbar ────────────────────────────────────────────────────────── */

/* Layout only: the surface itself is .ph-card, so the toolbar cannot drift from
   the panel recipe. 16px of padding around a 36px control row plus the 1px
   borders is what makes the panel 70px tall, and the 24px margin is the white
   gap before the content panel. */
.pace-hero .ph-toolbar {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  margin-bottom: var(--ph-gap); padding: 16px;
}
/* text-xl = 20px/28px, and it carries no tracking of its own: the first build's
   18px with -0.36px letter-spacing was DRIFT-11. Verified in ref-5, where
   "Accounts" inks 14.5px tall — a 20px cap height, not an 18px one. */
.pace-hero .ph-toolbar h2 {
  font-size: 20px; font-weight: 600; line-height: 28px;
}
/* `flex items-center gap-4` — the title then the Synced pill, 16px apart. */
.pace-hero .ph-toolbar-left { display: flex; align-items: center; gap: 16px; }
.pace-hero .ph-toolbar-right { display: flex; align-items: center; gap: 8px; }

/* §3.1 — the "Synced N hrs ago" pill: `hidden md:flex items-center gap-2 text-xs
   text-muted-foreground bg-secondary/20 px-3 py-1.5 rounded-full border`.
   34px tall, not the 30 §3.1 derives: its tallest child is the h-5 w-5 ghost
   refresh button, and ref-5's border rows sit at y121 and y154 — exactly 34.
   Staying under 36 is load-bearing, because the toolbar panel's 70px height is
   its padding plus its tallest child and the content panel's y198 hangs off it. */
.pace-hero .ph-synced {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 12px;
  border: 1px solid var(--ph-border); border-radius: 999px;
  background: rgba(244, 244, 245, 0.2);
  font-size: 12px; font-weight: 400; line-height: 16px; color: var(--ph-muted-fg);
  white-space: nowrap;
}
/* The freshness dot: amber-500 for the 8-24h band, which is where ref-5's ten
   hours lands (green under 8h, red past 24h). Measured #f59e0b exactly. */
.pace-hero .ph-synced-dot {
  flex-shrink: 0; width: 8px; height: 8px; border-radius: 50%; background: #F59E0B;
}
.pace-hero .ph-synced-label { font-size: 12px; font-weight: 400; line-height: 16px; }
/* The ghost refresh: a 20px box with `ml-1 -mr-1`, so it hangs 4px past the
   pill's own right padding. Its glyph is written h-3 w-3 and rendered at 16px,
   because the Button's [&_svg]:size-4 outranks the class — ref-5's ink is 13px,
   which is a 16px lucide glyph rather than a 12px one. */
.pace-hero .ph-synced-refresh {
  flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; margin-left: 4px; margin-right: -4px;
}
.pace-hero .ph-synced-refresh svg { width: 16px; height: 16px; }

/* §4.12 — the outline square the app uses for every toolbar control and for both
   header triggers: 36x36, 10.8px radius, 1px border, shadow-sm, and a 16px glyph
   whatever size the icon asks for (the Button forces [&_svg]:size-4, which
   outranks even the header icons' own h-5 w-5 — ref-5's Bell inks 13x15 css px,
   a 16px lucide glyph; a 20px one inks 16x18).
   Declared once for the inert replica (.ph-tool) and the two real controls
   (.ph-icon-btn) together: the interactive-only declarations live up in the
   header section, so neither side can drift the recipe. */
.pace-hero .ph-tool,
.pace-hero .ph-icon-btn {
  display: flex; align-items: center; justify-content: center;
  width: 36px; height: 36px;
  border: 1px solid var(--ph-border); border-radius: var(--ph-radius-md);
  background: var(--ph-bg);
  box-shadow: var(--ph-shadow-sm);
}
/* The one place the two diverge, and it is not a shared value: button.tsx's
   `outline` variant sets no text colour, so a toolbar control and the table row's
   gear inherit foreground, while header.tsx:814 adds text-muted-foreground to the
   Bell and Sparks triggers explicitly. Measured in ref-5 — the darkest pixel of
   all seven toolbar glyphs and of the row gear is #09090b, and of both header
   triggers #71717b. Declaring the colour once for both would be wrong twice. */
.pace-hero .ph-tool { color: var(--ph-fg); }
.pace-hero .ph-icon-btn { color: var(--ph-muted-fg); }
.pace-hero .ph-tool svg,
.pace-hero .ph-icon-btn svg { width: 16px; height: 16px; }
/* The two size variants, and both of them appear in one §5.2 portfolio row: the
   gear takes the default icon-button size (h-10 w-10) where every toolbar control
   overrides it to h-9 w-9, and the expand chevron overrides that same default the
   other way to h-8 w-8. Same radius, same border, same 16px glyph — box only, so
   neither restates the recipe above. Easy to miss, and visible in a row. */
.pace-hero .ph-tool-lg { width: 40px; height: 40px; }
.pace-hero .ph-tool-sm { width: 32px; height: 32px; }

/* ── §4 primitive library ────────────────────────────────────────────────── */

/* Every rule below is one of spec §4.2-§4.14, in ascending section order. Two
   caveats, both real: §4.1 has no CSS of its own, and a primitive whose only
   rules are layout for ONE view lives with that view instead (§5.2's platform
   stack and Goals cell both moved out on that ground). So this is the primitive
   library in spec order, not every §4 selector in the file. The views compose
   these and add layout only: a view that restates a border, a radius or a type
   size here has drifted. Heights are never declared — each one falls out of its
   padding, its content box and its border, which is how the app gets them
   (26 / 24 / 30 / 50 / 48) and how they stay right when a type size moves. */

/* §4.2 — the table shell and its header band. Clipping the card is what rounds
   the band's top corners and crops the last row at the canvas edge. */
.pace-hero .ph-table-card { overflow: hidden; }
/* table-layout: fixed is what makes the per-view widths on the header band the
   columns' real widths. The app's are content-driven (`min-w-fit`) and its
   measured set is a snapshot of its own data, so §5.0 has the rebuild hard-code
   them — and under an auto layout a hard-coded width is only a hint that a
   column's min-content can override, silently moving every edge to its right. */
.pace-hero .ph-table {
  width: 100%; table-layout: fixed; border-collapse: separate; border-spacing: 0;
}
/* 48px as a border box: 46px of #f4f4f5 over the 2px #f1f1f4 the row's border-b
   and the th's border-b-2 draw together. Labels are 14px/500 near-black —
   [&_th]:text-foreground beats ui/table.tsx's own text-muted-foreground, so the
   band's labels are NOT the grey 12px the base th would give. */
.pace-hero .ph-table th {
  height: 48px; padding: 0 16px;
  background: var(--ph-muted);
  border-bottom: 2px solid var(--ph-border);
  font-size: 14px; font-weight: 500; line-height: 20px; color: var(--ph-fg);
  text-align: left; white-space: nowrap;
}
.pace-hero .ph-table th:first-child { border-top-left-radius: var(--ph-radius-lg); }
.pace-hero .ph-table th:last-child { border-top-right-radius: var(--ph-radius-lg); }
/* The band's own shadow-md (TableHeader is `sticky ... shadow-md`): a soft ramp
   that reaches #dfdfdf under the rule and fades to white ~9px into the first
   row. The first build drew a hard #d4d4d9 line instead, which is the single
   loudest thing about its table. On thead rather than each th, so the blur does
   not double up at every column boundary. */
.pace-hero .ph-table thead {
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
}

/* §4.3 — row and cell. `p-4` all round plus one 1px separator, which is the 33px
   the app adds to its tallest cell; the first build's 12px vertical padding lost
   8px a row. Sized at 14px explicitly: the root is 16px, so an inherited cell
   renders large and shortens the row.
   The pitch that falls out of this is 83px, not §4A(b)'s measured 85: the app's
   52px content box is its spend cell, and 2 of those 52 px come from the A$
   prefix that this hero drops (measured — see the note on .ph-money). With plain
   `$` the tallest cell is the 50px account pill, which is exactly why the app's
   own Changes rows are 83px. SETTLED by the controller: the app's 16px padding
   stays and the pitch is 83px. No view buys the 2px back. */
.pace-hero .ph-table td {
  padding: 16px; vertical-align: middle; white-space: nowrap;
  border-bottom: 1px solid var(--ph-border);
  font-size: 14px; line-height: 20px; color: var(--ph-fg);
}
.pace-hero .ph-table tbody tr:last-child td { border-bottom: 0; }
/* Yesterday / Daily / Remaining / Pace / Goal are `align-bottom` and wrap their
   pill in `relative bottom-1`, so every pill's bottom edge lands on the progress
   bar's line (y310 in ref-5's first row) whatever the pill's own height. That
   shared reading line across the row is a signature the first build lost by
   centring every cell. */
.pace-hero .ph-table td.ph-td-bottom { vertical-align: bottom; }
.pace-hero .ph-lift { position: relative; bottom: 4px; }
.pace-hero .ph-center { text-align: center; }
/* The th rule above declares text-align: left at (0,2,1), which outranks the
   bare .ph-center class — so the centred header needs its own specific rule.
   The td one is stated for symmetry rather than left to inheritance. */
.pace-hero .ph-table th.ph-center { text-align: center; }
.pace-hero .ph-table td.ph-center { text-align: center; }
/* §5.2's expanded row: `<TableCell colSpan={n} className="p-0">` hosting a second
   complete table. `p-0` is the whole rule — the sub-table is NOT indented (§5.2
   verified its band running the full panel interior and its first account pill
   landing on the same x49 the standalone grid's does), so any padding, margin or
   left inset here would be a fidelity bug rather than a refinement. The 1px
   border-bottom inherited from the td rule above stays: it is the line under the
   whole nested block, which the sub-table's own last row does not draw. */
.pace-hero .ph-table td.ph-td-nested { padding: 0; }

/* §4.4 — the money display: the app's CurrencyDisplay wrapper (an inline-flex
   baseline row with the currency group carrying mr-[1px]) minus the superscript
   A$ prefix, which the rebuild drops in favour of a plain $ with cents.
   Measured, because §4.3's row pitch turns on it: an inline-flex money box does
   NOT by itself grow a 14px/20px line box. What grew the app's spend line to 22
   is the 0.8em prefix — the same recipe measures 20px plain, 21px with the
   prefix on the amount, and 22px with it on the amount and the budget both.
   No font-size here on purpose, as in the app: the wrapper inherits, and every
   shell that hosts it sizes itself (the §4.6/§4.9 pills at 12px, a spend amount
   at 14px). A money() dropped into an unsized box inherits the 16px root. */
.pace-hero .ph-money { display: inline-flex; align-items: baseline; white-space: nowrap; }
.pace-hero .ph-money-sym { margin-right: 1px; }

/* §4.5 — the account-name pill: an outline Button wrapping a Link. 8 + the 32px
   avatar + 8 + 2 borders = 50px, the tallest thing in a table row. */
.pace-hero .ph-account {
  display: inline-flex; align-items: center; gap: 12px;
  max-width: 100%; padding: 8px 16px;
  border: 1px solid var(--ph-border); border-radius: var(--ph-radius-md);
  background: transparent; box-shadow: var(--ph-shadow-sm);
  font-size: 14px; font-weight: 500; line-height: 20px; color: var(--ph-fg);
  white-space: nowrap;
}
.pace-hero .ph-account-name {
  min-width: 0; overflow: hidden; text-overflow: ellipsis;
  font-size: 14px; font-weight: 500; line-height: 20px;
}
/* AccountAvatar: a solid disc from the 12-token palette with a bold white
   initial and shadow-sm. The base is size="sm" (32px / text-sm), the size
   variants are the app's other four uses — xs 20px, the Changes actor's 24px
   Avatar, Quick Actions' 28px override and md 40px — each with the type the
   source gives it. The two type overrides carry the raw shadcn Avatar's
   semibold-no-shadow (dropdown items) and its plain 400 (Changes actors). */
.pace-hero .ph-acct-avatar {
  flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: 50%;
  box-shadow: var(--ph-shadow-sm);
  color: #FFFFFF; font-size: 14px; font-weight: 700; line-height: 1;
}
.pace-hero .ph-ava-20 { width: 20px; height: 20px; font-size: 10px; }
.pace-hero .ph-ava-24 { width: 24px; height: 24px; font-size: 10px; }
.pace-hero .ph-ava-28 { width: 28px; height: 28px; font-size: 10px; }
.pace-hero .ph-ava-36 { width: 36px; height: 36px; font-size: 16px; }
.pace-hero .ph-ava-40 { width: 40px; height: 40px; font-size: 16px; }
.pace-hero .ph-ava-flat { box-shadow: none; }
.pace-hero .ph-ava-w600 { font-weight: 600; }
.pace-hero .ph-ava-w400 { font-weight: 400; }

/* §4.6 / §4.9 — one outline Badge serves the icon-value pills and the goal
   badge: transparent fill, 1px #f1f1f4, 10.8px radius, 4/8 padding, 12px/500
   near-black over a 16px line box. 26px tall with text alone; 30px once the
   nested trend chip's 20px content box is inside it. The first build painted a
   flat #f4f4f5 chip with no border and no glyph — three per row, eight rows. */
.pace-hero .ph-pill,
.pace-hero .ph-goal {
  display: inline-flex; align-items: center;
  padding: 4px 8px;
  border: 1px solid var(--ph-border); border-radius: var(--ph-radius-md);
  background: transparent;
  font-size: 12px; font-weight: 500; line-height: 16px; color: var(--ph-fg);
  white-space: nowrap;
}
/* The leading glyph: 12px (h-3 w-3) with mr-1, inheriting the pill's colour. */
.pace-hero .ph-pill-ico,
.pace-hero .ph-goal-ico {
  display: flex; align-items: center; flex-shrink: 0; margin-right: 4px;
}
.pace-hero .ph-pill-ico svg,
.pace-hero .ph-goal-ico svg { width: 12px; height: 12px; }
/* The optional label: text-[10px] uppercase tracking-wider (0.05em = 0.5px at
   10px) in muted-foreground, with its colon. Absent from the Accounts and
   Portfolios tables, which pass showLabel={false}. */
.pace-hero .ph-pill-label,
.pace-hero .ph-goal-label {
  margin-right: 4px;
  font-size: 10px; line-height: 16px; letter-spacing: 0.5px;
  text-transform: uppercase; color: var(--ph-muted-fg);
}
/* FloorDailyPill's `≥`: `<span className="mr-0.5">` before the CurrencyDisplay
   (floor-pacing-indicators.tsx:113-115). A floor account holds a configured
   daily floor instead of a remaining-derived target, and this is the whole
   visual difference in the Daily column. Sized explicitly like every other text
   node here: it inherits 12px from the pill today, but the root is 16px and
   nothing may depend on inheritance. */
.pace-hero .ph-pill-prefix { margin-right: 2px; font-size: 12px; line-height: 16px; }
/* The Remaining cell's "not applicable" content on a floor-pacing row: the app
   keeps RemainingPill's Badge and its Wallet and swaps the CurrencyDisplay for
   `<span className="text-muted-foreground">—</span>`
   (account-table-row.tsx:201-214), so the dash is muted where the pill's own text
   is near-black. Sized explicitly for the same reason as every other text node. */
.pace-hero .ph-pill-na {
  font-size: 12px; line-height: 16px; color: var(--ph-muted-fg);
}
.pace-hero .ph-goal-value {
  font-size: 12px; font-weight: 500; line-height: 16px; color: var(--ph-fg);
}
/* The conversion-action variant's `truncate max-w-[80px]`, which is what renders
   as "COMPLETE R…" in ref-5's fifth row. */
.pace-hero .ph-goal-trunc {
  display: inline-block; max-width: 80px;
  overflow: hidden; text-overflow: ellipsis;
}
/* The no-metrics variant is the un-overridden Badge base: rounded-full, 10/2
   padding, text-xs font-semibold in muted-foreground. */
.pace-hero .ph-goal-nodata {
  padding: 2px 10px; border-radius: 999px;
  font-size: 12px; font-weight: 600; line-height: 16px; color: var(--ph-muted-fg);
}

/* §4.7 — the tinted pace pill. `rounded` is Tailwind's own 4px: only lg/md/sm
   are remapped in this project, so this is the tightest radius in the app and a
   deliberate contrast against every other pill. No border, 24px tall. */
.pace-hero .ph-pace-pill {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 8px; border-radius: 4px;
  font-size: 12px; font-weight: 500; line-height: 16px; white-space: nowrap;
}
.pace-hero .ph-pace-value { font-size: 12px; font-weight: 500; line-height: 16px; }
.pace-hero .ph-pace-pill.ph-tier-green { background: #F0FDF4; color: #16A34A; }
.pace-hero .ph-pace-pill.ph-tier-yellow { background: #FEFCE8; color: #CA8A04; }
.pace-hero .ph-pace-pill.ph-tier-orange { background: #FFF7ED; color: #EA580C; }
.pace-hero .ph-pace-pill.ph-tier-red { background: #FEF2F2; color: #DC2626; }
/* An account with no budget: the neutral `- %` variant the first build had no
   tier for at all (account-table-row.tsx:246). */
.pace-hero .ph-pace-pill.ph-tier-none { background: var(--ph-muted); color: var(--ph-muted-fg); }
/* amber-50 / amber-600 — the one pair the four variance bands do not carry, and
   the third of FloorPacePill's three status tints (above-ceiling, the soft
   tripwire). floorPacePill() maps the other two onto the green and red rules
   above rather than restating those hexes, which is what the app does too: both
   components paint the same bg-*-50 / text-*-600 pairs from different semantics.
   There is no violet floor pill any more — that pair was the first build's
   invention. The app renders FloorPacePill (floor-pacing-indicators.tsx:78-95),
   keyed on yesterday-vs-floor status, and on DEMO's floor row that is green. */
.pace-hero .ph-pace-pill.ph-tier-amber { background: #FFFBEB; color: #D97706; }

/* §4.8 — the nested trend chip. Never standalone in the tables: it sits inside
   the goal badge, 8px (ml-2) after the value. text-[10px] sets size only, so the
   16px line box comes from the ancestor's text-xs — which is exactly why the
   badge around it grows from 26px to 30px. */
.pace-hero .ph-trend {
  display: inline-flex; align-items: center; gap: 2px;
  margin-left: 8px; padding: 2px 6px; border-radius: 4px;
  font-size: 10px; font-weight: 700; line-height: 16px; white-space: nowrap;
}
.pace-hero .ph-trend svg { width: 12px; height: 12px; flex-shrink: 0; }
/* invertColor decides which direction is good, so a red chip can point up and a
   green one down — visible in ref-5's first two rows. */
.pace-hero .ph-trend-good { background: #DCFCE7; color: #15803D; }
.pace-hero .ph-trend-bad { background: #FEE2E2; color: #B91C1C; }
.pace-hero .ph-trend-zero { background: var(--ph-muted); color: var(--ph-muted-fg); }

/* §4.10 — the platform chip. There is no CSS chip: every mark paints its own
   white disc and 2-unit #E5E7EB ring inside its 48-unit viewBox, so a border or
   a shadow here would double the ring. These rules do nothing but size it —
   PlatformIcon's four sizes, md (32px) being the table's. */
.pace-hero .ph-platform { width: 32px; height: 32px; }
.pace-hero .ph-platform-16 { width: 16px; height: 16px; }
.pace-hero .ph-platform-24 { width: 24px; height: 24px; }
.pace-hero .ph-platform-32 { width: 32px; height: 32px; }
.pace-hero .ph-platform-48 { width: 48px; height: 48px; }
/* The table's `flex w-[60px] justify-center` box: the mark's centre lands 46px
   from the cell's left edge, not on the column's centre. */
.pace-hero .ph-plat-box { display: flex; width: 60px; justify-content: center; }
.pace-hero .ph-table td.ph-center > .ph-platform { margin: 0 auto; }
/* §4.11 — the spend progress bar. A 12px transparent track with a 1px border
   and 1.5px of padding, so the fill is 7px; 128px wide (w-32 shrink-0) with the
   budget figure beside it, or the full width of a Home tile. */
.pace-hero .ph-bar-row { display: flex; align-items: center; gap: 8px; }
.pace-hero .ph-bar {
  display: inline-block; vertical-align: middle; flex: none;
  width: 128px; height: 12px; padding: 1.5px;
  border: 1px solid var(--ph-border); border-radius: 999px;
  background: transparent;
}
.pace-hero .ph-bar-full .ph-bar { width: 100%; }
.pace-hero .ph-bar-fill { height: 100%; border-radius: 999px; }
/* The budget, bare: no "of", and 12px muted rather than the first build's 11px. */
.pace-hero .ph-bar-label {
  font-size: 12px; line-height: 16px; color: var(--ph-muted-fg); white-space: nowrap;
}

/* Pacing tiers. The same .ph-tier-* class lands on the bar fill (a solid 300
   shade) and on the pace pill (a 50 tint plus a 600 text colour), so both sides
   are scoped to their element. */
.pace-hero .ph-bar-fill.ph-tier-green { background: #86EFAC; }
.pace-hero .ph-bar-fill.ph-tier-yellow { background: #FDE047; }
.pace-hero .ph-bar-fill.ph-tier-orange { background: #FDBA74; }
.pace-hero .ph-bar-fill.ph-tier-red { background: #FCA5A5; }
/* §4.11's fifth fill: an account with no variance to colour by gets bg-muted,
   which is effectively invisible against the track — and that is the app's
   behaviour, not an omission. Without this rule the class exists with no
   declaration, which is the same silent hole the neutral pace tier had. */
.pace-hero .ph-bar-fill.ph-tier-none { background: var(--ph-muted); }
/* Floor pacing: purple with a slow sweep, production's .floor-bar-fill. The
   no-repeat is load-bearing — the default tiling paints a second highlight band
   that gets cut off at the loop wrap and reads as a blink. */
.pace-hero .ph-bar-fill.ph-tier-floor {
  /* globals.css:358 verbatim — purple-300 at 70%, which over the transparent
     track reads #E4CBFE. §4.11 states the opaque violet-300 #C4B5FD instead; that
     is 32 units of red out and source is decisive here (DRIFT-17 in
     fidelity-header-accounts §3.4 had it right). Kept as the source declaration
     rather than the flattened hex, exactly as the synced pill's
     bg-secondary/20 is. */
  background-color: rgb(216 180 254 / 0.7);
  background-image: linear-gradient(100deg, transparent 35%, rgba(255, 255, 255, 0.45) 50%, transparent 65%);
  background-size: 250% 100%;
  background-repeat: no-repeat;
  animation: ph-shimmer 4s linear infinite;
}
@keyframes ph-shimmer {
  0% { background-position: 150% 0; }
  100% { background-position: -150% 0; }
}

/* §4.13 — the dropdowns' segmented control. `flex justify-start p-2` around a
   content-sized track, so the band is 56px and the control is left-aligned
   rather than full width. The active half is white + shadow-sm with NO border:
   the dark outline visible in both crops is a focus-visible ring-ink artefact of
   how the captures were driven (§6.4), not the design. */
.pace-hero .ph-seg-wrap { display: flex; justify-content: flex-start; padding: 8px; }
.pace-hero .ph-seg {
  display: inline-flex; align-items: center;
  height: 40px; padding: 4px;
  border-radius: var(--ph-radius-md); background: var(--ph-muted);
}
.pace-hero .ph-seg-item {
  display: flex; align-items: center;
  height: 28px; padding: 0 16px;
  border-radius: var(--ph-radius-sm);
  font-size: 12px; font-weight: 500; line-height: 16px; color: var(--ph-muted-fg);
  white-space: nowrap;
}
.pace-hero .ph-seg-item.ph-seg-on {
  background: var(--ph-bg); color: var(--ph-fg); box-shadow: var(--ph-shadow-sm);
}

/* §4.14 — an account avatar with its platform badge hung off the bottom-right
   corner. The app uses three pairings and the difference is legible at 100%:
   24px at a 6px offset (Home's SparkCard), 24px at 4px (the feeds), 24px at 2px
   with no shadow (Quick Actions) and a bare 20px at 4px (both dropdowns, which
   have no backing plate at all). The mark fills the badge box, so one wrapper
   size drives both, and it stays a direct child so the plate never wraps it. */
.pace-hero .ph-ava { position: relative; display: inline-flex; flex-shrink: 0; }
.pace-hero .ph-plat-badge { position: absolute; display: block; }
.pace-hero .ph-plat-badge > .ph-platform { width: 100%; height: 100%; }
.pace-hero .ph-badge-16 { width: 16px; height: 16px; }
.pace-hero .ph-badge-20 { width: 20px; height: 20px; }
.pace-hero .ph-badge-24 { width: 24px; height: 24px; }
.pace-hero .ph-badge-off-2 { right: -2px; bottom: -2px; }
.pace-hero .ph-badge-off-4 { right: -4px; bottom: -4px; }
.pace-hero .ph-badge-off-6 { right: -6px; bottom: -6px; }
.pace-hero .ph-badge-plate { background: var(--ph-bg); border-radius: 50%; }
.pace-hero .ph-badge-plate-shadow { box-shadow: var(--ph-shadow-sm); }

/* ── Shared cross-view composites ─────────────────────────────────────────── */

/* Surfaces that two or more views build out of the §4 primitives. They are NOT
   primitives — nothing in spec §4.2-§4.14 names them — so they cannot live in the
   block above, whose header promises a reader that every rule inside it maps to a
   spec subsection. They are not view-local either, so they cannot be filed under
   whichever view happened to build one first. This is their home.

   (The `Shared view surfaces` section further up is a different thing: shell-level
   surfaces — the panel recipe, the icon plates, the inset frame — that the header
   and the views share. These are cell COMPOSITES.) */

/* §5.1's and §5.2's Spend cell, emitted by spendCell() and rendered by three
   surfaces: the accounts grid, an expanded portfolio's account sub-table and the
   portfolio rollup row itself. `flex flex-col gap-1 min-w-[160px] pb-1.5 pt-1`
   holding a 14px/700 amount over the §4.11 bar row. 4 + 20 + 4 + 16 + 6 = 50, the
   same height as the account pill, which is what makes 50 the row's tallest cell.
   min-width is INERT here and kept only as documentation of the source: the column
   is 292.5px under a fixed layout, so nothing about this cell's width depends on
   it. Do not read it as a live constraint. */
.pace-hero .ph-spend {
  display: flex; flex-direction: column; gap: 4px;
  min-width: 160px; padding-top: 4px; padding-bottom: 6px;
}
.pace-hero .ph-spend-amount { font-size: 14px; font-weight: 700; line-height: 20px; }

/* ── Portfolios view ───────────────────────────────────────────────────── */

/* Two §5.2 surfaces that are NOT §4 primitives and were filed inside the
   primitive library by mistake: that block's header states every rule in it is
   one of spec §4.2-§4.14, and a page-level create panel is neither reusable nor
   a primitive. Moved verbatim — no declaration changed, and nothing between
   here and their old position declares either selector, so the cascade is
   unchanged. (The one rule that DID depend on file order, .ph-toolbar-gap-3,
   was given real specificity in the same pass.) */

/* §5.2's Platforms cell — `flex -space-x-2 overflow-hidden` over one
   `inline-block ring-2 ring-card rounded-full bg-card` wrapper per mark. Layout
   width for n marks is 32 + 24(n-1), measured at exactly 80 for three in ref-4.
   The ring is a 2px WHITE outline drawn outside each wrapper, and it is the only
   thing separating the overlapped discs: the mark's own #E5E7EB hairline is inside
   it, so a stack without the ring reads as one smeared shape. box-shadow rather
   than a border, because a border would take layout width and break the 24px step. */
.pace-hero .ph-plat-stack { display: flex; overflow: hidden; }
.pace-hero .ph-plat-ring {
  display: inline-block; flex-shrink: 0;
  border-radius: 999px; background: var(--ph-bg);
  box-shadow: 0 0 0 2px var(--ph-bg);
}
/* Paint order is DOM order, so each mark overlaps the one to its left — measured
   in ref-4, where the right-hand disc's own hairline is what survives the overlap. */
.pace-hero .ph-plat-ring + .ph-plat-ring { margin-left: -8px; }

/* §5.2's Goals cell — `flex flex-row gap-2 items-center` holding the primary goal
   badge and, when there is one, the secondary. The only place in the app two §4.9
   badges share a cell, and what makes that column 350.5px wide. Measured in ref-4:
   badge 1 ends at x1631 and badge 2 starts at 1640, so the gap is 8, not the 9.5
   §5.2 records (that figure counts the badges' own antialiased edges). The badges
   size themselves, so this rule is layout only. */
.pace-hero .ph-goals { display: flex; align-items: center; gap: 8px; }

/* §5.2's "Create another portfolio" panel (portfolios/page.tsx:39-49):
   `w-full rounded-lg border-2 border-dashed border-border p-6 flex items-center
   justify-center gap-6`. No fill and no shadow — so faint that §5.2 needed a
   0.6-threshold line scan to find its border at all. 2 + 24 + 40 + 24 + 2 = the
   92px it measures, and the 24px margin is the page's own `space-y-6` below the
   table card. */
.pace-hero .ph-create {
  display: flex; align-items: center; justify-content: center; gap: 24px;
  margin-top: var(--ph-gap); padding: 24px;
  border: 2px dashed var(--ph-border); border-radius: var(--ph-radius-lg);
}
/* The default (h-10 px-4 py-2) outline Button, not the h-9 the toolbar controls
   take: 40px tall with a 16px Plus 8px in front of a 14px/500 label. Inert, so it
   is a span — nothing below the top-level nav is a control. */
.pace-hero .ph-create-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 40px; padding: 0 16px;
  border: 1px solid var(--ph-border); border-radius: var(--ph-radius-md);
  background: transparent; box-shadow: var(--ph-shadow-sm);
  font-size: 14px; font-weight: 500; line-height: 20px; color: var(--ph-fg);
  white-space: nowrap;
}
.pace-hero .ph-create-ico { display: flex; flex-shrink: 0; }
.pace-hero .ph-create-ico svg { width: 16px; height: 16px; }

/* ── Home view ───────────────────────────────────────────────────────────── */

/* home-page-content.tsx's four stacked bands, `space-y-6` = 24px apart. Band 1
   is content-driven and comes out at 199px from its own paddings; bands 2 and 3
   carry the app's own `h-[500px]` / `h-[420px]`, which is why band 3 runs past
   the 1240 canvas and is cropped by the bottom fade. That crop is the point —
   the app visibly continues below the frame — so nothing here is tuned to fit.
   Band 3 ends at y1271, 31px past the frame; what the frame WAS sized for is the
   Performance card's plot, whose box ends at y1246 and whose date labels ink to
   y1235, both inside 1240 and both cut at 1200.
   Band 4 ("Recent Alerts" + "Industry News", y1295-1715) starts 55px below the
   canvas and is not rendered; see the task report. */
.pace-hero .ph-home { display: flex; flex-direction: column; gap: 24px; }
/* `md:grid-cols-[7fr_4fr_auto]`, the auto track being QuickActions' own
   w-[200px]: the fr split runs over 1856 - 48 - 200 = 1608, giving 1023.27 /
   584.73. Written as the source fractions rather than the measured pixels so the
   browser reproduces its own subpixel edges. */
.pace-hero .ph-home-mid {
  display: grid; grid-template-columns: 7fr 4fr 200px; gap: 24px; height: 500px;
}
/* `md:grid-cols-3` with `md:col-span-2` on the chart, NOT `2fr 1fr`: three equal
   602.67px tracks let the chart absorb the interior gap (602.67x2 + 24 =
   1229.33), where a 2fr/1fr split of 1192 gives 794.67 and moves both inner
   edges 8px. */
.pace-hero .ph-home-band {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; height: 420px;
}
.pace-hero .ph-home-band > .ph-perf { grid-column: span 2; }
/* Every Home card is `rounded-xl`, which tailwind.config.ts does NOT remap — so
   12px, one step tighter than the 12.8px .ph-card carries for the table views'
   `rounded-lg` panels. (0,3,0) beats .ph-card's (0,2,0). */
.pace-hero .ph-home .ph-card { border-radius: var(--ph-radius-home); }

/* ── Band 1 — pacing summary bar ─────────────────────────────────────────── */

/* `rounded-xl border border-border bg-card p-5 shadow-sm`. 1 + 20 + 26 (month
   pill) + 16 (mb-4) + 115 (tile row) + 20 + 1 = 199, the measured band height,
   with no height declared anywhere. */
.pace-hero .ph-summary { padding: 20px; }
.pace-hero .ph-summary-head { display: flex; align-items: center; gap: 8px; margin-bottom: 16px; }
.pace-hero .ph-month-pill {
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px;
  border: 1px solid var(--ph-border); border-radius: 999px;
  background: rgba(244, 244, 245, 0.3);
  font-size: 12px; font-weight: 500; line-height: 16px; white-space: nowrap;
}
.pace-hero .ph-month-pill svg { width: 12px; height: 12px; }
.pace-hero .ph-summary-sub { font-size: 12px; line-height: 16px; color: var(--ph-muted-fg); }

.pace-hero .ph-tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
/* `p-3 rounded-xl border border-border/70 bg-card/10` — the alpha border is the
   lightest hairline in the app, and bg-card/10 is white on white. Kept as the
   source declaration (like the nav track's bg-muted/60) rather than the
   composited #f5f5f7, which Chrome rounds one step differently anyway. */
.pace-hero .ph-tile {
  padding: 12px;
  border: 1px solid rgba(241, 241, 244, 0.7); border-radius: var(--ph-radius-home);
}
/* `inline-flex gap-1.5 px-2 py-1 rounded-full border border-border/60 bg-card
   shadow-sm mb-2` → 4 + 20 (the tinted disc) + 4 + 2 = 30px tall. */
.pace-hero .ph-tile-pill {
  display: inline-flex; align-items: center; gap: 6px; margin-bottom: 8px;
  padding: 4px 8px;
  border: 1px solid rgba(241, 241, 244, 0.6); border-radius: 999px;
  background: var(--ph-bg); box-shadow: var(--ph-shadow-sm);
  white-space: nowrap;
}
.pace-hero .ph-tile-label { font-size: 12px; font-weight: 500; line-height: 16px; }
.pace-hero .ph-tile-ico {
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  width: 20px; height: 20px; border-radius: 50%;
}
.pace-hero .ph-tile-ico svg { width: 12px; height: 12px; }
.pace-hero .ph-tile-chev { color: var(--ph-muted-fg); }
.pace-hero .ph-tile-chev svg { width: 12px; height: 12px; }
.pace-hero .ph-tile-value { display: flex; align-items: baseline; gap: 6px; }
/* text-xl = 20px over an explicit 28px line box (1.75rem), which is what makes
   the value row 28px tall and tile 4 the 115px that sets the row's height. */
.pace-hero .ph-tile-num { font-size: 20px; font-weight: 700; line-height: 28px; }
/* text-[10px] sets size only, so the line box is the root ratio: 15px. */
.pace-hero .ph-tile-of {
  font-size: 10px; line-height: 15px; color: var(--ph-muted-fg); white-space: nowrap;
}
.pace-hero .ph-tile-note {
  margin-top: 4px; font-size: 10px; line-height: 15px; color: var(--ph-muted-fg);
}
/* The app's own `<div className="mt-2">` around SpendProgressBar. The bar itself
   is the §4.11 primitive at full width — spendBar(..., {full: true}) — and NOT a
   second full-width mechanism: the first build also carried
   `.ph-tile .ph-bar { display:block; width:100%; margin-top:6px }`, whose stale
   margin would offset the bar inside the primitive's flex row. That rule is
   gone; .ph-bar-full is the only way a bar goes full width. */
.pace-hero .ph-tile-bar { margin-top: 8px; }

/* The trend chip the tiles show is the §4.8 primitive, whose own `ml-2` is what
   puts it 14px clear of the value (6px flex gap + 8px margin). One rule, up in
   the primitive block — the first build restated it here at a 6px radius.

   The one thing Home changes is the chip's line box. `text-[10px]` sets a size
   and no line-height, so the box comes from whatever the chip inherits: inside
   §4.9's goal badge that is `text-xs`'s 1rem = 16px, but on Home the nearest
   ancestor with a line-height is the root's unitless 1.5, which at 10px is 15px.
   So the chip is 2 + 15 + 2 = 19px here against 20px in a table row — measured
   at 19 in ref-1's tiles 2 and 3, whose fill spans y224-243 against the 223-243
   a 16px box gives. */
.pace-hero .ph-tile .ph-trend,
.pace-hero .ph-slot .ph-trend { line-height: 15px; }

.pace-hero .ph-dots { display: flex; align-items: center; gap: 6px; margin-top: 8px; }
.pace-hero .ph-dot-group { display: inline-flex; align-items: center; gap: 4px; }
.pace-hero .ph-dot { width: 8px; height: 8px; border-radius: 50%; }
/* One step stronger than the bar fills (TIER_FILL): production's count dots are
   the 400 shades where the bars are the 300s. */
.pace-hero .ph-dot-green { background: #4ADE80; }
.pace-hero .ph-dot-yellow { background: #FACC15; }
.pace-hero .ph-dot-orange { background: #FB923C; }
.pace-hero .ph-dot-red { background: #F87171; }
.pace-hero .ph-dot-count { font-size: 10px; line-height: 15px; color: var(--ph-muted-fg); }

/* ── Band 2, column 1 — Recent Sparks ────────────────────────────────────── */

/* sparks-row.tsx:87 — the one Home card with no uniform padding: a `p-6 pb-2`
   header over a `flex-1 p-6 pt-2` body, so the 16px between the subtitle and the
   inset frame is 8 + 8 rather than a margin. */
.pace-hero .ph-sparks { display: flex; flex-direction: column; overflow: hidden; }
.pace-hero .ph-sparks-head { padding: 24px 24px 8px; }
.pace-hero .ph-sparks-body {
  flex: 1; min-height: 0; display: flex; flex-direction: column;
  padding: 8px 24px 24px;
}
/* `space-y-3` between cards. Two are rendered: the second is clipped by the
   frame, which is production's own "there is more" cue on this card. */
.pace-hero .ph-scroller > .ph-spark + .ph-spark { margin-top: 12px; }

/* spark-card.tsx:70 — `flex flex-col gap-3 rounded-lg border p-4`, ~311px tall.
   This is the card the first build replaced with a 56px chip. */
.pace-hero .ph-spark {
  display: flex; flex-direction: column; gap: 12px; padding: 16px;
  border: 1px solid var(--ph-border); border-radius: var(--ph-radius-lg);
}
.pace-hero .ph-spark-head { display: flex; align-items: center; justify-content: space-between; }
.pace-hero .ph-spark-ident { display: flex; align-items: center; gap: 12px; }
/* The shadcn Avatar's own `border`, which AccountAvatar never carries: a 1px
   #f1f1f4 ring inside the 40px box (border-box), over the account colour. */
.pace-hero .ph-spark-ident .ph-acct-avatar { border: 1px solid var(--ph-border); }
.pace-hero .ph-spark-who { display: flex; flex-direction: column; min-width: 0; }
.pace-hero .ph-spark-acct { font-size: 14px; font-weight: 600; line-height: 20px; }
/* `text-xs text-muted-foreground capitalize` — "Microsoft Ads". The line the
   first build dropped, replacing it with a second copy of the account name. */
.pace-hero .ph-spark-plat {
  font-size: 12px; line-height: 16px; color: var(--ph-muted-fg);
  text-transform: capitalize;
}
/* `text-[10px] font-semibold text-yellow-600 bg-yellow-100 px-1.5 py-0.5
   rounded` — yellow-600, not the yellow-700 the first build used, over a 4px
   Tailwind-default radius rather than 6px. */
.pace-hero .ph-new {
  flex-shrink: 0; padding: 2px 6px; border-radius: 4px;
  background: #FEF9C3; color: #CA8A04;
  font-size: 10px; font-weight: 600; line-height: 15px;
}
/* `grid grid-cols-1 gap-1 pt-2` — 4px between every child, 8px above the first. */
.pace-hero .ph-spark-body {
  display: flex; flex-direction: column; gap: 4px; padding-top: 8px;
}
/* `text-lg leading-none` on its own grid row: 18px at line-height 1, ABOVE the
   title rather than inline with it. Emoji, so no stroke colour applies. */
.pace-hero .ph-spark-sev { font-size: 18px; line-height: 1; }
.pace-hero .ph-spark-title { font-size: 14px; font-weight: 600; line-height: 20px; }
/* `text-sm opacity-90 leading-relaxed` → 14px at 1.625 = 22.75px. */
.pace-hero .ph-spark-analysis { font-size: 14px; line-height: 22.75px; opacity: 0.9; }
/* ui/Recommendation.tsx — `mt-2 p-3 rounded-md border bg-muted/50 border-border`
   with a 14px/700 muted heading and 12px muted body: 12 + 20 + 4 + 16 + 12 + 2 =
   66px. The 8px margin sits on top of the body grid's own 4px gap. */
.pace-hero .ph-rec {
  margin-top: 8px; padding: 12px;
  border: 1px solid var(--ph-border); border-radius: var(--ph-radius-md);
  background: rgba(244, 244, 245, 0.5);
}
.pace-hero .ph-rec-head {
  margin-bottom: 4px;
  font-size: 14px; font-weight: 700; line-height: 20px; color: var(--ph-muted-fg);
}
.pace-hero .ph-rec-text { font-size: 12px; line-height: 16px; color: var(--ph-muted-fg); }
.pace-hero .ph-spark-foot {
  display: flex; align-items: center; justify-content: space-between; margin-top: 4px;
}
.pace-hero .ph-spark-time { font-size: 12px; line-height: 16px; color: var(--ph-muted-fg); }
/* `Button variant="ghost" size="sm" className="h-8 px-3 text-xs bg-background
   border shadow-sm"` — a 32px white pill, so the ghost variant's transparent
   fill and missing border are both overridden at the call site. */
.pace-hero .ph-spark-btn {
  display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
  height: 32px; padding: 0 12px;
  border: 1px solid var(--ph-border); border-radius: var(--ph-radius-md);
  background: var(--ph-bg); box-shadow: var(--ph-shadow-sm);
  font-size: 12px; font-weight: 500; line-height: 16px; white-space: nowrap;
}

/* ── Band 2, column 2 — Recent Changes ───────────────────────────────────── */

/* The subtitle's own `mb-4`, which is what puts the inset frame at y420. */
.pace-hero .ph-changes .ph-card-sub { margin-bottom: 16px; }
.pace-hero .ph-activity { position: relative; display: flex; align-items: flex-start; gap: 12px; }
/* `absolute left-4 top-9 -bottom-1 w-0.5 bg-border -translate-x-1/2`: a 2px rule
   centred on item-left + 16, running from the avatar's lower edge (top-9 = 36,
   which is mt-1 + the 32px avatar) to 4px past the item's bottom. It passes
   BEHIND the avatars, which are lifted above it. */
.pace-hero .ph-activity-line {
  position: absolute; left: 16px; top: 36px; bottom: -4px; width: 2px;
  margin-left: -1px; background: var(--ph-border);
}
.pace-hero .ph-activity > .ph-ava { position: relative; z-index: 10; margin-top: 4px; }
/* `flex-grow mb-2 rounded-lg border border-border bg-card p-3` → 12 + 20 + 2 +
   20 + 12 + 2 = 68px per row, 8px apart. */
.pace-hero .ph-activity-card {
  flex: 1; min-width: 0; margin-bottom: 8px; padding: 12px;
  border: 1px solid var(--ph-border); border-radius: var(--ph-radius-lg);
  background: var(--ph-bg);
}
.pace-hero .ph-activity-top { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.pace-hero .ph-activity-actor {
  font-size: 14px; font-weight: 600; line-height: 20px; text-transform: capitalize;
}
.pace-hero .ph-activity-ago {
  font-size: 12px; line-height: 16px; color: var(--ph-muted-fg); white-space: nowrap;
}
/* `Badge variant="secondary" className="text-[10px] px-1.5 py-0"` — the same
   secondary base .ph-tag carries, but at 10px with its padding overridden to
   6px/0, so it is 0 + 15 + 0 + 2 borders = 17px and does not grow the 20px
   baseline row the actor sets. */
.pace-hero .ph-activity-badge {
  display: inline-flex; align-items: center; flex-shrink: 0;
  padding: 0 6px;
  border: 1px solid transparent; border-radius: 999px;
  background: var(--ph-muted); color: #18181B;
  font-size: 10px; font-weight: 600; line-height: 15px; white-space: nowrap;
}
.pace-hero .ph-activity-desc {
  display: block; margin-top: 2px; font-size: 14px; line-height: 20px;
}
.pace-hero .ph-em { font-weight: 500; }

/* ── Band 2, column 3 — Quick Actions ────────────────────────────────────── */

/* quick-actions.tsx:63 — a hard w-[200px] from the grid's auto track, `p-4 pb-2`
   over a `flex-1 px-3 pb-3 gap-1.5` body. The five nav rows and the RECENT block
   fill the 500px exactly, leaving ~3px under the last row. */
.pace-hero .ph-qa { display: flex; flex-direction: column; overflow: hidden; }
.pace-hero .ph-qa-headblock { padding: 16px 16px 8px; }
/* `mb-1` on the icon+title row, exactly as every other Home card's head carries
   it — without it the whole rail sits 4px high and the 3px of slack under the
   last nav row that ref-1 measures becomes 7. */
.pace-hero .ph-qa-head { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.pace-hero .ph-qa-head svg { width: 16px; height: 16px; }
/* text-sm, so this is the one Home card whose title is 14px rather than 18 —
   tracking-tight at 14px is -0.35px. */
.pace-hero .ph-qa-title {
  font-size: 14px; font-weight: 600; line-height: 20px; letter-spacing: -0.35px;
}
.pace-hero .ph-qa-body {
  flex: 1; min-height: 0; overflow: hidden;
  display: flex; flex-direction: column; gap: 6px;
  padding: 0 12px 12px;
}
/* The label's own `<div className="mb-1">` wrapper; NAVIGATE's adds `mt-1`. */
.pace-hero .ph-qa-grouprow { margin-bottom: 4px; }
.pace-hero .ph-qa-grouprow-nav { margin-top: 4px; }
/* `text-[11px] font-medium uppercase tracking-wider px-1` — 0.05em is 0.55px at
   11px, the inset is 4px on BOTH sides, and the line box is the root ratio. */
.pace-hero .ph-qa-group {
  padding: 0 4px;
  font-size: 11px; font-weight: 500; line-height: 16.5px; letter-spacing: 0.55px;
  text-transform: uppercase; color: var(--ph-muted-fg);
}
/* `Button variant="outline" w-full justify-start gap-2.5 h-auto p-2` — so
   transparent, 1px #f1f1f4, 10.8px, shadow-sm and 8px of padding, which makes a
   nav row 8 + 32 + 8 + 2 = 50px and a RECENT row 46. The first build's 3px
   padding lost 10px a row across five rows. */
.pace-hero .ph-qa-row {
  display: flex; align-items: center; gap: 10px; padding: 8px;
  border: 1px solid var(--ph-border); border-radius: var(--ph-radius-md);
  background: transparent; box-shadow: var(--ph-shadow-sm);
}
/* `w-8 h-8 rounded-lg` = 32px at 12.8px, not the 8.8px the first build used. */
.pace-hero .ph-qa-ico {
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  width: 32px; height: 32px; border-radius: var(--ph-radius-lg);
}
.pace-hero .ph-qa-ico svg { width: 16px; height: 16px; }
/* The rail's glyphs are the 500 shades where the pacing tiles' are the 600s
   (quick-actions.tsx:25-56 vs pacing-summary-bar.tsx's METRIC_OPTIONS) — same
   50-shade plates, one step lighter ink. (0,3,0) beats the shared .ph-ico-* at
   (0,2,0), so only the colour is restated. */
.pace-hero .ph-qa-ico.ph-ico-emerald { color: #10B981; }
.pace-hero .ph-qa-ico.ph-ico-blue { color: #3B82F6; }
.pace-hero .ph-qa-ico.ph-ico-violet { color: #8B5CF6; }
.pace-hero .ph-qa-ico.ph-ico-orange { color: #F97316; }
.pace-hero .ph-qa-label {
  font-size: 14px; font-weight: 500; line-height: 20px; white-space: nowrap;
}
/* The RECENT rows' `truncate`, which the nav rows do not carry. */
.pace-hero .ph-qa-label-trunc { min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* ── Band 3, column 1 — Performance ──────────────────────────────────────── */

/* `mb-3` on the header block, and `flex items-start justify-between mb-3` on the
   title/subtitle row inside it, so the metric grid sits 12px under the subtitle
   and the plot 12px under the grid. */
.pace-hero .ph-perf-head { margin-bottom: 12px; }
.pace-hero .ph-perf-titlerow {
  display: flex; align-items: flex-start; justify-content: space-between;
  margin-bottom: 12px;
}
/* `grid grid-cols-2 lg:grid-cols-4 gap-3` of the SAME shell as the pacing tiles.
   12 + 28 + 4 + 28 + 2 + 24 + 12 + 2 = 112px, which is what leaves the plot 182.
   The trend row is 24 and not the chip's own 19: `.ph-slot-trend` is a block, so
   the row is that block's 16px/1.5 strut with the inline-flex chip inside it.
   __tests__/views.test.ts composes this same sum off these rules. */
.pace-hero .ph-slots { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.pace-hero .ph-slot {
  padding: 12px;
  border: 1px solid rgba(241, 241, 244, 0.7); border-radius: var(--ph-radius-home);
}
/* MetricPicker's trigger: `flex h-7 w-full items-center gap-1.5` inside a `mb-1`
   wrapper, folded into one box because the two never differ. */
.pace-hero .ph-slot-pick {
  display: flex; align-items: center; gap: 6px;
  height: 28px; margin-bottom: 4px;
}
.pace-hero .ph-slot-dot { flex-shrink: 0; width: 10px; height: 10px; border-radius: 50%; }
.pace-hero .ph-slot-label {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 12px; font-weight: 500; line-height: 16px;
}
.pace-hero .ph-slot-chev {
  display: flex; align-items: center; flex-shrink: 0; margin-left: auto; opacity: 0.5;
}
.pace-hero .ph-slot-chev svg { width: 14px; height: 14px; }
.pace-hero .ph-slot-values {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
}
.pace-hero .ph-slot-num { font-size: 20px; font-weight: 700; line-height: 28px; }
/* The app's `-ml-2` wrapper, which exists purely to cancel the §4.8 chip's own
   `ml-2` so the chip's left edge lines up with the value above it.

   It is a BLOCK, not a flex box, and that is load-bearing: the chip is
   inline-flex, so the wrapper generates a line box whose height is the greater
   of the chip and the wrapper's own strut — and the strut here is the root's
   16px at line-height 1.5, i.e. 24px. So the chip's row is 24px even though the
   chip is 19, which is why the slot is 112px rather than the 107 a flex wrapper
   would give. Measured in ref-7: the slot boxes y940 -> 1052 and the chip's own
   green/red fill y1017.5 -> 1036.5 inside a 1015 -> 1039 line box. Both terms of
   the strut are declared rather than inherited, per R1's sizing rule. */
.pace-hero .ph-slot-trend {
  display: block; margin-left: -8px;
  font-size: 16px; line-height: 24px;
}
/* `flex-grow min-h-0` around a 100%/100% ResponsiveContainer: 420 - 2 - 48 - 188
   of header = 182px. The svg's viewBox is emitted at exactly this box's size, so
   one user unit is one design pixel and the strokes never smear. */
.pace-hero .ph-chart-wrap { flex: 1; min-height: 0; }
.pace-hero .ph-chart { display: block; width: 100%; height: 100%; }
/* XAxis: `axisLine={{stroke:'#e2e8f0'}}`, `tickLine={{stroke:'#94a3b8'}}`,
   `tickSize={8}`, 12px labels in the same slate. There is no CartesianGrid and
   no Legend in the app — both are imported and never rendered, and both were
   hero-only inventions in the first build. */
.pace-hero .ph-chart-axis { stroke: #E2E8F0; stroke-width: 1; }
.pace-hero .ph-chart-tick { stroke: #94A3B8; stroke-width: 1; }
.pace-hero .ph-chart-ticklabel { font-size: 12px; fill: #94A3B8; }

/* ── Band 3, column 2 — Top Performers ───────────────────────────────────── */

.pace-hero .ph-top .ph-card-sub { margin-bottom: 12px; }
/* `flex-grow min-h-0` around a `max-h-[350px] overflow-y-auto pr-2 space-y-2`. */
.pace-hero .ph-top-wrap { flex: 1; min-height: 0; overflow: hidden; }
.pace-hero .ph-top-list {
  display: flex; flex-direction: column; gap: 8px;
  max-height: 350px; overflow: hidden; padding-right: 8px;
}
/* `flex items-center gap-3 p-3 rounded-lg border border-border bg-card` → the
   36px text column, not the 32px avatar, sets the 62px row. */
.pace-hero .ph-top-row {
  display: flex; align-items: center; gap: 12px; padding: 12px;
  border: 1px solid var(--ph-border); border-radius: var(--ph-radius-lg);
  background: var(--ph-bg);
}
.pace-hero .ph-top-rank {
  flex-shrink: 0; width: 20px;
  font-size: 14px; font-weight: 700; line-height: 20px;
  text-align: right; color: var(--ph-muted-fg);
}
.pace-hero .ph-top-main { flex: 1; min-width: 0; }
.pace-hero .ph-top-name {
  display: block; font-size: 14px; font-weight: 500; line-height: 20px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pace-hero .ph-top-metric {
  display: block; font-size: 12px; line-height: 16px; color: var(--ph-muted-fg);
}
/* The one right-aligned column, as in production: money reads down the edge. */
.pace-hero .ph-top-spend { flex-shrink: 0; text-align: right; }
.pace-hero .ph-top-spend-value {
  display: block; font-size: 14px; font-weight: 600; line-height: 20px;
}
.pace-hero .ph-top-spend-note {
  display: block; font-size: 12px; line-height: 16px; color: var(--ph-muted-fg);
}

/* ── Changes view ────────────────────────────────────────────────────────── */

/* §5.3's `grid-cols-[3fr_1fr] gap-6`. NOT `.ph-changes` — that class is already
   Home's Recent Changes card. `align-items: start` is the rail's own
   `lg:self-start`: it stops at its content height (585px on this data) while the
   table column keeps going past the canvas edge. Without it the rail would
   stretch to the table's height and its bottom border would land off-screen. */
.pace-hero .ph-changes-grid {
  display: grid; grid-template-columns: 3fr 1fr; gap: 24px; align-items: start;
}
/* This view's toolbar right group is `gap-3`, not the `gap-2` Accounts and
   Portfolios use (changes/page.tsx:360). 12px, measured: the filter button ends
   at x1621 and the date pill starts at 1633.
   Scoped to .ph-toolbar-right rather than left bare: the rule it overrides is
   also (0,2,0), so a bare .ph-toolbar-gap-3 would win only by sitting later in
   the file, and moving this block — which is exactly what item 10 of this review
   does to the Portfolios rules — would silently revert the cluster to 8px with
   every test green. (0,3,0) wins on specificity instead of on file order. */
.pace-hero .ph-toolbar-right.ph-toolbar-gap-3 { gap: 12px; }

/* The DateRangePicker trigger: an outline Button at size="sm" — h-9, rounded-md,
   px-3, font-normal — over bg-card. 36px, so it does not grow the toolbar panel
   past the 70px that puts the content panel at y198. The glyph carries `mr-2` on
   top of the Button's own `gap-2`, which is why the label sits 16px clear of it
   and not 8. */
.pace-hero .ph-daterange {
  display: inline-flex; align-items: center; gap: 8px;
  height: 36px; padding: 0 12px;
  border: 1px solid var(--ph-border); border-radius: var(--ph-radius-md);
  background: var(--ph-bg); box-shadow: var(--ph-shadow-sm);
  font-size: 14px; font-weight: 400; line-height: 20px; color: var(--ph-fg);
  white-space: nowrap;
}
.pace-hero .ph-daterange-ico {
  display: flex; align-items: center; flex-shrink: 0; margin-right: 8px;
}
.pace-hero .ph-daterange-ico svg { width: 16px; height: 16px; }
.pace-hero .ph-daterange-label { font-size: 14px; font-weight: 400; line-height: 20px; }

/* The Date & Time header cell is a control, not a label: a ghost Button whose own
   px-4 insets the text a further 16px from the th's padding, with a 16px
   ArrowUpDown after it. `gap-2` + the glyph's `ml-2` = the 16px measured between
   the label's right edge and the glyph's box. Ghost sets no colour, so it takes
   the band's own [&_th]:text-foreground near-black. */
.pace-hero .ph-sort {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 0 16px;
  font-size: 14px; font-weight: 500; line-height: 20px; color: var(--ph-fg);
}
.pace-hero .ph-sort-ico { display: flex; align-items: center; flex-shrink: 0; margin-left: 8px; }
.pace-hero .ph-sort-ico svg { width: 16px; height: 16px; }

/* §5.3's User cell: `bg-card pl-1 pr-3 py-1 rounded-full shadow-sm border
   border-border/50`. 4 + 24 + 4 + 2 = 34px. The border is border/50, a full step
   lighter than every other border in the view — kept as the source declaration
   rather than the flattened #f8f8fa, exactly as the synced pill's tint is. */
.pace-hero .ph-actor {
  display: inline-flex; align-items: center; gap: 8px;
  /* The same containment §4.5's account pill carries, and for a reason the app
     does not have: the app's User column is content-driven, so a long display
     name simply widens it. Under `table-layout: fixed` this column is 133.5px,
     and an over-wide pill runs THROUGH the Date column — measured at x394 for a
     39-character name, four columns wide. `formatDisplayName`'s "Mia N." form
     keeps the real names to 161.6px, but this hero ships to a marketing page
     where `ui-sans-serif` resolves to Segoe UI or Noto Sans, whose metrics are
     not SF Pro's, so the headroom is closed structurally rather than left to the
     data. The pill stops at the cell and the name ellipsises, which is what the
     app's own `truncate` does when its column IS constrained. */
  max-width: 100%;
  padding: 4px 12px 4px 4px;
  border: 1px solid var(--ph-border-50); border-radius: 999px;
  background: var(--ph-bg); box-shadow: var(--ph-shadow-sm);
  white-space: nowrap;
}
.pace-hero .ph-actor-name {
  min-width: 0; overflow: hidden; text-overflow: ellipsis;
  font-size: 14px; font-weight: 500; line-height: 20px;
}

/* The Pace actor's avatar. `/pace-avatar.png` is a white field with one mid-grey
   disc ~22% of the frame, so it renders as a dot floating in a white circle: 5px
   at the row's 24px and 4px at the rail's 20px. Two boxes rather than a
   percentage, because a 20.83% width would land on a fractional device pixel and
   soften the one detail this view is most recognisable by. */
.pace-hero .ph-pace-ava {
  flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: 50%; background: #FFFFFF;
}
.pace-hero .ph-pace-ava-20 { width: 20px; height: 20px; }
.pace-hero .ph-pace-dot {
  width: 5px; height: 5px; border-radius: 50%; background: #717171;
}
.pace-hero .ph-pace-ava-20 .ph-pace-dot { width: 4px; height: 4px; }

/* §5.3's Date & Time cell: `rounded-md bg-card px-2.5 py-1 shadow-sm border
   border-border/50`, holding a 12px/500 date and a 10px muted clock 6px apart.
   **30px, not §5.3's 28** — measured in ref-6 at y356 → 386 and reproduced at the
   same +26 → +56 offset inside the row. The extra 2 comes from ONE of the two
   spans, not both: the date is `text-xs`, which sets its own 16px line box, but
   the time is `text-[10px]`, an arbitrary size that sets font-size ONLY — so it
   inherits the cell's `text-sm` 20px line-height at a 10px font. items-center
   makes the pill's content box the taller of the two, so 4 + 20 + 4 + 2 = 30.
   Both line boxes are declared below rather than inherited, because the hero's
   root is 16px and this is exactly the inheritance the height turns on. */
.pace-hero .ph-datepill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px;
  border: 1px solid var(--ph-border-50); border-radius: var(--ph-radius-md);
  background: var(--ph-bg); box-shadow: var(--ph-shadow-sm);
  white-space: nowrap;
}
/* text-xs — its own 16px box, which is NOT what sets the pill's height. */
.pace-hero .ph-datepill-date { font-size: 12px; font-weight: 500; line-height: 16px; }
/* text-[10px] over the cell's inherited text-sm 20px box. This line is the 30. */
.pace-hero .ph-datepill-time {
  font-size: 10px; font-weight: 400; line-height: 20px; color: var(--ph-muted-fg);
}

/* The Change cell. `white-space: normal` is the one deliberate override of the
   table's own nowrap: the app writes `whitespace-normal` on this description
   button, and without it a long description would run the column out instead of
   wrapping onto a second 20px line. `vertical-align: middle` is inherited from
   the td rule; the stack's blocks are what make this the tallest cell in a row
   that carries one. */
.pace-hero .ph-table td.ph-td-change { white-space: normal; }
/* `flex items-center gap-2 mb-1.5 pb-1.5 border-b border-border/50` — the badge,
   6px, a 1px rule across the whole cell, then 6px. +25 on the row. */
.pace-hero .ph-chg-group {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 6px; padding-bottom: 6px;
  border-bottom: 1px solid var(--ph-border-50);
}
/* GroupBadge at the call site's `px-2 py-0.5 text-[10px]`. 24px, not 20: the
   arbitrary size sets font-size only, so the badge keeps the cell's inherited
   20px line box. Its two colours are inline, from the 12-token palette. */
.pace-hero .ph-group-badge {
  display: inline-flex; align-items: center;
  padding: 2px 8px; border-radius: 999px;
  font-size: 10px; font-weight: 500; line-height: 20px;
}
/* `flex flex-wrap items-center gap-1.5 mb-1.5` — block-level, so it puts its
   chips on their own line and adds 18 to the row where the inline In-platform and
   Pace AI chips would have sat abreast of the description. */
.pace-hero .ph-chg-incidents {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-bottom: 6px;
}
.pace-hero .ph-incident {
  display: inline-flex; align-items: center;
  padding: 2px 8px; border-radius: 999px;
  font-size: 10px; font-weight: 500; line-height: 20px;
}
/* sky-100 / sky-900 for informational, amber-100 / amber-900 for a notice. */
.pace-hero .ph-incident-info { background: #E0F2FE; color: #0C4A6E; }
.pace-hero .ph-incident-notice { background: #FEF3C7; color: #78350F; }
/* The description: `Button variant="outline" className="justify-start py-2 px-4
   h-auto text-left whitespace-normal"` — 8 + 20 + 8 + 2 = 38px for one line and
   +20 per wrapped line. */
.pace-hero .ph-chg-btn {
  display: inline-flex; align-items: center; justify-content: flex-start;
  padding: 8px 16px;
  border: 1px solid var(--ph-border); border-radius: var(--ph-radius-md);
  background: transparent; box-shadow: var(--ph-shadow-sm);
  font-size: 14px; font-weight: 500; line-height: 20px; color: var(--ph-fg);
  text-align: left;
}
/* The no-`details` variant: the app drops the button shell entirely and renders
   the description as bare cell text, at the table's own 14px/400. */
.pace-hero .ph-chg-bare { font-size: 14px; font-weight: 400; line-height: 20px; }

/* ── Changes view — the 7-day summary rail ───────────────────────────────── */

/* Not a Card in the app but CARD_CLASS, which is the same recipe plus p-4 — so
   it is .ph-card with padding and nothing else restated. 458 wide by the grid,
   424 of interior. */
.pace-hero .ph-rail { padding: 16px; }
/* `flex items-center gap-2 mb-3`, 16px tall: a 16px BarChart3 in blue-500 beside
   a 12px/600 uppercase muted label. */
.pace-hero .ph-rail-head {
  display: flex; align-items: center; gap: 8px; margin-bottom: 12px;
}
.pace-hero .ph-rail-head-ico {
  display: flex; align-items: center; flex-shrink: 0; color: #3B82F6;
}
.pace-hero .ph-rail-head-ico svg { width: 16px; height: 16px; }
/* The rail's four section labels share one recipe: text-xs font-semibold
   uppercase tracking-wider (0.05em = 0.6px at 12px) in muted-foreground. The
   source strings are written in title case and uppercased here, as the app does,
   so the DOM text and the app's own JSX stay the same string. */
.pace-hero .ph-rail-head-label,
.pace-hero .ph-rail-label {
  font-size: 12px; font-weight: 600; line-height: 16px; letter-spacing: 0.6px;
  text-transform: uppercase; color: var(--ph-muted-fg);
}
.pace-hero .ph-rail-label { margin-bottom: 8px; }
/* Every block but the last carries `mb-4`. */
.pace-hero .ph-rail-block { margin-bottom: 16px; }
.pace-hero .ph-rail-block-last { margin-bottom: 0; }
.pace-hero .ph-rail-count { font-size: 24px; font-weight: 700; line-height: 32px; }
.pace-hero .ph-rail-sub {
  font-size: 12px; font-weight: 400; line-height: 16px; color: var(--ph-muted-fg);
}
/* `w-full h-10` with preserveAspectRatio="none": a 200-unit path stretched 2.12x
   across the 424px interior while the stroke stays a true 2px.
   NOT `.ph-spark` — that class is already Home's Spark CARD, a bordered 12.8px
   panel, and an svg wearing it drew a 424x40 rounded rule box around the plot. */
.pace-hero .ph-sparkline { display: block; width: 100%; height: 40px; }
/* `flex justify-between text-[10px] text-muted-foreground mt-1`. The 15px line
   box is the inherited 1.5 ratio at 10px, and it is what puts the next section
   label at y406 — declared rather than inherited, because the root is 16px. */
.pace-hero .ph-rail-axis {
  display: flex; justify-content: space-between; margin-top: 4px;
  font-size: 10px; font-weight: 400; line-height: 15px; color: var(--ph-muted-fg);
}
.pace-hero .ph-rail-axis span { font-size: 10px; line-height: 15px; }

/* BY PLATFORM and TOP ACCOUNTS are `space-y-1.5` over INLINE-flex chips with
   `mr-2`, so the chips flow and wrap in an inline formatting context rather than
   a flex one. Two consequences the reference shows and a flex container would
   lose: the first row sits 6px below the label's margin (the second chip's own
   margin-top grows the line box above its baseline) and the row pitch is the line
   box, measured at 41 for a 34px chip and 36 for a 30px one. The font-size and
   line-height are declared because they are the strut those line boxes are built
   from, and the root is 16px. */
.pace-hero .ph-rail-flow { font-size: 16px; line-height: 24px; }
.pace-hero .ph-rail-flow .ph-rchip { margin-right: 8px; }
.pace-hero .ph-rail-flow .ph-rchip + .ph-rchip { margin-top: 6px; }
/* BY USER is a real `flex flex-wrap gap-1.5`. */
.pace-hero .ph-rail-wrap { display: flex; flex-wrap: wrap; gap: 6px; }
/* One shell for all three groups: `inline-flex items-center gap-2 rounded-full
   bg-muted/50 pl-1 pr-1 py-1 shadow-sm border border-border/50`. 4px of padding
   on every side, so the height is the leading element's + 10. */
.pace-hero .ph-rchip {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 4px;
  border: 1px solid var(--ph-border-50); border-radius: 999px;
  background: rgba(244, 244, 245, 0.5); box-shadow: var(--ph-shadow-sm);
  white-space: nowrap;
}
.pace-hero .ph-rchip-label { font-size: 12px; font-weight: 500; line-height: 16px; }
/* TOP ACCOUNTS is the one group whose label truncates (`truncate max-w-[140px]`). */
.pace-hero .ph-rchip-acct .ph-rchip-label {
  display: block; max-width: 140px; overflow: hidden; text-overflow: ellipsis;
}
/* The count: `text-[10px] font-semibold bg-card rounded-full min-w-[20px] h-5
   px-1.5 shadow-sm` — a 20px white pill that never narrows below 20 however few
   digits it holds. */
.pace-hero .ph-rchip-count {
  flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  min-width: 20px; height: 20px; padding: 0 6px;
  border-radius: 999px; background: var(--ph-bg); box-shadow: var(--ph-shadow-sm);
  font-size: 10px; font-weight: 600; line-height: 16px; color: var(--ph-fg);
}

/* ── Dropdowns view ──────────────────────────────────────────────────────── */

/* §6.2 — both popovers are the same `PopoverContent className="w-96 p-0
   bg-popover" align="end"`, so one rule builds them and only the header string,
   the list and one item shape differ.

   496px is not a chosen height, it is the sum of the bands: 1 border + 52 header
   + 1 separator + 56 tabs + 1 separator + 384 (`max-h-96`) + 1 border. Both lists
   overflow that clamp, so both are cut mid-item — the flat cut IS the app's
   "there is more below" cue, and `overflow: hidden` is what performs it while
   also keeping the bottom corners' 10.8px radius over the last row.

   rounded-md is tighter than every panel in the app body (rounded-lg, 12.8px) and
   is the one radius the crops' corner arcs pin exactly.

   Positioned against .ph-header-panel's padding box, so `top` counts from the
   header row's own top: 14px of centring + the 36px trigger + Radix's
   sideOffset 4 = 54, which lands the panel's top border 4px under the trigger's
   bottom edge (measured at exactly 4 in ref-2) and 9px into the header panel,
   over its bottom border and its shadow-sm. */
.pace-hero .ph-dd {
  position: absolute;
  top: 54px;
  width: 384px; height: 496px;
  background: var(--ph-bg);
  border: 1px solid var(--ph-border);
  border-radius: var(--ph-radius-md);
  box-shadow: var(--ph-shadow-md);
  overflow: hidden;
}
/* Explicit rather than left to the UA sheet: everything above is an author rule,
   and a closed panel must not paint, measure or take pointer events. */
.pace-hero .ph-dd[hidden] { display: none; }
/* align="end" — each panel's right edge is flush with its own trigger's. Right to
   left from the header panel's padding-box right edge: 16 row padding + 32 avatar
   button + 16 gap = 64 for the Bell, then + 36 + 16 = 116 for Sparks. Canvas x
   1839 and 1787, both measured in ref-2/ref-3 as the popover's own right edge. */
.pace-hero .ph-dd[data-dropdown="alerts"] { right: 64px; }
.pace-hero .ph-dd[data-dropdown="sparks"] { right: 116px; }

/* `flex items-center justify-between p-4` over a `text-sm font-medium` heading:
   16 + 20 + 16 = 52. */
.pace-hero .ph-dd-head {
  display: flex; align-items: center; justify-content: space-between;
  height: 52px; padding: 16px;
}
.pace-hero .ph-dd-title {
  font-size: 14px; font-weight: 500; line-height: 20px; color: var(--ph-fg);
}
/* `Button variant="link" size="sm" className="h-auto p-0 text-xs"` with a leading
   Check, rendered only while something is unread. `h-auto` makes it 16px — the
   glyph, not the 20px heading — so it never touches the band's 52px, which the
   heading sets. The gap is the button base's own `gap-2` plus the icon's `mr-1`.
   Both references were captured with nothing unread, so neither shows it. */
.pace-hero .ph-dd-mark {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 12px; font-weight: 500; line-height: 16px;
}
/* [&_svg]:size-4 on the button base outranks the icon's own h-3 w-3 — the same
   specificity trap the header's Bell hits, and the reason this is 16 not 12. */
.pace-hero .ph-dd-mark svg { width: 16px; height: 16px; margin-right: 4px; }
/* The `link` variant's text-primary, which --primary: 226 4.3% 0.1% resolves to
   black; InsightsPopover overrides it to text-muted-foreground. */
.pace-hero .ph-dd-alerts .ph-dd-mark { color: #000000; }
.pace-hero .ph-dd-sparks .ph-dd-mark { color: var(--ph-muted-fg); }
/* <Separator /> — `shrink-0 bg-border h-[1px] w-full`, full bleed. */
.pace-hero .ph-dd-rule { height: 1px; background: var(--ph-border); }

/* `max-h-96 overflow-y-auto` holding a TabsContent with `mt-2`, so the first item
   starts 8px inside the region rather than against the separator. The replica
   clips where the app scrolls; the visible result is identical. */
.pace-hero .ph-dd-list { height: 384px; overflow: hidden; }
.pace-hero .ph-dd-tab { margin-top: 8px; }

/* §6.6 — `border-b p-4 last:border-b-0` over a `grid grid-cols-1 gap-3`. */
.pace-hero .ph-dd-item {
  display: grid; grid-template-columns: 1fr; gap: 12px;
  padding: 16px;
  border-bottom: 1px solid var(--ph-border);
}
.pace-hero .ph-dd-item:last-child { border-bottom: 0; }
.pace-hero .ph-dd-row {
  display: flex; align-items: center; justify-content: space-between;
}
.pace-hero .ph-dd-ident { display: flex; align-items: center; gap: 8px; min-width: 0; }
/* `font-semibold text-base text-foreground` — 16px, the largest type in the panel
   and larger than the item's own title. */
.pace-hero .ph-dd-acct {
  font-size: 16px; font-weight: 600; line-height: 24px; color: var(--ph-fg);
}
/* `h-2 w-2 rounded-full`. The hue is a property of which popover you are in —
   red-500 in the bell, yellow-500 in Sparks — so it is scoped, not passed in. */
.pace-hero .ph-dd-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: 50%; }
.pace-hero .ph-dd-alerts .ph-dd-dot { background: var(--ph-red); }
.pace-hero .ph-dd-sparks .ph-dd-dot { background: #EAB308; }

/* `grid grid-cols-1 gap-1 pt-2`. */
.pace-hero .ph-dd-body {
  display: grid; grid-template-columns: 1fr; gap: 4px; padding-top: 8px;
}
.pace-hero .ph-dd-item-title { font-size: 14px; font-weight: 600; line-height: 20px; }
/* `text-sm opacity-90` at the DEFAULT 20px line-height — no leading-relaxed here,
   unlike Home's SparkCard body, which is what makes the dropdown's copy denser. */
.pace-hero .ph-dd-desc { font-size: 14px; line-height: 20px; opacity: 0.9; }
/* §6.7 — `text-lg` on its own row with NO leading-none, so the 18px glyph sits in
   a 28px line box. The Home SparkCard's is the same size in an 18px box. */
.pace-hero .ph-dd-sev { font-size: 18px; line-height: 28px; }

/* `flex items-center justify-between mt-1` — and mt-3 on a Spark. */
.pace-hero .ph-dd-foot {
  display: flex; align-items: center; justify-content: space-between; margin-top: 4px;
}
.pace-hero .ph-dd-sparks .ph-dd-foot { margin-top: 12px; }
.pace-hero .ph-dd-time { font-size: 12px; line-height: 16px; color: var(--ph-muted-fg); }
/* `Button variant="outline" size="sm" className="h-8 px-3"` — 14px, not the Home
   SparkCard footer button's 12px, and an outline rather than a ghost. */
.pace-hero .ph-dd-btn {
  flex-shrink: 0;
  display: flex; align-items: center;
  height: 32px; padding: 0 12px;
  border: 1px solid var(--ph-border); border-radius: var(--ph-radius-md);
  background: var(--ph-bg); box-shadow: var(--ph-shadow-sm);
  font-size: 14px; font-weight: 500; line-height: 20px; color: var(--ph-fg);
}

/* ── Ambient motion ──────────────────────────────────────────────────────── */

/* The replay layer, and nothing else in the file animates a view's contents.
   Everything here is additive: with these rules deleted the hero still renders
   its resting composition, because that is the state the markup ships in. */

/* Armed only while a view is replaying, which is the whole reason the class
   exists. Left on .ph-bar-fill unconditionally, the reset to 0 that starts a
   replay would itself animate — a 600ms drain to empty before the fill — and a
   host that re-parents the hero would get a spurious fill on every move. */
.pace-hero .ph-view.ph-replay .ph-bar-fill { transition: width 600ms ease-out; }

/* The 8s alert arriving at the top of the Notifications list.

   The JS takes this class off again ~370ms later, and that cleanup is
   load-bearing rather than tidiness: leaving a display:none subtree RESTARTS a
   canceled animation, so a class left on a row inside a closed popover would
   replay the entrance every single time the popover was opened. Deleting the
   cleanup timer in landIncomingAlert() reintroduces slide-in-on-every-open. */
.pace-hero .ph-dd-item-new { animation: ph-item-in 320ms ease-out; }
@keyframes ph-item-in {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: none; }
}

/* The backstop. setupMotion() reads the same preference through matchMedia and
   never arms the timeline, so in the ordinary case none of this is reached —
   these rules are what protect a host that mounts the markup without the script,
   and a preference that flips after mount. */
@media (prefers-reduced-motion: reduce) {
  .pace-hero .ph-view-enter { animation: none; }
  .pace-hero .ph-nav.ph-nav-measured .ph-nav-indicator { transition: none; }
  /* Not `animation: none` — a parked ring sits as a lighter 2px halo down the
     dot's right and bottom edges, which is not a state the app ever paints. */
  .pace-hero .ph-ping-ring { display: none; }
  .pace-hero .ph-view.ph-replay .ph-bar-fill { transition: none; }
  .pace-hero .ph-dd-item-new { animation: none; }
  /* The image goes too, not just the animation: a parked 250%-wide sweep leaves
     a half-faded white ramp sitting at the fill's right edge. */
  .pace-hero .ph-bar-fill.ph-tier-floor { animation: none; background-image: none; }
}
