/* ============================================================
   mystyle — Apple-style home revamp (branch: mystyle)
   Scoped to body.mystyle so the rest of the SPA is untouched.
   No gradients, no glow halos. Single accent #1570f0 (Silver
   Studio, see docs/brand/BRAND.md). Generous spacing, light
   type, controlled color, big imagery.
   ============================================================ */

/* Custom momentum smooth-scroll: our engine adds html.ms-smooth and drives window.scrollTo()
   per animation frame; disable native smooth so it doesn't fight our interpolation.
   Reduced-motion / engine-off users never get the class → keep native html{scroll-behavior:smooth}. */
html.ms-smooth { scroll-behavior: auto; }

body.mystyle{
  /* Alias shared tokens — values stay in app.css :root as the single source of truth */
  --ms-ink:          var(--text);
  --ms-ink-2:        var(--text-2);
  --ms-ink-3:        var(--text-3);
  --ms-ink-4:        var(--text-4);
  --ms-surface:      var(--surface);
  --ms-surface-warm: var(--bg-3);
  --ms-bg:           var(--bg);   /* Ink Editorial: one page ground everywhere */
  --ms-accent:       var(--accent);
  --ms-accent-hov:   var(--accent-2);
  --ms-ease:         var(--ease);

  /* Values not in app.css — owned here */
  --ms-surface-cool: var(--bg-2);
  --ms-accent-tint:  var(--accent-tint);
  --ms-line:         rgba(16,32,64,.10);
  --ms-line-soft:    rgba(16,32,64,.06);
  --ms-dark:         var(--ink);
  --ms-dark-2:       var(--ink-2);
  --ms-ease-out:     cubic-bezier(.16, 1, .3, 1);
  --ms-dur-quick:    180ms;
  --ms-dur-state:    260ms;
  --ms-dur-layout:   480ms;
  --ms-pad-y:        clamp(96px, 12vw, 192px);
  --ms-pad-x:        clamp(20px, 5vw, 96px);
  --ms-maxw:         1400px;

  background: var(--ms-bg);
  color: var(--ms-ink);
}

body.mystyle #app{
  /* take back the wrapper styling — sections handle their own padding */
  padding: 0;
  margin: 0;
  display: block;
  width: 100%;
}

/* Before the SPA renders, #app is empty and the static newsletter/footer sit in the
   first paint — then the whole home injects above them (CLS ~1.0). Reserve a full
   viewport so that shift happens off-screen. */
main#app:empty{ min-height: 100vh; }

/* hide the existing footer reveal jitter while mystyle is on */
body.mystyle .foot{ margin-top: 0; }

/* Footer links: 16px-tall targets → 24px (WCAG 2.5.8) with the same visual pitch
   (4px padding each side, gap trimmed 10px → 2px to compensate). */
.foot ul{ gap: 2px; }
.foot ul a{ display: inline-block; padding: 4px 0; }

/* ---------- Scrollbars: thin, dark blue, flush to the edge (site-wide).
   Overrides the app.css html scrollbar hide; class-level hides on pill rows
   (.ms-dash-tabs, .scroller, .intg-tabs) still win over these universals.
   Chromium path = 5px ::-webkit-scrollbar. The standard scrollbar-width/color
   props make Chromium IGNORE the webkit styling, so they are Firefox-only. */
html{ scrollbar-width: auto; }
/* The top bar is now a floating pill inset from the viewport edges (WU-1), so it
   never reaches the scrollbar column. The 60px nav-dark band and matching track
   margin that used to hide the notch beside the flush black bar are orphans of
   that change — a dark stub above a pale page is the artifact now, not the fix. */
html::-webkit-scrollbar{
  display: block; width: 5px; height: 5px;
  background: transparent;
}
*::-webkit-scrollbar{ width: 5px; height: 5px; background: transparent; }
*::-webkit-scrollbar-thumb{ background: var(--ink); border-radius: 999px; }
*::-webkit-scrollbar-track{ background: transparent; }
*::-webkit-scrollbar-corner{ background: transparent; }
@supports not selector(::-webkit-scrollbar){
  html, *{ scrollbar-width: thin; scrollbar-color: var(--ink) transparent; }
}

/* (Scroll-progress rail removed — the styled page scrollbar is the single
   scroll indicator; two parallel bars at the right edge read as a bug.) */

/* ---------- Accessibility utilities ----------
   Two things, neither of them decoration.

   .sr-only hides an element from sight while leaving it in the accessibility tree.
   display:none and visibility:hidden would remove it from both, which is why the
   clip-rect form below is used instead. The route announcer relies on this.

   .skip-link is the first focusable element on the page: it lets a keyboard or screen
   reader user jump the nav rather than tabbing through it on every route. It is
   invisible until focused, then lands top-left over the fixed 60px bar (z-index 102,
   one above the nav's 101). Styled to the house rules: white surface, single accent,
   a hairline border rather than a shadow, no gradient and no motion beyond the
   position change. 44px min-height is the minimum comfortable target size. */
.sr-only{
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  border: 0;
}
.skip-link{
  position: absolute; left: -9999px; top: 0;
  z-index: 102;
  display: inline-flex; align-items: center;
  min-height: 44px; padding: 10px 18px;
  background: #fff; color: var(--accent);
  border: 0.5px solid var(--accent); border-radius: 8px;
  font-size: 14px; font-weight: 500; text-decoration: none;
}
.skip-link:focus{ left: 8px; top: 8px; }
.skip-link:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
/* #app is focused programmatically after each route change so the next Tab starts in
   the new page rather than back at the top of the nav. The user did not tab here, so a
   ring around the entire page would be noise — the route announcer carries the signal. */
#app:focus{ outline: none; }

/* ---------- Section primitive ---------- */
.ms-section{
  padding: var(--ms-pad-y) var(--ms-pad-x);
  position: relative;
}
.ms-section--surface{ background: var(--ms-surface); }
.ms-section--warm   { background: var(--ms-surface-warm); }
.ms-section--cool   { background: var(--ms-surface-cool); }
.ms-section--dark   { background: var(--ms-dark); color: var(--ms-bg); }
.ms-section--dark .ms-eyebrow,
.ms-section--dark .ms-sub{ color: rgba(229,232,236,.6); }
.ms-section--dark .ms-divider{ background: rgba(255,255,255,.12); }

.ms-inner{
  max-width: var(--ms-maxw);
  margin: 0 auto;
  width: 100%;
}

/* ---------- Type scale (Apple-grade) ---------- */
.ms-eyebrow{
  font-family: var(--f-display);
  font-size: clamp(13px, 1vw, 14px);
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ms-ink-3);
  margin: 0 0 12px;
}

.ms-display{
  font-family: var(--f-display);
  font-size: clamp(44px, 6.4vw, 96px);
  font-weight: 600;
  letter-spacing: -0.025em;
  line-height: 1.02;
  color: var(--ms-ink);
  margin: 0;
  max-width: 16ch;
}

.ms-headline{
  font-family: var(--f-display);
  font-size: clamp(34px, 4.4vw, 64px);
  font-weight: 600;
  letter-spacing: -0.022em;
  line-height: 1.05;
  color: var(--ms-ink);
  margin: 0;
  max-width: 18ch;
}

.ms-subhead{
  font-family: var(--f-display);
  font-size: clamp(12px, 1vw, 14px);
  font-weight: 400;
  letter-spacing: -0.012em;
  line-height: 1.45;
  color: var(--ms-ink-2);
  margin: 10px auto 0;
  max-width: 44ch;
}

.ms-sub{
  font-family: var(--f-display);
  font-size: clamp(16px, 1.2vw, 18px);
  font-weight: 400;
  letter-spacing: -0.005em;
  line-height: 1.5;
  color: var(--ms-ink-3);
  margin: 16px 0 0;
  max-width: 42ch;
}

.ms-h2{
  font-family: var(--f-display);
  font-size: clamp(20px, 1.6vw, 24px);
  font-weight: 600;
  letter-spacing: -0.012em;
  line-height: 1.2;
  color: inherit;
  margin: 0;
}

/* ---------- Hero ----------
   One explicit layer ladder, home only, every layer a child of `.ms-hero` and
   every layer carrying its own z-index: the poster plate and the film at z 0
   (plate first in DOM order, so the film paints over it), the orbiting sphere at
   z 2, the type stack at z 3.

   The z-indexes are explicit because the implicit version broke: M3 moved the
   film INTO `.ms-inner` to render it as a phone card, which silently made
   `.ms-inner` both its containing block and its stacking context. `inset: 0` then
   resolved against the text column instead of the hero, and an absolutely
   positioned z-0 child paints above its non-positioned siblings' text — so the
   film shrank to the centre column AND covered the headline, the CTAs, the chips
   and the stats. The film is a direct child of `.ms-hero` again.

   The stack stays anchored to the hero's TOP rather
   than vertically centred, because the orbit path below is calibrated in
   hero-top-relative pixels and a centred stack would slide under it as the
   viewport height changes. */
.ms-hero{
  min-height: 100vh;
  padding-top: calc(var(--nav-h) + 56px);
  padding-bottom: clamp(32px, 4vh, 48px);
  display: flex;
  align-items: flex-start;
  position: relative;
  /* Project rule: `clip` never `hidden` — `hidden` makes this a scroll
     container and kills `position: sticky` inside it. Both axes, so the
     ribbons video and the orbiting sphere still get cut at the hero edge. */
  overflow: clip;
}
.ms-hero .ms-inner{
  position: relative;
  /* Above the film (0), the orbit (2) and any scrim between them. The audit raise
     to 1001 (`.ms-hero.is-auditing:not(.is-lowered) .ms-inner`) is measured against
     the click film's z 1000 and still clears it from here. */
  z-index: 3;
  text-align: center;
}
/* Glass-ribbon ambience, at full strength. Owner 2026-09-16: the film plays
   opaque, no dimming. It used to sit at .9 so the type could hold its contrast
   through it; the type now holds its contrast by being ABOVE it at z 3, which is
   what this layer ladder is for.
   R4-2: the film carries the sphere as well as the ribbons and simply loops.
   `.ms-hero-plate` is its own first frame as a still, in the identical box, so it
   grounds the hero until the video has decoded. U8: it then hides, on the video's
   first `playing` -- assets/mystyle.js puts `is-live` on the hero there. At .9 its
   frozen sphere used to read through the video's remaining 10% as a second,
   motionless sphere; at opacity 1 nothing bleeds through, and the fade stays
   because the still must still get out of the way on the reduced-motion path. */
.ms-hero-ribbons,
.ms-hero-plate{
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 1;
  pointer-events: none;
}
/* Owner 2026-10-12: the roaming-sphere film is back, "a bit bright". One brightness on the film
   and on its frame-0 plate so the two never pop against each other, desktop only: phones play
   their own encode under this same class, and the reduced-motion still is .ms-hero's own
   background, which this does not touch. */
@media (min-width: 768px){
  .ms-hero-ribbons,
  .ms-hero-plate{ filter: brightness(1.1); }
}
/* The film is painting, so the still underneath has nothing left to ground. */
.ms-hero.is-live .ms-hero-plate{
  opacity: 0;
  transition: opacity .4s var(--ease-out);
}
/* Idle sphere. Default (mobile + reduced motion) = one static frame, top-right,
   clear of the type. The orbit is opt-in, in the media query further down. */
.ms-hero-orbit{
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
}
/* The sphere is a square stage, not an image: the turntable stacks the frames
   it is blending inside it (WU-10b). Without the roam it shows one still frame,
   at exactly the box the single <img> used to occupy. */
.ms-hero-sphere{
  position: absolute;
  top: calc(var(--nav-h) - 24px);
  right: 0;
  width: 130px;
  aspect-ratio: 1;
}
.ms-sphere-l{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
}
.ms-sphere-l:first-child{ opacity: 1; }
/* The lens light. A cyan spot on the lens itself, so it moves as the sphere
   turns and dims as the lens turns away — the catch-light this replaces was a
   ::after on an <img>, which never rendered. */
.ms-sphere-glow{
  position: absolute;
  left: 50%;
  top: 47%;
  width: 40%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: radial-gradient(circle, var(--glow), transparent 70%);
  mix-blend-mode: screen;
  opacity: .5;
  transform: translate(-50%, -50%);
  pointer-events: none;
}
/* Mono eyebrow — the mockup's letterspaced micro-label. */
.ms-hero .ms-eyebrow{
  font-family: var(--f-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.16em;
  color: var(--text-3);
}
/* Critical fix: h1 is block-level — needs margin:auto to center within text-align:center parent */
.ms-hero-display{
  font-size: clamp(34px, 5vw, 72px);
  letter-spacing: -0.03em;
  line-height: 1.0;
  font-weight: 700;
  color: var(--ms-ink);
  margin: 12px auto 0;
  max-width: 100%;
  width: 100%;
}

/* hero orb removed (no-gradient rule) — hero is clean type only */
.ms-hero-orb{ display: none; }
.ms-hero-cta{
  margin-top: 56px;
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
  align-items: center;
}
/* The one sanctioned shadow in the system (plan §1) — the landing audit pill. */
.ms-hero-form{
  display: flex;
  gap: 8px;
  width: 100%;
  /* 520, not the mockup's ~477: the mockup shows 11 chips and a narrower rendered
     placeholder — at 477 the real placeholder clips mid-word. */
  max-width: 540px;
  margin: 20px auto 0;
  background: var(--ms-surface);
  border: 1px solid var(--ms-line);
  border-radius: var(--r-pill);
  padding: 6px 6px 6px 16px;
  box-shadow: 0 8px 24px var(--shadow-accent);
  transition: box-shadow var(--ms-dur-state) var(--ms-ease), border-color var(--ms-dur-state) var(--ms-ease);
}
.ms-hero-form:focus-within{
  border-color: var(--ms-accent);
  box-shadow: 0 0 0 4px var(--ms-accent-tint), 0 8px 24px var(--shadow-accent);
}
.ms-hero-form input{
  flex: 1;
  /* an input's min-width:auto is its intrinsic size (~20ch), which pushed the
     submit button outside the pill on narrow screens */
  min-width: 0;
  border: 0;
  outline: none;
  background: transparent;
  font: inherit;
  font-size: 14px;
  color: var(--ms-ink);
  padding: 12px 0;
}
.ms-hero-form input::placeholder{ color: var(--ms-ink-4); }
.ms-hero-spark{ display: inline-flex; align-items: center; flex: none; margin-right: 2px; color: var(--ms-ink-3); }
/* The placeholder is longer than the pill can show once the submit button takes its share,
   so on phones it hard-clipped mid-word against the button. Truncate it with an ellipsis
   instead. Text is unchanged; the full string is still read out by aria-label. */
@media (max-width: 560px){
  .ms-hero-form input{ text-overflow: ellipsis; white-space: nowrap; }
}
.ms-hero-note{
  margin-top: 16px;
  font-size: 14px;
  color: var(--ms-ink-3);
  letter-spacing: -0.005em;
}

/* Animated hero line — rises & fades out the top, re-enters from below, repeats. */
.ms-hero-cycling{
  font-family: var(--f-display);
  font-size: clamp(24px, 2.5vw, 36px);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--text);
  margin: 10px 0 0;
}
.ms-tryit{
  display: inline-block;
  font-weight: 600;
  color: var(--text);
  /* One-shot entrance — the infinite blink was a leftover from a multi-phrase cycler
     and left the line invisible ~30% of the time with a single phrase. */
  animation: ms-tryit-slide 0.9s cubic-bezier(0.16,1,0.3,1) both;
}
@keyframes ms-tryit-slide{
  from{ opacity: 0; transform: translateY(0.5em); }
  to  { opacity: 1; transform: translateY(0); }
}
/* 🔴 Not optional, and it has to name every animated element: an element left out here keeps
   moving for a reader who asked the OS for no motion. */
@media (prefers-reduced-motion: reduce){
  .ms-tryit{ animation: none; opacity: 1; transform: none; }
}

/* Hero CTA pair (read-it-then-run-it positioning) */
.ms-hero-ctas{
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
  margin-top: 26px;
}
/* Mockup's secondary CTA is a white pill on the pale ground, not a transparent
   outline — scoped to the hero so the dark sections keep the ghost button. */
.ms-hero-ctas .ms-btn--ghost{ background: var(--surface); }

/* Hero stats strip — three columns split by thin vertical hairlines (mockup),
   not a bordered band. */
/* Owner reference 2026-10-11: one card, four cells split by .5px hairlines, each a line
   icon beside its number and label. Near-opaque white so the captions keep AA over the film. */
.ms-hero-stats{
  display: flex;
  gap: 0;
  margin: 40px auto 0;
  width: 100%;
  max-width: 720px;
  background: rgba(255,255,255,.86);
  border: .5px solid var(--border-2);
  border-radius: var(--r-md);   /* owner 2026-10-12: rounder, 10 -> 14px */
}
.ms-hero-stat{
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 18px;                     /* owner 2026-10-12: icon at the cell's left edge, text further from it */
  padding: 14px 14px 14px 12px;
}
.ms-hero-stat + .ms-hero-stat{ border-left: .5px solid var(--border-2); }
.ms-hero-stat-ico{ flex: none; display: inline-flex; color: var(--ms-ink-3); }
.ms-hero-stat-ico svg{ width: 26px; height: 26px; }   /* owner 2026-10-12: line art bigger (22 -> 26) and greyer */
.ms-hero-stat-txt{ display: flex; flex-direction: column; align-items: flex-start; gap: 3px; min-width: 0; text-align: left; }
.ms-hero-stat-num{
  font-size: clamp(17px, 1.5vw, 21px);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--ms-ink);
  line-height: 1;
}
.ms-hero-stat-label{
  white-space: nowrap;
  font-size: 11px;
  color: var(--text-3);
  letter-spacing: -0.003em;
  line-height: 1.2;
}
/* The MCP figure's second line (owner, 2026-10-03). Pulled up under its label so the
   row's numbers stay on one baseline; the extra line hangs below the other three. */
.ms-hero-stat-sub{
  margin-top: 0;
  font-size: 11px;
  line-height: 1.2;
  white-space: nowrap;
  color: var(--text-3);
  letter-spacing: -0.003em;
}
/* Subscriber pill — real count from /api/stats; absent when unavailable. */
/* Owner 2026-09-23: "the green pill is making it ugly make it white and bigger".
   The old pill was 13px of #1d7a46 on a 10% green tint, sitting over a moving film -- it
   read as washed out because the surface was see-through, not because the colour failed a
   contrast check (it was ~4.8:1, a pass). A real opaque surface is the fix, and it joins
   the white-pill family the hero already has: the ghost CTA, the chips, the chevrons.
   The green DOT stays -- that is the live signal, and it is the part that was never the
   problem. Contrast goes to roughly 17:1. No glow, no gradient, no shadow: a .5px hairline
   does the lifting, per "a 0.5px border instead of a box shadow". */
.ms-hero-subpill{
  display: flex;
  align-items: center;
  gap: 8px;
  width: max-content;
  margin: 18px auto 0;
  padding: 9px 18px;
  border-radius: 999px;
  background: var(--surface);
  border: .5px solid var(--border-2);
  color: var(--text);
  font-size: 14px;
  letter-spacing: -0.003em;
}
.ms-hero-subpill strong{ font-weight: 650; }
.ms-subpill-dot{
  width: 7px; height: 7px; border-radius: 50%;
  background: #34c759;
  flex: none;
}
.ms-hero-scroll-cue{
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  margin-top: clamp(48px, 8vh, 88px);
  font-family: var(--f-mono);
  font-size: 11px; font-weight: 500; letter-spacing: .16em; text-transform: uppercase;
  color: var(--text-3);
}
/* label + chevron share one column; the pulsing hairline below is the ::after. */
.ms-hero-scroll-cue span{
  display: flex; flex-direction: column; align-items: center; gap: 14px;
}
.ms-hero-scroll-cue span::after{
  content: "";
  width: 10px; height: 10px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
}
.ms-hero-scroll-cue::after{
  content: "";
  display: block;
  width: 1px;
  height: 56px;
  background: var(--ms-ink-3);
  animation: ms-scroll-pulse 2.6s ease-in-out infinite;
  transform-origin: top center;
}
@keyframes ms-scroll-pulse{
  0%   { transform: scaleY(0); transform-origin: top center; }
  40%  { transform: scaleY(1); transform-origin: top center; }
  60%  { transform: scaleY(1); transform-origin: bottom center; }
  100% { transform: scaleY(0); transform-origin: bottom center; }
}

/* ---------- Hero roaming sphere (plan §3-amendment, WU-8) ----------
   Owner: "the sphere should travel around the screen and look at the cursor".
   The offset-path arc is gone. mystyle.js lifts this layer out of the hero
   (the hero is `overflow: clip`, which would cut a viewport-wide wander) and
   parks it on <body> as a fixed, pointer-transparent sheet under the home nav
   pill (`.nav.nav--floating`, z 101) and over the page. JS writes ONE transform per frame: position from a
   two-sine wander, tilt from the cursor angle. CSS owns nothing but the box,
   so there is a single place where transforms compose. */
.ms-hero-orbit.is-roaming{
  position: fixed;
  inset: 0;
  z-index: 30;
  pointer-events: none;
  opacity: 1;
  transition: opacity .45s var(--ease-out);
}
/* Hero scrolled away / tab hidden: the ambience belongs to the hero, so it
   leaves with it instead of hovering over the next section. */
.ms-hero-orbit.is-roaming.is-away{ opacity: 0; }
.ms-hero-orbit.is-gone{ display: none; }
/* R4-2 film mode. The only sphere on a desktop visit is the one inside the
   loop, and the click no longer takes it out, so the DOM orbit has no job at
   all: it is hidden where it stands rather than lifted onto <body>. `display`
   rather than `opacity` -- nothing here has to animate, and it keeps the layer
   out of hit-testing and the a11y tree. The static states (<768px, reduced
   motion) never add this class and keep their still sphere top-right. */
.ms-hero-orbit.is-film{ display: none; }
.is-roaming .ms-hero-sphere{
  position: absolute;
  top: 0;
  left: 0;
  right: auto;
  /* The mockup sphere is ~200px at 1440. Viewport-relative so it keeps that
     presence on a laptop without swallowing a small window. JS reads the
     rendered width back rather than duplicating this clamp. */
  width: clamp(150px, 14vw, 230px);
  will-change: transform;
  transform-origin: 48.4% 49.3%;   /* the lens iris in the 512px turntable frames */
}
.is-roaming .ms-sphere-l{ opacity: 0; }   /* JS owns all four, every frame */
/* Roaming: the light rides the lens. JS writes its centre in stage pixels, and
   three dials off the same nearness value so the reaction is unmissable — it
   brightens (--gO .10 to .95), it widens (--gS .7x to 1.5x) and it burns hotter
   (--gB, a brightness() on THIS layer only, never on the frames). Multiplied by
   `facing`, so a lens turned away stays dark however close the cursor is. */
.is-roaming .ms-sphere-glow{
  left: 0;
  top: 0;
  opacity: var(--gO, 0);
  transform: translate(calc(var(--gx, 0px) - 50%), calc(var(--gy, 0px) - 50%)) scale(var(--gS, 1));
  filter: brightness(var(--gB, 1));
}
/* One brightening as the audit click takes the sphere over. */
.is-roaming .ms-hero-sphere.is-flare .ms-sphere-glow{ opacity: 1; --gS: 1.6; --gB: 1.8; }

/* Reduced motion / phones: no orbit. The jpg is painted on the hero itself so there is
   always a ground under the film, and the plate goes either way — it is a third stacked
   copy of the same picture and U8 already showed it bleeding through the video.

   The film itself now RUNS on phones (owner 2026-09-14: "for device, optimized the video
   playing in hero separately"): setupHeroLayers() points the <video> at the 960-wide
   hero-loop.m.mp4 encode and hands it to setupHeroFilm, so play/pause follows visibility
   exactly as on desktop. Reduced motion still gets the still, and so does a browser
   reporting Save-Data — that one is a JS decision, since there is no media query for it. */
@media (max-width: 767px), (prefers-reduced-motion: reduce){
  .ms-hero{
    background-image: url("/image/hero-loop.jpg");
    background-size: cover;
    background-position: center;
  }
  .ms-hero-plate{ display: none; }
}
@media (prefers-reduced-motion: reduce){
  .ms-hero-ribbons{ display: none; }
}
@media (max-width: 767px){
  /* No sphere at all on a phone (owner 2026-09-14): the film is the hero here, so the
     120px band that used to be reserved for the corner sphere goes with it. */
  .ms-hero{ padding-top: calc(var(--nav-h) + 8px); }
  .ms-hero-orbit{ display: none; }
  /* 16px keeps iOS from zooming the page on focus */
  .ms-hero-form input{ font-size: 16px; }
  .ms-hero-form{ padding-left: 18px; }
  .ms-hero-form .ms-btn{ padding: 13px 18px; }
}

/* Buttons */
.ms-btn{
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 14px 26px;
  border-radius: var(--r-pill);
  font-family: inherit;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.005em;
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  transition:
    background var(--ms-dur-state) var(--ms-ease),
    color var(--ms-dur-state) var(--ms-ease),
    border-color var(--ms-dur-state) var(--ms-ease),
    transform var(--ms-dur-state) var(--ease-spring);
  white-space: nowrap;
}
.ms-btn:active{ transform: scale(0.97); }
.ms-btn--primary{ background: var(--ms-accent); color: #fff; }
.ms-btn--primary:hover{ background: var(--ms-accent-hov); }
.ms-btn--ghost{
  background: transparent;
  color: var(--ms-ink);
  border-color: var(--ms-line);
}
.ms-btn--ghost:hover{ border-color: var(--ms-ink-3); }
.ms-btn--inhero{ padding: 13px 26px; font-size: 15px; }

/* Word reveal animation (hero h1) */
.ms-word{
  display: inline-block;
  opacity: 0;
  transform: translateY(0.4em);
  transition:
    opacity 460ms var(--ms-ease-out) var(--ms-d, 0ms),
    transform 460ms var(--ms-ease-out) var(--ms-d, 0ms);
}
.ms-words.is-in .ms-word{
  opacity: 1;
  transform: translateY(0);
}

/* ---------- Section head pattern ---------- */
.ms-section-head{
  max-width: 800px;
  margin: 0 0 64px;
}
.ms-section-head--center{
  text-align: center;
  margin: 0 auto 64px;
}
.ms-divider{
  height: 1px;
  background: var(--ms-line);
  width: 100%;
  margin-bottom: 48px;
  transform-origin: left center;
  transform: scaleX(0);
  transition: transform var(--ms-dur-layout) var(--ms-ease-out);
}
.ms-divider.is-in{ transform: scaleX(1); }

/* ---------- Try-it block (the playground) ---------- */
.ms-try{
  display: grid;
  grid-template-columns: 1fr;
  gap: 32px;
  margin-top: 32px;
}
.ms-prompts{
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}
.ms-prompt{
  padding: 12px 18px;
  background: var(--ms-surface);
  border: 1px solid var(--ms-line);
  border-radius: var(--r-pill);
  font: inherit;
  font-size: 14px;
  color: var(--ms-ink);
  cursor: pointer;
  transition:
    background var(--ms-dur-state) var(--ms-ease),
    border-color var(--ms-dur-state) var(--ms-ease),
    color var(--ms-dur-state) var(--ms-ease),
    transform 120ms var(--ms-ease);
}
.ms-prompt:hover{
  border-color: var(--ms-ink);
  transform: translateY(-1px);
}
.ms-prompt[aria-pressed="true"]{
  background: var(--ms-ink);
  color: #fff;
  border-color: var(--ms-ink);
}
.ms-output{
  background: var(--ms-surface);
  border: 1px solid var(--ms-line);
  border-radius: 16px;
  padding: 28px 32px;
  min-height: 180px;
  font-size: 16px;
  line-height: 1.6;
  color: var(--ms-ink-2);
  white-space: pre-wrap;
  word-wrap: break-word;
  position: relative;
  transition: border-color var(--ms-dur-state) var(--ms-ease);
}
.ms-output:empty::before{
  content: "Pick a prompt above to see Groq think.";
  color: var(--ms-ink-4);
  font-style: italic;
}
.ms-output.is-busy{
  border-color: var(--ms-accent);
}
.ms-output-meta{
  margin-top: 12px;
  font-size: 12px;
  color: var(--ms-ink-4);
  letter-spacing: 0.02em;
  font-feature-settings: "tnum" 1;
}
.ms-cursor{
  display: inline-block;
  width: 8px;
  height: 1em;
  background: var(--ms-accent);
  margin-left: 2px;
  vertical-align: text-bottom;
  animation: ms-blink 1s steps(2, end) infinite;
}
@keyframes ms-blink{
  50% { opacity: 0; }
}
@keyframes ms-fade-in{ to { opacity: 1; } }

/* The .ms-cards-3 / .ms-card* block lived here. Its only producer, agentCardMarkup(),
   had no caller and was removed along with loadAgentImages(), whose #ms-hero-agents host
   never existed either. */

/* ============================================================
   Cybernetic Bento Grid — agent cards (dark on light section)
   ============================================================ */
.ms-bento-grid{
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: minmax(190px, auto);
  gap: 14px;
  margin-top: 48px;
}

/* Layout — mirrors the React CyberneticBentoGrid shape */
.ms-bento-item:nth-child(1){ grid-column: span 2; grid-row: span 2; }
.ms-bento-item:nth-child(3){ grid-row: span 2; }
.ms-bento-item:nth-child(5){ grid-column: span 3; }

.ms-bento-item{
  position: relative;
  background: linear-gradient(135deg, #0f0f14 0%, #0d1220 100%);
  border: 1px solid rgba(255,255,255,0.07);
  border-radius: 20px;
  padding: 28px;
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  display: flex;
  flex-direction: column;
  cursor: pointer;
  /* Initial scroll state — JS adds .anim-in to reveal */
  opacity: 0;
  filter: blur(8px);
  transition:
    opacity 0.75s cubic-bezier(0.16,1,0.3,1),
    filter 0.65s ease,
    border-color 0.35s ease,
    transform 0.35s cubic-bezier(0.16,1,0.3,1),
    box-shadow 0.35s ease;
}
.ms-bento-item.anim-in{
  opacity: 1;
  filter: blur(0px);
}
.ms-bento-item.anim-in:hover{
  transform: translateY(-3px);
  box-shadow: 0 20px 60px rgba(0,0,0,0.35);
}

/* Mouse-tracking radial glow — position set via JS --mouse-x/y */
.ms-bento-item::before{
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(
    500px circle at var(--mouse-x, -200px) var(--mouse-y, -200px),
    rgba(255,255,255,0.07) 0%,
    transparent 60%
  );
  pointer-events: none;
  z-index: 0;
  border-radius: inherit;
}

/* Inner border highlight on hover */
.ms-bento-item::after{
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  border: 1px solid transparent;
  transition: border-color 0.35s ease;
  pointer-events: none;
  z-index: 0;
}
.ms-bento-item:hover::after{ border-color: rgba(255,255,255,0.14); }

/* hover handled via .anim-in:hover above */

/* All direct content above the glow layer */
.ms-bento-item > *{ position: relative; z-index: 1; }

/* Artwork — absolute bottom-right, behind text content */
.ms-bento-art{
  position: absolute;
  bottom: -8px; left: 0; right: 0;
  height: 74%;
  pointer-events: none;
  z-index: 1;
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
}
.ms-bento-art img{
  width: 74%; max-height: 100%;
  object-fit: contain;
  object-position: bottom right;
  filter:
    drop-shadow(0 16px 32px rgba(0,0,0,0.65))
    drop-shadow(0 4px 10px rgba(0,0,0,0.45));
}
/* Large 2×2 card — bigger */
.ms-bento-item:nth-child(1) .ms-bento-art{ height: 82%; bottom: -14px; }
.ms-bento-item:nth-child(1) .ms-bento-art img{ width: 80%; }
/* Wide 3-col Job Tracker card — fills red rectangle, fades at bottom */
.ms-bento-item:nth-child(5) .ms-bento-art{ height: 100%; bottom: 0; align-items: flex-start; }
.ms-bento-item:nth-child(5) .ms-bento-art img{
  width: 88%;
  object-position: top right;
  -webkit-mask-image: linear-gradient(to bottom, black 45%, transparent 92%);
  mask-image: linear-gradient(to bottom, black 45%, transparent 92%);
}
/* Text content sits above artwork */
.ms-bento-badge, .ms-bento-name, .ms-bento-cta{ z-index: 2; }

.ms-bento-badge{
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  padding: 4px 11px;
  background: rgba(255,255,255,0.05);
  border: 1px solid rgba(255,255,255,0.09);
  border-radius: 20px;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.38);
  margin-bottom: 14px;
  transition: border-color 0.3s ease, color 0.3s ease;
}
.ms-bento-item:hover .ms-bento-badge{
  border-color: rgba(255,255,255,0.18);
  color: rgba(255,255,255,0.6);
}

.ms-bento-name{
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.014em;
  color: var(--ink-text);
  line-height: 1.2;
  margin: 0;
}
.ms-bento-item:nth-child(1) .ms-bento-name{
  font-size: 24px;
}

.ms-bento-desc{
  font-size: 14px;
  color: rgba(255,255,255,0.38);
  line-height: 1.6;
  margin: 10px 0 0;
  flex: 1;
  transition: color 0.35s ease;
}
.ms-bento-item:hover .ms-bento-desc{ color: rgba(255,255,255,0.58); }

.ms-bento-cta{
  margin-top: 20px;
  font-size: 13px;
  font-weight: 500;
  color: var(--accent);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  letter-spacing: 0.01em;
  transition: color 0.3s ease, gap 0.3s ease;
}
.ms-bento-item:hover .ms-bento-cta{
  color: var(--accent);
  gap: 10px;
}

@media (max-width: 900px){
  .ms-bento-grid{
    grid-template-columns: repeat(2, 1fr);
    grid-auto-rows: minmax(160px, auto);
  }
  .ms-bento-item:nth-child(1){ grid-column: span 2; grid-row: span 1; }
  .ms-bento-item:nth-child(3){ grid-row: span 1; }
  .ms-bento-item:nth-child(5){ grid-column: span 2; }
}
@media (max-width: 560px){
  .ms-bento-grid{ grid-template-columns: 1fr; }
  .ms-bento-item:nth-child(1),
  .ms-bento-item:nth-child(3),
  .ms-bento-item:nth-child(5){ grid-column: span 1; grid-row: span 1; }
}

/* ---------- Tools section — light-theme bento grid ---------- */
.ms-tools-bento{
  display: grid;
  grid-template-columns: 5fr 4fr 4fr;
  grid-template-rows: minmax(200px,auto) minmax(160px,auto);
  gap: 14px;
  margin-top: 48px;
}
.ms-tools-card:nth-child(1){ grid-row: span 2; }
.ms-tools-card:nth-child(3){ grid-row: span 2; }

.ms-tools-card{
  background: #fff;
  border: 1px solid #e5e5ea;
  border-radius: 20px;
  padding: 28px;
  position: relative;
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  display: flex;
  flex-direction: column;
  cursor: pointer;
  transition: transform 0.32s cubic-bezier(0.16,1,0.3,1), box-shadow 0.32s ease, border-color 0.32s ease;
}
.ms-tools-card::before{
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(
    400px circle at var(--mouse-x,-200px) var(--mouse-y,-200px),
    color-mix(in srgb, var(--accent) 5%, transparent) 0%,
    transparent 60%
  );
  pointer-events: none;
  z-index: 0;
  border-radius: inherit;
}
.ms-tools-card > *{ position: relative; z-index: 1; }
.ms-tools-card:hover{
  transform: translateY(-4px);
  box-shadow: 0 16px 48px rgba(0,0,0,0.08);
  border-color: #c7c7cc;
}

.ms-tc-icon-wrap{
  width: 52px; height: 52px;
  background: var(--accent);
  border-radius: 14px;
  display: flex; align-items: center; justify-content: center;
  margin-bottom: 20px;
  flex-shrink: 0;
}
.ms-tc-title{
  font-size: 20px; font-weight: 600;
  letter-spacing: -0.018em;
  color: var(--text);
  margin: 0 0 10px;
}
.ms-tc-body{
  font-size: 14px; line-height: 1.6;
  color: var(--text-3);
  margin: 0;
  flex: 1;
}
.ms-tc-cta{
  display: inline-block;
  margin-top: 24px;
  font-size: 14px; font-weight: 500;
  color: var(--accent);
}
.ms-tc-eyebrow{
  font-size: 12px; font-weight: 500; letter-spacing: 0.06em;
  text-transform: uppercase; color: #8e8e93;
  margin: 0 0 14px;
}
.ms-tc-chips{
  display: flex; flex-wrap: wrap; gap: 8px;
  margin-bottom: 14px;
}
.ms-tc-chip{
  font-size: 12px; font-weight: 500;
  background: var(--bg-2); color: var(--text);
  border: 1px solid #e5e5ea;
  border-radius: 20px;
  padding: 4px 12px;
}
.ms-tc-count{
  font-size: 13px; color: #8e8e93;
  margin: auto 0 0;
}
.ms-tools-card--stat .ms-tc-bignum{
  font-size: 72px; font-weight: 700;
  letter-spacing: -0.04em;
  color: var(--text);
  line-height: 1;
  margin-bottom: 16px;
}
.ms-tc-bignum-x{
  font-size: 0.45em; font-weight: 700;
  vertical-align: super; margin-left: 2px;
}
.ms-tc-shortcut-icon{
  width: 40px; height: 40px;
  background: var(--bg-2); border-radius: 10px;
  display: flex; align-items: center; justify-content: center;
  color: var(--text); margin-bottom: 14px;
}
.ms-tc-label{
  font-size: 17px; font-weight: 600;
  color: var(--text); margin: 0 0 8px;
}
.ms-tc-hint{
  font-size: 13px; color: var(--text-3); margin: 0; flex: 1;
}
.ms-tc-kbd-row{
  display: flex; align-items: center; gap: 8px;
  margin-top: 20px;
}
.ms-tc-kbd{
  font-family: var(--f-mono);
  font-size: 13px; font-weight: 500;
  background: var(--bg-2); border: 1px solid #e5e5ea;
  border-radius: 7px; padding: 5px 12px;
  color: var(--text);
}
.ms-tc-plus{ color: #8e8e93; font-size: 16px; }

@media (max-width: 860px){
  .ms-tools-bento{ gap: 8px; margin-top: 24px;
    grid-template-rows: minmax(140px,auto) minmax(110px,auto); }
  .ms-tools-card{ padding: 14px; border-radius: 14px; }
  .ms-tc-icon-wrap{ width: 34px; height: 34px; border-radius: 9px; margin-bottom: 10px; }
  .ms-tc-icon-wrap svg{ width: 18px; height: 18px; }
  .ms-tc-title{ font-size: 13px; margin-bottom: 6px; }
  .ms-tc-body{ font-size: 12px; line-height: 1.45; }
  .ms-tc-cta{ font-size: 12px; margin-top: 12px; }
  .ms-tc-eyebrow{ font-size: 12px; margin-bottom: 8px; }
  .ms-tc-chips{ gap: 4px; margin-bottom: 8px; }
  .ms-tc-chip{ font-size: 12px; padding: 2px 7px; }
  .ms-tc-count{ font-size: 12px; }
  .ms-tools-card--stat .ms-tc-bignum{ font-size: 36px; margin-bottom: 8px; }
  .ms-tc-label{ font-size: 12px; margin-bottom: 4px; }
  .ms-tc-hint{ font-size: 12px; }
  .ms-tc-shortcut-icon{ width: 28px; height: 28px; margin-bottom: 8px; }
  .ms-tc-shortcut-icon svg{ width: 14px; height: 14px; }
  .ms-tc-kbd-row{ margin-top: 10px; gap: 4px; }
  .ms-tc-kbd{ font-size: 12px; padding: 3px 7px; }
}

/* ---------- Compare section — dark two-column ticker ---------- */
.ms-compare-section{ padding: 0; }
.ms-compare-inner{
  display: grid;
  grid-template-columns: 1fr 1fr;
}
.ms-compare-left{
  padding: calc(var(--ms-pad-y) * 1.5) 56px calc(var(--ms-pad-y) * 1.5) 80px;
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 0;
}
.ms-compare-left .ms-headline{ margin: 12px 0 20px; }
.ms-compare-left .ms-sub{ margin-bottom: 40px; }

.ms-compare-pill{
  display: inline-flex;
  align-items: center;
  gap: 8px;
  background: #fff;
  color: var(--text);
  font-size: 15px;
  font-weight: 500;
  padding: 12px 22px;
  border-radius: 100px;
  text-decoration: none;
  width: fit-content;
  transition: background 0.2s, color 0.2s;
  z-index: 1;
}
.ms-compare-pill:hover{ background: var(--bg-2); }

/* Wireframe deco shapes */
.ms-compare-deco{
  position: absolute;
  bottom: -20px; left: 40px;
  width: 300px; height: 300px;
  pointer-events: none;
}
.ms-deco-tri{
  position: absolute;
  bottom: 60px; left: 20px;
  width: 90px; height: 78px;
  border-left: 1.5px solid rgba(255,255,255,0.12);
  border-right: 1.5px solid rgba(255,255,255,0.12);
  border-bottom: 1.5px solid rgba(255,255,255,0.12);
  clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
  border: 1.5px solid rgba(255,255,255,0.12);
}
.ms-deco-rect{
  position: absolute;
  border: 1.5px solid rgba(255,255,255,0.08);
}
.ms-deco-rect--a{
  width: 80px; height: 80px;
  bottom: 20px; left: 0;
  transform: rotate(-18deg);
}
.ms-deco-rect--b{
  width: 110px; height: 110px;
  bottom: -10px; left: 80px;
  transform: rotate(8deg);
}

/* Right — ticker */
.ms-compare-right{
  border-left: 1px solid rgba(255,255,255,0.07);
  overflow: hidden;
  position: relative;
}
.ms-compare-right::before,
.ms-compare-right::after{
  content: '';
  position: absolute;
  left: 0; right: 0;
  height: 80px;
  z-index: 2;
  pointer-events: none;
}
.ms-compare-right::before{
  top: 0;
  background: linear-gradient(to bottom, #0f0f11, transparent);
}
.ms-compare-right::after{
  bottom: 0;
  background: linear-gradient(to top, #0f0f11, transparent);
}
.ms-compare-ticker-wrap{
  height: 705px;
  overflow: hidden;
  padding: 24px 24px;
}
.ms-compare-ticker{
  display: flex;
  flex-direction: column;
  gap: 12px;
  animation: cmp-scroll 18s linear infinite;
}
@keyframes cmp-scroll{
  0%   { transform: translateY(0); }
  100% { transform: translateY(-50%); }
}

/* Individual compare card */
.ms-cmp-card{
  background: #1a1a1e;
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 6px;
  padding: 22px 22px 18px;
  text-decoration: none;
  color: inherit;
  display: flex;
  flex-direction: column;
  gap: 10px;
  position: relative;
  transition: border-color 0.25s, background 0.25s;
  flex-shrink: 0;
}
.ms-cmp-card:hover{ border-color: rgba(255,255,255,0.18); background: #222226; }
.ms-cmp-card--accent{
  background: var(--accent);
  border-color: transparent;
}
.ms-cmp-card--accent:hover{ background: var(--accent-2); border-color: transparent; }
.ms-cmp-logos{
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 4px;
}
.ms-cmp-logo{
  width: 36px; height: 36px;
  border-radius: 10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  font-weight: 700;
  color: #fff;
  flex-shrink: 0;
}
.ms-cmp-vs{
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.35);
}
.ms-cmp-title{
  font-size: 17px;
  font-weight: 600;
  color: var(--ink-text);
  margin: 0;
  letter-spacing: -0.012em;
}
.ms-cmp-card--accent .ms-cmp-title{ color: #fff; }
.ms-cmp-body{
  font-size: 13px;
  line-height: 1.5;
  color: rgba(229,232,236,0.5);
  margin: 0;
}
.ms-cmp-card--accent .ms-cmp-body{ color: rgba(255,255,255,0.75); }
.ms-cmp-arrow{
  align-self: flex-end;
  width: 34px; height: 34px;
  border-radius: 50%;
  background: rgba(255,255,255,0.1);
  display: flex; align-items: center; justify-content: center;
  color: #fff;
  margin-top: 4px;
  transition: background 0.2s;
}
.ms-cmp-card:hover .ms-cmp-arrow{ background: rgba(255,255,255,0.18); }
.ms-cmp-card--accent .ms-cmp-arrow{ background: rgba(255,255,255,0.25); }

@media (max-width: 860px){
  .ms-compare-left{ padding: 28px 14px 28px 18px; }
  .ms-compare-left .ms-headline{ font-size: clamp(16px, 3.5vw, 24px); margin: 6px 0 10px; }
  .ms-compare-left .ms-sub{ font-size: 12px; margin-bottom: 18px; max-width: 22ch; }
  .ms-compare-pill{ font-size: 12px; padding: 8px 14px; }
  .ms-compare-ticker-wrap{ padding: 12px 10px; height: 340px; }
  .ms-cmp-card{ padding: 12px 12px 10px; border-radius: 4px; gap: 6px; }
  .ms-cmp-logo{ width: 24px; height: 24px; font-size: 12px; border-radius: 6px; }
  .ms-cmp-vs{ font-size: 12px; }
  .ms-cmp-title{ font-size: 12px; }
  .ms-cmp-body{ font-size: 12px; line-height: 1.4; }
  .ms-cmp-arrow{ width: 24px; height: 24px; }
  .ms-compare-deco{ display: none; }
}

/* ---------- 3 calm ideas (icon + text) ---------- */
.ms-ideas{
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 64px 32px;
}
@media (max-width: 900px){
  .ms-ideas{ grid-template-columns: 1fr; gap: 48px; }
}
.ms-idea-num{
  font-family: var(--f-mono);
  font-size: 13px;
  color: var(--ms-ink-4);
  letter-spacing: 0.02em;
  margin-bottom: 16px;
}
.ms-idea-title{
  font-size: 22px;
  font-weight: 500;
  letter-spacing: -0.012em;
  color: var(--ms-ink);
  margin: 0 0 12px;
}
.ms-idea-copy{
  font-size: 16px;
  color: var(--ms-ink-3);
  line-height: 1.55;
  letter-spacing: -0.005em;
  margin: 0;
  max-width: 28ch;
}

/* ---------- 2-column feature row (image left, text right and vice-versa) ---------- */
.ms-feature{
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(40px, 5vw, 96px);
  align-items: center;
}
@media (max-width: 900px){
  .ms-feature{ grid-template-columns: 1fr; }
}
.ms-feature--reverse .ms-feature-img{ order: 2; }
.ms-feature-img{
  aspect-ratio: 4 / 5;
  background: var(--ms-surface-cool);
  border-radius: 28px;
  overflow: hidden;
  position: relative;
}
.ms-feature-img img{
  width: 100%; height: 100%;
  object-fit: cover; object-position: center;
  filter: saturate(0.85);
  opacity: 0;
  transform: scale(1.06);
  transition:
    opacity 900ms var(--ms-ease),
    transform 1500ms var(--ms-ease-out);
}
.ms-feature-img img.is-loaded{ opacity: 1; transform: scale(1); }

/* ============================================================
   Skills hero section — big image left, text right
   ============================================================ */
.ms-skills-section{ background: var(--ms-surface); padding-top: calc(var(--ms-pad-y) * 0.8); padding-bottom: calc(var(--ms-pad-y) * 0.8); }
.ms-agents-section{ padding-top: calc(var(--ms-pad-y) * 0.8); padding-bottom: calc(var(--ms-pad-y) * 0.8); }
#ms-sec-tools{ padding-top: calc(var(--ms-pad-y) * 0.8); padding-bottom: calc(var(--ms-pad-y) * 0.8); }

.ms-skills-layout{
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(40px, 6vw, 80px);
  align-items: center;
}
@media (max-width: 900px){
  .ms-skills-layout{ grid-template-columns: 1fr; }
}

.ms-skills-hero-img{
  border-radius: 28px;
  overflow: hidden;
  background: var(--ms-surface-cool);
  aspect-ratio: 3 / 4;
  width: 100%;
}
.ms-skills-hero-img{
  transition: transform 0.45s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.45s ease;
}
.ms-skills-hero-img:hover{
  transform: translateY(-8px) scale(1.012);
  box-shadow: 0 32px 72px rgba(0,0,0,0.13);
}
.ms-skills-hero-img img{
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center center;
  animation: ms-img-reveal 900ms var(--ms-ease) both;
}

/* ============================================================
   MCP section — 3-up square + 1 wide panoramic (matches reference)
   ============================================================ */
.ms-mcp-section{ padding-top: calc(var(--ms-pad-y) * 0.8); padding-bottom: calc(var(--ms-pad-y) * 0.8); }

.ms-mcp-header{
  max-width: 640px;
  margin-bottom: clamp(40px, 5vw, 64px);
}

/* inline header row: eyebrow + explore link on same line */
.ms-mcp-header-top{
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 0;
}
.ms-mcp-top-link{
  font-size: 15px;
  font-weight: 500;
  color: var(--accent);
  text-decoration: none;
  letter-spacing: -0.01em;
  transition: opacity 0.25s ease;
}
.ms-mcp-top-link:hover{ opacity: 0.7; }

/* 3 + 1 card grid */
.ms-mcp-grid{
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: 360px 260px;
  gap: 10px;
  overflow: hidden;
  border-radius: 24px;
}
.ms-mcp-card--wide{
  grid-column: 1 / -1;
}

.ms-mcp-card{
  position: relative;
  overflow: hidden;
  background: var(--ms-dark-2);
  display: flex;
  align-items: flex-end;
  text-decoration: none;
  color: inherit;
}
.ms-mcp-card img{
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  object-position: center center;
  filter: saturate(0.85) brightness(0.9) contrast(1.02);
  animation: ms-img-reveal 800ms var(--ms-ease) both;
}
@keyframes ms-img-reveal{
  from{ opacity: 0; transform: scale(1.04); }
  to  { opacity: 1; transform: scale(1); }
}

/* settled state — enables hover after fan animation completes */
.ms-mcp-card.mcp-settled{
  cursor: pointer;
}

/* card label — no gradient, larger text */
.ms-mcp-card-label{
  position: relative;
  z-index: 2;
  padding: 18px 24px;
  font-size: 20px;
  font-weight: 600;
  color: var(--ink-text);
  letter-spacing: -0.015em;
  width: 100%;
  background: none;
}
.ms-mcp-card-label--dark{
  font-size: 22px;
  padding: 22px 28px;
}

.ms-mcp-footer{
  margin-top: 40px;
  display: flex;
}

/* MCP ghost button on dark surface */
.ms-btn--mcp{
  background: transparent;
  color: #fff;
  border-color: rgba(255,255,255,.22);
}
.ms-btn--mcp::before{ background: #fff; }
.ms-btn--mcp:hover{ color: var(--ms-dark); background: transparent; border-color: rgba(255,255,255,.22); }

@media (max-width: 900px){
  .ms-mcp-grid{
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 260px 260px 220px;
  }
  .ms-mcp-card:nth-child(3){ grid-column: 1 / -1; }
  .ms-mcp-card--wide{ grid-column: 1 / -1; }
  .ms-mcp-header-top{ flex-direction: column; align-items: flex-start; gap: 8px; }
}
@media (max-width: 560px){
  .ms-mcp-grid{
    grid-template-columns: 1fr;
    grid-template-rows: 280px 280px 240px 200px;
  }
  .ms-mcp-card:nth-child(3){ grid-column: auto; }
}

/* ---------- Quote / manifesto ---------- */
.ms-quote{
  text-align: center;
  max-width: 920px;
  margin: 0 auto;
}
.ms-quote-text{
  font-size: clamp(32px, 4.4vw, 56px);
  font-weight: 500;
  letter-spacing: -0.022em;
  line-height: 1.1;
  color: var(--ms-ink);
  margin: 0;
}
.ms-quote-text em{
  font-style: normal;
  color: var(--ms-accent);
}

/* ---------- Final CTA ---------- */
.ms-cta-final{
  text-align: center;
  max-width: 720px;
  margin: 0 auto;
}

/* ---------- Line-mask headline reveal (Lusion-style) ----------
   setupLineReveal() splits headlines into measured lines; each line slides up
   from behind an overflow mask with a per-line stagger, then unwraps to plain
   text so resize re-wraps naturally. Padding compensates descender clipping. */
.ms-lines .ms-lm{ display: block; overflow: hidden; padding-bottom: .12em; margin-bottom: -.12em; }
.ms-lines .ms-lm-i{
  display: block;
  transition: transform .85s var(--ms-ease-out) var(--ms-lm-d, 0ms);
  will-change: transform;
}
/* Same reasoning as the stagger rule above: the masked lines start off-screen only
   when there is JavaScript to bring them back. */
.js .ms-lines .ms-lm-i{ transform: translateY(112%); }
.ms-lines.is-in .ms-lm-i{ transform: translateY(0); }
@media (prefers-reduced-motion: reduce){
  .ms-lines .ms-lm-i{ transform: none !important; transition: none !important; }
}

/* ---------- Staggered card entrances ----------
   Keyframes (not transitions) so per-card hover transitions stay untouched.
   Markup sets class="ms-stagger" style="--sd:n" on children of a .ms-reveal. */
/* Gated on .js (set by the inline script in index.html's head). Ungated, this rule
   hid every staggered child until the IntersectionObserver reached it, so a reader
   with JS off saw an empty page and a reader with JS on saw one until the observer
   armed - measured 2026-09-16 as 2.3-2.4s to first visible text on home. The class
   is on <html>, so it applies from the first byte of CSS, and prerender.js strips it
   from the snapshots it writes: a prerendered page has to stay readable on its own. */
.js .ms-reveal:not(.is-in) .ms-stagger{ opacity: 0; }
.ms-reveal.is-in .ms-stagger{ animation: ms-rise .8s var(--ms-ease-out) both; animation-delay: calc(var(--sd, 0) * 90ms); }
@keyframes ms-rise{ from{ opacity: 0; transform: translateY(20px); } }

/* ---------- Reveal-on-scroll ---------- */
.ms-reveal{
  opacity: 0;
  transform: translateY(32px);
  filter: blur(7px);
  transition:
    opacity var(--ms-dur-layout) var(--ms-ease-out),
    transform var(--ms-dur-layout) var(--ms-ease-out),
    filter var(--ms-dur-state) ease;
  will-change: opacity, transform, filter;
}
.ms-reveal.is-in{
  opacity: 1;
  transform: translateY(0);
  filter: blur(0px);
}
/* Elements that have scrolled past above viewport — blur back out */
.ms-reveal.is-above{
  opacity: 0.15;
  transform: translateY(-14px);
  filter: blur(6px);
  transition:
    opacity var(--ms-dur-state) ease,
    transform var(--ms-dur-state) ease,
    filter var(--ms-dur-quick) ease !important;
  transition-delay: 0ms !important;
}
.ms-reveal[data-d="1"]{ transition-delay: 80ms; }
.ms-reveal[data-d="2"]{ transition-delay: 160ms; }
.ms-reveal[data-d="3"]{ transition-delay: 240ms; }
.ms-reveal[data-d="4"]{ transition-delay: 320ms; }

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce){
  body.mystyle *,
  body.mystyle *::before,
  body.mystyle *::after{
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  body.mystyle .ms-reveal,
  body.mystyle .ms-divider,
  body.mystyle .ms-words .ms-word{
    opacity: 1 !important;
    transform: none !important;
  }
  body.mystyle .ms-hero-orb{ display: none; }
}

/* ---------- Footer override (matches new dark sections) ---------- */
body.mystyle .foot{
  background: var(--ms-dark);
  margin-top: 0;
}

/* ============================================================
   PLAYGROUND — centered column (same tone as dashboard Run tab)
   ============================================================ */
.ms-pg2{
  max-width: 680px;
  margin: 32px auto 0;
}
.ms-pg-pill-ready{
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: var(--r-pill);
  background: rgba(31,138,59,.10);
  color: var(--success);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: -0.005em;
}
.ms-pg-pill-ready::before{
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--success);
}
.ms-pg-textarea{
  width: 100%;
  box-sizing: border-box;
  min-height: 200px;
  padding: 18px 20px;
  background: var(--ms-bg);
  border: 1px solid var(--ms-line);
  border-radius: var(--r-md);
  font: inherit;
  font-size: 15px;
  line-height: 1.6;
  color: var(--ms-ink);
  resize: vertical;
  outline: none;
  transition: border-color var(--ms-dur-quick) var(--ms-ease), box-shadow var(--ms-dur-quick) var(--ms-ease);
}
.ms-pg-textarea::placeholder{ color: var(--ms-ink-4); }
.ms-pg-textarea:focus{
  border-color: var(--ms-accent);
  box-shadow: 0 0 0 3px var(--ms-accent-tint);
}
.ms-pg-example{
  font-size: 13px;
  color: var(--ms-ink-3);
  letter-spacing: -0.005em;
  cursor: pointer;
  background: transparent;
  border: 0;
  padding: 8px 0 0;
  text-align: left;
  font: inherit;
}
.ms-pg-example:hover{ color: var(--ms-ink); }
.ms-pg-example b{ font-weight: 500; color: var(--ms-ink-3); }
.ms-pg-foot{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--ms-line-soft);
  flex-wrap: wrap;
}
.ms-pg-runs-line{
  font-size: 13px;
  color: var(--ms-ink-3);
  font-feature-settings: "tnum" 1;
}
.ms-pg-run-btn{
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 22px;
  border-radius: var(--r-pill);
  background: var(--ms-accent);
  color: #fff;
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  border: 0;
  cursor: pointer;
  transition: background var(--ms-dur-quick) var(--ms-ease), transform 120ms var(--ms-ease);
}
.ms-pg-run-btn:hover{ background: var(--ms-accent-hov); }
.ms-pg-run-btn:active{ transform: scale(0.97); }
.ms-pg-run-btn:disabled{ background: var(--ms-ink-4); cursor: wait; }
.ms-pg-run-btn::after{ content: "▶"; font-size: 12px; }
.ms-pg-output{
  margin-top: 4px;
  padding: 18px 20px;
  background: var(--ms-bg);
  border: 1px solid var(--ms-line);
  border-radius: var(--r-md);
  font-size: 15px;
  line-height: 1.6;
  color: var(--ms-ink-2);
  white-space: pre-wrap;
  word-wrap: break-word;
  display: none;
}
.ms-pg-output.is-visible{ display: block; }
.ms-pg-output-meta{
  margin-top: 10px;
  font-size: 12px;
  color: var(--ms-ink-4);
  font-family: var(--f-mono);
  letter-spacing: 0.02em;
  display: none;
}
.ms-pg-output-meta.is-visible{ display: block; }
.ms-pg-quota-bar{
  height: 4px;
  background: var(--ms-surface-cool);
  border-radius: var(--r-pill);
  overflow: hidden;
  width: 120px;
}
.ms-pg-quota-fill{
  height: 100%;
  background: var(--ms-accent);
  border-radius: var(--r-pill);
  width: 0%;
  transition: width var(--ms-dur-state) var(--ms-ease);
}

/* Inner Try / Overview tabs (playground + dashboard Run pane) */
.ms-pg-tabs{
  display: flex;
  gap: 0;
  border-bottom: 1px solid var(--ms-line);
  margin-bottom: 20px;
}
.ms-pg-tab{
  font: inherit;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -0.005em;
  color: var(--ms-ink-3);
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 10px 16px;
  cursor: pointer;
  white-space: nowrap;
  margin-bottom: -1px;
  transition: color var(--ms-dur-quick) var(--ms-ease), border-color var(--ms-dur-quick) var(--ms-ease);
}
.ms-pg-tab:hover{ color: var(--ms-ink); }
.ms-pg-tab.is-active{ color: var(--ms-accent); border-bottom-color: var(--ms-accent); }
.ms-pg-pane{ display: none; }
.ms-pg-pane.is-active{ display: block; }

/* Example prompts list */
.ms-pg-examples{ margin: 8px 0 12px; }
.ms-pg-examples-label{
  font-size: 12px;
  color: var(--ms-ink-4);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  margin: 0 0 8px;
}
.ms-pg-example-row{
  display: block;
  width: 100%;
  text-align: left;
  padding: 9px 14px;
  border-radius: 8px;
  background: transparent;
  border: 1px solid var(--ms-line);
  font: inherit;
  font-size: 13px;
  color: var(--ms-ink-2);
  cursor: pointer;
  margin-bottom: 6px;
  letter-spacing: -0.005em;
  transition: background var(--ms-dur-quick) var(--ms-ease), color var(--ms-dur-quick) var(--ms-ease);
}
.ms-pg-example-row:last-child{ margin-bottom: 0; }
.ms-pg-example-row:hover{ background: var(--ms-surface-cool); color: var(--ms-ink); }

/* Overview pane */
.ms-pg-overview{ padding: 8px 0 16px; }
.ms-pg-ov-title{
  font-size: 22px;
  font-weight: 600;
  letter-spacing: -0.012em;
  color: var(--ms-ink);
  margin: 0 0 14px;
}
.ms-pg-ov-desc{
  font-size: 15px;
  line-height: 1.65;
  color: var(--ms-ink-2);
  letter-spacing: -0.005em;
  margin: 0 0 28px;
}
.ms-pg-ov-meta{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding-top: 20px;
  border-top: 1px solid var(--ms-line-soft);
}
.ms-pg-ov-model{
  font-family: var(--f-mono);
  font-size: 12px;
  color: var(--ms-ink-4);
  letter-spacing: 0.02em;
}

/* ============================================================
   STEP-BY-STEP demo (inside Three calm ideas)
   ============================================================ */
.ms-stepper{
  margin-top: 56px;
  background: var(--ms-surface);
  border: 1px solid var(--ms-line);
  border-radius: 18px;
  padding: 28px 28px 24px;
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
}
@media (min-width: 760px){
  .ms-stepper{ grid-template-columns: 220px minmax(0, 1fr); align-items: start; gap: 36px; }
}
.ms-stepper-eyebrow{
  font-family: var(--f-mono);
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ms-ink-4);
  margin: 0 0 14px;
}
.ms-stepper-list{
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.ms-stepper-list li{
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-radius: 10px;
  font-size: 14px;
  color: var(--ms-ink-3);
  transition:
    background var(--ms-dur-quick) var(--ms-ease),
    color var(--ms-dur-quick) var(--ms-ease);
  cursor: pointer;
  letter-spacing: -0.005em;
}
.ms-stepper-list li:hover{ background: var(--ms-surface-cool); color: var(--ms-ink); }
.ms-stepper-list li[aria-current="true"]{
  background: var(--ms-accent-tint);
  color: var(--ms-ink);
}
.ms-stepper-list li[aria-current="true"] .ms-stepper-num{
  background: var(--ms-accent);
  color: #fff;
}
.ms-stepper-num{
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--ms-surface-cool);
  color: var(--ms-ink-3);
  font-family: var(--f-mono);
  font-size: 12px;
  font-weight: 500;
  flex-shrink: 0;
  transition: background var(--ms-dur-quick) var(--ms-ease), color var(--ms-dur-quick) var(--ms-ease);
}
.ms-stepper-stage{
  background: var(--ms-bg);
  border: 1px solid var(--ms-line);
  border-radius: var(--r-md);
  padding: 24px;
  min-height: 220px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  position: relative;
  overflow: hidden;
}
.ms-stepper-stage-title{
  font-size: 18px;
  font-weight: 600;
  letter-spacing: -0.012em;
  color: var(--ms-ink);
  margin: 0;
}
.ms-stepper-stage-body{
  font-size: 14px;
  line-height: 1.6;
  color: var(--ms-ink-2);
  letter-spacing: -0.005em;
}
.ms-stepper-stage-body code{
  font-family: var(--f-mono);
  font-size: 13px;
  background: var(--ms-surface-cool);
  border-radius: 4px;
  padding: 2px 6px;
}
.ms-stepper-tool-line{
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--f-mono);
  font-size: 13px;
  color: var(--ms-ink-2);
  padding: 8px 12px;
  background: var(--ms-surface-cool);
  border-radius: 8px;
}
.ms-stepper-tool-line::before{
  content: "→";
  color: var(--ms-accent);
}
.ms-stepper-approve-row{
  display: flex;
  gap: 10px;
  margin-top: auto;
  padding-top: 12px;
}
.ms-stepper-controls{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-top: 4px;
}
.ms-stepper-progress{
  display: flex;
  gap: 6px;
}
.ms-stepper-dot{
  width: 24px;
  height: 4px;
  border-radius: var(--r-pill);
  background: var(--ms-surface-cool);
  transition: background var(--ms-dur-quick) var(--ms-ease);
}
.ms-stepper-dot.is-active{ background: var(--ms-accent); }
.ms-stepper-next{
  padding: 8px 16px;
  border-radius: var(--r-pill);
  background: var(--ms-ink);
  color: #fff;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  border: 0;
  cursor: pointer;
  transition: background var(--ms-dur-quick) var(--ms-ease), transform 120ms var(--ms-ease);
}
.ms-stepper-next:hover{ background: var(--ms-ink-2); }
.ms-stepper-next:active{ transform: scale(0.97); }

/* fade between steps */
.ms-stepper-stage > .ms-stepper-step{
  display: none;
}
.ms-stepper-stage > .ms-stepper-step.is-current{
  display: flex;
  flex-direction: column;
  gap: 12px;
  height: 100%;
  animation: ms-step-in 320ms var(--ms-ease-out);
}
@keyframes ms-step-in{
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* ============================================================
   DASHBOARD v2 (tab-based, playground-style center column)
   ============================================================ */
body.mystyle-dashboard #app{ padding: 0; }

/* Dashboard mode: hide top nav — sidebar is the only navigation */
body.mystyle-dashboard .nav{ display: none; }

/* Sidebar fills full viewport; shell adapts to content so footer is reachable */
body.mystyle-dashboard .shell{ min-height: 0; }
body.mystyle-dashboard .side{
  min-height: 100vh;
  height: auto;
  position: sticky;
  top: 0;
}

/* Sidebar collapseable in dashboard — toggle button always visible */
body.mystyle-dashboard .side-toggle{ display: none; }
.ms-dash-v2{ background: var(--ms-bg); }
.ms-dash-v2-inner{ max-width: 800px; margin: 0 auto; padding: 0; }

/* Greeting */
.ms-dash-v2-head{ margin-bottom: 40px; }
.ms-dash-v2-head .ms-eyebrow{ margin-bottom: 12px; }
.ms-dash-v2-greeting{
  font-size: clamp(32px, 3.8vw, 48px);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.08;
  color: var(--ms-ink);
  margin: 0 0 8px;
}
.ms-dash-v2-sub{
  font-size: 16px;
  color: var(--ms-ink-3);
  margin: 0;
  letter-spacing: -0.005em;
}

/* Tab nav */
.ms-dash-tabs{
  display: flex;
  gap: 0;
  border-bottom: 1px solid var(--ms-line);
  margin-bottom: 32px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.ms-dash-tabs::-webkit-scrollbar{ display: none; }
.ms-dash-tab{
  font: inherit;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -0.005em;
  color: var(--ms-ink-3);
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 12px 20px;
  cursor: pointer;
  white-space: nowrap;
  margin-bottom: -1px;
  transition:
    color var(--ms-dur-quick) var(--ms-ease),
    border-color var(--ms-dur-quick) var(--ms-ease);
}
.ms-dash-tab:hover{ color: var(--ms-ink); }
.ms-dash-tab.is-active{
  color: var(--ms-accent);
  border-bottom-color: var(--ms-accent);
}

/* Panes */
.ms-dash-pane{ display: none; }
.ms-dash-pane.is-active{ display: block; }

/* Agent picker pills */
.ms-dash-agent-pick{
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 16px;
}
.ms-dash-agent-pill{
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: -0.005em;
  color: var(--ms-ink-2);
  background: var(--ms-surface);
  border: 1px solid var(--ms-line);
  border-radius: var(--r-pill);
  padding: 6px 14px;
  cursor: pointer;
  white-space: nowrap;
  transition:
    background var(--ms-dur-quick) var(--ms-ease),
    border-color var(--ms-dur-quick) var(--ms-ease),
    color var(--ms-dur-quick) var(--ms-ease);
}
.ms-dash-agent-pill:hover{
  background: var(--ms-surface-cool);
  border-color: var(--ms-ink-3);
  color: var(--ms-ink);
}
.ms-dash-agent-pill[aria-pressed="true"]{
  background: var(--ms-accent-tint);
  border-color: var(--ms-accent);
  color: var(--ms-accent);
}

/* Agent info line */
.ms-dash-agent-info{
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 14px;
  flex-wrap: wrap;
}
.ms-dash-agent-name{
  font-size: 15px;
  font-weight: 600;
  letter-spacing: -0.007em;
  color: var(--ms-ink);
}
.ms-dash-agent-model{
  font-family: var(--f-mono);
  font-size: 12px;
  color: var(--ms-ink-4);
  letter-spacing: 0.02em;
}

/* Quota inline in foot */
.ms-dash-quota-inline{
  display: flex;
  align-items: center;
  gap: 12px;
}

/* Audit pane CTA */
.ms-dash-audit-cta{
  padding: 48px 0;
  max-width: 560px;
}
.ms-dash-audit-cta .ms-sub{ margin: 16px 0 32px; }

/* Agents / Skills pane — list rows */
.ms-dash-card-head{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}
.ms-dash-card-title{
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.012em;
  color: var(--ms-ink);
  margin: 0;
}
.ms-dash-card-link{
  font-size: 13px;
  color: var(--ms-accent);
  text-decoration: none;
  font-weight: 500;
}
.ms-dash-card-link:hover{ color: var(--ms-accent-hov); }

.ms-dash-runs{
  display: flex;
  flex-direction: column;
}
.ms-dash-run{
  display: grid;
  grid-template-columns: 36px 1fr auto;
  gap: 16px;
  align-items: center;
  padding: 14px 8px;
  border-bottom: 1px solid var(--ms-line-soft);
  text-decoration: none;
  color: inherit;
  border-radius: 10px;
  transition: background var(--ms-dur-quick) var(--ms-ease);
}
.ms-dash-run:last-child{ border-bottom: 0; }
.ms-dash-run:hover{ background: var(--ms-surface-cool); }
.ms-dash-run-icon{
  width: 36px; height: 36px;
  border-radius: 10px;
  background: var(--ms-surface-cool);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--ms-ink-3);
  font-family: var(--f-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.02em;
  flex-shrink: 0;
}
.ms-dash-run-text h4{
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -0.005em;
  color: var(--ms-ink);
  margin: 0;
}
.ms-dash-run-text p{
  font-size: 12px;
  color: var(--ms-ink-3);
  margin: 2px 0 0;
}
.ms-dash-run-side{
  font-size: 12px;
  color: var(--ms-ink-3);
  font-family: var(--f-mono);
  letter-spacing: 0.02em;
  text-align: right;
}

/* ============================================================
   Editor's Choice — full-width dark/light split background
   ============================================================ */
.ms-ec-section{
  padding-top: clamp(64px, 6vw, 96px);
  padding-bottom: 0;
}
.ms-ec-section .ms-inner{ margin-bottom: 24px; }

/* Full-width, no border-radius — it's a section background */
.ms-ec-split{
  display: grid;
  grid-template-columns: 42% 58%;
  min-height: 500px;
  cursor: pointer;
}

/* ---- left dark panel ---- */
.ms-ec-split-left{
  background: #0a0b0d;
  padding: clamp(48px, 6vw, 80px) clamp(40px, 5vw, 72px);
  display: flex;
  flex-direction: column;
  gap: 22px;
  justify-content: center;
}

/* FEATURED header */
.ms-ec-feat-header{
  display: flex;
  align-items: center;
  gap: 16px;
}
.ms-ec-feat-line{
  width: 32px;
  height: 1.5px;
  background: rgba(229,232,236,0.5);
  transition: width 0.7s cubic-bezier(0.16,1,0.3,1);
}
.ms-ec-split:hover .ms-ec-feat-line{ width: 56px; }
.ms-ec-feat-label{
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.28em;
  color: rgba(229,232,236,0.5);
  text-transform: uppercase;
  transition: letter-spacing 0.7s cubic-bezier(0.16,1,0.3,1), color 0.5s ease;
}
.ms-ec-split:hover .ms-ec-feat-label{
  letter-spacing: 0.34em;
  color: rgba(229,232,236,0.75);
}

/* Split title — two animated lines */
.ms-ec-split-title{
  font-size: clamp(36px, 4vw, 60px);
  font-weight: 400;
  letter-spacing: -0.022em;
  line-height: 1.06;
  color: var(--ink-text);
  margin: 4px 0 0;
  display: flex;
  flex-direction: column;
}
.ms-ec-tl1{
  display: block;
  transition: transform 0.7s cubic-bezier(0.16,1,0.3,1);
}
.ms-ec-tl2{
  display: block;
  transition: transform 0.7s cubic-bezier(0.16,1,0.3,1) 40ms;
}
.ms-ec-split:hover .ms-ec-tl1{ transform: translateY(-3px); }
.ms-ec-split:hover .ms-ec-tl2{ transform: translateX(14px); }

.ms-ec-split-desc{
  font-size: 16px;
  color: rgba(229,232,236,0.45);
  line-height: 1.6;
  max-width: 30ch;
  transition: color 0.5s ease, transform 0.7s cubic-bezier(0.16,1,0.3,1);
}
.ms-ec-split:hover .ms-ec-split-desc{
  color: rgba(229,232,236,0.65);
  transform: translateY(-3px);
}

/* EXPLORE button */
.ms-ec-explore{
  display: inline-flex;
  align-items: center;
  gap: 18px;
  margin-top: 4px;
  text-decoration: none;
}
.ms-ec-explore-icon{
  width: 56px;
  height: 56px;
  border-radius: 50%;
  border: 1.5px solid rgba(229,232,236,0.25);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ink-text);
  font-size: 20px;
  flex-shrink: 0;
  transition:
    background 0.45s cubic-bezier(0.16,1,0.3,1),
    border-color 0.45s ease,
    color 0.45s ease,
    transform 0.5s cubic-bezier(0.16,1,0.3,1),
    box-shadow 0.45s ease;
}
.ms-ec-split:hover .ms-ec-explore-icon{
  background: var(--ink-text);
  color: #0a0b0d;
  border-color: var(--ink-text);
  transform: scale(1.1) rotate(6deg);
  box-shadow: 0 8px 32px rgba(229,232,236,0.18);
}
.ms-ec-explore-text{
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.24em;
  color: rgba(229,232,236,0.4);
  text-transform: uppercase;
  transition: opacity 0.5s ease, transform 0.6s cubic-bezier(0.16,1,0.3,1), letter-spacing 0.5s ease;
  transform: translateX(-10px);
  opacity: 0.4;
}
.ms-ec-split:hover .ms-ec-explore-text{
  opacity: 0.9;
  transform: translateX(0);
  letter-spacing: 0.28em;
}

/* ---- right image panel ---- */
.ms-ec-split-right{
  position: relative;
  overflow: hidden;
  background: #ebebeb;
}
.ms-ec-split-right img{
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  transition: transform 0.85s cubic-bezier(0.16,1,0.3,1);
  animation: ms-img-reveal 900ms var(--ms-ease) both;
}
.ms-ec-split:hover .ms-ec-split-right img{
  transform: scale(1.05);
}

@media (max-width: 860px){
  .ms-ec-split{ grid-template-columns: 1fr; }
  .ms-ec-split-right{ min-height: 320px; }
  .ms-ec-split-left{ padding: 48px 32px; }
}

/* ============================================================
   Spotlight glow — ported from React spotlight-card (vanilla)
   Works on any element with [data-glow]. Pointer coords set via
   JS as --x/--y/--xp/--yp CSS custom properties.
   ============================================================ */
[data-glow]{
  --glow-base:    220;
  --glow-spread:  200;
  --glow-radius:  18;
  --glow-border:  2;
  --glow-size:    240;
  --glow-border-size:    calc(var(--glow-border) * 1px);
  --glow-spotlight-size: calc(var(--glow-size) * 1px);
  --glow-hue: calc(var(--glow-base) + (var(--xp, 0) * var(--glow-spread)));
  background-image: radial-gradient(
    var(--glow-spotlight-size) var(--glow-spotlight-size) at
    calc(var(--x, -9999) * 1px) calc(var(--y, -9999) * 1px),
    hsl(var(--glow-hue) 100% 70% / 0.07),
    transparent
  );
  background-attachment: fixed;
  position: relative;
  overflow: visible;
}

[data-glow]::before,
[data-glow]::after{
  pointer-events: none;
  content: "";
  position: absolute;
  inset: calc(var(--glow-border-size) * -1);
  border: var(--glow-border-size) solid transparent;
  border-radius: calc(var(--glow-radius) * 1px);
  background-attachment: fixed;
  background-size: calc(100% + (2 * var(--glow-border-size))) calc(100% + (2 * var(--glow-border-size)));
  background-repeat: no-repeat;
  background-position: 50% 50%;
  mask: linear-gradient(transparent, transparent), linear-gradient(white, white);
  mask-clip: padding-box, border-box;
  mask-composite: intersect;
}

[data-glow]::before{
  background-image: radial-gradient(
    calc(var(--glow-spotlight-size) * 0.75) calc(var(--glow-spotlight-size) * 0.75) at
    calc(var(--x, -9999) * 1px) calc(var(--y, -9999) * 1px),
    hsl(var(--glow-hue) 100% 50% / 0.9),
    transparent 100%
  );
  filter: brightness(2);
}

[data-glow]::after{
  background-image: radial-gradient(
    calc(var(--glow-spotlight-size) * 0.5) calc(var(--glow-spotlight-size) * 0.5) at
    calc(var(--x, -9999) * 1px) calc(var(--y, -9999) * 1px),
    hsl(0 100% 100% / 0.5),
    transparent 100%
  );
}

@media (prefers-reduced-motion: reduce){
  body.mystyle [data-glow]::before,
  body.mystyle [data-glow]::after{ display: none; }
}

/* ============================================================
   Sweep button — ported from animated-button (vanilla)
   Wrap button text in <span> to keep it above the blob.
   ============================================================ */
.ms-btn--sweep{
  position: relative;
  overflow: hidden;
  background: #fff;
  color: var(--ms-ink);
  border: 1px solid var(--ms-line);
}
.ms-btn--sweep::before{
  content: "";
  position: absolute;
  bottom: 0; left: 0;
  width: 12rem; height: 12rem;
  border-radius: 50%;
  background: var(--ms-accent);
  transform: translateX(-100%) translateY(100%) rotate(-40deg);
  transition: transform 500ms ease-out, margin 500ms ease-out;
  margin-bottom: 2.25rem;
  margin-left: 2.25rem;
  pointer-events: none;
  z-index: 0;
}
.ms-btn--sweep:hover::before{
  transform: translateX(0) translateY(0) rotate(-40deg);
  margin-bottom: 8rem;
  margin-left: 0;
}
.ms-btn--sweep span{
  position: relative;
  z-index: 1;
}
.ms-btn--sweep:hover{
  color: #fff;
  background: #fff;
}
/* hero CTA — keep the primary blue until hover sweeps in */
.ms-btn--primary.ms-btn--sweep{
  background: var(--ms-accent);
  color: #fff;
  border-color: var(--ms-accent);
}
.ms-btn--primary.ms-btn--sweep::before{ background: #fff; }
.ms-btn--primary.ms-btn--sweep:hover{ color: var(--ms-accent); background: var(--ms-accent); }
/* Owner reference 2026-10-11: the hero's audit button is ink, not accent. The sweep's white
   blob still brings the hover; the label turns ink over it. Focus keeps the ink fill. */
.ms-hero-form .ms-btn--primary.ms-btn--sweep{ background: var(--ink); border-color: var(--ink); color: var(--ink-text); }
.ms-hero-form .ms-btn--primary.ms-btn--sweep:hover{ color: var(--ink); background: var(--ink); }
.ms-hero-form .ms-btn--primary.ms-btn--sweep:focus-visible:not(:hover){ color: var(--ink-text); }

@media (prefers-reduced-motion: reduce){
  body.mystyle .ms-btn--sweep::before{ transition: none; }
}

/* ============================================================
   Guide section (home) — single horizontal card + black CTA
   Lives inside #ms-sec-guide in mystyle.js
   ============================================================ */
body.mystyle .ms-guide-section{ background: var(--ms-bg); }
body.mystyle .ms-guide-card{
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
  gap: 0;
  margin-top: 48px;
  background: #fff;
  border: 1px solid var(--ms-line);
  border-radius: 18px;
  overflow: hidden;
}
body.mystyle .ms-guide-media{
  position: relative;
  aspect-ratio: 4 / 3;
  background: #0a0a0a;
}
body.mystyle .ms-guide-media img{
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
body.mystyle .ms-guide-body{
  padding: 44px 44px 44px 44px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
body.mystyle .ms-guide-meta{
  display: flex; align-items: center; gap: 14px;
  margin-bottom: 16px;
}
body.mystyle .ms-guide-level{
  display: inline-block;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.10em;
  color: var(--ms-accent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  padding: 5px 12px;
  border-radius: 999px;
}
body.mystyle .ms-guide-min{ font-size: 13px; color: var(--ms-ink-2); }
body.mystyle .ms-guide-title{
  font-family: var(--ms-f-display, inherit);
  font-size: 28px;
  line-height: 1.18;
  letter-spacing: -0.018em;
  font-weight: 600;
  color: var(--ms-ink);
  margin: 0 0 14px;
}
body.mystyle .ms-guide-desc{
  font-size: 16px;
  line-height: 1.55;
  color: var(--ms-ink-2);
  margin: 0 0 20px;
  max-width: 52ch;
}
body.mystyle .ms-guide-tags{
  display: flex; flex-wrap: wrap; gap: 8px;
  margin-bottom: 22px;
}
body.mystyle .ms-guide-tag{
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--ms-ink-2);
  background: #f4f4f5;
  padding: 5px 11px;
  border-radius: 999px;
}
body.mystyle .ms-guide-readlink{
  font-size: 15px;
  font-weight: 500;
  color: var(--ms-accent);
  text-decoration: none;
}
body.mystyle .ms-guide-readlink:hover{ text-decoration: underline; }

body.mystyle .ms-guide-cta-wrap{
  margin-top: 36px;
  text-align: center;
}
body.mystyle .ms-btn--black{
  background: #111;
  color: #fff;
  border-color: #111;
  font-size: 16px;
  padding: 16px 30px;
}
body.mystyle .ms-btn--black:hover{ background: #000; border-color: #000; }

@media (max-width: 820px){
  body.mystyle .ms-guide-card{
    grid-template-columns: 1fr;
  }
  body.mystyle .ms-guide-media{ aspect-ratio: 16 / 9; }
  body.mystyle .ms-guide-body{ padding: 28px; }
  body.mystyle .ms-guide-title{ font-size: 22px; }
}

/* ============================================================
   Guide hub (/guide dashboard mode) — video-first card grid
   Used by viewGuideHub in app.js. Vanilla, single-file SPA.
   ============================================================ */
.btn.btn-black{
  background:#111;
  color:#fff;
  border:1px solid #111;
}
.btn.btn-black:hover{ background:#000; border-color:#000; }

/* ============================================================
   Floating charcoal nav pill (WU-1, blue/white reskin)
   Opt-in via .nav.nav--floating (kept gated so .nav defaults intact)
   ============================================================ */
/* HOME / scrollview bar — ONE floating charcoal pill inset from the viewport edges,
   per final_mockup/final_landing_page.png. It is fixed, not full-bleed: the pale hero
   ground runs behind and around it on all four sides. --nav-inset-y/-x are local to
   this block; every offset that used to derive from a flush bar adds it back below.
   The mockup's bar is a flatter rounded rectangle, not a capsule: it sits tight to the
   top edge and is inset further at the sides, so y and x insets differ. 20px radius on
   a 58px box reads as "rounded rectangle"; the Playground button inside stays a pill. */
.nav.nav--floating{
  --nav-inset-y: 8px;
  --nav-inset-x: 32px;
  position: fixed;
  top: var(--nav-inset-y);
  left: var(--nav-inset-x);
  right: var(--nav-inset-x);
  width: auto;
  /* Local height only — --dash-bar-h (60px) still drives the dashboard shell offsets. */
  height: 58px;
  background: var(--ink);
  border: none;
  border-radius: 20px;
  box-shadow: none;
  z-index: 101;
  transition: transform .35s cubic-bezier(.22,.61,.36,1);
  will-change: transform;
}
/* scrollview <-> side-panel switch is instant — top bar stays static (no nav slide). */
@media (prefers-reduced-motion: reduce){
  .nav.nav--floating{ transition: none; }
}

/* Home burger toggles ONLY the middle (nav buttons + Try Free agent). Kept in flow
   (opacity + transform only) so the bar doesn't reflow when hidden. */
/* Brand sits hard left, links follow it, the right cluster is pushed to the far edge
   by margin-left:auto — NOT space-between, which would centre the link row. */
.nav.nav--floating .nav-inner{
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 16px;
  height: 58px;
  padding: 0 12px 0 14px;
  max-width: 100%;
}
/* left group: burger + brand */
.nav.nav--floating .nav-brand-group{
  display: inline-flex; align-items: center; gap: 9px;
}
.nav.nav--floating .brand{
  display: inline-flex; align-items: center; gap: 10px;
  background: none; border-radius: 0; padding: 0; height: auto;
  color: var(--ink-text); text-decoration: none;
  font-weight: 600; font-size: 15px; letter-spacing: -0.01em;
}
.nav.nav--floating .brand .brand-mark{ width: 26px; height: 26px; border-radius: 6px; }
/* The 3-line burger's rules lived here. It was removed from index.html; sidebar collapsing
   is the .side-collapse-fab circle on the sidebar seam. */

/* bell + avatar — dark-on-light, identical to the side-panel .dash-bar */
.nav-bell{
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px;
  border: none; border-radius: 50%;
  background: transparent; color: var(--text-2);
  cursor: pointer;
  transition: background-color .2s ease;
}
.nav-bell:hover{ background: var(--bg-2); }
/* Phone opener for the all-sections sheet — same 38px ghost circle as the bell, so the
   pill's right-hand group stays one rhythm. Off above 833px, where the link row is back
   and there is nothing for it to open onto. */
.nav-menu{
  display: none;
  align-items: center; justify-content: center;
  width: 38px; height: 38px;
  border: none; border-radius: 50%;
  background: transparent; color: var(--text-2);
  cursor: pointer;
  transition: background-color .2s ease;
}
.nav-menu:hover{ background: var(--bg-2); }
.nav-menu:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
.nav-avatar{
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px;
  border-radius: 50%;
  background: var(--bg-2);
  color: var(--text-2);
  border: 1px solid var(--border);
  overflow: hidden;
  text-decoration: none;
  transition: background-color .2s ease;
}
.nav-avatar:hover{ background: var(--bg-3); }
.nav-avatar img{ width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
/* when a photo is present, hide the signed-out fallback icon */
.nav-avatar img + .nav-avatar-fallback,
.nav-avatar:has(img) .nav-avatar-fallback{ display: none; }

/* link group: Audit / Learn / Tools / Agents / Skills / Weekly / About — plain white
   links on the RIGHT, just before the bell (owner 2026-10-12: next to the brand they read
   as part of the logo). Hover changes opacity ONLY (one quiet affordance, no lift). */
.nav.nav--floating .nav-links{
  position: relative;       /* containing block for the dropdown panels */
  display: flex;
  align-items: center;
  gap: 30px;
  margin-left: auto;
}
/* Pure white at rest per the mockup — the links are primary, not secondary. The one
   affordance is the hover dim; .active needs no rule (app.css:212 already sets #fff,
   which is what rest is now), so hovering an active link still reads. */
.nav.nav--floating .nav-btn{
  color: var(--ink-text);
  opacity: 1;
  height: auto;
  font-weight: 400;
  font-size: 15px;
  transition: opacity var(--dur-quick) var(--ease);
}
.nav.nav--floating .nav-btn:hover{
  color: var(--ink-text);
  opacity: .72;
}
.nav.nav--floating .nav-btn:focus-visible{
  outline: 2px solid var(--ink-text);
  outline-offset: 3px;
  border-radius: var(--r-xs);
}

/* right group: Playground + bell + avatar, pinned to the pill's right edge */
.nav.nav--floating .nav-right{
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: 28px;
}
/* Playground — white-outline pill on the charcoal (mockup) */
.nav.nav--floating .nav-right .btn-ghost{
  background: transparent;
  color: var(--ink-text);
  border: 1px solid var(--ink-border-2);
  height: 36px;
  padding: 0 18px;
  border-radius: var(--r-pill);
  font-size: 14px;
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  transition: border-color var(--dur-quick) var(--ease);
}
.nav.nav--floating .nav-right .btn-ghost:hover{
  background: transparent;
  border-color: var(--ink-text);
}
.nav.nav--floating .nav-right .btn-ghost:focus-visible{
  outline: 2px solid var(--ink-text);
  outline-offset: 2px;
}

/* Push page content below the fixed persistent nav.
   Home (mystyle) hero owns its own top spacing → 0. Other explore pages (dashShell-wrapped,
   e.g. /about) need to clear the nav now that there's no in-flow bar inside #app. */
body.explore-mode.mystyle main#app{ padding-top: 0; }
/* The pill is inset 16px from the top and floats free, so clearing it costs the bar
   height PLUS that inset PLUS an equal gap below — otherwise content tucks under it. */
body.explore-mode:not(.mystyle) main#app{ padding-top: calc(var(--dash-bar-h) + 32px); }

/* Explore mode → hide sidebar, show floating nav, expand main content */
body.explore-mode .side{ display: none !important; }
body.explore-mode .shell{ grid-template-columns: 1fr !important; }
body.explore-mode .main{ max-width: 100% !important; }

/* Single persistent header: the floating nav stays in BOTH modes (no second .dash-bar swap).
   The burger toggles modes; entering side-panel mode the nav links + CTA slide up and a
   breadcrumb appears, while the sidebar slides in from the left. */
body.dashboard-mode #nav-backdrop{ display: none !important; }

/* Breadcrumb slot inside the persistent bar — hidden in top-nav mode, shown in side-panel mode */
.dash-crumbs{ display: none; }
body.dashboard-mode .dash-crumbs{ display: flex; margin-left: 4px; padding-left: 12px; border-left: 1px solid var(--border); }

/* Side-panel mode is SIDEBAR-FIRST: the top bar goes away entirely and the rail owns
   navigation, notifications, and the account menu.

   The bar is hidden, never removed from the DOM. #dash-crumbs lives inside it and is the
   staging area dashShell() writes the breadcrumb trail into before dash-hero.js copies it
   into the floating row over the hero — delete the nav and every breadcrumb silently
   empties. Hiding also keeps auth.js's .nav-bell/.nav-avatar lookups resolving and leaves
   the prerendered HTML of all 215 routes structurally identical.

   Zeroing --dash-bar-h is the whole offset change: #app's padding-top, .side's top/height
   and .shell's height all derive from it (see the block below), so the hero panel lands
   flush at y=0 without touching any of the dependent rules.

   The predecessor of these two lines slid .nav-links and .nav-cta-ghost offscreen with a
   transform to fake an empty bar. With the bar itself gone that hack — and the transition
   that existed only to animate it — has nothing left to do. */
body.dashboard-mode{ --dash-bar-h: 0px; }
body.dashboard-mode .nav.nav--floating{ display: none; }

/* Flat full-width top bar on every side-panel page */
:root{ --dash-bar-h: 60px; --subhead-h: 184px; }
.dash-bar{
  position: sticky; top: 0; z-index: 41;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  height: var(--dash-bar-h);
  padding: 0 20px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.dash-bar-left{ display: flex; align-items: center; gap: 12px; min-width: 0; }
.dash-bar-right{ display: flex; align-items: center; gap: 6px; }
/* plain 3-line hamburger — no circle — toggles sidebar shrink/expand */
.dash-burger{
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  width: 36px; height: 36px; padding: 0; border-radius: 8px;
  background: transparent; border: 0; cursor: pointer;
  transition: background .12s ease;
}
.dash-burger span{ width: 18px; height: 2px; background: var(--text); border-radius: 2px; }
.dash-burger:hover{ background: var(--bg-2); }
.dash-brand{ display: inline-flex; align-items: center; gap: 9px; text-decoration: none; color: var(--text); font-weight: 600; font-size: 15px; letter-spacing: -0.01em; }
/* Match the home nav logo (26px) exactly so the bar shows no logo-size jump on toggle. */
.dash-brand .brand-mark{ width: 26px; height: 26px; border-radius: 6px; }
.dash-bar .crumbs{ margin-left: 8px; padding-left: 12px; border-left: 1px solid var(--border); }
/* bell + avatar rendered dark on the light bar */
.nav-bell--dark{ color: var(--text-2); width: 38px; height: 38px; }
.nav-bell--dark:hover{ background: var(--bg-2); }
.nav-avatar--dark{ background: var(--bg-2); color: var(--text-2); width: 38px; height: 38px; border: 1px solid var(--border); }
.nav-avatar--dark:hover{ background: var(--bg-3); }

/* Dashboard mode → sidebar + content start just under the flat bar */
body.dashboard-mode .shell{ min-height: 0; }
/* WU-4: the rail is a panel FLOATING on the --bg sheet, not a flush column. Its height now
   comes from the grid row (.shell is a fixed-height grid), so the viewport calc that used to
   size it is gone: with .shell padded by --shell-inset, calc(100vh - --dash-bar-h) overshoots
   the track by exactly the vertical inset and the panel's bottom corners fall off-screen. */
body.dashboard-mode .side{ top: 0; height: 100%; min-height: 0; }

/* Dashboard mode → fixed app-shell: top bar + sidebar + sub-header stay put,
   only the inner content region scrolls. Scoped to body.dashboard-mode so the
   home/explore scroll experience is untouched. */
body.dashboard-mode{ overflow: clip; height: 100vh; }
/* Sweep 2026-09-13: clip, not hidden (the house rule). hidden left the body programmatically
   scrollable, so focusing anything below the fold (Tab past the footer, iOS next-field, a
   hash, any scrollIntoView) slid the whole fixed shell off-screen for the rest of the
   session. The newsletter strip is a body child below the 100vh box, unreachable on a hub
   route by any scroll gesture (the hubs carry the footer; /weekly its own subscribe strip),
   so it is hidden there instead of standing as an invisible focus trap. */
body.dashboard-mode #nl-strip{ display: none; }
/* padding-top clears the fixed persistent nav (no in-flow bar inside #app anymore) */
body.dashboard-mode #app{ height: 100vh; overflow: clip; padding-top: var(--dash-bar-h); }
/* WU-4: one pale sheet under everything ("bg remains same throughout the scrollview and
   sidebar panel"), with the rail and the content column floating on it. The inset is grid
   padding + gap rather than margins on the two children, so the 60px collapsed track keeps
   measuring the panel and nothing about the collapse transition has to know about it. */
body.dashboard-mode .shell{
  --shell-inset: 12px;
  height: calc(100vh - var(--dash-bar-h));
  padding: var(--shell-inset);
  gap: var(--shell-inset);
  background: var(--bg);
}
/* Sidebar slides in from the left when entering side-panel mode (one-shot, only on enter).
   Pure transform slide (no opacity fade) on a promoted compositor layer (will-change) so the
   glide stays smooth and jank-free instead of muddily fading the dark panel over the content. */
@keyframes dashSideIn{ from{ transform: translateX(-100%); } to{ transform: translateX(0); } }
body.entering-dash.dashboard-mode .side{ animation: dashSideIn .42s cubic-bezier(.22,.61,.36,1) both; will-change: transform; }
body.dashboard-mode .main{ height: 100%; min-height: 0; display: flex; flex-direction: column; overflow: hidden; padding: 0; max-width: none; margin-inline: 0; }
body.dashboard-mode .main > .gd-wrap{ display: flex; flex-direction: column; height: 100%; min-height: 0; overflow: hidden; }
/* fixed sub-header that holds the pill tabs — in-flow, never overlaps content */
/* 24px, not 40px: this is the ONE dashboard gutter. It used to be 40px here and 24px inside
   .gd-wrap (:2983 below), which put the content column at x=224 on most routes and x=240 on
   /weekly, /community, /top-50, /free-agents (both retired), /my-roadmap, /skill/:id and /mcp/:id — a 16px
   step you see as the page jumps sideways when you move between them. */
/* WU-4: the toolbar band is part of the --bg sheet now. The white pill inside it (the tab
   strip) is what the mockups draw, so the band's own surface fill and bottom rule went —
   two stacked white boxes with a hairline between them is not the shape in comapre_01.png. */
body.dashboard-mode .dash-subhead{ flex: 0 0 auto; display: flex; align-items: center; gap: 16px; padding: 14px 24px 4px; border-bottom: 0; background: transparent; }
/* dash-hero.js can sweep every child out of the subhead, leaving a bare band: 16px of padding,
   with its fill and rule now zeroed, only that padding paints. Hide it — but the node MUST stay in the DOM,
   because :2971's `.gd-wrap:has(.dash-subhead)` is what grants those routes `max-width: none`.
   Removing it instead shrinks them to the 1180px base. A class, not `[hidden]`: the rule above
   sets `display:flex`, which outranks the UA stylesheet. */
body.dashboard-mode .dash-subhead.is-empty{ display: none; }
/* the only scrollable region; data-lenis-prevent makes __msScroll defer to it */
body.dashboard-mode .dash-scroll{ flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 14px 24px 32px; max-width: none; margin-inline: 0; width: 100%; }
/* The static footer is re-homed into the scroller here (dash-hero.js placeFooter) — the body
   is overflow:hidden above, so left beside #app it is simply unreachable.
   🔴 It sits INSIDE the scroller's gutter on purpose. The obvious full-bleed version
   (`margin-inline: -40px`) was measured and reverted: this scroller's horizontal padding is
   not one value — .gd-wrap variants override it to 24px and 0 — so a fixed negative margin
   overshot by 16px per side and put a horizontal scrollbar inside .dash-scroll on /agents,
   /tools, /guide, /dashboard, /settings, /news and /roadmap. No CSS can read the padding it
   needs to cancel, so the band is inset and the rounded top edge makes that read as
   deliberate. margin-bottom stays 0 for the same reason: the scroller's bottom padding is
   also per-variant, and a negative one would clip the last rows off a scroll container. */
body.dashboard-mode .dash-scroll > .foot{
  margin: 56px 0 0; padding: 40px 0 32px; border-radius: 12px 12px 0 0;
}
/* dash-hero.js placeFooter() collects the page's own content into this one block so the
   footer that follows it starts BELOW the fold — the owner's rule is that the footer never
   reads as part of a hub, it arrives when you scroll past the content. The scroller is a
   flex item with a definite height inside .main / .gd-wrap (both `display:flex;
   flex-direction:column; height:100%`), so the percentage resolves against its content box
   and a short page (/settings, /trending's empty state, /jobs) still fills the first
   viewport. A plain block wrapper, not a flex/BFC one: margins still collapse through it,
   so no hub's existing spacing moves. */
body.dashboard-mode .dash-scroll > .dash-body{ min-height: 100%; }
/* pin Settings to the bottom of the sidebar; groups scroll above it */
body.dashboard-mode .side{ display: flex; flex-direction: column; }
body.dashboard-mode .side-scroll{ flex: 1 1 auto; min-height: 0; overflow-y: auto; }
body.dashboard-mode .side-foot{ flex: 0 0 auto; border-top: 1px solid var(--border); padding-top: 8px; margin-top: 8px; }
/* Sidebar scrollbar hugs the panel border: the aside's 16px right padding moves
   onto the scroller's content (and the foot), so the scrollbar renders flush
   at the panel edge instead of 16px inside it. */
body.dashboard-mode .side{ padding-right: 0; }
body.dashboard-mode .side-scroll{ padding-right: 16px; }
body.dashboard-mode .side-foot{ padding-right: 16px; }
body.side-collapsed.dashboard-mode .side-scroll,
body.side-collapsed.dashboard-mode .side-foot{ padding-right: 8px; }

/* ---- Hover / focus peek on the collapsed rail -------------------------------------
   Pinning stays exactly as it was: .side-collapsed rides grid-template-columns and
   persists to localStorage. The peek layers on top and deliberately does NOT touch the
   grid — the track stays 60px and only the aside itself grows, so it floats over .main
   and nothing behind it reflows. (Owner's call: "float over the content".)

   :focus-within is not optional. Tabbing into a rail whose labels are hidden would
   otherwise move focus through eleven unreadable icons.

   The delay is asymmetric on purpose: 120ms going in, so a pointer merely crossing the
   rail on its way to the content never triggers it, and 0 coming out, so it gets out of
   the way the instant you leave. Declaring the delay on the base rule and overriding it
   on :hover is what makes those two directions differ.

   Group headers (.side-lbl) stay collapsed while peeking. Restoring them would animate
   height 0 -> auto and shove every row vertically mid-peek; keeping the rail's vertical
   rhythm identical to its resting state means the item under your cursor is still the
   item under your cursor. The pinned-open rail shows the groups. */
body.side-collapsed.dashboard-mode .side{
  width: 60px;
  transition: width .2s var(--ease) 0s;
}
/* Width IS the affordance here; a box-shadow on top would be a second one on the same
   hover, and the house rule allows exactly one. The rail's own border-right carries the
   seam. */
body.side-collapsed.dashboard-mode .side:hover,
body.side-collapsed.dashboard-mode .side:focus-within{
  width: var(--side-w);
  transition-delay: .12s;
}
body.side-collapsed.dashboard-mode .side:hover .lbl,
body.side-collapsed.dashboard-mode .side:focus-within .lbl{
  flex: 1 1 auto; width: auto;
  opacity: 1; visibility: visible; transform: none;
  transition: opacity .18s var(--ease) .16s, transform .18s var(--ease) .16s, visibility 0s;
}
/* The seam handle sits at left:60px — dead centre of a peeked panel. Fade it out rather
   than stack it: while the rail is open the Collapse row in .side-foot is the pin control,
   so the circle has nothing to do until the pointer leaves. */
body.side-collapsed.dashboard-mode .side:hover ~ .side-collapse-fab,
body.side-collapsed.dashboard-mode .side:focus-within ~ .side-collapse-fab{
  opacity: 0; pointer-events: none;
}
@media (prefers-reduced-motion: reduce){
  /* The peek still works — it just stops sliding. The 120ms entry delay is kept: it is a
     mis-trigger guard, not motion. */
  body.side-collapsed.dashboard-mode .side,
  body.side-collapsed.dashboard-mode .side .lbl{ transition-duration: 0s; }
}

/* ---- Rail head, Notifications row, Account row -------------------------------------
   The hidden top bar used to carry the brand, the bell and the avatar. All three live
   here now. The two controls keep their .nav-bell / .nav-avatar class names so auth.js
   drives them unchanged — which also drags in the bar's 38px-circle styling, undone below. */

/* Popovers have to escape the rail. .side carries overflow-y:auto from app.css, and any
   overflow other than visible clips BOTH axes, so the account and notification popovers
   would be sheared off at the rail's edge. Dropping it is safe: .side-scroll is the real
   scroller in dashboard mode and .side is a fixed-height flex column that never scrolls.

   z-index has to be here, unconditionally, NOT on the collapsed rule. .side is
   position:sticky, which creates a stacking context whatever its z-index is — so with
   z-index:auto the rail is a z-0 context that paints BEFORE .main, and a popover inside it
   is trapped there no matter that .acct-pop/.bell-pop declare z-index:60. Measured: in the
   default expanded rail, elementFromPoint at the popover's centre returned an .agent-card
   and a real click timed out. This could not happen before, when both controls lived in the
   fixed top bar. */
body.dashboard-mode .side{ overflow: visible; z-index: 8; }
/* ...which in turn puts the rail above the seam handle, so the handle has to clear it or its
   inner half disappears behind the panel it straddles. */
body.dashboard-mode .side-collapse-fab{ z-index: 9; }

body.dashboard-mode .side-head{
  flex: 0 0 auto;
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; margin-bottom: 12px;
  color: #fff; text-decoration: none;
  font-weight: 600; font-size: 15px; letter-spacing: -.01em;
  white-space: nowrap; overflow: hidden;
  transition: color var(--ms-dur-quick) var(--ms-ease-out);
}
body.dashboard-mode .side-head:hover{ color: rgba(255,255,255,.68); }
/* 8px aside padding + 8px head padding + a 28px mark = 44px, so the logo lands on the same
   centre line as every icon below it and does not move when the rail peeks open. */
body.side-collapsed.dashboard-mode .side-head{ padding: 8px; }
body.dashboard-mode .side-head .brand-mark{ flex: 0 0 28px; width: 28px; height: 28px; border-radius: 6px; }

/* Undo the top bar's circle: in the rail this is a full-width row, not a 38px button.
   display and justify-content have to be undone too, not just the dimensions —
   .nav-bell{display:inline-flex; justify-content:center} lives in this file and .side-item
   {display:flex} lives in app.css, which loads FIRST, so at equal specificity the circle's
   layout wins on source order. Left as-is the row shrink-wrapped and centred itself in the
   default pinned-open rail while its three neighbours filled the width. */
body.dashboard-mode .side .nav-bell{
  display: flex; justify-content: flex-start;
  width: 100%; height: auto; border-radius: var(--r-md);
}
/* Anchor the badge to the icon. .nav-bell is position:relative in auth.css, which in a
   200px row would park the badge at the far right end instead of on the bell itself. */
body.dashboard-mode .side .nav-bell .ico{ position: relative; }
body.dashboard-mode .side .nav-bell-badge{ top: -3px; right: -3px; }

/* The avatar is the account row's icon, so it sizes to the icon COLUMN, not to itself: every
   other rail icon is an 18px svg, and the .ico box is shrink-to-fit while the rail is open, so
   a 24px circle made the Account label start 6px right of every other label. 18px puts the
   circle on the same left edge AND the same width as the icons above it; in the collapsed rail
   it also centres properly in the 28px box, where the 24px circle used to sit 3px proud of the
   glyphs beside it. */
body.dashboard-mode .side .nav-avatar{
  flex: 0 0 18px; width: 18px; height: 18px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; overflow: hidden;
  background: rgba(255,255,255,.10); border: .5px solid rgba(255,255,255,.18);
  color: #fff;
}
body.dashboard-mode .side .nav-avatar img{ width: 100%; height: 100%; object-fit: cover; }
body.dashboard-mode .side .nav-avatar-initial{ font: 600 10px/1 var(--f-text); color: #fff; }

/* syncAvatar paints EVERY .nav-avatar now, and its signed-out branch adds .nav-avatar--glow
   — an infinitely pulsing box-shadow halo, which is on the banned list. It was tolerable as
   a one-off on the top bar; on the rail it is also clipped mid-pulse by .side-item's
   overflow:hidden. Kill it on both new copies and leave the top bar's alone. */
body.dashboard-mode .side .nav-avatar--glow,
.dash-tabbar .nav-avatar--glow{ animation: none; }

/* .side-item has never had a focus style. That was survivable while the rail was secondary
   chrome; it is not now that the rail IS the navigation and :focus-within is the documented
   way to reveal its labels. #5aa9ff is the rail's existing light-on-dark marker (below), so
   this adds no new colour, and the inset offset keeps .side-scroll from clipping the ring. */
body.dashboard-mode .side-item:focus-visible,
body.dashboard-mode .side-head:focus-visible{ outline: 2px solid #5aa9ff; outline-offset: -2px; }

/* Popovers open UP and to the RIGHT. Both controls sit at the bottom of the rail, so the
   top bar's "below, right-aligned" placement would push them off the bottom of the screen
   and off the left edge. Anchored on .side (sticky, so it is their containing block) via
   the .side-foot mount point chosen in auth.js. Hovering or focusing an open popover keeps
   the rail peeked, since the popover is a descendant of .side — so it never slides. */
body.dashboard-mode .side .bell-pop,
body.dashboard-mode .side .acct-pop{
  top: auto; bottom: 8px; right: auto; left: calc(100% + 10px);
}
body.dashboard-mode .side .bell-pop{ width: min(340px, calc(100vw - 90px)); max-height: 60vh; }

/* ============================================================
   Mobile: floating bottom bar + "More" sheet  (<=900px, dashboard mode)
   Replaces the rail drawer and its .dash-topbar "Menu" button, which sat top-left — the
   furthest point on a phone screen from a thumb.
   ============================================================ */
.dash-tabbar, .dash-sheet{ display: none; }

@media (max-width: 900px){
  body.dashboard-mode .dash-topbar{ display: none; }
  /* The rail goes away outright on a phone — display:none, not the translateX(-100%) the
     drawer used. Two things depend on that being a real removal rather than an off-screen
     park: (1) a translated element still returns client rects, so auth.js's _one() would
     keep picking the drawer's hidden .nav-bell/.nav-avatar and mount both popovers into a
     panel sitting at x=-280; (2) a translated element stays in the tab order, stranding 21
     focusable rows with no visible focus indicator ahead of the bar. The .dash-topbar
     "Menu" button hidden on the line above was the drawer's only opener anyway, and the
     sheet carries the full DASH_GROUPS list, so nothing becomes unreachable.
     This also makes the peek rules above inert here, which is what we want. */
  body.dashboard-mode .side{ display: none; }

  body.dashboard-mode .dash-tabbar{
    position: fixed; z-index: 70;
    left: 12px; right: 12px;
    bottom: calc(10px + env(safe-area-inset-bottom));
    display: flex; align-items: stretch; gap: 2px;
    padding: 6px;
    border-radius: 20px;
    background: var(--surface);
    border: .5px solid var(--border);
    box-shadow: var(--sh-3);
  }
  .dash-tabbar .tabbar-item{
    flex: 1 1 0; min-width: 0;
    display: inline-flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
    min-height: 44px;              /* thumb-target floor; do not shrink below this */
    padding: 6px 2px;
    border: 0; background: transparent; border-radius: 14px;
    color: var(--text-3); text-decoration: none; cursor: pointer;
    transition: color .14s var(--ease), background-color .14s var(--ease);
  }
  .dash-tabbar .tabbar-item .ico{ display: inline-flex; position: relative; }
  .dash-tabbar .tabbar-item svg{ width: 22px; height: 22px; }
  /* Labelled only when active: six labels will not fit across 375px, and "Notifications"
     alone is wider than a sixth of it. aria-label carries the name for everyone else. */
  .dash-tabbar .tabbar-lbl{
    display: none; font: 600 10.5px/1 var(--f-text);
    max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  /* --accent on --bg-2 computes to 4.31:1, under the 4.5:1 floor for text this size.
     --accent-press is the same hue, already in the palette (app.css:28), and clears it at
     6.38:1. The icon keeps the full accent — a graphic only owes 3:1. */
  .dash-tabbar .tabbar-item.active{ background: var(--bg-2); color: var(--accent-press); }
  .dash-tabbar .tabbar-item.active .ico{ color: var(--accent); }
  .dash-tabbar .tabbar-item.active .tabbar-lbl{ display: block; }
  /* The active item grows so its label fits: "Notifications" and "Dashboard" ellipsised at
     equal widths below 448px / 414px (sweep 2026-09-13). */
  .dash-tabbar .tabbar-item.active{ flex-grow: 1.5; }
  .dash-tabbar .tabbar-item:focus-visible{ outline: 2px solid var(--accent); outline-offset: -2px; }
  .dash-tabbar .nav-bell-badge{ top: -4px; right: -7px; border-color: var(--surface); }
  .dash-tabbar .nav-avatar{
    width: 22px; height: 22px; border-radius: 50%; overflow: hidden;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--bg-2); border: .5px solid var(--border);
    /* Restate colour: .nav-avatar sets color:#fff for the dark rail, and an element's own
       declaration beats what it would inherit from .tabbar-item — so the signed-out person
       glyph (stroke="currentColor") drew white on var(--bg-2), i.e. an empty circle. */
    color: var(--text-3);
  }
  .dash-tabbar .nav-avatar img{ width: 100%; height: 100%; object-fit: cover; }
  .dash-tabbar .nav-avatar svg{ width: 15px; height: 15px; }
  .dash-tabbar .nav-avatar-initial{ font: 600 11px/1 var(--f-text); color: var(--accent); }
  /* The bar floats over the scroller, so the last card needs room to clear it. */
  body.dashboard-mode .dash-scroll{ padding-bottom: calc(78px + env(safe-area-inset-bottom)); }
  /* ...and that padding is exactly what .dash-body's `min-height: 100%` does NOT cover: the
     percentage resolves against the scroller's CONTENT box, so every pixel of bottom padding
     pulls the footer back up into the first viewport. 32px of it desktop-side is swallowed by
     the 56px top margin; 78px here was not — measured 2026-09-07, the footer's first 22px sat
     on screen at 375 on /trending and /notifications. Same 24px clearance as desktop
     (32 + 24 = 56), expressed against the same reserve: 78 + 24 = 102. */
  body.dashboard-mode .dash-scroll > .foot{ margin-top: calc(102px + env(safe-area-inset-bottom)); }
  /* Popovers opened from the bar rise ABOVE it — below would be off-screen. */
  body.dashboard-mode .dash-tabbar .acct-pop,
  body.dashboard-mode .dash-tabbar .bell-pop{ top: auto; bottom: calc(100% + 10px); right: 0; left: auto; }
  body.dashboard-mode .dash-tabbar .bell-pop{ width: min(340px, calc(100vw - 40px)); }

  /* ---- "More" sheet: the full DASH_GROUPS list the six slots are a shortcut over ----
     Home (body.mystyle) emits the same sheet for the nav pill's ☰, so every rule below
     carries both body scopes. The scope cannot simply be dropped: the dark-chrome rail
     rules are `body.dashboard-mode .side-item` (0,2,1), which outranks a bare
     `.dash-sheet .side-item` (0,2,0) and would repaint the hub sheet white-on-white. */
  body.dashboard-mode .dash-sheet,
  body.mystyle .dash-sheet{ display: block; position: fixed; inset: 0; z-index: 80; }
  body.dashboard-mode .dash-sheet[hidden],
  body.mystyle .dash-sheet[hidden]{ display: none; }
  .dash-sheet-backdrop{ position: absolute; inset: 0; background: rgba(0,0,0,.34); }
  .dash-sheet-panel{
    position: absolute; left: 0; right: 0; bottom: 0;
    max-height: 76vh; display: flex; flex-direction: column;
    padding: 8px 14px calc(16px + env(safe-area-inset-bottom));
    background: var(--surface);
    border-top: .5px solid var(--border);
    border-radius: 20px 20px 0 0;
    animation: sheetUp .24s cubic-bezier(.16,1,.3,1) both;
  }
  @keyframes sheetUp{ from{ transform: translateY(100%); } to{ transform: translateY(0); } }
  .dash-sheet-grip{ width: 36px; height: 4px; border-radius: 999px; background: var(--border); margin: 4px auto 12px; }
  .dash-sheet-scroll{ overflow-y: auto; min-height: 0; }
  /* The sheet reuses .side-item, which the dark-chrome block paints WHITE for the rail.
     On a white sheet that is invisible, so every inherited colour is restated here. */
  body.dashboard-mode .dash-sheet .side-lbl,
  body.mystyle .dash-sheet .side-lbl{ color: var(--text-4); }
  body.dashboard-mode .dash-sheet .side-item,
  body.mystyle .dash-sheet .side-item{ color: var(--text-2); min-height: 44px; }
  body.dashboard-mode .dash-sheet .side-item .ico,
  body.mystyle .dash-sheet .side-item .ico{ color: var(--text-3); opacity: .7; }
  body.dashboard-mode .dash-sheet .side-item:hover,
  body.mystyle .dash-sheet .side-item:hover{ background: var(--bg-2); color: var(--text); }
  /* --accent-press for the same 4.5:1 reason as the tab bar's active label. */
  body.dashboard-mode .dash-sheet .side-item.active,
  body.mystyle .dash-sheet .side-item.active{ color: var(--accent-press); font-weight: 600; background: var(--bg-2); }
  body.dashboard-mode .dash-sheet .side-item.active .ico,
  body.mystyle .dash-sheet .side-item.active .ico{ color: var(--accent); opacity: 1; }
  body.dashboard-mode .dash-sheet .side-item:focus-visible,
  body.mystyle .dash-sheet .side-item:focus-visible{ outline: 2px solid var(--accent); outline-offset: -2px; }
  body.dashboard-mode .dash-sheet .side-item.active::before,
  body.mystyle .dash-sheet .side-item.active::before{ content: none; }
}
@media (max-width: 900px) and (prefers-reduced-motion: reduce){
  .dash-sheet-panel{ animation: none; }
}
@media (max-width: 900px){ body.dashboard-mode .dash-subhead, body.dashboard-mode .dash-scroll{ padding-left: 18px; padding-right: 18px; } }
/* WU-4: the rail is display:none here, so the sheet's only job is to inset the banner. A
   smaller inset than desktop — 12px each side off 375 is a lot of the screen. */
@media (max-width: 900px){ body.dashboard-mode .shell{ --shell-inset: 10px; } }
/* ============================================================
   Dark chrome — top bar + sidebar  (#1C2226)
   Top bar is dark in BOTH modes (home + dashboard) so it matches
   the sidebar; the sidebar rules below stay dashboard-only.
   Foreground flipped to light so the dark panels stay readable;
   the active sidebar item keeps its accent icon.
   ============================================================ */
/* Top bar — the floating pill owns its own fill, radius and hover states in the
   "Floating charcoal nav pill" block above. The four overrides that used to live
   here (background, brand colour, nav-btn colour, btn-ghost outline) restated the
   same intent against a flush full-width bar and now only fight the pill, so they
   are gone; --ink is the single source for the charcoal. */
.dash-crumbs{ border-left-color: rgba(255,255,255,.16); }
body.dashboard-mode .dash-crumbs .crumb{ color: rgba(255,255,255,.62); }
body.dashboard-mode .dash-crumbs .crumb:hover{ color: #fff; }
body.dashboard-mode .dash-crumbs .crumb-current{ color: #fff; }
body.dashboard-mode .dash-crumbs .crumb-sep{ color: rgba(255,255,255,.22); }
.nav-bell,
.nav-menu{ color: rgba(255,255,255,.78); }
.nav-bell:hover,
.nav-menu:hover{ background: rgba(255,255,255,.10); }
.nav-avatar{ background: rgba(255,255,255,.10); color: #fff; }
.nav-avatar:hover{ background: rgba(255,255,255,.16); }

/* Sidebar — WU-4: floating charcoal panel (final_mockup/comapre_01.png). --ink, not the old
   #1C2226 one-off, and no border-right: the panel is surrounded by the --bg sheet now, so a
   seam line would be drawing an edge that no longer touches anything. */
body.dashboard-mode .side{
  background: var(--ink);
  border-right: 0;
  border-radius: var(--r-xl);
}
body.dashboard-mode .side-lbl{
  color: var(--ink-text-2);
  font-family: var(--f-mono); font-size: 12px; letter-spacing: .1em;
  padding-bottom: 6px;
}
/* Tighter group rhythm than the 24px base, for the mockup's density and because the panel
   is 24px shorter than the old flush column: at a 900px-tall window the last two Discover
   rows had dropped below the fold and into .side-scroll. Row pitch is 34px, measured off
   comapre_01.png (Learn y=112, Tools y=146); the panel's bottom padding no longer has to
   clear a viewport edge now that it floats. */
body.dashboard-mode .side-sec{ margin-bottom: 16px; }
body.dashboard-mode .side{ padding-bottom: 12px; }
body.dashboard-mode .side .side-item{ padding-top: 7px; padding-bottom: 7px; }
body.dashboard-mode .side-item{ color: rgba(255,255,255,.76); }
body.dashboard-mode .side-item .ico{ color: #fff; opacity: .85; }
/* One affordance: the --ink-2 wash. No border, no lift. */
body.dashboard-mode .side-item:hover{ background: var(--ink-2); color: #fff; }
body.dashboard-mode .side-item:hover .ico{ opacity: 1; }
body.dashboard-mode .side-item.active{ background: transparent; color: #fff; font-weight: 700; box-shadow: none; position: relative; }
body.dashboard-mode .side-item.active::before{
  content: "";
  position: absolute;
  left: 0; top: 6px; bottom: 6px;
  width: 2px;
  background: var(--accent);
}
body.dashboard-mode .side-item.active .ico{ color: #fff; opacity: 1; }
body.dashboard-mode .side-foot{ border-top-color: var(--ink-border); }
/* Collapse — an outlined pill in the mockup, not another flat row: it acts on the chrome
   rather than navigating, and the hairline is what says so. In the collapsed rail the
   border comes off again, because learn.png shows a bare icon at the foot of the rail.
   Scoped under .side so the mobile sheet, which reuses .side-item, never grows one. */
body.dashboard-mode .side .side-collapse{
  margin-top: 6px;
  border: 1px solid var(--ink-border-2);
  border-radius: var(--r-sm);
  /* box-sizing is border-box, so the hairline eats into the padding box and pushed this row's
     icon 1px right of the three above it. Give the border its 1px back out of the padding. */
  padding-left: 9px; padding-right: 9px;
}
body.dashboard-mode .side .side-collapse:hover{ background: var(--ink-2); }
/* border-color, not border-width: the hairline only goes transparent here, so the 1px is still
   in the box and the padding still has to pay for it. */
body.side-collapsed.dashboard-mode .side .side-collapse{
  border-color: transparent;
  padding-left: 7px; padding-right: 7px;
}

/* ---- Dashboard top bar: burger stays visible (toggles scrollview <-> side-panel) ---- */
body.dashboard-mode .nav.nav--floating .brand .brand-mark{ width: 34px; height: 34px; }
/* Pull the brand left so the logo centers over the sidebar icon column (between the
   52px collapsed rail center ~26px and the expanded icon center ~35px). */
body.dashboard-mode .nav.nav--floating .nav-brand-group{ margin-left: -6px; }
/* Bottom collapse toggle — rendered as a .side-item; its .lbl auto-hides when collapsed */
.side-collapse{ width: 100%; background: transparent; border: 0; font: inherit; text-align: left; }

/* Activity Log tab panels */
.act-panel--hidden{ display: none; }
/* .gd-wrap is reused in dashboard mode purely for the fixed app-shell flex layout, so nothing
   here may inherit the Guide-detail 1180px centering — a side-panel page that centres itself
   reads as broken next to its neighbours.

   🔴 This used to be an allowlist: `:has(.dash-subhead)` plus `.intg-page`. Two problems, both
   measured 2026-08-15. It made the page WIDTH depend on the presence of a DOM node, so
   dash-hero.js emptying and removing a subhead silently shrank six routes to 1180px. And it
   missed /settings outright, which has never had a subhead and had therefore always rendered
   a 1180px column inside a 1400px shell. Blanket rule instead: every dashboard-mode wrap is
   full width, no exceptions to get out of sync with. Blast radius when this replaced the
   allowlist was exactly one route — /settings — because no live route relies on the centering
   the old comment protected (guide pages carry .guide-page, which has a subhead, or no
   .gd-wrap at all). */
body.dashboard-mode .main > .gd-wrap{ max-width: none; margin-inline: 0; }
body.dashboard-mode .gd-wrap .dash-scroll{ max-width: none; margin-inline: 0; }
/* Tabbed views (Activity Log, Settings): a shorter pill-tab row than the standard subhead.
   VERTICAL ONLY. The horizontal gutter is the base rule's now that it is also 24px — pinning
   it here would out-specify the ≤900px rule below and leave these pages at a 24px gutter on a
   phone while every other page tightened to 18px. The .dash-scroll horizontal override that
   used to sit here went for the same reason: it is redundant at desktop and wrong on mobile. */
body.dashboard-mode .gd-wrap .dash-subhead{ padding-block: 12px; }
/* Integrations static header: stacks title + filters, taller than the single-row
   tabbed subhead. Placed after the rules above so it wins the cascade.
   These three variants are VERTICAL-ONLY for the same reason as the tabbed subhead above:
   a hardcoded horizontal padding here out-specifies the ≤900px rule and leaves the category
   strip 6px out of line with the cards under it on a phone. */
body.dashboard-mode .gd-wrap .intg-subhead{ flex-direction: column; align-items: stretch; gap: 12px; padding-block: 18px; }
body.dashboard-mode .gd-wrap.intg-page .dash-scroll{ padding-top: 18px; }
.intg-subhead .page-head{ margin: 0; }
/* Learn hub static header: same stacked title + dark-strip + search pattern as Integrations. */
body.dashboard-mode .gd-wrap .guide-subhead{ flex-direction: column; align-items: stretch; gap: 14px; padding-block: 18px; }
body.dashboard-mode .gd-wrap.guide-page .dash-scroll{ padding-top: 18px; }
.guide-subhead .page-head{ margin: 0; }
/* Generic hub static header (Tools / Skills / MCP / Agents / Compare): same stacked pattern. */
body.dashboard-mode .gd-wrap .hub-subhead{ flex-direction: column; align-items: stretch; gap: 14px; padding-block: 18px; }
body.dashboard-mode .gd-wrap.hub-page .dash-scroll{ padding-top: 18px; }
.hub-subhead .page-head{ margin: 0; }
/* Was a fixed --subhead-h (184px), calibrated back when this bar also held the title, the
   description and the CTAs. Those moved onto the hero panel, so a fixed height now just
   leaves ~120px of empty bar under the category strip. Sizing to content is what keeps every
   page ending on the same line, because every page's bar now holds the same one row.
   No overflow:hidden: the search suggest dropdown must escape. */
body.dashboard-mode .gd-wrap .dash-subhead{ height: auto; }
/* Keep the title block + CTAs on one flex line (the longer merged descriptions were
   pushing the CTA row onto a wrapped second line, blowing past the fixed height). */
body.dashboard-mode .dash-subhead .page-head{ flex-wrap: nowrap; }
body.dashboard-mode .dash-subhead .page-head .sub{ max-width: 72ch; margin-bottom: 0; }
/* Tighter vertical rhythm so title + description + strip fit the fixed height. */
body.dashboard-mode .dash-subhead .page-head h1{ line-height: 1.15; }
/* Match the tab strip's height so the two pills in the toolbar row line up (mockup). */
.hub-controls.intg-filters .hub-search--strip .hub-search-input{ padding-top: 13px; padding-bottom: 13px; }
/* Learn/Integrations wrap their whole header in .intg-head-inner — distribute inside it too. */
.guide-subhead .intg-head-inner,
.intg-subhead .intg-head-inner{ flex: 1 1 auto; display: flex; flex-direction: column; justify-content: space-between; min-height: 0; }
/* Row pairing extra header items with the dark strip on one line. */
.subhead-row{ display: flex; align-items: center; justify-content: space-between; gap: 12px; min-width: 0; }
.subhead-row .hub-controls{ flex: 1 1 auto; min-width: 0; }
.subhead-row .agents-view-pills{ margin: 0; flex: 0 0 auto; }
/* Compact icon+label points (replaces the big MCP intro cards). */
.subhead-points{ display: flex; align-items: center; gap: 18px; flex: 0 0 auto; }
.subhead-point{ display: inline-flex; align-items: center; gap: 7px; font-size: 13px; color: var(--text-2); white-space: nowrap; }
.subhead-point svg{ width: 16px; height: 16px; color: var(--accent); }
/* Compact inline stats (replaces the Dashboard stat tiles). */
.subhead-stats{ display: flex; align-items: baseline; gap: 22px; flex: 0 0 auto; font-size: 13px; color: var(--text-3); white-space: nowrap; }
.subhead-stats strong{ font-size: 15px; font-weight: 600; color: var(--text); margin-right: 4px; }
/* Jobs: slim job-boards row (replaces the boards card). */
.jobs-boards-row{ display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.jobs-boards-label{ font-size: 13px; color: var(--text-3); }
/* hubControls now renders as the dark strip + inline search row (reuses .intg-filters look).
   .intg-filters = flex, space-between, nowrap; the dark strip flexes, the search is fixed width. */
.hub-controls.intg-filters{ margin: 0; flex-wrap: nowrap; justify-content: space-between; gap: 16px; align-items: center; }
.hub-controls.intg-filters .hub-search--strip{ flex: 0 0 auto; min-width: 240px; width: 280px; }
/* Phone widths: the fixed --subhead-h is calibrated to the DESKTOP sidebar icon rows,
   and the nowrap strip+search row assumes room for the 280px search box. At 390px that
   left ~46px for the tab strip, so the category tabs rendered underneath the search
   input (elementFromPoint on a tab hit .hub-search-input) and the whole controls row
   overflowed the fixed-height subhead into .dash-scroll — every hub tab click was
   intercepted (the mobile e2e Flow 3 blocker). Below the 900px breakpoint the sidebar
   is an off-canvas drawer, so the icon-row calibration is moot: let the stacked header
   size to its content and stack the search under the tab strip. */
@media (max-width: 900px){
  body.dashboard-mode .gd-wrap .dash-subhead{ height: auto; }
  .hub-controls.intg-filters{ flex-wrap: wrap; }
  .hub-controls.intg-filters .hub-search--strip{ flex: 1 1 100%; width: auto; min-width: 0; }
  /* WU-4b: the same starvation hits the pages that use the BARE .intg-filters row
     (/guide, /integrations) - only the .hub-controls variant above ever got the wrap.
     There the 240px-min .intg-search keeps its width, so .intg-tabwrap collapsed to 50px,
     the .intg-tabs scroller measured 4px and the 48px active chip spilled out of it. That
     spill - a dark rounded chip with the white scroll chevron sitting on top of it - is
     the nub the WU-4 375 shot caught. Wrap the search onto its own row and the strip gets
     the whole line, exactly as the hub pages already do. */
  .intg-filters{ flex-wrap: wrap; }
  .intg-filters > .intg-controls{ flex: 1 1 100%; }
  .intg-filters > .intg-controls .intg-search{ width: 100%; min-width: 0; }
  /* R2-11: one level up, .subhead-row itself never wrapped at any breakpoint, so the two
     rules above could not help a hub whose toolbar has a SIBLING of .hub-controls. /mcp is
     the only one: its three .subhead-points chips are nowrap and flex 0 0 auto (~362px
     intrinsic), so at 375px they claimed the whole line and starved .hub-controls, which
     carries min-width 0 and duly shrank to nothing. Measured before this rule: .intg-tabwrap
     14px wide (only the two chevrons drawn, every label unreachable), .hub-search--strip 0px
     (the empty white box under the strip) and the chips still ending at x=401.6 in a 375px
     viewport. Wrapping the row and giving each toolbar block its own full-width line fixes
     all three at once; the chips wrap to a second line rather than scroll, so nothing hides
     behind a swipe. .subhead-row.rm-toolbar (/roadmap) is deliberately NOT in the list — it
     is a sentence plus two small buttons, it already wraps (app.css:480), and stretching
     those to full width would be a regression. */
  .subhead-row{ flex-wrap: wrap; }
  .subhead-row > .hub-controls,
  .subhead-row > .intg-tabwrap,
  .subhead-row > .subhead-points,
  .subhead-row > .subhead-stats,
  .subhead-row > .agents-view-pills{ flex: 1 1 100%; min-width: 0; }
  .subhead-points{ flex-wrap: wrap; gap: 8px 14px; }
  .subhead-stats{ flex-wrap: wrap; gap: 6px 18px; }
}
/* Activity Log: tabs only, no search → size the dark strip to its content (don't stretch). */
.act-tabwrap{ flex: 0 0 auto; align-self: flex-start; }
/* Daily Brief (news hub) as a static-header page: neutralize the old 1000px cap so the shell
   fills width; category strip is full-width; weekly digest cards. */
body.dashboard-mode .main > .gd-wrap.nh-wrap{ max-width: none; }
.nh-cat-strip{ width: 100%; }
.nh-panel--hidden{ display: none; }
.nh-weekly-head{ margin: 0 0 18px; }
.nh-weekly-list{ display: flex; flex-direction: column; gap: 16px; }
.nh-weekly-card{ display: flex; gap: 18px; padding: 16px; border: 1px solid var(--border, #ebebeb); border-radius: 14px; background: #fff; text-decoration: none; transition: border-color .15s; }
.nh-weekly-card:hover{ border-color: var(--text-3, #c7c7cc); }
.nh-weekly-img{ flex: 0 0 auto; width: 180px; height: 110px; border-radius: 10px; background: #f4f4f5 center/cover no-repeat; }
.nh-weekly-body{ flex: 1 1 auto; min-width: 0; }
.nh-weekly-rank{ font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase; color: var(--accent); margin-bottom: 6px; }
.nh-weekly-title{ font-size: 17px; font-weight: 650; color: var(--text-1); line-height: 1.3; margin-bottom: 8px; }
.nh-weekly-brief{ font-size: 14px; color: var(--text-2); line-height: 1.55; margin: 0; }

/* R2-10 removed the whole .wd-* block that used to live here: the editorial
   "Weekly Digest" header, its side rows and its numbers strip. /weekly is one
   editorial page now and renders .wde-* sections, defined at the end of this
   file. Nothing referenced these rules any more. */
/* The base .gd-wrap carries padding:28px 0 80px (for the centered Guide-detail page). In
   dashboard mode that leaks a ~28px band above the static header and an ~80px white strip
   below the scroll region. Zero it for the full-shell pages so the header sits flush at the
   top and the scroll region fills to the bottom. */
body.dashboard-mode .main > .gd-wrap.intg-page,
body.dashboard-mode .main > .gd-wrap.guide-page,
body.dashboard-mode .main > .gd-wrap.hub-page{ padding: 0; }

/* ============================================================
   Custom cursor — two-layer (dot + lagging ring)
   JS owns positioning + scale via rAF lerp. No CSS transitions.
   ============================================================ */
@media (hover: hover) and (pointer: fine){
  html.cursor-on, html.cursor-on body, html.cursor-on *{ cursor: none !important; }
  .cursor-dot,
  .cursor-ring{
    position: fixed;
    top: 0; left: 0;
    pointer-events: none;
    z-index: 99999;
    transform: translate3d(-100px, -100px, 0);
    will-change: transform;
  }
  .cursor-dot{
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--accent);
    mix-blend-mode: difference;
    filter: invert(1);
  }
  .cursor-ring{
    width: 28px; height: 28px;
    border-radius: 50%;
    border: 1.5px solid color-mix(in srgb, var(--accent) 80%, transparent);
  }
  body.dashboard-mode .cursor-dot{ background: var(--accent); }
  body.dashboard-mode .cursor-ring{ border-color: color-mix(in srgb, var(--accent) 85%, transparent); }
}
@media (prefers-reduced-motion: reduce){
  html.cursor-on, html.cursor-on body, html.cursor-on *{ cursor: auto !important; }
  .cursor-dot, .cursor-ring{ display: none !important; }
}

/* Phone/tablet: the link row and Playground drop out of the pill. The opener they lacked
   is the ☰ below: it shows at exactly this width and opens the same all-sections sheet the
   hubs' tab bar "More" slot opens. The pill keeps a tighter inset so the brand + bell +
   avatar never crowd a 375px viewport, and the inner padding stays vertically centred —
   the old `14px 20px 0` was written for a flush bar and pushes every child off-centre
   inside a 60px pill. */
@media (max-width: 833px){
  .nav.nav--floating{ --nav-inset-x: 12px; }
  .nav.nav--floating .nav-links{ display: none; }
  .nav.nav--floating .nav-right{ margin-left: auto; }
  .nav-menu{ display: inline-flex; }
  .nav.nav--floating .nav-inner{ padding: 0 10px 0 12px; }
  .nav.nav--floating .nav-right .btn-ghost{ display: none; }
  .nav.nav--floating .brand{ font-size: 14px; }
  /* app.css:1194 hides the wordmark under 420px — a rule written for the flush bar,
     where brand + links + CTA fought for the same row. Inside the pill the link row and
     Playground are already gone at this width, so the wordmark fits with ~150px to
     spare and the pill otherwise reads as an empty charcoal slab. */
  .nav.nav--floating .brand span:last-child{ display: inline; }
}

@media (prefers-reduced-motion: reduce){
  .nav.nav--floating .brand,
  .nav.nav--floating .nav-links,
  .nav.nav--floating .nav-right .btn-ghost,
  .nav.nav--floating .nav-link{ transition: none; }
}

/* Bento: the first card takes two columns and both rows, everything else is 1x1. Three
   columns, not four — measured off learn.png, whose grid is 1509px wide with 16px gutters,
   a 962x422 lead card and 491px cards under it.
   Rows are a fixed height rather than 1fr because the lead card spans two of them: with 1fr
   the row height would be resolved from the tallest CARD, and the two-row card is by
   definition the tallest, which feeds back into its own height. A flat row height also keeps
   every card the same size no matter how the provider filter reflows the grid. */
.guide-grid{
  --gc-row: 205px;             /* mockup row: 491x204 small card, 962x422 lead */
  display:grid;
  grid-template-columns:repeat(3, 1fr);
  grid-auto-rows:var(--gc-row);
  gap:16px;
  margin-top:8px;
}
#guide-grid > .gc-feat-cell{ grid-column:span 2; grid-row:span 2; }
/* The grid had no breakpoints at all — it was a hard 3-up at every width, so on a phone it
   rendered three unreadable columns. */
@media (max-width:1100px){ .guide-grid{ grid-template-columns:repeat(2, 1fr); } }
/* One column: the lead card drops to 1x1 with everyone else. Spanning two rows here would
   make a 420px block at the top of a phone screen and push the second card below the fold,
   and the display type it carries at desktop does not fit 375px anyway. */
@media (max-width:700px){
  .guide-grid{ grid-template-columns:1fr; --gc-row: 208px; }
  #guide-grid > .gc-feat-cell{ grid-column:span 1; grid-row:span 1; }
}
/* The grid item is the [data-gprovider] wrapper; make it + the card fill the stretched cell
   so short-description cards don't float shorter than their neighbours. */
#guide-grid > [data-gprovider]{ display:flex; }
#guide-grid > [data-gprovider] > .guide-card{ height:100%; width:100%; }
.guide-card{
  display:block;
  background:var(--text);      /* letterbox behind a poster that doesn't fill the cell */
  border-radius:var(--r-sm);   /* mockup corner measures ~9px, not the 18px --r-lg */
  overflow:hidden;
  transition:transform .18s var(--ease);
}
/* One affordance, per the house rule: the lift, and nothing stacked on it. The border used to
   change colour on hover; on a dark card that read as a rim flickering on and off. */
.guide-card:hover{ transform:translateY(-1px); }
@media (prefers-reduced-motion: reduce){
  .guide-card{ transition:none; }
  .guide-card:hover{ transform:none; }
}
/* The media IS the card. No aspect-ratio any more: the row height sets it, so a 2x2 card and
   a 1x1 card fill their cells exactly instead of fighting the grid. */
.guide-card-media{
  position:relative;
  height:100%;
  background:var(--text);
}
/* The poster gets its OWN layer, carrying the url through --gc-img, so the exposure filter
   below lifts the photograph and nothing else. Filtering .guide-card-media instead would
   filter its ::after scrim and the white text with it, which is self-defeating: it would
   raise the background under the copy by exactly as much as the copy. */
.guide-card-media::before{
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background-image:var(--gc-img);
  background-size:cover; background-position:center; background-repeat:no-repeat;
  filter:brightness(3.4) contrast(0.9);
}
/* One badge, top-right. Insets measured off the mockup: 15px down, 18px in. */
.guide-card-media-top{
  position:absolute; top:0; left:0; right:0; z-index:2;
  display:flex; align-items:center; justify-content:flex-end; gap:8px;
  padding:15px 18px;
}
/* Three layers: a bottom ramp for the title and CTA, a left ramp for the text column, and a
   light flat dim under both.
   The flat one is tuned to the SOURCE images and has been retuned once already. The cards
   used to point at the catalogue thumbnails, which are near-white product screenshots
   (tool_chatgpt.jpg means 246 of 255) with their own headlines burned in, and those needed
   .58 flat just to stop reading as a screenshot wearing a gradient. image/guides/poster-*.jpg
   replaced them and means 11-37 with p95 31-157 — already in the range learn.png's cards sit
   in (corner ~30, brightest poster pixel ~90-115). Carrying the old .58 over crushed them to
   black. .22 keeps the brightest poster near 115 and lets the picture stay a picture.
   The two ramps carry the text, and they have to: the tool name starts a third of the way up
   a 205px card, where a bottom-only scrim is still translucent. Legibility work, not
   decoration; every number here was measured against the worst poster, not eyeballed. */
.guide-card-media::after{
  content:""; position:absolute; inset:0; z-index:1; pointer-events:none;
  background:
    linear-gradient(180deg, rgba(0,0,0,0) 45%, rgba(0,0,0,.12) 75%, rgba(0,0,0,.22) 100%),
    linear-gradient(90deg, rgba(0,0,0,.20) 0%, rgba(0,0,0,.08) 40%, rgba(0,0,0,0) 70%),
    linear-gradient(rgba(0,0,0,.12), rgba(0,0,0,.12));
}
/* The lead card is twice as tall and twice as wide for the same three lines, so the same
   percentage stops would drag both ramps across a poster that has room to breathe. Start
   each one later; the flat dim is unchanged, because that one is about the source images,
   not the layout. */
.gc-feat .guide-card-media::after{
  background:
    linear-gradient(180deg, rgba(0,0,0,0) 55%, rgba(0,0,0,.10) 80%, rgba(0,0,0,.20) 100%),
    linear-gradient(90deg, rgba(0,0,0,.18) 0%, rgba(0,0,0,.06) 34%, rgba(0,0,0,0) 62%),
    linear-gradient(rgba(0,0,0,.12), rgba(0,0,0,.12));
}
/* Every number below is measured off learn.png with PIL, not eyeballed. The reference card is
   the Grok card at (1088,289)-(1618,494): badge 25px tall inset 15/18, tool line 30px, title
   19px on a 24px line, CTA 149x40 inset 27 left / 13 bottom, card corner ~9px.
   Badge: a plain dark pill, no hairline. The mockup's chip has no border and the one I had
   put on it drew a bright rectangle in the corner of every poster. */
.gc-pill{
  font-size:14px;
  font-weight:600;
  letter-spacing:0;
  color:#fff;
  background:rgba(0,0,0,.72);
  padding:5px 11px;
  border-radius:var(--r-pill);
  white-space:nowrap;
}
/* A guide's `badge` ("New", owner 2026-09-30) takes the top-left corner; the runtime pill
   keeps the right. Same dark pill, only the accent colour marks it. */
.gc-pill--badge{ margin-right:auto; background:var(--accent, #0071e3); }
/* Tool line + title + CTA, stacked on the scrim. */
.gc-foot{
  position:absolute; left:26px; right:26px; bottom:14px; z-index:2;
  display:flex; flex-direction:column; align-items:flex-start; gap:16px;
}
/* The copy stays FULL WIDTH. learn.png wraps its titles at ~59% of the card, and matching
   that was tried: at our card widths (375px with the rail open, 433px collapsed, against the
   mockup's 491px) a 74% column clamped three of the ten titles to two lines and ate the rest
   of the words. A truncated title is a worse failure than a dim corner, so the column keeps
   the width it needs and the poster is exposed ABOVE the copy instead of beside it. */
/* The copy carries its OWN wash instead of the whole card being darkened to protect it.
   That split is the only way to have both things the mockup has: a poster you can actually
   read (monitors, code, the lit street) and white text that holds 4.5:1 over it. A uniform
   scrim cannot do both — brightening the image by k and darkening everything by k cancel
   exactly, which is what the first two attempts here ran into, measured.
   So: the card-wide ::after is now barely there, the poster is exposed, and this wash sits
   only behind the text — full card width, because the title box IS full width and the
   contrast floor is measured over the whole box, feathered upward so it reads as the foot of
   the photograph rather than as a panel. Strong enough (>=.55 by the tool line) to hold even
   a blown-out highlight underneath. */
.gc-foot::before{
  content:""; position:absolute; z-index:-1; pointer-events:none;
  top:-40px; left:-26px; right:-26px; bottom:-14px;   /* covers the copy column + a feather, NOT the whole card */
  background:
    linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,.64) 32%, rgba(0,0,0,.86) 100%),
    linear-gradient(90deg, rgba(0,0,0,.38) 0%, rgba(0,0,0,.30) 55%, rgba(0,0,0,.10) 85%, rgba(0,0,0,0) 100%);
}
.gc-feat .gc-foot::before{ top:-52px; left:-30px; right:-30px; bottom:-20px; }
.gc-tool{ display:flex; align-items:center; gap:10px; min-width:0; max-width:100%; }
/* 30px, matching the mark in the mockup. Only two files in image/logos/ read as the white
   line art the mockup uses once they are on a dark card — cursor.png and grok.png, both of
   which are a light mark on a black ground. Everything else there is a coloured tile
   (chatgpt is teal) or a light-ground wordmark (gemini), and the mockup shows neither; the
   Veo card in the mockup has no mark at all, just the name, which is what the other eight
   cards get. See GUIDE_TOOL_LOGO in app.js. */
.gc-tool-logo{
  width:30px; height:30px; flex:0 0 auto;
  border-radius:var(--r-xs);
  object-fit:contain;
}
/* The mockup sets the tool name as the display element on the card — bigger than the title,
   which is the opposite of the usual hierarchy and is the thing that makes these read as
   tool cards rather than article cards. 30px measured. */
.gc-tool-name{
  color:#fff;
  font-family:var(--f-display, inherit);
  font-size:30px; font-weight:500; line-height:1.2; letter-spacing:-.02em;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
/* Clamped to two lines so a long title can't push itself off the poster. */
.guide-card-title{
  margin:0;
  color:#fff;
  font-size:19px; font-weight:600; line-height:24px; letter-spacing:-.01em;
  display:-webkit-box; -webkit-line-clamp:2; line-clamp:2; -webkit-box-orient:vertical;
  overflow:hidden;
}
/* A real anchor, not the old decorative play badge: it gives every card a keyboard route to
   its guide, which the click-handler-on-an-<article> card never had. stopPropagation on the
   element keeps the card's own onclick from firing a second navigate.
   Softly rounded rectangle, not a pill, and no hairline — both measured off the mockup. */
.gc-cta{
  display:inline-flex; align-items:center; gap:12px;
  height:40px; padding:0 16px;
  margin-top:-6px;                     /* the mockup tightens title -> CTA to ~10px */
  border-radius:var(--r-sm);
  background:rgba(255,255,255,.14);
  color:#fff;
  font-size:15px; font-weight:500; text-decoration:none;
  white-space:nowrap;
  transition:background-color .15s var(--ease);
}
.gc-cta:hover{ background:rgba(255,255,255,.24); }
.gc-cta:focus-visible{ outline:2px solid #fff; outline-offset:2px; }
.gc-cta svg{ width:20px; height:20px; flex:0 0 auto; }
@media (prefers-reduced-motion: reduce){ .gc-cta{ transition:none; } }
/* The lead card. In the mockup the mark, the name, the badge and the CTA are all the SAME
   size as on a small card — only the title grows, 19px -> 30px. That single change is what
   makes it read as the lead rather than a card with everything scaled up. */
.gc-feat .gc-foot{ left:30px; right:30px; bottom:20px; gap:18px; }
.gc-feat .guide-card-title{ font-size:30px; line-height:36px; letter-spacing:-.02em; }
.gc-feat .gc-cta{ height:44px; padding:0 18px; }
/* Phones: the 30px tool name and the 19px title do not both fit a 375px card, and the CTA
   needs its 44px touch target. */
@media (max-width:768px){
  .gc-foot,
  .gc-feat .gc-foot{ left:18px; right:18px; bottom:14px; gap:12px; }
  .gc-tool-name{ font-size:23px; }
  .guide-card-title{ font-size:17px; line-height:22px; }
  .gc-feat .guide-card-title{ font-size:20px; line-height:25px; }
  .gc-cta,
  .gc-feat .gc-cta{ height:44px; font-size:14px; }
  .gc-tool-logo{ width:26px; height:26px; }
}
.guide-card-meta{ display:flex; align-items:center; gap:10px; }
.guide-card-level{
  display:inline-block;
  font-size:12px;
  font-weight:600;
  letter-spacing:.08em;
  color:var(--accent);
  background:var(--accent-tint);
  padding:4px 10px;
  border-radius:999px;
}
/* Guide Detail (/guide/:id) */
.gd-wrap{ max-width:1180px; margin:0 auto; padding:28px 0 80px; }

/* R2-1: one column. The stage used to be `minmax(0,1fr) 320px` with an instructor card in
   the second track; the owner asked for the player at full page width and the instructor
   inside the tabs, which leaves the stage a plain block and .gd-video-col with no job. */
.gd-stage{ display:block; }
/* The detail page is the one .gd-wrap that spends its width on a 16:9 frame, so it takes the
   whole content column instead of the shared 1180px measure. The reading blocks under the
   frame set a measure of their own rather than inheriting this one. */
.gd-wrap.gd-detail{ max-width:none; }
.gd-detail .gd-panel{ max-width:820px; }
/* The column takes the content width, but never more of it than a 16:9 frame can spend
   without pushing its own controls off screen: unbounded, the frame is 729px tall at
   1440x900 and the control bar lands 69px BELOW the fold — measured, not guessed. 260px is
   the chrome above it (crumb row + header block). Capping the WRAP rather than the frame
   keeps the header, the actions row, the tabs and the related row on the video's edges
   instead of drifting out to a wider column beside it. Phones are exempt: the stage is
   short there and the cap would make the page narrower than the column it sits in. */
/* On body, not on .gd-detail: the crumb row above the column is a sibling of .dash-scroll,
   outside the wrap, and it has to resolve the same measure to keep its left edge on the
   video's. One declaration, two consumers, no repeated magic number. */
body{ --gd-col: calc((100vh - 260px) * 16 / 9); }
@media (min-width:901px){
  .gd-wrap.gd-detail{ max-width:var(--gd-col); }
}
/* No video of either kind (/guide/8, /guide/9): a 16:9 frame the width of the page, with a
   still picture in it, costs the reader a whole screen before the first sentence of a
   written guide. Held at 840px — the width the old two-column stage gave it (1180 content
   measure minus the 320px instructor track and its 28px gap). */
.gd-stage--still{ max-width:840px; }

/* Header */
.gd-header{ margin-bottom:24px; }
.gd-header-meta{ display:flex; align-items:center; gap:10px; margin-bottom:12px; }
.gd-title{ font-size:28px; font-weight:700; letter-spacing:-.5px; margin:0 0 14px; line-height:1.2; }
.gd-tags{ display:flex; gap:6px; flex-wrap:wrap; }

/* Like + comment bar under the guide header (detail page) */
.gd-engage{ display:flex; align-items:center; gap:20px; margin-top:16px; }
/* min-height, not padding: the pill keeps its shape and gains a 40px tap target. */
.gd-like{ display:inline-flex; align-items:center; justify-content:center; gap:7px; min-height:40px; font-size:14px; color:var(--text-2,#6b7280); background:transparent; border:.5px solid var(--border,#e5e7eb); border-radius:999px; padding:7px 14px; cursor:pointer; transition:color .15s, border-color .15s; }
.gd-like:hover{ color:var(--accent); border-color:var(--accent); }
.gd-like.is-liked{ color:var(--accent); border-color:var(--accent); }
.gd-like.is-liked svg{ fill:var(--accent); }
.gd-engage-stat{ display:inline-flex; align-items:center; gap:6px; font-size:14px; color:var(--text-2,#6b7280); }

/* One slim row directly under the video (owner Q2): the engage bar left, the completion
   button right. .gd-engage carried its own top margin when it sat under the header. */
.gd-actions{ display:flex; align-items:center; justify-content:space-between; gap:16px; flex-wrap:wrap; margin:16px 0 0; }
.gd-actions .gd-engage{ margin-top:0; }

/* Comments section at the bottom of the detail page */
.gd-comments{ margin-top:40px; padding-top:28px; border-top:.5px solid var(--border,#e5e7eb); max-width:680px; }
.gd-cmt-compose{ display:flex; gap:10px; align-items:flex-start; margin-bottom:20px; }
.gd-cmt-input{ flex:1; padding:10px 12px; font-size:14px; font-family:inherit; border:.5px solid var(--border,#e5e7eb); border-radius:10px; resize:vertical; color:var(--text); background:#fff; }
.gd-cmt-input:focus{ outline:none; border-color:var(--accent); }
.gd-cmt-list{ display:flex; flex-direction:column; gap:18px; }
.gd-cmt{ display:flex; gap:12px; }
.gd-cmt-avatar{ flex:0 0 auto; width:34px; height:34px; border-radius:50%; background:#f0f1f3; color:var(--text-2,#6b7280); display:flex; align-items:center; justify-content:center; font-size:14px; font-weight:600; text-transform:uppercase; }
.gd-cmt-name{ font-size:13px; font-weight:600; color:var(--text); margin:0 0 3px; }
.gd-cmt-text{ font-size:14px; color:var(--text); margin:0; line-height:1.5; }
.gd-cmt-empty{ font-size:14px; margin:0; }

/* Video player */
.gd-video-wrap{ position:relative; width:100%; aspect-ratio:16/9; background:#0a0b0d; border-radius:var(--r-md); overflow:hidden; margin-bottom:0; }
/* initGuidePlayer gives the wrap tabindex=0 so the stage shortcuts have somewhere to land. */
.gd-video-wrap:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
/* The picture and its big play button, as one box. On desktop it simply fills the wrap and
   nothing about the layout changes; at <=700px it becomes the aspect box in its own right so
   the control bar can dock under it as a sibling. */
.gd-frame{ position:absolute; inset:0; }
.gd-video{ width:100%; height:100%; object-fit:contain; display:block; cursor:pointer; }

/* R2-1: a light bar floating 12px inside the frame, in place of a black strip welded to its
   bottom edge. --surface at 96% over a hairline and --sh-2 — the same white chrome as the
   rest of the page. No backdrop-filter: blur-behind-glass is on the banned list. */
.gd-controls{
  position:absolute; left:12px; right:12px; bottom:12px;
  display:flex; align-items:center; gap:6px;
  padding:6px 8px;
  background:color-mix(in srgb, var(--surface) 96%, transparent);
  border:.5px solid var(--border);
  border-radius:var(--r-md);
  box-shadow:var(--sh-2);
  transition:opacity var(--dur-state) var(--ease);
}
/* Hidden until the pointer moves over the video or a control takes focus (owner ask,
   2026-09-13): initGuidePlayer sets .is-idle at load, lifts it on pointer movement, focus or
   a play/pause change, and puts it back 2.5s after the pointer rests, or as soon as it
   leaves. While the video is paused the bar stays as long as the pointer rests on the
   picture. Focus inside the bar always wins — a hidden focused control loses the page. */
.gd-video-wrap.is-idle .gd-controls{ opacity:0; pointer-events:none; }
.gd-video-wrap.is-idle:not(.is-playing):hover .gd-controls,
.gd-video-wrap.is-idle:focus-within .gd-controls{ opacity:1; pointer-events:auto; }
.gd-ctrl{ flex:0 0 auto; width:32px; height:32px; display:flex; align-items:center; justify-content:center; background:none; border:none; border-radius:var(--r-xs); color:var(--text); cursor:pointer; padding:0; transition:background var(--dur-quick) var(--ease); }
.gd-ctrl:hover{ background:var(--bg-2); }
.gd-ctrl:focus-visible{ outline:2px solid var(--accent); outline-offset:-2px; }
.gd-volume{ flex:0 0 auto; width:64px; accent-color:var(--accent); cursor:pointer; }
.gd-time{ flex:0 0 auto; font-family:var(--f-mono); font-size:12px; color:var(--text-2); font-variant-numeric:tabular-nums; white-space:nowrap; padding:0 4px; }

/* Scrubber: a 4px rail carrying the buffered range in --border-2 under an --accent fill,
   with a knob that only appears on hover or focus so the resting bar stays a thin line. */
/* The inset hairline is what makes a 4px --bg-2 rail legible on a white bar; without it the
   empty part of the track vanishes and the scrubber reads as a floating blue stub. */
.gd-scrub{ flex:1 1 auto; min-width:40px; height:4px; margin:0 4px; background:var(--bg-2); box-shadow:inset 0 0 0 .5px var(--border-2); border-radius:2px; cursor:pointer; position:relative; }
.gd-scrub-buf{ position:absolute; inset:0 auto 0 0; width:0; background:var(--border-2); border-radius:2px; }
.gd-progress-fill{ position:absolute; inset:0 auto 0 0; width:0; background:var(--accent); border-radius:2px; }
.gd-scrub-knob{ position:absolute; right:0; top:50%; width:12px; height:12px; margin:-6px -6px 0 0; border-radius:50%; background:var(--accent); box-shadow:var(--sh-1); opacity:0; transition:opacity var(--dur-quick) var(--ease); }
.gd-scrub:hover .gd-scrub-knob,
.gd-video-wrap:focus-within .gd-scrub-knob{ opacity:1; }
/* Play/pause + mute icon swap via wrap state class */
.gd-i-pause, .gd-i-mute{ display:none; }
.gd-video-wrap.is-playing .gd-i-play{ display:none; }
.gd-video-wrap.is-playing .gd-i-pause{ display:flex; }
.gd-video-wrap.is-muted .gd-i-vol{ display:none; }
.gd-video-wrap.is-muted .gd-i-mute{ display:flex; }
/* Big centre play: a white circle with an ink glyph, sitting on the poster. */
.gd-bigplay{ position:absolute; inset:0; margin:auto; width:72px; height:72px; border-radius:50%; background:var(--surface); border:none; color:var(--ink); display:flex; align-items:center; justify-content:center; cursor:pointer; padding-left:4px; box-shadow:var(--sh-3); transition:background var(--dur-quick) var(--ease), opacity var(--dur-quick) var(--ease); }
.gd-bigplay svg{ width:26px; height:26px; }
.gd-bigplay:hover{ background:var(--bg-2); }
.gd-bigplay:focus-visible{ outline:2px solid var(--accent); outline-offset:3px; }
.gd-video-wrap.is-playing .gd-bigplay{ opacity:0; pointer-events:none; }
/* Captions */
.gd-ctrl.is-active{ color:var(--accent); }
/* Captions at half the browser's default (owner ask, 2026-09-13): Chrome draws a cue at 5%
   of the video's height, 32px on the 1140px stage, which covered a third of the picture.
   Fullscreen scales back up with the viewport. */
.gd-video::cue{ background:rgba(0,0,0,.72); color:#fff; font-family:var(--f-text); font-size:clamp(11px, 1.15vw, 16px); line-height:1.35; }
.gd-video-wrap:fullscreen .gd-video::cue{ font-size:clamp(16px, 1.4vw, 28px); }
@media (prefers-reduced-motion: reduce){
  .gd-controls, .gd-bigplay, .gd-ctrl, .gd-scrub-knob{ transition:none; }
}

/* No-videoUrl fallback: the guide image, plain. The play button, "Video coming
   soon" label and dark scrim were removed 2026-08-30 (launch rule: no dead video
   affordances) — restore them only alongside a real videoUrl. */
.gd-video-placeholder{ position:relative; width:100%; height:100%; background-size:cover; background-position:center; }
/* YouTube-hosted guides. .gd-video-wrap already supplies the 16:9 box and the clipping, so
   the frame just fills it; no custom controls, because YouTube's own chrome is inside. */
.gd-yt{ position:absolute; inset:0; width:100%; height:100%; border:0; display:block; }
/* Click-to-load facade: the poster plus the same big play button the self-hosted player
   uses. See guideYouTubeFrame in app.js for why the iframe is not live until clicked. */
.gd-yt-facade{ position:absolute; inset:0; background-size:cover; background-position:center; cursor:pointer; }

/* .gd-media-row / .gd-media-col removed with the MCP setup-video column. The brief
   is now the only child, so it spans the full content column instead of sitting in
   a 1.6fr track with dead space beside it. margin-top moves here because the grid
   wrapper used to supply it. */
.gd-side-brief{ background:var(--surface); border:1px solid var(--border); border-radius:14px; padding:20px 22px; margin-top:24px; }
.gd-brief-h{ font-size:12px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:var(--text-3); margin:0 0 8px; }
.gd-brief-h ~ .gd-brief-h{ margin-top:20px; }
.gd-brief-overview{ font-size:14px; line-height:1.6; color:var(--text-2); margin:0; }
.gd-brief-article{ font-size:14px; line-height:1.6; color:var(--text-2); }
.gd-brief-article p{ margin:0 0 10px; }
.gd-brief-article > :last-child{ margin-bottom:0; }
.gd-brief-install{ display:flex; align-items:center; gap:8px; background:var(--bg-2); border:1px solid var(--border); border-radius:9px; padding:8px 10px; margin:0 0 14px; }
.gd-brief-install code{ flex:1; min-width:0; font-family:var(--f-mono,ui-monospace,monospace); font-size:12px; color:var(--text-2); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.gd-brief-steps{ list-style:none; counter-reset:gdb; padding:0; margin:0; }
.gd-brief-steps > li{ counter-increment:gdb; position:relative; padding:2px 0 14px 32px; }
.gd-brief-steps > li:last-child{ padding-bottom:0; }
.gd-brief-steps > li::before{ content:counter(gdb); position:absolute; left:0; top:0; width:22px; height:22px; border-radius:50%; background:var(--accent-tint); color:var(--accent); font-size:12px; font-weight:600; display:flex; align-items:center; justify-content:center; }
.gd-brief-steps .st-t{ display:block; font-size:13.5px; font-weight:600; color:var(--text); margin:0 0 2px; }
.gd-brief-steps .st-d{ display:block; font-size:12.5px; color:var(--text-3); line-height:1.5; }
.gd-brief-skel{ padding:24px 4px; }

/* Instructor tab. Was a 320px sidebar card beside the video; as a tab panel the photo and
   the text sit side by side instead of stacking inside a 320px column. */
.gd-instructor{ display:flex; align-items:flex-start; gap:20px; }
.gd-instructor-photo{ flex:0 0 auto; width:96px; height:96px; border-radius:50%; object-fit:cover; object-position:top center; display:block; }
.gd-instructor-body{ min-width:0; }
.gd-instructor-name{ font-family:var(--f-display); font-size:17px; font-weight:600; letter-spacing:-.2px; margin:2px 0 4px; }
.gd-instructor-role{ font-family:var(--f-mono); font-size:11.5px; letter-spacing:.08em; text-transform:uppercase; color:var(--text-3); margin:0 0 12px; }
.gd-instructor-bio{ font-size:15px; color:var(--text-2); line-height:1.7; margin:0; max-width:56ch; }
@media (max-width:520px){ .gd-instructor{ flex-direction:column; gap:14px; } }

/* Completion button. Was a full-width accent block inside the instructor card; on the
   actions row it is an --ink button at --r-sm, the shape the float boxes already use. */
.gd-complete-btn{ display:inline-flex; align-items:center; justify-content:center; gap:7px; height:40px; padding:0 16px; font-family:inherit; font-size:13.5px; font-weight:600; color:var(--ink-text); background:var(--ink); border:none; border-radius:var(--r-sm); cursor:pointer; transition:background var(--dur-quick) var(--ease); }
.gd-complete-btn:hover{ background:var(--ink-2); }
.gd-complete-btn:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }

/* Tabs */
.gd-tabs{ display:flex; gap:0; border-bottom:1px solid #e8e8ed; margin:28px 0 0; }
.gd-brief-tabbar{ margin:0 0 14px; }
.gd-brief-tabbar .gd-tab{ padding:8px 14px; font-size:13px; }
.gd-tab{ background:none; border:none; border-bottom:2px solid transparent; padding:10px 20px; font-size:14px; font-weight:500; color:var(--text-3); cursor:pointer; margin-bottom:-1px; transition:color .15s, border-color .15s; }
.gd-tab:hover{ color:#000; }
.gd-tab.active{ color:#000; border-bottom-color:#000; }

/* Tab panels */
.gd-panel{ padding:28px 0 0; }
.gd-panel--hidden{ display:none; }

/* Section heading shared by the tab panels and the related row. */
.gd-sec-h{ font-family:var(--f-display); font-size:17px; font-weight:600; letter-spacing:-.01em; color:var(--text); margin:28px 0 10px; }
.gd-panel > .gd-sec-h:first-child{ margin-top:0; }
/* Level / runtime / step count, straight off the record. */
.gd-meta-row{ font-family:var(--f-mono); font-size:11.5px; letter-spacing:.08em; text-transform:uppercase; color:var(--text-3); margin:0 0 18px; }

/* Overview tab */
.gd-desc{ font-size:15px; color:var(--text-2); line-height:1.7; margin:0; }
.gd-learn-list{ list-style:none; padding:0; margin:0; display:flex; flex-direction:column; gap:9px; }
.gd-learn-list li{ font-size:15px; color:var(--text-2); line-height:1.6; padding-left:24px; position:relative; }
.gd-learn-list li::before{ content:'\2713'; position:absolute; left:0; top:0; color:var(--accent); font-size:13px; font-weight:700; }
.gd-topics{ margin-bottom:4px; }
.gd-detail .gd-article{ margin-top:28px; }

/* Instructions tab: numbered step cards on a 24px rhythm — mono step counter, heading,
   body, then the authored screenshot with the step title as its caption. */
.gd-step{ display:block; padding:24px 0; border-bottom:1px solid var(--border); }
.gd-step:first-child{ padding-top:4px; }
.gd-step:last-child{ border-bottom:none; }
.gd-step-num{ font-family:var(--f-mono); font-size:11px; font-weight:600; letter-spacing:.1em; text-transform:uppercase; color:var(--text-3); margin:0 0 8px; }
.gd-step-title{ font-family:var(--f-display); font-size:17px; font-weight:600; margin:0 0 8px; letter-spacing:-.2px; }
.gd-step-desc{ font-size:15px; color:var(--text-2); line-height:1.7; margin:0 0 16px; }
.gd-step-desc:last-child{ margin-bottom:0; }
.gd-step-media{ margin:0; max-width:520px; }
.gd-step-media img{ width:100%; height:auto; display:block; border:.5px solid var(--border); border-radius:var(--r-sm); }
.gd-step-cap{ font-size:12.5px; color:var(--text-3); margin:8px 0 0; }
.gd-step-ph{ font-size:12px; color:#aaa; letter-spacing:.03em; }

/* "More guides like this": the hub's own card, four up, under the tab panels. */
.gd-more{ margin-top:44px; }
.gd-more .gd-sec-h{ margin:0 0 14px; }
/* --gc-row stays at the hub's 205px: the card's foot block is absolutely positioned against
   the media, and a shorter row pushes it up into the duration pill. */
/* Three across like the hub (sweep 2026-09-13): four 272px cards clamped 2-4 of their titles
   mid-word at 1440; the hub's 375px cards show the same titles in full. */
.gd-more-grid{ grid-template-columns:repeat(3, 1fr); margin-top:0; }

@media (max-width:1100px){ .gd-more-grid{ grid-template-columns:repeat(2, 1fr); } }
@media (max-width:900px){
  .gd-volume{ display:none; }
}
/* The bar has to fit inside a 351px frame at 375, so the controls tighten and
   picture-in-picture (the one control with a system alternative) drops out. */
@media (max-width:700px){
  .gd-more-grid{ grid-template-columns:1fr; }
  /* The bar DOCKS UNDER the frame here. Floating it inside a 319x179 stage put it over the
     poster and across the 72px play circle (measured on guide-1-top-375.png). The wrap stops
     being the aspect box — .gd-frame takes that over — and becomes a column: picture, then
     controls, sharing one rounded outline. */
  .gd-video-wrap{ aspect-ratio:auto; background:transparent; border-radius:0; overflow:visible; }
  .gd-frame{ position:relative; inset:auto; aspect-ratio:16/9; background:#0a0b0d; border-radius:var(--r-md) var(--r-md) 0 0; overflow:hidden; }
  .gd-controls{
    position:static; gap:0; padding:6px 4px; flex-wrap:wrap; row-gap:4px;
    border-radius:0 0 var(--r-md) var(--r-md); border-top:0; box-shadow:none;
  }
  /* Nothing to uncover, so nothing hides: the auto-hide exists to get the bar off the
     picture, and docked it never is. The class still toggles; it just has no effect. */
  .gd-video-wrap.is-idle .gd-controls{ opacity:1; pointer-events:auto; }
  .gd-ctrl{ width:40px; height:40px; }
  .gd-controls [data-gd="pip"]{ display:none; }
  .gd-time{ font-size:11px; padding:0 2px; }
  /* Two rows. One row of buttons leaves the scrubber about 50px of a 319px frame — measured
     — which is not a drag target, so the scrubber and the clock take the first line and the
     buttons take the second. The pair is sized to exactly 100% so nothing else can join
     them: 84px is the clock's widest form (gdFmt never emits hours), and with 40px targets
     the six buttons below need 240 of the 311 available at 375. */
  .gd-scrub{ order:-1; flex:0 0 calc(100% - 88px); margin:2px 0 4px; }
  .gd-time{ order:-1; flex:0 0 84px; text-align:right; }
  .gd-complete-btn{ height:44px; }
  /* .btn-sm is 38px at this width (app.css); the composer's Post button is a primary tap
     target on this page, so it joins the 40px floor the rest of the row keeps. */
  .gd-cmt-compose .btn{ min-height:40px; }
  .gd-actions{ gap:12px; }
  /* Three tabs at 375 land within a pixel of the right edge; let the strip scroll rather
     than clip. Scoped to the detail page — /tool/:id and the briefings keep two tabs. */
  .gd-detail .gd-tabs{ overflow-x:auto; scrollbar-width:none; }
  .gd-detail .gd-tabs::-webkit-scrollbar{ display:none; }
  .gd-detail .gd-tab{ white-space:nowrap; }
}
/* Fullscreen (owner report 2026-09-16, "the video does not expand"). The wrap is what goes
   fullscreen (app.js initGuidePlayer), and below 700px it is a column — frame, then a docked
   bar — with the frame pinned to 16:9. Fullscreen on a phone therefore showed a 16:9 strip at
   the top of a black screen and read as a stalled picture with the sound still going. Here
   the wrap fills the screen at every width, the frame takes whatever height the bar leaves
   and letterboxes the picture inside it (object-fit: contain), and the bar keeps its own
   layout: overlaid on desktop, docked on phones. Same rules under the -webkit- alias for
   Safari on iPad; iPhone has no element fullscreen and app.js falls back to the native
   player there. */
.gd-video-wrap:fullscreen, .gd-video-wrap:-webkit-full-screen{
  display:flex; flex-direction:column; width:100%; height:100%;
  aspect-ratio:auto; background:#000; border-radius:0; overflow:hidden; margin:0;
}
.gd-video-wrap:fullscreen .gd-frame, .gd-video-wrap:-webkit-full-screen .gd-frame{
  position:relative; inset:auto; flex:1 1 auto; min-height:0;
  aspect-ratio:auto; background:#000; border-radius:0; overflow:hidden;
}
.gd-video-wrap:fullscreen .gd-video, .gd-video-wrap:-webkit-full-screen .gd-video,
.gd-video-wrap:fullscreen .gd-video-placeholder, .gd-video-wrap:-webkit-full-screen .gd-video-placeholder{
  width:100%; height:100%; object-fit:contain;
}
.gd-video-wrap:fullscreen .gd-controls, .gd-video-wrap:-webkit-full-screen .gd-controls{
  flex:0 0 auto; border-radius:0;
}

/* ================================================================
   News section v2 — dark hero + auto-scroll image-card ticker
   ================================================================ */

/* Hero: dark editorial card, slide-up reveal on scroll-into-view */
.ms-news-hero{
  display: block;
  background: #0a0b0d;
  color: #fff;
  border-radius: 18px;
  padding: 36px 44px 32px;
  text-decoration: none;
  margin: 8px 0 28px;
  position: relative;
  transition: transform .3s cubic-bezier(.16,1,.3,1), box-shadow .3s ease;
  opacity: 0;
  transform: translateY(40px);
  filter: blur(8px);
}
.ms-news-hero.is-in{
  opacity: 1;
  transform: translateY(0);
  filter: blur(0);
  transition:
    opacity .7s cubic-bezier(.16,1,.3,1),
    transform .7s cubic-bezier(.16,1,.3,1),
    filter .7s cubic-bezier(.16,1,.3,1);
}
.ms-news-hero:hover{
  transform: translateY(-2px);
  box-shadow: 0 18px 48px rgba(0,0,0,0.18);
}
.ms-news-hero-eyebrow{
  display: flex;
  align-items: center;
  gap: 14px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.65);
  margin: 0 0 18px;
}
.ms-news-hero-rule{
  display: inline-block;
  width: 44px;
  height: 1px;
  background: rgba(255,255,255,0.35);
}
.ms-news-hero-title{
  font-size: clamp(28px, 3.4vw, 40px);
  line-height: 1.15;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin: 0 0 14px;
  max-width: 22ch;
  color: #fff;
}
.ms-news-hero-sub{
  font-size: 15px;
  line-height: 1.55;
  color: rgba(255,255,255,0.72);
  margin: 0;
  max-width: 56ch;
}
.ms-news-hero-cta{
  position: absolute;
  right: 36px;
  bottom: 28px;
  display: inline-flex;
  align-items: center;
  padding: 12px 22px;
  background: #fff;
  color: #0a0b0d;
  border-radius: 999px;
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
  transition: background .2s ease, transform .2s ease;
}
.ms-news-hero:hover .ms-news-hero-cta{ background: #f2f2f4; transform: translateX(2px); }

/* Ticker wrap — clips overflow on both sides */
.ms-news-ticker-wrap{
  overflow: hidden;
  position: relative;
  padding: 6px 0 4px;
  mask-image: linear-gradient(to right, transparent, #000 48px, #000 calc(100% - 48px), transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 48px, #000 calc(100% - 48px), transparent);
}
.ms-news-ticker{
  display: flex;
  flex-wrap: nowrap;
  gap: 16px;
  width: max-content;
  animation: ms-news-scroll 60s linear infinite;
  will-change: transform;
}
@keyframes ms-news-scroll{
  0%   { transform: translateX(0); }
  100% { transform: translateX(-50%); }
}

/* Card — fixed size, image + date + title */
.ms-news-card{
  flex: 0 0 220px;
  width: 220px;
  height: 240px;
  display: flex;
  flex-direction: column;
  background: #fff;
  border-radius: 12px;
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  border: 1px solid rgba(0,0,0,0.06);
  transition: transform .25s cubic-bezier(.16,1,.3,1), box-shadow .25s ease;
}
.ms-news-card:hover{
  transform: translateY(-3px);
  box-shadow: 0 14px 32px rgba(0,0,0,0.10);
}
.ms-news-card-img{
  height: 150px;
  background-size: cover;
  background-position: center;
  background-color: #efeff2;
  transition: transform .4s cubic-bezier(.16,1,.3,1);
}
.ms-news-card:hover .ms-news-card-img{ transform: scale(1.04); }
.ms-news-card-body{
  flex: 1;
  padding: 10px 14px 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.ms-news-card-date{
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-3);
  margin: 0;
}
.ms-news-card-title{
  font-size: 14px;
  line-height: 1.3;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--text);
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Reduced motion: pause ticker, simplify hero reveal */
@media (prefers-reduced-motion: reduce){
  .ms-news-ticker{ animation: none; }
  .ms-news-hero{ opacity: 1; transform: none; filter: none; }
  .ms-news-card:hover{ transform: none; }
  .ms-news-card:hover .ms-news-card-img{ transform: none; }
}

/* Mobile: smaller hero padding */
@media (max-width: 768px){
  .ms-news-hero{ padding: 28px 24px 88px; }
  .ms-news-hero-cta{ right: 24px; bottom: 24px; }
  .ms-news-hero-eyebrow{ gap: 10px; }
  .ms-news-hero-rule{ width: 28px; }
}

/* News inline expand */
#ms-sec-news.is-expanded .ms-news-hero{ margin-bottom: 8px; }
.ms-news-expanded{ margin-top: 24px; }
.ms-news-rows{ display: flex; flex-direction: column; gap: 1px; border: 1px solid var(--ms-line); border-radius: 14px; overflow: hidden; background: var(--ms-surface); }
.ms-news-row{
  display: grid; grid-template-columns: 72px 1fr; gap: 14px;
  align-items: center; padding: 14px 16px;
  border-bottom: 1px solid var(--ms-line-soft);
  text-decoration: none; color: inherit;
  transition: background 0.15s;
}
.ms-news-row:last-child{ border-bottom: none; }
.ms-news-row:hover{ background: var(--ms-bg); }
.ms-news-row-img{ width:72px; height:52px; border-radius:8px; background-size:cover; background-position:center; flex-shrink:0; }
.ms-news-row-date{ font-size:12px; color:var(--ms-ink-3); margin:0 0 3px; }
.ms-news-row-title{ font-size:14px; font-weight:600; letter-spacing:-0.01em; margin:0 0 4px; color:var(--ms-ink); }
.ms-news-row-sum{ font-size:13px; color:var(--ms-ink-3); margin:0; line-height:1.4; }
.ms-news-show-more{
  margin-top: 12px; display: block; width: 100%;
  padding: 13px; border: 1px solid var(--ms-line); border-radius: 10px;
  background: var(--ms-surface); font-size: 14px; font-weight: 500;
  color: var(--ms-ink-2); cursor: pointer; text-align: center;
  transition: background 0.15s, color 0.15s;
}
.ms-news-show-more:hover{ background: var(--ms-bg); color: var(--ms-ink); }
/* news card is now a button — reset button styles */
.ms-news-card{ border: none; background: none; padding: 0; cursor: pointer; text-align: left; }

/* =========================================================
   Hub controls — search + category + sort row on /agents.
   Designed for the dashShell skin: light bg, single accent.
   ========================================================= */
/* Agents hub view switcher (Agents / Editor's picks, then the Free agents and Written guides links) — in-flow pill row.
   Sits left-aligned above the search controls; wraps on narrow widths so it never
   collides with the hero CTA (the old -56px pull-up overlapped "Open playground"). */
.agents-view-pills{
  display: flex;
  justify-content: flex-start;
  flex-wrap: wrap;
  gap: 4px;
  margin: 8px 0 16px;
}
.agents-view-pill{
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px;
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  background: #fff;
  font: inherit;
  font-size: 13px;
  color: var(--text-2);
  cursor: pointer;
}
.agents-view-pill svg{ width: 16px; height: 16px; }
.agents-view-pill:hover{ border-color: var(--text-3); }
.agents-view-pill.active{ color: var(--text); border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-tint); }
@media (max-width: 760px){ .agents-view-pills{ margin-top: 4px; justify-content: flex-start; flex-wrap: wrap; } }

.hub-controls{
  display: flex;
  gap: 12px;
  align-items: center;
  margin: 12px 0 28px;
  flex-wrap: wrap;
}
.hub-search{
  flex: 1 1 320px;
  min-width: 240px;
  position: relative;
}
.hub-search > svg{
  position: absolute;
  left: 14px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-2);
  pointer-events: none;
  width: 18px;
  height: 18px;
}
/* WU-4: --r-lg, not --r-pill. In comapre_01.png the search box is a rounded rectangle the
   same shape as the toolbar strip beside it, not a stadium. */
.hub-search-input{
  width: 100%;
  padding: 13px 16px 13px 44px;
  border: .5px solid var(--border);
  border-radius: var(--r-lg);
  background: #fff;
  font: inherit;
  font-size: 14px;
  color: var(--text);
  box-sizing: border-box;
}
.hub-search-input:focus{
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}
.hub-select{
  padding: 12px 36px 12px 16px;
  border: 1px solid var(--border);
  border-radius: var(--r-pill);
  background: #fff
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23424245' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>")
    no-repeat right 14px center;
  font: inherit;
  color: var(--text);
  cursor: pointer;
  min-width: 160px;
  appearance: none;
  -webkit-appearance: none;
  box-sizing: border-box;
}
.hub-select:focus{
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-tint);
}

.search-suggest{
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  right: 0;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  box-shadow: var(--sh-2);
  max-height: 420px;
  overflow: auto;
  z-index: 50;
  padding: 6px 0;
}
.search-suggest[hidden]{ display: none; }
.search-suggest-group-label{
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-2);
  padding: 10px 14px 4px;
}
.search-suggest-row{
  display: flex;
  gap: 10px;
  align-items: center;
  padding: 8px 14px;
  cursor: pointer;
  color: var(--text);
  text-decoration: none;
}
.search-suggest-row:hover,
.search-suggest-row[aria-selected="true"]{
  background: var(--bg-2);
}
.search-suggest-row .type-chip{
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-2);
  background: var(--bg-2);
  border: 1px solid var(--border);
  border-radius: var(--r-xs);
  padding: 2px 6px;
  flex: 0 0 auto;
}
.search-suggest-row[aria-selected="true"] .type-chip{ background: #fff; }
.search-suggest-row .name{ font-weight: 500; }

@media (max-width: 640px){
  .hub-controls{ gap: 8px; }
  .hub-select{ min-width: 0; flex: 1 1 140px; }
}

/* ============================================================
   JOBS TEASER — home page scroll section
   ============================================================ */
.ms-sec-jobs { background: #f9f9f9; }
.ms-jobs-teaser { display:flex; align-items:center; gap:40px; padding: 8px 0; }
.ms-jobs-teaser-left { flex:1; }
.ms-jobs-teaser-right { position:relative; width:380px; flex-shrink:0; height:280px; }
.ms-jobs-hero-img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; border-radius:16px; }
.ms-jobs-explore-link { font-size:15px; font-weight:600; color:var(--accent); display:inline-flex; align-items:center; gap:5px; text-decoration:none; margin-top:20px; transition:gap 0.2s; }
.ms-jobs-explore-link:hover { gap:10px; }
.ms-jobs-read-brief { position:absolute; top:0; right:0; }
.ms-jobs-float-card { position:absolute; background:#fff; border:1px solid #ebebeb; border-radius:12px; padding:12px 14px; display:flex; align-items:center; gap:10px; font-size:13px; box-shadow:0 4px 16px rgba(0,0,0,0.06); text-decoration:none; color:var(--text-1); }
.ms-jobs-float-card--l { top:20px; left:0; transition:opacity 0.5s cubic-bezier(.16,1,.3,1), transform 0.5s cubic-bezier(.16,1,.3,1); }
.ms-jobs-float-card--r { bottom:0; right:0; transition:opacity 0.5s 0.12s cubic-bezier(.16,1,.3,1), transform 0.5s 0.12s cubic-bezier(.16,1,.3,1); }
.ms-jobs-teaser-right:not(.is-in) .ms-jobs-float-card--l { opacity:0; transform:translateX(-14px); }
.ms-jobs-teaser-right:not(.is-in) .ms-jobs-float-card--r { opacity:0; transform:translateX(14px); }
.ms-jobs-teaser-right.is-in .ms-jobs-float-card--l,
.ms-jobs-teaser-right.is-in .ms-jobs-float-card--r { opacity:1; transform:translateX(0); }
.ms-jobs-card-title { font-size:13px; font-weight:600; color:var(--text-1); margin-bottom:2px; }
.ms-jobs-card-sub { font-size:12px; color:var(--text-3); }
.ms-jobs-card-chevron { color:var(--text-3); font-size:18px; margin-left:auto; flex-shrink:0; }
.ms-jobs-card-tags { display:flex; gap:5px; flex-wrap:wrap; margin-top:5px; }

/* ============================================================
   Weekly digest section (home scrollview)
   ============================================================ */
.ms-weekly-stories { list-style:none; margin:32px 0 0; padding:0; display:grid; gap:0; max-width:760px; }
.ms-weekly-story { display:flex; gap:18px; padding:20px 0; border-top:1px solid #ececec; }
.ms-weekly-story:last-child { border-bottom:1px solid #ececec; }
.ms-weekly-rank { font-family:var(--f-display); font-size:15px; font-weight:600; color:var(--accent); width:24px; flex-shrink:0; }
.ms-weekly-story-body { flex:1; }
.ms-weekly-story-head { font-family:var(--f-display); font-size:18px; font-weight:600; letter-spacing:-0.01em; margin:0 0 4px; color:var(--text-1); }
.ms-weekly-story-brief { font-size:14px; line-height:1.6; color:var(--text-3); margin:0 0 6px; }
.ms-weekly-source { font-size:12px; color:var(--text-3); text-transform:uppercase; letter-spacing:0.04em; }
.ms-weekly-stats { display:grid; grid-template-columns:repeat(4, 1fr); gap:1px; background:#ececec; border:1px solid #ececec; border-radius:12px; overflow:hidden; margin-top:40px; }
.ms-weekly-stat { background:#fff; padding:22px 20px; }
.ms-weekly-stat-value { font-family:var(--f-display); font-size:28px; font-weight:600; letter-spacing:-0.02em; color:var(--text-1); }
.ms-weekly-stat-label { font-size:13px; color:var(--text-3); margin-top:4px; }
.ms-weekly-stat-trend { font-size:12px; color:var(--accent); margin-top:8px; }
@media (max-width: 760px){ .ms-weekly-stats { grid-template-columns:repeat(2, 1fr); } }

/* ============================================================
   Guide article lessons (/guide/6-9) — long-form lesson bodies
   rendered by viewGuideVideo's lesson branch inside .gd-article.
   Content markup comes from the bundled lessons (data.js);
   styling maps that vocabulary onto the live design tokens.
   ============================================================ */
.gd-article{ max-width:760px; font-size:16px; line-height:1.7; color:var(--text-2); }
.gd-article p{ margin:0 0 16px; }
.gd-article p.lead{ font-size:18px; color:var(--text); }
.gd-article a{ color:var(--accent); text-decoration:none; }
.gd-article a:hover{ text-decoration:underline; }
.gd-article strong, .gd-article b{ color:var(--text); }
.gd-article h2{ font-family:var(--f-display); font-size:26px; font-weight:600; letter-spacing:-0.014em; color:var(--text); margin:44px 0 14px; display:flex; align-items:center; gap:12px; }
.gd-article h3{ font-family:var(--f-display); font-size:18px; font-weight:600; letter-spacing:-0.01em; color:var(--text); margin:26px 0 10px; }
.gd-article .section-num{ flex-shrink:0; width:28px; height:28px; border-radius:8px; background:var(--accent-tint); color:var(--accent); font-size:14px; font-weight:600; display:inline-flex; align-items:center; justify-content:center; }
.gd-article ul, .gd-article ol{ margin:0 0 16px; padding-left:24px; }
.gd-article li{ margin:0 0 8px; }
.gd-article code{ font-family:"SF Mono", ui-monospace, Menlo, Consolas, monospace; font-size:13.5px; background:var(--bg-2); border-radius:6px; padding:2px 6px; color:var(--text); }
.gd-article pre{ background:var(--bg-2); border:1px solid var(--border); border-radius:12px; padding:16px; overflow-x:auto; margin:0 0 16px; }
.gd-article pre code{ background:none; padding:0; }

/* TL;DR + recap panels */
.gd-article .tldr{ background:var(--bg-2); border-left:3px solid var(--accent); border-radius:12px; padding:16px 20px; margin:0 0 24px; }
.gd-article .tldr h2{ font-size:13px; text-transform:uppercase; letter-spacing:0.06em; color:var(--text-3); margin:0 0 8px; }
.gd-article .tldr p{ margin:0; }
.gd-article .recap{ background:var(--bg-2); border-radius:12px; padding:22px 24px; margin:40px 0 0; }
.gd-article .recap h2{ font-size:20px; margin:0 0 12px; }
.gd-article .recap ul{ margin:0; }

/* Table of contents */
.gd-article .toc{ border:1px solid var(--border); border-radius:12px; padding:16px 20px; margin:0 0 28px; }
.gd-article .toc h2{ font-size:13px; text-transform:uppercase; letter-spacing:0.06em; color:var(--text-3); margin:0 0 10px; }
.gd-article .toc ol{ margin:0; padding-left:20px; }
.gd-article .toc li{ margin:0 0 6px; }
.gd-article .toc a{ color:var(--text-2); }
.gd-article .toc a:hover{ color:var(--accent); text-decoration:none; }

/* Numbered step lists */
.gd-article ol.steps{ list-style:none; counter-reset:gd-steps; padding-left:0; margin:0 0 20px; }
.gd-article ol.steps > li{ counter-increment:gd-steps; position:relative; padding:0 0 18px 48px; margin:0; }
.gd-article ol.steps > li::before{ content:counter(gd-steps); position:absolute; left:0; top:0; width:32px; height:32px; border-radius:50%; background:var(--accent-tint); color:var(--accent); font-weight:600; font-size:14px; display:flex; align-items:center; justify-content:center; }
.gd-article ol.steps > li h3{ margin:4px 0 6px; }

/* Callouts — one quiet semantic edge each, white surface */
.gd-article .callout{ background:var(--surface); border:1px solid var(--border); border-left:3px solid var(--accent); border-radius:12px; padding:14px 18px; margin:0 0 18px; }
.gd-article .callout p{ margin:0 0 8px; }
.gd-article .callout p:last-child{ margin:0; }
.gd-article .callout .ctitle{ font-weight:600; color:var(--text); font-size:14px; }
.gd-article .callout.tip{ border-left-color:var(--success); }
.gd-article .callout.warn{ border-left-color:var(--warning); }

/* "For the 20%" collapsibles + FAQ */
.gd-article details{ border:1px solid var(--border); border-radius:12px; padding:0; margin:0 0 14px; }
.gd-article details summary{ cursor:pointer; padding:14px 18px; font-weight:600; color:var(--text); list-style:none; }
.gd-article details summary::-webkit-details-marker{ display:none; }
.gd-article details summary::before{ content:"+"; color:var(--text-3); margin-right:10px; }
.gd-article details[open] summary::before{ content:"\2212"; }
.gd-article details .body, .gd-article details .answer{ padding:0 18px 16px; }
.gd-article .techie .chip{ display:inline-block; background:var(--accent-tint); color:var(--accent); font-size:12px; font-weight:600; border-radius:999px; padding:2px 10px; margin-right:8px; }
.gd-article .faq details summary{ font-weight:500; }

/* Pull quote */
.gd-article .bigidea{ font-family:var(--f-display); font-size:22px; font-weight:600; letter-spacing:-0.014em; line-height:1.4; color:var(--text); border-left:3px solid var(--accent); padding:4px 0 4px 20px; margin:28px 0; }

/* Tables */
.gd-article .table-wrap{ overflow-x:auto; border:1px solid var(--border); border-radius:12px; margin:0 0 20px; }
.gd-article table{ width:100%; border-collapse:collapse; font-size:14px; }
.gd-article th{ text-align:left; background:var(--bg-2); color:var(--text); font-weight:600; padding:10px 14px; border-bottom:1px solid var(--border); }
.gd-article td{ padding:10px 14px; border-bottom:1px solid var(--border); vertical-align:top; }
.gd-article tr:last-child td{ border-bottom:0; }

/* Next lesson + footer */
.gd-article .next-up{ margin:28px 0 0; }
.gd-article .next-card{ display:flex; align-items:center; justify-content:space-between; gap:16px; border:1px solid var(--border); border-radius:12px; padding:16px 20px; color:var(--text); }
.gd-article .next-card:hover{ border-color:var(--accent); text-decoration:none; }
.gd-article .next-card .nlabel{ font-size:12px; text-transform:uppercase; letter-spacing:0.06em; color:var(--text-3); margin-bottom:2px; }
.gd-article .next-card .ntitle{ font-weight:600; }
.gd-article .next-card .arrow{ color:var(--accent); font-size:18px; }
.gd-article .lesson-foot{ margin:28px 0 0; font-size:13px; color:var(--text-3); border-top:1px solid var(--border); padding-top:16px; }

/* Token Optimizer widget (lesson 9) — markup IDs owned by initTokenTool() */
.gd-article .tool{ border:1px solid var(--border); border-radius:14px; margin:0 0 24px; overflow:hidden; }
.gd-article .tool-head{ display:flex; align-items:center; gap:12px; background:var(--bg-2); border-bottom:1px solid var(--border); padding:12px 18px; font-size:14px; color:var(--text); }
.gd-article .dotrow{ display:inline-flex; gap:5px; }
.gd-article .dotrow i{ width:9px; height:9px; border-radius:50%; background:var(--border); }
.gd-article .tool-body{ padding:20px; }
.gd-article .tool-body textarea{ width:100%; min-height:120px; resize:vertical; border:1px solid var(--border); border-radius:10px; padding:12px 14px; font:inherit; font-size:14px; color:var(--text); background:var(--surface); box-sizing:border-box; }
.gd-article .tool-body textarea:focus{ outline:none; border-color:var(--accent); }
.gd-article .tool-actions{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-top:12px; }
.gd-article .tool-actions .hint{ font-size:13px; color:var(--text-3); margin-left:auto; }
.gd-article .tok-grid{ display:grid; grid-template-columns:repeat(auto-fill, minmax(160px, 1fr)); gap:10px; margin:10px 0 0; }
.gd-article .tok-card{ border:1px solid var(--border); border-radius:10px; padding:12px 14px; }
.gd-article .tok-card .m{ font-size:13px; font-weight:600; color:var(--text); }
.gd-article .tok-card .n{ font-family:var(--f-display); font-size:24px; font-weight:600; color:var(--text); margin:4px 0 2px; }
.gd-article .tok-card .sub{ font-size:12px; color:var(--text-3); }
.gd-article .badge-exact, .gd-article .badge-est{ font-size:12px; font-weight:600; text-transform:uppercase; letter-spacing:0.04em; border-radius:999px; padding:1px 7px; vertical-align:middle; }
.gd-article .badge-exact{ background:rgba(31,138,59,0.1); color:var(--success); }
.gd-article .badge-est{ background:var(--bg-2); color:var(--text-3); }
.gd-article .meter{ height:8px; border-radius:999px; background:var(--bg-2); overflow:hidden; margin:0 0 6px; }
.gd-article .meter i{ display:block; height:100%; border-radius:999px; background:var(--accent); transition:width .3s ease; }
.gd-article mark.filler{ background:rgba(192,53,43,0.08); color:var(--danger); text-decoration:line-through; border-radius:3px; padding:0 2px; }
.gd-article .optimized{ background:var(--bg-2); border:1px solid var(--border); border-radius:10px; padding:14px 16px; font-family:"SF Mono", ui-monospace, Menlo, Consolas, monospace; font-size:13.5px; line-height:1.7; white-space:pre-wrap; }
.gd-article .savings-pill{ display:inline-block; background:rgba(31,138,59,0.1); color:var(--success); font-size:12px; font-weight:600; border-radius:999px; padding:2px 10px; }

/* Audit result — per-task breakdown rows (B2 audit engine). Type does the work:
   thin border-left accent, no gradients/glow per hard rules. */
.audit-bar{ padding:12px 0 12px 14px; border-left:2px solid var(--border); margin-bottom:10px; }
.audit-bar:last-child{ margin-bottom:0; }
.audit-bar .audit-bar-task{ font-weight:500; color:var(--text); margin-right:8px; }
.audit-bar .audit-bar-why{ font-size:13px; margin:6px 0 0; line-height:1.5; }

/* Audit result — RAG augmentability meter (single-role flow). Single accent,
   thin track, no gradients/glow per hard rules. */
.audit-meters{ display:grid; gap:12px; }
.audit-meter{ display:grid; grid-template-columns:160px 1fr 44px; gap:14px; align-items:center; font-size:14px; }
.audit-meter-task{ color:var(--text-2); }
.audit-track{ height:6px; background:var(--bg-2); border-radius:999px; overflow:hidden; }
.audit-fill{ height:100%; width:0; background:var(--accent); border-radius:999px; transition:width .7s cubic-bezier(.2,.7,.2,1); }
.audit-pct{ text-align:right; font-variant-numeric:tabular-nums; color:var(--text-2); }
@media (max-width:600px){ .audit-meter{ grid-template-columns:110px 1fr 40px; gap:10px; } }
/* Audit score meter (replaces the risk-ring for the inline result) */
.audit-score{ display:inline-flex; align-items:baseline; gap:10px; font-family:var(--f-display,inherit); }
.audit-score b{ font-size:48px; font-weight:600; color:var(--accent); line-height:1; font-variant-numeric:tabular-nums; }
.audit-score .audit-score-unit{ font-size:15px; color:var(--text-2); }
.audit-pillrow{ display:flex; gap:8px; flex-wrap:wrap; align-items:center; margin-top:4px; }
/* RAG guide cards (agents / read-next) — calm, single hairline, no tint */
.audit-guide-grid{ display:grid; gap:16px; margin-top:16px; }
.audit-guide-grid.cols-3{ grid-template-columns:repeat(3,1fr); }
.audit-guide-grid.cols-2{ grid-template-columns:repeat(2,1fr); }
.audit-guide-card{ border:1px solid var(--border); border-radius:12px; padding:16px; background:var(--surface); }
@media (max-width:760px){ .audit-guide-grid.cols-3, .audit-guide-grid.cols-2{ grid-template-columns:1fr; } }

/* ===== Full audit report (ported from standalone.html, recolored to var(--accent)) ===== */
.audit-report{ --ar-violet:#6D5BD0; --ar-amber:#B8860B; --ar-mint:#1E8A5A; --ar-rose:#C8324A; }
.audit-report .ar-card{ background:var(--surface,#fff); border:1px solid var(--border,rgba(0,0,0,.08)); border-radius:14px; padding:20px; }
.audit-report .ar-card-lg{ padding:28px; border-radius:20px; }
.audit-report .ar-card-flat{ background:var(--bg-2); }
.audit-report .ar-score{ display:inline-flex; align-items:baseline; gap:8px; font-family:var(--f-mono,ui-monospace,SFMono-Regular,Menlo,monospace); }
.audit-report .ar-score b{ font-size:48px; font-weight:600; color:var(--accent); line-height:1; font-variant-numeric:tabular-nums; }
.audit-report .ar-pill{ display:inline-flex; align-items:center; gap:6px; padding:4px 10px; border-radius:999px; font-size:12px; font-weight:500; background:var(--surface-2,#fff); color:var(--text-2); border:1px solid var(--border,rgba(0,0,0,.08)); }
.audit-report .ar-bars{ display:grid; gap:12px; margin-top:26px; }
.audit-report .ar-bar{ display:grid; grid-template-columns:140px 1fr 40px; gap:12px; align-items:center; font-size:13px; }
.audit-report .ar-bar .ar-task{ color:var(--text-2); text-transform:capitalize; text-align:left; }
.audit-report [data-ar-desc]{ text-align:left; }
.audit-report .ar-track{ height:6px; background:var(--surface-2,#f2f2ee); border-radius:999px; overflow:hidden; }
.audit-report .ar-fill{ height:100%; width:0%; background:var(--accent); border-radius:999px; transition:width .8s cubic-bezier(.34,1.56,.64,1); }
.audit-report .ar-fill.warn{ background:var(--ar-amber); }
.audit-report .ar-fill.risk{ background:var(--ar-rose); }
.audit-report .ar-fill.human{ background:color-mix(in srgb, var(--accent) 70%, #1E8A5A); }
/* AI/human toggle pills */
.audit-report .ar-toggle{ display:inline-flex; gap:4px; padding:3px; border-radius:999px; background:var(--surface-2,#f2f2ee); border:1px solid var(--border,rgba(0,0,0,.08)); }
.audit-report .ar-tpill{ border:0; background:transparent; color:var(--text-2); font-size:12px; font-weight:500; padding:5px 12px; border-radius:999px; cursor:pointer; transition:background .15s, color .15s; }
.audit-report .ar-tpill:hover{ color:var(--text); }
.audit-report .ar-tpill.is-on{ background:var(--accent); color:var(--ink-text); }
/* full-report grid below the score card is single-column (aside lives in the card) */
/* (0,3,0) so it beats `.audit-report .ar-grid` below: a single grid has no aside to keep
   320px for, so it spans the row (owner 2026-10-14, "extend till the end"). */
.audit-report .ar-grid.ar-grid-single{ grid-template-columns:minmax(0,1fr); }
/* /audit report header */
.ar-head{ display:flex; justify-content:space-between; align-items:flex-start; gap:24px; flex-wrap:wrap; }
.ar-head-title{ margin:8px 0 0; font-family:var(--f-display,inherit); font-weight:600; font-size:clamp(30px,4vw,46px); line-height:1.05; letter-spacing:-0.02em; }
/* Owner 2026-10-14: no dash, the role on its own line in plain bold black. */
.ar-head-title em{ display:block; font-style:normal; font-weight:700; color:var(--text); }
.audit-report .ar-pct{ font-family:var(--f-mono,ui-monospace,SFMono-Regular,Menlo,monospace); text-align:right; }
.audit-report .ar-note{ font-size:12px; color:var(--text-3); margin-top:8px; }
.audit-report .ar-grid{ display:grid; grid-template-columns:1fr 320px; gap:32px; align-items:stretch; margin-top:24px; }
.audit-report .ar-stack > * + *{ margin-top:20px; }
.audit-report .ar-grid-2{ display:grid; grid-template-columns:repeat(2,1fr); gap:16px; margin-top:20px; }
.audit-report .ar-grid-3{ display:grid; grid-template-columns:repeat(3,1fr); gap:16px; margin-top:20px; }
.audit-report .ar-agent-icon{ width:40px; height:40px; border-radius:10px; display:inline-flex; align-items:center; justify-content:center; background:var(--surface-2,#f2f2ee); color:var(--text-2); margin-bottom:12px; }
.audit-report .ar-agent-icon svg{ width:20px; height:20px; }
.audit-report .ar-agent-icon.accent{ background:color-mix(in srgb, var(--accent) 15%, transparent); color:var(--accent); }
.audit-report .ar-agent-icon.violet{ background:color-mix(in srgb, var(--ar-violet) 15%, transparent); color:var(--ar-violet); }
.audit-report .ar-agent-icon.mint{ background:color-mix(in srgb, var(--ar-mint) 15%, transparent); color:var(--ar-mint); }
.audit-report .ar-prose ol{ padding-left:20px; margin:0; line-height:1.7; }
.audit-report .ar-prose li{ margin-bottom:10px; color:var(--text-2); }
.audit-report .ar-prose li b{ color:var(--text); }
.audit-report .ar-save{ display:inline-flex; align-items:center; gap:8px; padding:10px 18px; border-radius:999px; background:transparent; border:1px solid var(--border,rgba(0,0,0,.08)); color:var(--text-2); cursor:pointer; font-size:14px; font-weight:500; transition:color .15s, border-color .15s; }
.audit-report .ar-save:hover{ color:var(--text); border-color:var(--text-3); }
.audit-report .ar-save svg{ width:16px; height:16px; }
.audit-report .ar-save.saved{ color:var(--accent); border-color:color-mix(in srgb, var(--accent) 40%, var(--border,rgba(0,0,0,.08))); }
.audit-report .ar-save.saved svg{ fill:var(--accent); }
@keyframes ar-fadeUp{ from{ opacity:0; transform:translateY(12px); } to{ opacity:1; transform:none; } }
.audit-report .ar-reveal{ animation:ar-fadeUp .5s cubic-bezier(.2,.7,.2,1) both; }
.audit-report .ar-reveal-2{ animation:ar-fadeUp .5s .1s cubic-bezier(.2,.7,.2,1) both; }
.audit-report .ar-reveal-3{ animation:ar-fadeUp .5s .2s cubic-bezier(.2,.7,.2,1) both; }
@media (max-width:900px){ .audit-report .ar-grid{ grid-template-columns:1fr; } }
@media (max-width:700px){ .audit-report .ar-grid-2, .audit-report .ar-grid-3{ grid-template-columns:1fr; } }
/* The audit's "What next" view (WU-A, 2026-10-02): three researched steps, each a numbered
   how-to with its sources, then agent and guide cards that are real links. One hover cue on
   a linked card: the border takes the accent. */
.audit-report .ar-next-step{ margin-top:22px; padding-top:20px; border-top:0.5px solid var(--border-2); }
.audit-report .ar-next-step .h3{ margin:4px 0 6px; font-size:17px; }
.audit-report .ar-next-step > .muted{ margin:0; font-size:14px; }
.audit-report .ar-next-how{ margin:12px 0 0; padding-left:20px; line-height:1.6; color:var(--text-2); font-size:14px; }
.audit-report .ar-next-how li{ margin-bottom:6px; }
.audit-report .ar-src{ display:flex; flex-wrap:wrap; gap:6px 12px; margin:12px 0 0; font-size:12px; }
.audit-report .ar-src span{ color:var(--text-3); font-weight:600; }
.audit-report .ar-src a, .audit-report .ar-next-sources a{ color:var(--accent); overflow-wrap:anywhere; }
.audit-report a.ar-card-flat{ display:flex; flex-direction:column; gap:6px; color:inherit; text-decoration:none; border:0.5px solid var(--border); transition:border-color .15s ease; }
.audit-report a.ar-card-flat:hover{ border-color:var(--accent); }
.audit-report a.ar-card-flat:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
.audit-report .ar-next-card .h3{ margin:0; font-size:15px; }
.audit-report .ar-next-card .muted{ margin:0; font-size:13px; }
.audit-report .ar-next-card .eyebrow{ margin-top:4px; }
.audit-report .ar-next-card .ar-next-how{ margin-top:4px; font-size:13px; }
.audit-report .ar-next-cta{ margin-top:auto; padding-top:8px; display:inline-flex; align-items:center; gap:4px; font-size:13px; font-weight:600; color:var(--accent); }
.audit-report .ar-next-cta svg{ width:14px; height:14px; }
.audit-report .ar-next-sources{ margin:14px 0 0; padding-left:20px; font-size:13px; line-height:1.7; }
.audit-report .ar-next-note{ display:flex; flex-wrap:wrap; align-items:center; gap:10px; margin:10px 0 0; font-size:14px; color:var(--text-2); }
.audit-report .ar-next-skel{ display:grid; gap:12px; margin-top:20px; }
.audit-report .ar-next-skel i{ display:block; height:72px; border-radius:12px; background:var(--bg-2); }
@media (max-width:600px){ .audit-report .ar-bar{ grid-template-columns:100px 1fr 36px; } }
/* /audit share popup — mirrors the auth modal family (auth.css .auth-overlay) */
.ar-share-overlay{ position:fixed; inset:0; z-index:60; background:rgba(0,0,0,.42); display:flex; align-items:center; justify-content:center; padding:24px; }
.ar-share-panel{ position:relative; width:min(420px,92vw); background:var(--surface,#fff); border:.5px solid var(--border,rgba(0,0,0,.08)); border-radius:18px; padding:24px; }
.ar-share-x{ position:absolute; top:14px; right:14px; width:32px; height:32px; display:inline-flex; align-items:center; justify-content:center; border:none; border-radius:50%; background:transparent; color:var(--text-2); cursor:pointer; transition:background-color .15s ease; }
.ar-share-x:hover{ background:var(--surface-2,#f2f2ee); }
.ar-share-row{ display:flex; align-items:center; gap:12px; width:100%; min-height:44px; padding:10px 12px; margin-top:8px; border:.5px solid var(--border,rgba(0,0,0,.08)); border-radius:12px; background:transparent; color:var(--text); font-size:14px; font-weight:500; text-decoration:none; cursor:pointer; transition:background-color .15s ease; }
.ar-share-row:hover{ background:var(--surface-2,#f2f2ee); }
.ar-share-row:focus-visible{ outline:2px solid var(--accent); outline-offset:2px; }
.ar-share-row svg{ flex:none; color:var(--text-2); }
/* Home: chips + result slot. The live-audit heading ("Will AI replace your job? / Audit live now.")
   was removed 2026-10-05 (owner, from a mockup): the hero line now asks the question, and the
   CTAs sit straight on the audit pill. */
/* "Swipe departments" -- the same small-label vocabulary as the eyebrow and the scroll cue.
   PHONE ONLY: "swipe" is a touch word, and on a mouse the strip explains itself by moving.
   Hidden via a min-width query rather than a bare later rule, because a media query adds no
   specificity and a plain `display:none` after this block would beat it on source order. */
.ms-swipe-label{
  margin: 14px 0 0;
  font-family: var(--f-mono);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .10em;
  text-transform: uppercase;
  color: var(--text-3);
}
@media (min-width: 901px){ .ms-swipe-label{ display: none; } }
/* Role chips: white + hairline; active = --accent fill (plan §1 active-chip rule,
   home/result side). One hover affordance only — the hairline darkens. */
/* 12 chips (the mockup drew 11) have to clear 1296px at 1440 — hence 7px gap and
   12px side padding rather than the mockup's roomier pill. */
.ms-audit-chips button{ background:var(--surface); border:1px solid var(--border); color:var(--text-2); padding:7px 12px; border-radius:var(--r-pill); font-size:12px; cursor:pointer; transition:color var(--dur-state) var(--ease), background-color var(--dur-state) var(--ease), border-color var(--dur-state) var(--ease); }
.ms-audit-chips button:hover{ border-color:var(--border-2); }
.ms-audit-chips button.is-active{ background:var(--accent); border-color:var(--accent); color:var(--ink-text); }

/* The strip is ONE component at every width (owner 2026-09-23, "same for destop as well"),
   so these rules are top level instead of living in a <=900 block. The 768-900 band, which
   already had the scroller and the arrows, is now identical to both of its neighbours.

   Two earlier decisions are kept here because they were OVERRULED, not wrong:
   - 2026-09-14: twelve job titles wrapped to FIVE rows at 390px, 192px of chips for a
     convenience nobody has to use, so the row became one swipeable line bleeding off both
     edges -- while DESKTOP kept wrapping, because twelve chips fit one centred line at 1440.
     Desktop now scrolls and drifts too, which is exactly why the max-width below exists.
   - 2026-09-16: this used to end "No arrows: these are a shortcut into the input above them,
     not navigation." The owner reversed it; scrollview_01.png draws a chevron and the row
     carries one, under the "exhausted arrow fades, never disappears" contract. If these ever
     stop being navigation-shaped, that is the argument for taking the arrows out again. */
.ms-audit-chips{
  display:flex; gap:6px; flex-wrap:nowrap; justify-content:flex-start; margin-top:20px;
  overflow-x:auto;
  -webkit-overflow-scrolling:touch; overscroll-behavior-x:contain;
  scrollbar-width:none; -ms-overflow-style:none;
  /* The bleed lives on .ms-chipwrap; the row re-pays the gutter plus the arrow's lane
     inside itself, so the first and last chip clear the discs at either end.
     scroll-padding keeps that true for a chip the arrows or focus scroll to, not just the
     one sitting there at rest. */
  padding-inline:var(--ms-chip-lane); scroll-padding-inline:var(--ms-chip-lane);

  /* 🔴 NO scroll-behavior:smooth here, and that is not an oversight. Assigning scrollLeft
     honours it, so the slider's per-frame write would kick off a fresh smooth-scroll
     animation every frame and the loop seam would visibly animate backwards. The arrows
     lose nothing -- their handler asks for smooth explicitly, via
     scrollBy({ behavior: "smooth" }) in setupChipScroller(). */

  /* The edge fade. A MASK, not a background: root CLAUDE.md bans linear-gradient on
     BACKGROUNDS, and .ms-news-ticker-wrap already fades a scroller exactly this way. It
     could not be a painted pseudo-element in any case -- the hero film MOVES behind this
     row, and a painted ramp would have to match a moving image. The ramp ends precisely
     where the lane starts, so a chip the arrows scroll to is at full opacity, while chips
     passing behind the discs are already partly faded, which is what makes the glass read
     as glass rather than as a sticker. */
  /* Five stops, not three, and the extra pair is the point: a plain 0 -> 100% ramp across
     the lane leaves a chip sitting BEHIND a glass disc at roughly 30-90% opacity, which
     reads as the half-buried chip this whole change exists to get rid of rather than as
     something passing behind glass. Holding the mask at .18 across the disc's own width
     ghosts whatever is under it, then the last 18px carry it to full — so the first chip
     at rest is solid, and the chips the loop carries past the discs are clearly underneath
     them. */
  -webkit-mask-image:linear-gradient(to right, transparent 0, rgba(0,0,0,.18) calc(var(--ms-chev-inset) + var(--ms-chev-w)), #000 var(--ms-chip-lane), #000 calc(100% - var(--ms-chip-lane)), rgba(0,0,0,.18) calc(100% - var(--ms-chev-inset) - var(--ms-chev-w)), transparent 100%);
          mask-image:linear-gradient(to right, transparent 0, rgba(0,0,0,.18) calc(var(--ms-chev-inset) + var(--ms-chev-w)), #000 var(--ms-chip-lane), #000 calc(100% - var(--ms-chip-lane)), rgba(0,0,0,.18) calc(100% - var(--ms-chev-inset) - var(--ms-chev-w)), transparent 100%);
}
.ms-audit-chips::-webkit-scrollbar{ display:none; height:0; width:0; }
.ms-audit-chips button{ flex:0 0 auto; }

/* The arrows ride OVER the bleeding row, anchored to the hero's own gutter so they sit where
   the reference draws them rather than out on the screen edge with the chips.
   🔴 44x44 by owner ruling, 2026-09-23. They had been rendering at 36px because
   mobile.css's bare .intg-chev rule caught them -- collateral from 3a704b0, whose 36px
   decision was about the six HUB strips and not about these. That rule is now scoped to
   .intg-tabwrap, so the hubs keep 36 and these keep the 44 this file always asked for. */
.ms-chip-chev{
  display: inline-flex; align-items: center; justify-content: center;
  position: absolute; top: 50%; transform: translateY(-50%);
  width: var(--ms-chev-w); height: var(--ms-chev-w); padding: 0; z-index: 2;
  border-radius: 50%; color: var(--text-2);
  font-size: 18px; line-height: 1; cursor: pointer;
  /* Apple-style frosted glass, owner 2026-09-23 ("the bg as glasim apple style").
     🔴 RECORDED OWNER OVERRIDE. Root CLAUDE.md:236 and BRAND.md:303 ban "glassmorphism";
     this is written down here rather than left as a silent exception, the way the arrow
     reversal above is recorded. It is not novel either: the same saturate(180%) blur(20px)
     already ships on .m-fab and .m-drawer (mobile.css), and the owner's own mobile_version
     brief asks for "Apple-style frosted glass". No box-shadow -- the .5px hairline does that
     job, per "a 0.5px border instead of a box shadow". */
  background: rgba(255,255,255,.72);
  border: .5px solid rgba(255,255,255,.6);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
          backdrop-filter: saturate(180%) blur(20px);
  transition: opacity var(--dur-state) var(--ease), border-color var(--dur-state) var(--ease);
}
/* Without backdrop-filter a .72 white disc over a moving film is a smear, so fall back to
   the opaque surface the row used before the glass. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))){
  .ms-chip-chev{ background: var(--surface); border-color: var(--border); }
}
.ms-chip-chev--prev{ left: var(--ms-chev-inset); }
.ms-chip-chev--next{ right: var(--ms-chev-inset); }
.ms-chip-chev:hover:not(:disabled){ border-color: var(--border-2); }
.ms-chip-chev:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
/* 🔴 These fade their GLYPH, not the whole button, which is where they part company with
   .scroller-btn:disabled{opacity:.35} (app.css:1446). The category strips can fade the whole
   control because their chevrons are flex SIBLINGS of the row -- nothing passes behind them.
   These float over a row that bleeds to both screen edges, so a 35%-opaque button lets the
   chips scroll visibly through it and reads as a rendering fault.
   The contract itself is intact and is the point: still displayed, still 44x44, still
   obviously unavailable -- it is the ink that dims rather than the disc. */
.ms-chip-chev:disabled{ opacity: 1; color: var(--border-2); cursor: default; }

/* The glyph carries its own box so the hint animates transform/opacity on the INK. Moving
   the disc would drag its backdrop-filter sample along with it, which reads as the blur
   sloshing about. BRAND.md:274 -- transform and opacity only on the hot path. */
.ms-chip-chev-g{ display: block; line-height: 1; }
@keyframes ms-chev-breathe{ 0%,100%{ opacity: 1 } 50%{ opacity: .28 } }
@keyframes ms-chev-nudge{
  0%  { transform: translateX(-3px); opacity: 0 }
  20% { transform: translateX(0);    opacity: 1 }
  70% { transform: translateX(3px);  opacity: 1 }
  100%{ transform: translateX(3px);  opacity: 0 }
}
/* 🔴 Gated on .is-sliding, which the JS adds ONLY while the row is genuinely drifting, and
   never on an exhausted arrow. The moment the viewer engages -- hover, touch, focus, an
   arrow press -- the JS drops the class and the hints stop along with the motion they were
   describing. A hint that keeps pointing at a stopped row is a lie. 2.6s is the scroll
   cue's cadence (.ms-hero-scroll-cue), so the hero keeps one tempo. */
.ms-chipwrap.is-sliding .ms-chip-chev--prev:not(:disabled) .ms-chip-chev-g{ animation: ms-chev-breathe 2.6s ease-in-out infinite; }
.ms-chipwrap.is-sliding .ms-chip-chev--next:not(:disabled) .ms-chip-chev-g{ animation: ms-chev-nudge 2.6s ease-in-out infinite; }
/* Belt for a LIVE matchMedia flip: the JS never adds .is-sliding under reduced motion, but a
   viewer who turns it on mid-visit would otherwise keep the hints. Same specificity as the
   two rules above and later in source, so it wins without !important. The MASK stays -- a
   static fade is not motion. */
@media (prefers-reduced-motion: reduce){
  .ms-chipwrap.is-sliding .ms-chip-chev:not(:disabled) .ms-chip-chev-g{ animation: none; }
}

/* The wrapper is the arrows' containing block and nothing else -- no padding, no background.
   --ms-chev-w is the single source for BOTH the disc's box and the lane the row clears for
   it, so the two can no longer drift apart; that drift is what made the first chip's
   clearance a coincidence rather than a contract. The +8px IS that clearance, stated
   instead of implied. */
.ms-chipwrap{
  position: relative;
  --ms-chev-w: 44px;
  /* How far the discs sit inside the wrapper. On a phone that is the hero's own gutter, so
     the chips bleed to both screen edges while the arrows line up with the copy above them.
     Desktop zeroes it below, where there is no bleed to pay back. */
  --ms-chev-inset: var(--ms-pad-x);
  --ms-chip-lane: calc(var(--ms-chev-inset) + var(--ms-chev-w) + 18px);
  /* The bleed, moved here off the row on 2026-09-23 so the arrows' containing block and the
     row share ONE origin. hero-chips-verify.mjs asserts that they do. */
  margin-inline: calc(var(--ms-pad-x) * -1);
}
/* Without this the desktop half of the feature is a no-op. At 1440 the hero's inner column
   is 1296px and twelve chips measure about the same, so the row would not overflow at all:
   nothing to scroll, nothing to fade, no arrow with a job, no runway for the drift. Capping
   the WRAPPER rather than the row keeps the two boxes identical, which is the contract the
   suite holds. Centred, so the strip stays under the audit pill it belongs to. */
@media (min-width: 901px){
  .ms-chipwrap{
    --ms-chev-inset: 0px;
    max-width: 760px;
    margin-inline: auto;
  }
}
.ms-audit-result{ margin-top:24px; }

/* Audit loading states (RAG score + breakdown hydrate after mount). */
.audit-loading{ display:flex; align-items:center; gap:10px; color:var(--text-2); font-size:14px; }
.audit-ring-loading{ display:flex; align-items:center; justify-content:center; min-height:120px; }
.audit-spinner{ width:18px; height:18px; border-radius:50%; border:2px solid var(--border); border-top-color:var(--accent); animation:audit-spin .7s linear infinite; }

/* Plan §3 beat 3 — the click film. One full-viewport overlay above every other
   layer (nav is z 41, the share modal z 60). In over 200ms as the pill is
   pressed, out over 300ms once the film has run out and the response has
   landed; the honest "Auditing…" line rides a readable chip and never fakes
   progress. A video error, reduced motion, Save-Data or a file that has not
   buffered inside AUDIT_FILM_WARM_MS flips .ar-film-off, leaving a single frame
   of the film as a still behind the same line. A phone no longer does (owner
   2026-09-14): it plays the film from its own 960x540 encode instead.

   The ground is flat page colour, not the poster: R4-2 opens the stage scaled
   down onto the audit button, so for the first 800ms there IS ground showing
   around the film, and a full-bleed copy of frame 0 behind a small copy of
   frame 0 would read as two spheres. The poster only comes back on the
   no-film path, where it is the whole picture. */
.ar-film{
  position:fixed; inset:0; z-index:1000;
  background:var(--bg);
  opacity:0; transition:opacity .2s var(--ease-out);
}
.ar-film.is-on{ opacity:1; }
.ar-film:not(.is-on){ transition-duration:.3s; }
/* U6 — the still is NOT the poster. The poster is frame 0, a small sphere the
   open anchors on the audit button; blown up to full bleed it is mostly empty
   white. This is frame 23 of the owner's 2026-09-10 film, the largest whole
   sphere before the shell panels lift at frame 24: r 0.38, y 0.4556, and
   x 0.4927. Used only here, on the no-film path, where it is the whole picture.

   That film opens centred and the sphere grows in place without leaving the
   centre line, so the still is centred and `center` is the right origin: on a
   375px-wide phone cover keeps only the middle 26% of the frame, and the sphere
   sits inside it. The 60% origin the previous film needed is gone with it — its
   frame 51 sphere sat at x 0.57 and had to be pulled back to the middle. */
.ar-film.ar-film-off{ background:var(--bg) center/cover no-repeat url("/image/audit-click-still.jpg"); }
/* R4-2 — the film opens ON the audit button and grows out of it. JS writes the
   pose (a scale about a transform-origin at the button's centre, translated so
   the film's own frame-0 sphere lands there) before the overlay fades in, then
   drops the transform so this transition eases the film to full bleed while its
   sphere unfolds. The box is the viewport exactly: R3's 140% overhang existed
   only to keep a page edge out of a translated frame, and the open now shows
   the ground on purpose, so the overhang bought nothing but a 40% crop of
   every frame. */
.ar-film-stage{ position:absolute; inset:0; }
.ar-film-stage.is-centring{ transition:transform .8s var(--ease-out); }
@media (prefers-reduced-motion: reduce){ .ar-film-stage.is-centring{ transition:none; } }
.ar-film-video{ position:absolute; inset:0; display:block; width:100%; height:100%; object-fit:cover; }
.ar-film.ar-film-off .ar-film-video{ display:none; }
/* The chip is a beat of the film, not of the click: it arrives when the film
   assembles (AUDIT_CLICK.assembly_s), which is also when the audit pill leaves.
   Where no film plays at all it is armed at once — it is the only signal left. */
.ar-film .audit-loading{
  position:absolute; left:50%; bottom:48px; transform:translateX(-50%);
  margin:0; white-space:nowrap;
  font-family:var(--f-mono,ui-monospace,SFMono-Regular,Menlo,monospace);
  font-size:12px; letter-spacing:.06em;
  background:rgba(255,255,255,.85); border:1px solid var(--border);
  border-radius:var(--r-pill); padding:8px 16px;
  opacity:0; transition:opacity .2s var(--ease-out);
}
.ar-film.is-chip .audit-loading{ opacity:1; }
@media (prefers-reduced-motion: reduce){
  .ar-film{ transition:none; }
  .ar-film .audit-loading{ transition:none; }
}
@keyframes audit-spin{ to{ transform:rotate(360deg); } }

/* Plan §3 beat 2 — the audit pill fires one ring pulse on the click. Transform
   + opacity only; the pill carries the ring because .ms-btn--sweep clips its
   own ::before with overflow:hidden. R4-2: there is no sphere glide to go with
   it any more, so the pulse is the whole of beat 2 on the DOM side. */
.ms-hero-form{ position:relative; }
.ms-hero-form.is-pulsing::after{
  content:""; position:absolute; inset:-1px; border-radius:var(--r-pill);
  border:2px solid var(--accent); pointer-events:none;
  animation:ms-audit-ring .6s var(--ease-out) 1 both;
}
@keyframes ms-audit-ring{
  from{ transform:scale(1); opacity:.6; }
  to  { transform:scale(1.12); opacity:0; }
}
@media (prefers-reduced-motion: reduce){ .ms-hero-form.is-pulsing::after{ display:none; } }

/* R4-2 beat 2b — the hero while the audit runs.

   The click film covers the viewport, so the hero has to get out of its way:
   the copy fades out over AUDIT_CLICK.copyFadeMs and the loop drops to
   AUDIT_CLICK.loopDim and KEEPS PLAYING, which is what makes an Escape or a
   route change land back on a hero that never stopped.

   The pill is the exception -- it stays readable on top of the film until the
   film's assembly beat takes it. That costs a stacking-context move: `.ms-inner`
   sets `z-index: 1`, which traps anything inside it below the overlay's z 1000,
   so `.ms-inner` itself is what rises. Everything else it holds (chips, stats,
   subscriber pill, scroll cue, the empty result slot) rides up with it, which
   is exactly why they all fade with the copy rather than only the six blocks
   the audit choreography names. `.ms-hero` and `.ms-section` are both
   `position: relative` with no z-index, so nothing between here and the root
   swallows the raise.

   The raise lasts exactly as long as the pill needs it. `is-lowered` arrives
   one pill-fade after the assembly beat and puts `.ms-inner` back under the
   overlay, so the reveal is the plain thing it was before: the film fades out
   and the result is already sitting underneath it. Without that the result
   card would paint ON TOP of the closing overlay and then drop behind it
   mid-fade. */
.ms-hero.is-auditing:not(.is-lowered) .ms-inner{ z-index:1001; }
.ms-hero.is-auditing .ms-inner > *:not(.ms-hero-form){
  opacity:0; pointer-events:none;
  transition:opacity .3s var(--ease-out);
}
/* U8: `:not(.is-live)` because once the film paints the plate is already at 0
   and must stay there -- dimming it to .3 here would put the ghost sphere back
   for exactly the state it was most visible in. The plate only takes this dim
   on the fallback path, where the video never fired `playing`. */
.ms-hero.is-auditing .ms-hero-ribbons,
.ms-hero.is-auditing:not(.is-live) .ms-hero-plate{
  opacity:.3; transition:opacity .3s var(--ease-out);
}
.ms-hero.is-auditing.is-pill-gone .ms-hero-form{
  opacity:0; pointer-events:none;
  transition:opacity .15s var(--ease-out);
}
@media (prefers-reduced-motion: reduce){
  .ms-hero.is-auditing .ms-inner > *:not(.ms-hero-form),
  .ms-hero.is-auditing .ms-hero-ribbons,
  .ms-hero.is-auditing:not(.is-live) .ms-hero-plate,
  .ms-hero.is-live .ms-hero-plate,
  .ms-hero.is-auditing.is-pill-gone .ms-hero-form{ transition:none; }
}

/* Plan §3 beats 4–7 — the resting result state (final_mockup/result.png). The
   headline block gives way to the report; the chip row stays on top with the
   audited role active, stats + scroll cue stay below, and the idle orbit hands
   off to the sphere docked on the card. */
.ms-hero--result .ms-eyebrow,
.ms-hero--result .ms-hero-display,
.ms-hero--result .ms-hero-cycling,
.ms-hero--result .ms-hero-gap,
.ms-hero--result .ms-hero-ctas,
.ms-hero--result .ms-hero-trust,
.ms-hero--result .ms-swipe-label,
.ms-hero--result .ms-hero-form,
.ms-hero--result .ms-hero-orbit{ display:none; }
.ms-hero--result{ padding-top:calc(var(--nav-h) + 28px); }
.ms-hero--result .ms-audit-chips{ margin-top:0; }
/* Clears the 58% of the docked sphere that stands above the card, whatever
   --ar-sphere-w resolves to, so it never collides with the chip row. */
.ms-hero--result .ms-audit-result{ margin-top:clamp(78px, 12vw, 172px); }
.ms-audit-result{ text-align:left; }

/* §3-amendment — the landing. R4-2 cut the last of the DOM dolly zoom: the
   film's burst is the move that hands the screen over, and a second scale
   underneath it was the "it zooms in 3 times" the owner counted. What is left
   is a 450ms fade on the result. The hero loop behind it is untouched -- it has
   been running the whole time and simply comes back up to full opacity. */
@media (prefers-reduced-motion: no-preference){
  .ms-audit-result.ar-vertigo{ animation:ar-vertigo-fore .45s var(--ease-out) 1 both; }
}
@keyframes ar-vertigo-fore{
  from{ opacity:0; }
  to  { opacity:1; }
}

/* Sphere docked on the card's top edge. Two layers, no clip: the sphere sits
   behind an opaque card, so its lower ~42% is simply covered. Reduced motion
   gets the resting position with no drop.

   Horizontal: result.png centres the sphere on the VIEWPORT, not on the card.
   `.ar-main` is the `1fr` track of `.ar-grid` (1fr / 32px gap / 320px aside)
   inside the centred `.ms-inner`, so the viewport centre sits exactly
   (320+32)/2 = 176px right of `.ar-main`'s own centre. Deriving it from the
   box rather than 50vw keeps it honest when a scrollbar is present. The
   `min()` is the guard rail: the sphere can never cross into the aside
   column, whatever the track widths do. */
.audit-report .ar-main{ position:relative; }
.audit-report .ar-main > .ar-card{ position:relative; z-index:1; }
.audit-report .ar-sphere{
  /* result.png shows the docked sphere at ~250px on a 1440 viewport. The 42%
     that hides behind the card scales with it, so the margin below grows too. */
  --ar-sphere-w: clamp(118px, 17.5vw, 250px);
  --ar-sphere-gap: 32px;   /* .ar-grid gap */
  --ar-sphere-half: 176px; /* (320px aside + 32px gap) / 2 */
  position:absolute; top:0; z-index:0;
  left:min(calc(50% + var(--ar-sphere-half)),
           calc(100% + var(--ar-sphere-gap) - var(--ar-sphere-w) / 2 - 16px));
  display:block; width:var(--ar-sphere-w);
  transform:translate(-50%,-58%); pointer-events:none;
}
/* Below the 1100px two-column threshold the aside stacks under the card, so
   the card IS the full width — centre on it again. */
@media (max-width:1099px){
  .audit-report .ar-sphere{ left:50%; }
}
.audit-report .ar-sphere img{ display:block; width:100%; height:auto; }
.audit-report .ar-sphere::after{
  content:""; position:absolute; inset:0; border-radius:50%;
  border:2px solid var(--glow); opacity:0; pointer-events:none;
}
@media (prefers-reduced-motion: no-preference){
  .audit-report .ar-sphere{ transform:translate(-50%,-140%); transition:transform .4s var(--ease-out); }
  .audit-report .ar-sphere.is-docked{ transform:translate(-50%,-58%); }
  .audit-report .ar-sphere.is-pulsing::after{ animation:ar-dock-ring .5s var(--ease-out) 1 both; }
}
@keyframes ar-dock-ring{
  from{ transform:scale(.92); opacity:.6; }
  to  { transform:scale(1.6); opacity:0; }
}

/* Result card, read off result.png: 64px mono score, standalone pill tabs,
   pale-blue bar track, the ramp colour set inline from the REAL value. */
.audit-report .ar-score b{ font-size:64px; }
.audit-report .ar-toggle{ background:transparent; border:0; padding:0; gap:8px; }
.audit-report .ar-tpill{
  background:var(--surface); border:1px solid var(--border);
  padding:8px 14px; font-size:13px;
}
.audit-report .ar-tpill:hover{ border-color:var(--border-2); }
.audit-report .ar-tpill.is-on{ background:var(--accent); border-color:var(--accent); color:var(--ink-text); }
.audit-report .ar-bar{ grid-template-columns:164px 1fr 52px; gap:16px; font-size:14px; }
.audit-report .ar-track{ height:8px; background:var(--accent-tint); }
.audit-report .ar-fill{ transition:width .6s var(--ease-out); }
/* .btn-outline has no definition anywhere in the stylesheets, so the CTA row
   rendered as bare text. Scoped to the report card only — the global class is
   left alone. "Explore next-step guides" is the mockup's one borderless CTA. */
.audit-report .ar-card-lg .btn.btn-outline{
  border:1px solid var(--border); border-radius:var(--r-pill);
  padding:12px 22px; background:var(--surface); color:var(--text);
  font-size:14px; font-weight:500;
  transition:border-color var(--dur-state) var(--ease);
}
.audit-report .ar-card-lg .btn.btn-outline:hover{ border-color:var(--border-2); }
.audit-report .ar-card-lg .btn.ar-cta-ghost{ border-color:transparent; background:transparent; font-weight:600; color:var(--text); }
.audit-report .ar-card-lg .btn.ar-cta-ghost:hover{ color:var(--accent); border-color:transparent; }
.audit-report .ar-stack .ar-card h4{ font-size:17px; }
/* Sidebar actions read as links in the mockup, not outline buttons. */
.audit-report .ar-stack .btn.btn-outline.btn-sm{
  display:block; margin-top:14px; padding:0; border:0; background:transparent;
  color:var(--accent); font-weight:500; text-align:center;
}
@media (max-width:600px){
  .audit-report .ar-bar{ grid-template-columns:104px 1fr 44px; gap:10px; font-size:13px; }
  .audit-report .ar-score b{ font-size:48px; }
  .ms-hero--result .ms-audit-result{ margin-top:72px; }
}

/* The report's freshness badge reads blue like the rest of the card, not the
   global green. Scoped here so every other surface keeps the green Live badge
   defined in app.css. */
.audit-report .data-badge--live{
  color:var(--accent); background:var(--accent-tint); border-color:var(--accent-tint);
}

/* Redesign F3 (app.js auditReadingHTML): key takeaways, the task rows with a status and a
   meter, the 30-day weeks. Static cards: 0.5px lines, no shadow, no hover of their own. */
.audit-report .ar-kt{ display:grid; grid-template-columns:repeat(auto-fit, minmax(200px,1fr)); gap:12px; margin-top:18px; }
.audit-report .ar-kt-card{ padding:14px 16px; border:.5px solid var(--border); border-radius:14px; background:var(--surface); }
.audit-report .ar-kt-card ul{ margin:8px 0 0; padding-left:18px; font-size:14px; line-height:1.6; color:var(--text-2); }
.audit-report .ar-kt-card li::first-letter{ text-transform:uppercase; }
.audit-report .ar-rows{ display:flex; flex-direction:column; gap:12px; margin-top:18px; }
.audit-report .ar-row{ display:grid; grid-template-columns:minmax(0,180px) 96px 1fr 52px; gap:14px; align-items:center; font-size:14px; }
.audit-report .ar-row-task{ color:var(--text); text-transform:capitalize; }
.audit-report .ar-row .badge{ justify-self:start; }
.audit-report .ar-row .ar-fill{ transition:none; }
.audit-report .ar-weeks{ display:grid; grid-template-columns:repeat(auto-fit, minmax(200px,1fr)); gap:12px; list-style:none; padding:0; margin:18px 0 0; }
.audit-report .ar-weeks li{ padding:14px 16px; border:.5px solid var(--border); border-radius:14px; }
.audit-report .ar-weeks p{ margin:8px 0 0; font-size:14px; line-height:1.55; color:var(--text-2); }

/* Home hero secondary path — "just try AI free" (B1 fear-hook). */
.ms-hero-alt{ margin-top:14px; text-align:center; }
.ms-hero-alt a{ color:var(--text-2); font-size:14px; text-decoration:none; border-bottom:1px solid transparent; transition:border-color .15s ease; }
.ms-hero-alt a:hover{ border-bottom-color:var(--text-3); }

/* ============================================================
   Home bucket heading bands (Learn / Build / Explore)
   ============================================================ */
.ms-section.ms-bucket-head{ padding-top: 40px; padding-bottom: 0; }
.ms-bucket-head .ms-headline{ margin: 6px 0 0; }

/* ============================================================
   Testimonials marquee (home)
   Cards glide left forever; card set is duplicated once in markup so
   translateX(-50%) loops seamlessly. Pause on hover; static under
   prefers-reduced-motion. Flat cards, hairline border, no shadow.
   ============================================================ */
.ms-tm-mask{
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.ms-tm-track{
  display: flex;
  gap: 16px;
  width: max-content;
  padding: 8px 24px;
  animation: ms-tm-scroll 60s linear infinite;
}
.ms-tm-mask:hover .ms-tm-track{ animation-play-state: paused; }
@keyframes ms-tm-scroll{ from{ transform: translateX(0); } to{ transform: translateX(-50%); } }
.ms-tm-card{
  flex: 0 0 360px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 18px;
  margin: 0;
  padding: 22px;
  border: 1px solid var(--border);
  border-radius: 16px;
  background: var(--surface);
  transition: border-color .15s ease;
}
.ms-tm-card:hover{ border-color: var(--text-3); }
.ms-tm-quote{ margin: 0; font-size: 15px; line-height: 1.5; color: var(--text); }
.ms-tm-who{ display: flex; align-items: center; gap: 12px; }
.ms-tm-avatar{
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--bg-2); color: var(--text-2);
  font-size: 13px; font-weight: 600; flex: 0 0 auto;
}
.ms-tm-meta{ display: flex; flex-direction: column; line-height: 1.3; }
.ms-tm-name{ font-size: 14px; font-weight: 600; color: var(--text); }
.ms-tm-role{ font-size: 13px; color: var(--text-2); }
@media (prefers-reduced-motion: reduce){
  .ms-tm-track{ animation: none; }
  .ms-tm-mask{ overflow-x: auto; -webkit-mask-image: none; mask-image: none; }
}

/* ============================================================
   HOME REDESIGN — full-width video / build / weekly / testimonials
   ============================================================ */

/* ---- "Explore all in dashboard" link ---- */
.ms-xall-wrap{ display: flex; justify-content: center; margin-top: 44px; }
.ms-xall{ display: inline-flex; align-items: center; gap: 12px; font-size: 15px; font-weight: 600; color: var(--text); text-decoration: none; transition: color .18s ease; }
.ms-xall:hover{ color: var(--accent); }
.ms-xall-ico{ display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; border-radius: 50%; border: 1px solid rgba(0,0,0,.14); color: var(--text); transition: border-color .18s ease, color .18s ease, transform .18s ease; }
.ms-xall:hover .ms-xall-ico{ border-color: var(--accent); color: var(--accent); transform: translateX(2px); }

/* ---- Video cards (Learn AI by watching) — an expanding strip ----
   Three cards sharing one row; the live one widens into the space the other two give up.
   flex-grow is the animated property, not width: the cards stay one flex line that always
   fills the row exactly, so nothing is measured and no gap opens mid-transition. Driven by
   window.aadGallery() in assets/dash-hero.js. */
.ms-vstrip{ position: relative; }
.ms-vrow{ display: flex; gap: 12px; height: 420px; }
.ms-vcard{
  position: relative; flex: 1 1 0%; min-width: 0; overflow: hidden;
  border-radius: 16px; text-decoration: none;
  background: var(--ms-dark); border: 1px solid var(--ms-line);
  transition: flex-grow 620ms var(--ms-ease-out), border-color var(--ms-dur-state) var(--ms-ease-out);
}
.ms-vcard.on{ flex-grow: 3; }
/* ONE hover affordance beyond the widening itself. The card this replaced stacked a lift
   AND a 44px shadow; house rule is colour OR border OR translate, never all three. */
.ms-vcard:hover{ border-color: var(--ms-accent); }
.ms-vcard:focus-visible{ outline: 2px solid var(--ms-accent); outline-offset: 2px; }
/* The stage fills the panel instead of setting its own ratio - in a strip the row owns the
   height and the panel's width is whatever the flex line hands it. */
.ms-vcard-stage{ position: relative; display: block; height: 100%; overflow: hidden; background: var(--ms-dark); }

/* Below the row rather than over it: every corner of a panel is already taken by a pill, the
   play badge or the title, and the one free corner moves as the live panel changes. */
.ms-vpause{
  display: block; margin: 12px 0 0 auto;
  padding: 4px 11px; border-radius: 999px;
  border: .5px solid var(--ms-line); background: transparent; color: var(--ms-ink-2,var(--text-2));
  font-size: 11px; font-weight: 600; cursor: pointer;
  transition: color var(--ms-dur-quick) var(--ms-ease-out);
}
.ms-vpause:hover{ color: var(--ms-accent); }
.ms-vpause:focus-visible{ outline: 2px solid var(--ms-accent); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce){
  .ms-vcard{ transition: border-color var(--ms-dur-state) var(--ms-ease-out); }
}
.ms-vcard-bg{ position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Scrim, not decoration: the title is white text sitting on an arbitrary photo, so it
   needs a guaranteed dark floor to stay legible. Bottom-weighted only. */
/* Ramps early and lands near-opaque on purpose. Some posters are video frames with their
   own title burned into the lower third, and a lighter scrim let that show through and
   read as a second headline under the real one. */
.ms-vcard-stage::before{ content: ""; position: absolute; inset: 0; z-index: 1; background: linear-gradient(180deg, rgba(0,0,0,0) 20%, rgba(0,0,0,.50) 48%, rgba(0,0,0,.94) 74%, rgba(0,0,0,.98) 100%); }
/* Level left, runtime right, both on the poster - the same pair guideCard() overlays on
   /guide. They replace the chip strip that used to sit under the image and repeat them. */
.ms-vcard-top{ position: absolute; top: 12px; left: 12px; right: 12px; z-index: 2; display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.ms-vcard-pill{ padding: 4px 9px; border-radius: 999px; background: rgba(0,0,0,.62); color: #fff; font-size: 11px; font-weight: 600; letter-spacing: .02em; white-space: nowrap; }
.ms-vcard-pill--level{ text-transform: uppercase; letter-spacing: .06em; }
/* A guide missing its level leaves the runtime alone in the row - it still belongs right. */
.ms-vcard-top > .ms-vcard-pill:only-child:not(.ms-vcard-pill--level){ margin-left: auto; }
.ms-vcard-play{ position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); z-index: 2; width: 54px; height: 54px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; background: rgba(255,255,255,.92); color: var(--ms-dark); }
/* Sized for the narrow state and grown on the live panel, so a collapsed card still shows a
   readable title instead of two clipped words. Three lines at both sizes - re-clamping
   mid-transition would snap the block height while the panel is still widening. */
.ms-vcard-title{ position: absolute; left: 16px; right: 16px; bottom: 14px; z-index: 2; color: #fff; font-family: var(--f-display); font-size: 15px; font-weight: 600; line-height: 1.28; letter-spacing: -.012em; display: -webkit-box; -webkit-line-clamp: 3; line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; transition: font-size 620ms var(--ms-ease-out); }
.ms-vcard.on .ms-vcard-title{ font-size: 19px; }

@media (max-width: 900px){
  .ms-vrow{ height: 340px; gap: 10px; }
  .ms-vcard.on .ms-vcard-title{ font-size: 17px; }
}
/* Under 768px three panels leave ~90px each, so the expansion is dropped for a swipeable
   strip. Kept in sync with the `narrow` query passed in setupVideoGallery(), which stops
   the cycle at exactly the width this stops reacting to it. */
@media (max-width: 768px){
  .ms-vrow{ height: 260px; overflow-x: auto; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; }
  .ms-vcard{ flex: 0 0 78%; scroll-snap-align: start; }
  .ms-vcard.on{ flex-grow: 0; }
  .ms-vcard-title{ font-size: 16px; }
  .ms-vpause{ display: none; }
}

/* ---- Build Agents — full width ---- */
.ms-build-grid{ display: grid; grid-template-columns: 1.5fr 1fr; gap: 14px; width: 100%; align-items: stretch; }
.ms-build-left{ display: flex; flex-direction: column; gap: 14px; }
.ms-build-card{ display: flex; gap: 26px; align-items: center; background: var(--surface); color: var(--text); border-radius: 16px; padding: 28px 30px; min-height: 172px; text-decoration: none; border: 1px solid var(--ms-line); transition: background .22s var(--ms-ease-out), border-color .22s var(--ms-ease-out), color .22s var(--ms-ease-out); }
.ms-build-card:hover{ background: var(--ms-dark); border-color: rgba(255,255,255,.12); color: #fff; }
.ms-build-card--wide{ flex: 1; }
.ms-build-flow-img{ flex: 0 0 44%; width: 44%; max-width: 44%; height: auto; border-radius: 10px; border: 1px solid var(--ms-line-soft); transition: transform .45s var(--ms-ease-out), border-color .22s ease; }
.ms-build-card:hover .ms-build-flow-img{ border-color: rgba(255,255,255,.10); transform: scale(1.015); }
.ms-build-copy{ display: flex; flex-direction: column; gap: 8px; }
.ms-build-title{ font-family: var(--f-display); font-size: 19px; font-weight: 600; letter-spacing: -0.014em; line-height: 1.25; color: inherit; }
.ms-build-title em{ font-style: normal; color: var(--ms-accent); }
.ms-build-desc{ font-size: 13.5px; color: var(--ms-ink-3); line-height: 1.55; text-transform: none; letter-spacing: 0; max-width: 34ch; }
.ms-build-card:hover .ms-build-desc{ color: rgba(255,255,255,.62); }
.ms-build-mdicon{ flex: 0 0 auto; width: 52px; height: 52px; border-radius: 12px; background: #fff; padding: 11px; object-fit: contain; border: 1px solid var(--ms-line-soft); }
.ms-build-card:hover .ms-build-mdicon{ border-color: rgba(255,255,255,.10); }
.ms-build-ec{ position: relative; display: block; border-radius: 16px; overflow: hidden; text-decoration: none; min-height: 0; background: var(--ms-dark); border: 1px solid rgba(255,255,255,.06); transition: transform .22s var(--ms-ease-out), box-shadow .22s var(--ms-ease-out); }
.ms-build-ec:hover{ transform: translateY(-2px); box-shadow: 0 16px 36px rgba(0,0,0,.22); }
.ms-build-ec-img{ position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; filter: grayscale(1) contrast(1.04); opacity: .92; transform: translate3d(0, var(--ms-par, 0px), 0) scale(1.08); }
/* The tile plays the owner's clip; the <img> is that clip's poster frame, held
   back for phones and reduced motion so the two can never disagree. Same
   cover/grayscale/parallax treatment either way -- it is the tile's design, not
   an artefact of the source (the clip is already black and white). */
.ms-build-ec-still{ display: none; }
@media (max-width: 767px), (prefers-reduced-motion: reduce){
  .ms-build-ec-video{ display: none; }
  .ms-build-ec-still{ display: block; }
}
.ms-build-ec-overlay{ position: absolute; inset: 0; display: flex; flex-direction: column; justify-content: space-between; padding: 26px; background: linear-gradient(180deg, rgba(0,0,0,.38), rgba(0,0,0,.55)); }
/* 30px flat overflowed the tile once the grid squeezed it (103px wide at a 375 viewport),
   so "EDITOR'S" was clipped to "EDITO". The vw term is tuned so 3.9vw only drops below 30px
   under 769px — at and above the existing 768 breakpoint the size is byte-for-byte unchanged. */
.ms-build-ec-title{ color: #fff; font-family: var(--f-display); font-size: clamp(16px, 3.9vw, 30px); font-weight: 700; line-height: 1.04; letter-spacing: -.018em; }
.ms-build-ec-tag{ color: rgba(255,255,255,.85); font-size: 12px; font-weight: 600; letter-spacing: .1em; }
/* On small phones the 26px overlay padding costs more than half the tile's width. */
@media (max-width: 420px){ .ms-build-ec-overlay{ padding: 14px; } }
/* Phones: one column. The 1.5fr/1fr split left the three cards ~226px wide and 266-379px
   tall at a 390 viewport — one word per line — and squeezed the editor's-choice tile to
   118x985, a column of picture nobody scrolls past. Stacking them gives each card the full
   width, which is what lets the padding and the type come DOWN rather than up.

   After the 420px rule on purpose: that 14px was sized for a 103px-wide tile, and a tile
   that now spans the viewport carries the same 18px inset as the cards beside it. Desktop
   is untouched — every rule here is inside the phone query. */
@media (max-width: 767px){
  .ms-build-grid{ grid-template-columns: minmax(0, 1fr); }
  .ms-build-card{ gap: 16px; padding: 18px; min-height: 0; }
  .ms-build-flow-img{ flex: 0 0 36%; width: 36%; max-width: 36%; }
  .ms-build-title{ font-size: 17px; }
  .ms-build-desc{ max-width: none; }
  .ms-build-ec{ aspect-ratio: 16 / 10; min-height: 200px; }
  .ms-build-ec-overlay{ padding: 18px; }
}

/* ---- Weekly Digest — newspaper / editorial, wired to real bundle.weekly data ---- */
/* Loading placeholders. The weekly digest ships empty and fills from the live
   news backend, so these hold the exact layout the real content will take. */
.ms-sk{ background: #eeeef1; border-radius: 8px; animation: ms-sk-pulse 1.6s ease-in-out infinite; }
.ms-sk-line{ display: block; height: 11px; border-radius: 5px; margin-bottom: 11px; }
@keyframes ms-sk-pulse{ 0%,100%{ opacity: 1 } 50%{ opacity: .5 } }
@media (prefers-reduced-motion: reduce){ .ms-sk{ animation: none } }
.ms-wk-empty{ grid-column: 1 / -1; padding: 44px 28px; text-align: center; }
.ms-wk-empty p{ margin: 0 auto 18px; max-width: 46ch; font-size: 14px; line-height: 1.6; color: var(--text-2); }

.ms-wk-paper{ width: 100%; background: #fff; border: 1px solid rgba(0,0,0,.10); border-radius: 16px; overflow: hidden; }
.ms-wk-masthead{ display: flex; align-items: baseline; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding: 22px 28px 16px; border-bottom: 2px solid var(--text); }
.ms-wk-mast-name{ font-family: Georgia, 'Iowan Old Style', 'Times New Roman', serif; font-size: 30px; font-weight: 700; letter-spacing: -.015em; color: var(--text); line-height: 1; }
.ms-wk-mast-meta{ font-size: 12px; font-weight: 600; letter-spacing: .07em; color: var(--text-3); text-transform: uppercase; }
.ms-wk-mast-dot{ margin: 0 8px; opacity: .5; }
.ms-wk-paper-grid{ display: grid; grid-template-columns: minmax(0,1.55fr) minmax(0,1fr); }
.ms-wk-lead{ position: relative; display: flex; flex-direction: column; padding: 24px 28px; border-right: 1px solid rgba(0,0,0,.10); transition: background .15s ease; }
.ms-wk-lead:hover, .ms-wk-item:hover{ background: rgba(0,0,0,.022); }
.ms-wk-lead-media{ display: block; aspect-ratio: 16 / 9; overflow: hidden; border-radius: 10px; border: 1px solid rgba(0,0,0,.08); background: #f0f0f2; margin-bottom: 16px; }
.ms-wk-lead-media img{ display: block; width: 100%; height: 100%; object-fit: cover; }
.ms-wk-source{ font-size: 12px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--accent); }
.ms-wk-lead-h{ font-family: Georgia, 'Iowan Old Style', 'Times New Roman', serif; font-size: 25px; font-weight: 700; line-height: 1.18; letter-spacing: -.01em; color: var(--text); margin: 8px 0 10px; }
.ms-wk-lead-brief{ font-size: 14px; line-height: 1.6; color: var(--text-2); margin: 0; }
.ms-wk-second{ display: flex; flex-direction: column; }
.ms-wk-item{ position: relative; display: flow-root; padding: 22px 26px; }
.ms-wk-item + .ms-wk-item{ border-top: 1px solid rgba(0,0,0,.10); }
.ms-wk-item-h{ font-family: Georgia, 'Iowan Old Style', 'Times New Roman', serif; font-size: 18px; font-weight: 700; line-height: 1.25; letter-spacing: -.005em; color: var(--text); margin: 7px 0 8px; }
.ms-wk-item-brief{ font-size: 13px; line-height: 1.55; color: var(--text-2); margin: 0; }
/* The side stories carry their own picture (owner, 2026-10-06), credited under it. */
.ms-wk-item-pic{ float: right; width: 112px; margin: 0 0 8px 16px; display: flex; flex-direction: column; gap: 6px; }
.ms-wk-item-pic:empty{ display: none; }
.ms-wk-item-media{ display: block; aspect-ratio: 4 / 3; overflow: hidden; border-radius: 10px; border: 1px solid rgba(0,0,0,.08); background: #f0f0f2; }
.ms-wk-item-media img{ display: block; width: 100%; height: 100%; object-fit: cover; }
.ms-wk-item .wdx-credit{ margin: 0; }

/* One equal column per card, so a short edition (two cards with the daily up) fills the row
   instead of leaving an empty third slot; a full row of three is unchanged. */
.ms-wk-cards{ display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0,1fr); gap: 14px; width: 100%; margin: 14px 0 0; align-items: stretch; }
.ms-wk-card{ position: relative; display: flex; flex-direction: column; overflow: hidden; border-radius: 16px; border: 1px solid var(--ms-line-soft); background: #fff; transition: border-color .18s ease, transform .22s var(--ms-ease-out); }
.ms-wk-card:hover{ border-color: color-mix(in srgb, var(--accent) 35%, transparent); transform: translateY(-2px); }
/* whole-card stretched link → dashboard; header link (z-index above) → source */
.ms-wk-hit{ position: absolute; inset: 0; z-index: 1; border-radius: 16px; text-decoration: none; }
.ms-wk-hit:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
.ms-wk-card-media{ display: block; aspect-ratio: 16 / 10; overflow: hidden; background: #f0f0f2; }
/* Two stretched cards would grow 16:10 pictures to ~425px; hold them near the 3-up height. */
.ms-wk-cards .ms-wk-card-media{ max-height: 280px; }
.ms-wk-card-media img{ display: block; width: 100%; height: 100%; object-fit: cover; object-position: center; transition: transform .7s var(--ms-ease-out); }
.ms-wk-card:hover .ms-wk-card-media img{ transform: scale(1.03); }
.ms-wk-card-body{ display: flex; flex-direction: column; gap: 5px; padding: 16px 18px 18px; flex: 1; }
.ms-wk-card-label{ font-size: 12px; font-weight: 700; letter-spacing: .08em; color: var(--text-3); }
.ms-wk-card-title{ font-size: 16px; font-weight: 600; color: var(--text); line-height: 1.3; letter-spacing: -.01em; }
.ms-wk-card-desc{ font-size: 13px; color: var(--text-2); line-height: 1.45; }
/* header is the only source/destination hyperlink — sits above the stretched hit */
.ms-wk-srclink{ position: relative; z-index: 2; display: block; width: fit-content; max-width: 100%; text-decoration: none; color: inherit; }
.ms-wk-srclink .ms-wk-arrow{ vertical-align: -1px; margin-left: 5px; transition: transform .16s ease; }
.ms-wk-srclink:hover{ color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.ms-wk-srclink:hover .ms-wk-arrow{ transform: translate(2px,-2px); }
/* WU-36: the daily brief row above the weekly paper - today's top story, full width
   (the Jobs tile left with /jobs, 2026-10-14). Both elements are grids or blocks with author display rules, so
   [hidden] needs its own rule to win. */
.ms-daily-section[hidden], .ms-wk-cards[hidden]{ display: none; }
.ms-daily-section{ padding-bottom: 0; }
.ms-daily-row{ display: grid; grid-template-columns: minmax(0,1fr); gap: 14px; width: 100%; margin: 14px 0 0; align-items: stretch; }
.ms-daily-lead{ flex-direction: row; }
.ms-daily-pic{ display: flex; flex-direction: column; flex: 0 0 52%; min-width: 0; }
.ms-daily-pic .ms-wk-card-media{ flex: 1; aspect-ratio: auto; min-height: 260px; }
.ms-daily-pic .wdx-credit{ padding: 8px 18px 10px; }
.ms-daily-lead .ms-wk-card-body{ justify-content: center; gap: 8px; padding: 24px 26px; }
.ms-daily-lead .ms-wk-card-title{ font-size: 22px; line-height: 1.2; }
.ms-daily-lead .ms-wk-card-desc{ font-size: 15px; }
.ms-daily-chip{ display: inline-block; margin-right: 8px; padding: 2px 8px; border-radius: 999px; background: var(--accent); color: #fff; font-size: 12px; letter-spacing: .08em; }
@media (max-width: 767px){
  .ms-daily-row{ grid-template-columns: minmax(0,1fr); }
  .ms-daily-lead{ flex-direction: column; }
  .ms-daily-pic{ flex: none; }
  .ms-daily-pic .ms-wk-card-media{ flex: none; aspect-ratio: 16 / 10; min-height: 0; }
  .ms-daily-lead .ms-wk-card-body{ padding: 16px 18px 18px; }
  .ms-daily-lead .ms-wk-card-title{ font-size: 18px; }
}
/* Sweep 2026-09-13. The paper had no phone rule: fr tracks default to min-width:auto, so
   the serif headlines widened the two columns to 431px inside a 341px card and the
   paper's overflow:hidden clipped the second column with no way to scroll to it; the
   cards row stayed three-up at 105px each. Below 700px both stack (minmax(0,…) above
   keeps a long headline from widening the grid at any width). The picture credit
   ("Image: {host}", "Photo by … on Pexels") sits under the paper's pictures as it does
   on /weekly and /news, lifted above the stretched card link so its owner link works. */
.ms-wk-lead-media:has(+ .wdx-credit){ margin-bottom: 6px; }
.ms-wk-lead .wdx-credit{ margin: 0 0 12px; }
.ms-wk-card .wdx-credit{ margin: 0; padding: 10px 18px 0; }
.ms-wk-card .wdx-credit + .ms-wk-card-body{ padding-top: 8px; }
.ms-wk-lead .wdx-credit, .ms-wk-item .wdx-credit, .ms-wk-card .wdx-credit{ position: relative; z-index: 2; width: fit-content; max-width: 100%; }
.ms-wk-mast-srcs::before{ content: ' · '; }
@media (max-width: 700px){
  .ms-wk-masthead{ padding: 18px 20px 14px; }
  .ms-wk-mast-srcs{ display: none; }
  .ms-wk-paper-grid{ grid-template-columns: minmax(0,1fr); }
  .ms-wk-lead{ padding: 20px; border-right: 0; border-bottom: 1px solid rgba(0,0,0,.10); }
  .ms-wk-lead-h{ font-size: 22px; }
  .ms-wk-item{ padding: 18px 20px; }
  .ms-wk-item-pic{ width: 96px; margin-left: 14px; }
  .ms-wk-cards{ grid-auto-flow: row; grid-template-columns: minmax(0,1fr); }
}

/* ---- Tighter side gutters + near-full-width for the redesigned sections ---- */
.ms-vid-section, .ms-build-section, .ms-wk-section{
  padding-left: clamp(16px, 2.2vw, 40px);
  padding-right: clamp(16px, 2.2vw, 40px);
}
.ms-vid-section .ms-inner, .ms-build-section .ms-inner, .ms-wk-section .ms-inner{ max-width: none; }
/* ---- Tighter vertical rhythm between home sections (scoped to home only — leaves dashboard/hub --ms-pad-y untouched) ---- */
.ms-vid-section, .ms-build-section, .ms-wk-section, .ms-says-section{
  padding-top: clamp(72px, 8vw, 120px);
  padding-bottom: clamp(72px, 8vw, 120px);
}

/* ---- Newsletter — "after the cleanse" dark card ---- */
.nl-cleanse{ background: transparent; padding: 40px 24px 8px; border: 0; }
.nl-cleanse .nl-inner{
  max-width: 760px; margin: 0 auto; display: block; text-align: center;
  background: #0b0b0c; border-radius: 22px; padding: 48px 40px;
}
.nl-cleanse .nl-head{ display: block; margin: 0; }
.nl-cleanse .nl-eyebrow{ color: rgba(255,255,255,.5); letter-spacing: .08em; font-size: 12px; font-weight: 600; text-transform: uppercase; }
.nl-cleanse .nl-title{ color: #fff; font-family: var(--f-display); font-size: clamp(24px, 4.5vw, 32px); font-weight: 600; letter-spacing: -0.018em; line-height: 1.15; margin: 14px 0 12px; }
/* The headline was one unbreakable 491px line at 375 and pushed body.scrollWidth to 555:
   .nl-title (app.css) sets white-space: nowrap, which this block never reset. Letting it
   wrap is the whole fix. The <br> stays visible — index.html has no space around it
   ("...use.<br>Every..."), so display:none on the br ran the two sentences together. */
@media (max-width: 560px){ .nl-cleanse .nl-title{ white-space: normal; } }
.nl-cleanse .nl-meta{ color: rgba(255,255,255,.6); font-size: 14px; max-width: 50ch; margin: 0 auto 24px; line-height: 1.5; }
.nl-cleanse .nl-form{ display: flex; gap: 10px; justify-content: center; max-width: 440px; margin: 0 auto; }
.nl-cleanse .nl-input{
  flex: 1; background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.16);
  color: #fff; border-radius: 999px; padding: 12px 18px; font-size: 14px;
}
.nl-cleanse .nl-input::placeholder{ color: rgba(255,255,255,.4); }
.nl-cleanse .nl-btn{
  background: var(--accent); color: #fff; border: 0; border-radius: 999px;
  padding: 12px 24px; font-size: 14px; font-weight: 600; cursor: pointer;
  transition: background .18s ease;
}
.nl-cleanse .nl-btn:hover{ background: var(--accent-2); }
.nl-cleanse .nl-join{ color: rgba(255,255,255,.55); font-size: 12px; margin: 18px 0 0; }

/* Playground route chrome (native Agent Builder fills the content area). */
body.route-playground .foot{ display:none; }
body.route-playground .dash-scroll{ overflow:hidden; padding:0; }

/* ============================================================================
   Visual Response — designed UI for playground assistant output.
   Rendered by assets/visual-response.js into #ms-pg-output / #ms-dp-out.
   Reuses --ms-* tokens; status colors are scoped locally to avoid :root drift.
   ========================================================================== */
.vr-root{
  --vr-success:#10b981; --vr-warning:#f59e0b; --vr-error:#ef4444; --vr-info:var(--ms-accent);
  --vr-radius:16px; --vr-radius-sm:12px;
  display:flex; flex-direction:column; gap:20px;
  opacity:0; transform:translateY(6px);
  transition:opacity var(--ms-dur-state) var(--ms-ease-out), transform var(--ms-dur-state) var(--ms-ease-out);
}
.vr-root.is-in{ opacity:1; transform:none; }
@media (prefers-reduced-motion: reduce){
  .vr-root{ transition:none; transform:none; opacity:1; }
}

/* Header */
.vr-head{ display:flex; align-items:flex-start; justify-content:space-between; gap:12px; }
.vr-title{ font-size:18px; font-weight:650; color:var(--ms-ink); letter-spacing:-.01em; }
.vr-subtitle{ font-size:13px; color:var(--ms-ink-3); margin-top:2px; }

/* Badges */
.vr-badge{
  display:inline-flex; align-items:center; font-size:12px; font-weight:600;
  padding:3px 9px; border-radius:999px; text-transform:capitalize; white-space:nowrap;
  color:var(--ms-ink-2); background:var(--ms-surface-cool); border:.5px solid var(--ms-line);
}
.vr-badge--ready,.vr-badge--success{ color:#0b7a5b; background:rgba(16,185,129,.10); border-color:rgba(16,185,129,.25); }
.vr-badge--warning,.vr-badge--draft{ color:#a5680a; background:rgba(245,158,11,.12); border-color:rgba(245,158,11,.28); }
.vr-badge--error{ color:#b42318; background:rgba(239,68,68,.10); border-color:rgba(239,68,68,.28); }

.vr-body{ display:flex; flex-direction:column; gap:16px; }
.vr-section-title{ font-size:12px; font-weight:600; color:var(--ms-ink-3); text-transform:uppercase; letter-spacing:.04em; margin-bottom:8px; }

/* Summary panel */
.vr-panel{ background:var(--ms-surface); border:.5px solid var(--ms-line); border-radius:var(--vr-radius); padding:18px 20px; }
.vr-panel-title{ font-size:14px; font-weight:600; color:var(--ms-ink); margin-bottom:8px; }
.vr-p{ font-size:14px; line-height:1.6; color:var(--ms-ink-2); margin:0 0 8px; }
.vr-p:last-child{ margin-bottom:0; }

/* Card grid */
.vr-grid{ display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr)); gap:14px; }
.vr-card{ background:var(--ms-surface); border:.5px solid var(--ms-line); border-radius:var(--vr-radius-sm); padding:16px; transition:border-color var(--ms-dur-quick) var(--ms-ease); }
.vr-card:hover{ border-color:var(--ms-ink-4); }
.vr-card-title{ font-size:14px; font-weight:600; color:var(--ms-ink); margin-bottom:6px; }
.vr-card-desc{ font-size:13px; line-height:1.55; color:var(--ms-ink-3); }
.vr-card--success{ border-color:rgba(16,185,129,.35); }
.vr-card--warning{ border-color:rgba(245,158,11,.35); }
.vr-card--error{ border-color:rgba(239,68,68,.35); }

/* Steps */
.vr-steps{ display:flex; flex-direction:column; gap:10px; }
.vr-step{ display:flex; gap:14px; align-items:flex-start; background:var(--ms-surface); border:.5px solid var(--ms-line); border-radius:var(--vr-radius-sm); padding:14px 16px; }
.vr-step-num{ flex:0 0 26px; height:26px; border-radius:999px; background:var(--ms-accent-tint); color:var(--ms-accent); font-size:13px; font-weight:650; display:flex; align-items:center; justify-content:center; }
.vr-step-title{ font-size:14px; font-weight:600; color:var(--ms-ink); }
.vr-step-desc{ font-size:13px; line-height:1.55; color:var(--ms-ink-3); margin-top:3px; }

/* Table */
.vr-table-wrap{ overflow-x:auto; border:.5px solid var(--ms-line); border-radius:var(--vr-radius-sm); }
.vr-table{ width:100%; border-collapse:collapse; font-size:13px; }
.vr-table th{ text-align:left; font-weight:600; color:var(--ms-ink-2); background:var(--ms-surface-cool); padding:10px 14px; border-bottom:.5px solid var(--ms-line); white-space:nowrap; }
.vr-table td{ padding:10px 14px; color:var(--ms-ink-2); border-bottom:.5px solid var(--ms-line-soft); }
.vr-table tr:last-child td{ border-bottom:0; }

/* Code panel */
.vr-code{ border:.5px solid var(--ms-line); border-radius:var(--vr-radius-sm); overflow:hidden; background:#0f1117; }
.vr-code-bar{ display:flex; align-items:center; justify-content:space-between; padding:8px 12px; background:#171a21; border-bottom:.5px solid rgba(255,255,255,.08); }
.vr-code-lang{ font-size:12px; font-weight:600; color:#9aa4b2; text-transform:uppercase; letter-spacing:.04em; }
.vr-code-copy{ display:inline-flex; align-items:center; gap:5px; font-size:12px; color:#c3cad6; background:transparent; border:.5px solid rgba(255,255,255,.14); border-radius:8px; padding:4px 9px; cursor:pointer; transition:background var(--ms-dur-quick) var(--ms-ease); }
.vr-code-copy:hover{ background:rgba(255,255,255,.08); }
.vr-code-pre{ margin:0; padding:14px 16px; overflow-x:auto; }
.vr-code-pre code{ font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:12.5px; line-height:1.6; color:#e6e9ef; white-space:pre; }

/* Callout */
.vr-callout{ display:flex; gap:12px; align-items:flex-start; border:.5px solid var(--ms-line); border-radius:var(--vr-radius-sm); padding:14px 16px; background:var(--ms-surface); }
.vr-callout-ico{ flex:0 0 auto; margin-top:1px; }
.vr-callout-title{ font-size:14px; font-weight:600; color:var(--ms-ink); margin-bottom:3px; }
.vr-callout-text{ font-size:13px; line-height:1.55; color:var(--ms-ink-2); }
.vr-callout--info{ background:var(--ms-accent-tint); border-color:color-mix(in srgb, var(--accent) 25%, transparent); } .vr-callout--info .vr-callout-ico{ color:var(--ms-accent); }
.vr-callout--warning{ background:rgba(245,158,11,.08); border-color:rgba(245,158,11,.28); } .vr-callout--warning .vr-callout-ico{ color:var(--vr-warning); }
.vr-callout--error{ background:rgba(239,68,68,.07); border-color:rgba(239,68,68,.28); } .vr-callout--error .vr-callout-ico{ color:var(--vr-error); }

/* Checklist */
.vr-checklist{ display:flex; flex-direction:column; gap:8px; }
.vr-check-row{ display:flex; gap:10px; align-items:center; font-size:14px; color:var(--ms-ink-2); }
.vr-check-ico{ color:var(--ms-ink-4); }
.vr-check-row.is-done .vr-check-ico{ color:var(--vr-success); }
.vr-check-row.is-done .vr-check-label{ color:var(--ms-ink-3); }

/* Action bar */
.vr-actions{ display:flex; gap:8px; flex-wrap:wrap; }
.vr-btn{ display:inline-flex; align-items:center; gap:6px; font-size:13px; font-weight:500; padding:7px 13px; border-radius:9px; cursor:pointer; border:.5px solid var(--ms-line); background:var(--ms-surface); color:var(--ms-ink-2); transition:border-color var(--ms-dur-quick) var(--ms-ease),background var(--ms-dur-quick) var(--ms-ease); }
.vr-btn:hover{ border-color:var(--ms-ink-4); }
.vr-btn--primary{ background:var(--ms-accent); border-color:var(--ms-accent); color:#fff; }
.vr-btn--primary:hover{ background:var(--ms-accent-hov); border-color:var(--ms-accent-hov); }
.vr-btn--ghost{ background:transparent; }
.vr-ico{ display:inline-flex; align-items:center; }

/* ============================================================
   P3-6: shared visible keyboard-focus ring for mystyle controls
   that suppress the default outline (outline:none spots).
   ============================================================ */
.ms-hero-form input:focus-visible,
.ms-pg-textarea:focus-visible,
.hub-search-input:focus-visible,
.hub-select:focus-visible,
.gd-cmt-input:focus-visible,
.gd-article .tool-body textarea:focus-visible{
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

/* ============================================================
   WP5 hub seed popup — moved here from agent-builder.css
   (2026-08-01). The rules are unchanged; only the file changed.

   It has to live in a globally loaded stylesheet. index.html
   loads agent-builder.css on the playground route ONLY (injected
   on demand by loadAgentBuilder), but aadSeedPopup() at
   assets/app.js:64 fires from the AGENTS HUB, where that file was
   never loaded. The popup therefore rendered with no styling at
   all, in normal document flow below the footer — measured
   signed-out on the 2026-08-01 preview deploy
   (seedPopupCssLoaded: false).

   The --ms-* tokens these rules fall back on are defined in this
   file, so this is also where they belong.
   ============================================================ */
.aad-seedpop{ position:fixed; inset:0; z-index:2000; display:flex; align-items:center; justify-content:center; padding:20px; }
.aad-seedpop-backdrop{ position:absolute; inset:0; background:rgba(15,17,21,.42); backdrop-filter:blur(2px); }
/* Task D: the drop shadow is gone — the house rule is a .5px border instead of a box
   shadow, and .auth-panel (auth.css) is the in-repo precedent for a modal card: .5px
   border, 18px radius, no shadow. The scrim behind it already does the separating. */
.aad-seedpop-card{
  position:relative; z-index:1; width:100%; max-width:520px; max-height:88vh; overflow:auto;
  background:var(--ms-surface,#fff); border:.5px solid var(--ms-line,rgba(0,0,0,.10)); border-radius:18px;
  padding:26px 26px 22px;
  font-family:Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif; color:var(--ms-ink,var(--text));
  animation:aad-seedpop-in 160ms ease-out;
}
/* Task D: was translateY(8px) scale(.985) — a fade-up-and-grow. Plain opacity now, and
   it finally honours prefers-reduced-motion, which it never did. */
@keyframes aad-seedpop-in{ from{ opacity:0; } to{ opacity:1; } }
@media (prefers-reduced-motion: reduce){
  .aad-seedpop-card{ animation:none; }
}
.aad-seedpop-x{
  position:absolute; top:14px; right:14px; width:30px; height:30px; border:0; border-radius:9px; cursor:pointer;
  background:transparent; color:var(--ms-ink-3,var(--text-3)); font-size:20px; line-height:1;
}
/* Task D: was background + colour together. One affordance. */
.aad-seedpop-x:hover{ color:var(--ms-ink,var(--text)); }
.aad-seedpop-agent{ font-size:12px; font-weight:700; letter-spacing:.04em; text-transform:uppercase; color:var(--ms-accent,var(--accent)); }
.aad-seedpop-q{ margin:8px 0 16px; font-size:19px; font-weight:650; letter-spacing:-.01em; line-height:1.3; }
.aad-seedpop-input{
  width:100%; box-sizing:border-box; padding:12px 14px; font-family:inherit; font-size:14px;
  border:1px solid var(--ms-line,rgba(0,0,0,.14)); border-radius:11px; background:var(--ms-surface,#fff); color:var(--ms-ink,var(--text));
}
.aad-seedpop-input:focus{ outline:none; border-color:var(--ms-accent,var(--accent)); box-shadow:0 0 0 3px var(--ms-accent-tint,var(--accent-tint)); }
.aad-seedpop-chips{ display:flex; flex-wrap:wrap; gap:8px; margin:14px 0 20px; }
.aad-seedpop-chip{
  border:1px solid var(--ms-line,rgba(0,0,0,.12)); background:var(--ms-surface,#fff); color:var(--ms-ink-2,#3a3a3c);
  border-radius:999px; padding:7px 14px; font-family:inherit; font-size:13px; font-weight:550; cursor:pointer;
  transition:border-color var(--ms-dur-quick,180ms) var(--ms-ease-out,ease);
}
/* Task D: was border-color + colour + background stacked three deep, on `transition:all`.
   One quiet affordance, and the transition now names the one property it animates. */
.aad-seedpop-chip:hover{ border-color:var(--ms-accent,var(--accent)); }
.aad-seedpop-run{
  width:100%; border:0; border-radius:12px; cursor:pointer; padding:13px 18px;
  background:var(--ms-accent,var(--accent)); color:#fff; font-family:inherit; font-size:14px; font-weight:650;
  transition:background var(--ms-dur-quick,180ms);
}
.aad-seedpop-run:hover:not(:disabled){ background:var(--ms-accent-hov,#0063c6); }
.aad-seedpop-run:disabled{ opacity:.5; cursor:not-allowed; }

@media (max-width:900px){
  .aad-seedpop-q{ font-size:17px; }
}

/* ============================================================================
   COMPARE, N-WAY (.cmp-*) — appended as one block by U5.

   compare-nway.js inlines its own layout, so this is deliberately small and mostly
   typographic. Two rules here are load-bearing rather than cosmetic; both are marked.

   No `display` on any table element. The matrix hides columns with the [hidden]
   attribute, and a `display` declaration on th/td would defeat that while also
   breaking the table's semantics for assistive tech.
   ============================================================================ */

/* 🔴 LOAD-BEARING. compare-nway.js emits `style="display:flex"` on .cmp-pager and
   `style="display:block"` on .cmp-opt, then toggles both with the [hidden] attribute.
   An inline declaration outranks the UA sheet's `[hidden]{display:none}`, so without
   this rule the mobile pager renders at every width and the picker's search filter
   hides nothing. An author !important is what outranks an inline style. */
.cmp-picker [hidden], .cmp-result [hidden]{ display:none !important; }

.cmp-cap{ font-size:13px; color:var(--text-3); }
.cmp-name-t{ display:block; font-size:13px; color:var(--text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.cmp-basis{ display:block; font-size:11px; color:var(--text-3); line-height:1.2; }
.cmp-val{ font-size:13px; font-weight:600; font-variant-numeric:tabular-nums; }
.cmp-from{ font-weight:400; color:var(--text-3); }
.cmp-axis span{ font-size:11px; color:var(--text-3); font-variant-numeric:tabular-nums; }

/* .tbl uppercases its headers. Tool names are proper nouns and must not shout. */
.cmp-matrix th[scope="col"]{ text-transform:none; letter-spacing:0; font-size:13px; }
.cmp-matrix th[scope="row"]{ color:var(--text-3); font-weight:500; width:148px; }
.cmp-matrix td{ vertical-align:top; }

.cmp-details summary{ font-size:13px; color:var(--accent); cursor:pointer; width:max-content; list-style:none; }
.cmp-details summary::-webkit-details-marker{ display:none; }
.cmp-details summary:hover{ color:var(--accent-press); }
.cmp-opt:hover{ background:var(--bg-2); cursor:pointer; }
.cmp-unpriced-n{ font-weight:500; }

@media (max-width:700px){
  .cmp-matrix th, .cmp-matrix td{ padding:11px 12px; font-size:13px; }
  .cmp-matrix th[scope="row"]{ width:92px; font-size:12px; }
}

/* Zero-state category presets. .pill is reused from app.css:932 so the chips match the
   filter pills elsewhere; only the row wrapper and its label are new. */
.cmp-presets{ display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin:18px 0 4px; }
.cmp-presets-l{ font-size:13px; color:var(--text-3); margin-right:2px; }

/* ---- capability benchmarks (.cmp-bench-*) — sibling of the cost block, same grammar ----
   Bars are single accent on a 0-100 axis and only ever compare ONE benchmark against
   itself. Elo gets a rank chip and no bar: a bar implies an absolute scale, and Elo is
   relative — it moves when other models improve without this tool changing at all. */
.cmp-bench{ margin-top:34px; }
.cmp-bench-h{ font-size:15px; font-weight:600; margin:0 0 6px; }
.cmp-bench-honesty{ font-size:13px; color:var(--text-3); margin:0 0 18px; max-width:64ch; }
.cmp-bench-g{ margin:0 0 22px; padding:0 0 18px; border-bottom:0.5px solid var(--border); }
.cmp-bench-g:last-of-type{ border-bottom:0; }
.cmp-bench-g-h{ font-size:13px; font-weight:600; margin:0 0 2px; }
.cmp-bench-cap{ font-size:12px; color:var(--text-3); margin:0 0 12px; max-width:60ch; }
.cmp-bench-fig{ display:grid; grid-template-columns:minmax(90px,1.1fr) minmax(0,3fr) auto; gap:8px 12px; align-items:center; }
.cmp-bench-axis{ font-size:11px; color:var(--text-3); font-variant-numeric:tabular-nums; }
.cmp-bench-cites{ margin:12px 0 0; padding:0; list-style:none; }
.cmp-bench-cite{ font-size:11px; color:var(--text-3); line-height:1.5; }
.cmp-bench-cite-n{ color:var(--text); }
.cmp-bench-cav-n{ font-weight:600; }

@media (max-width:700px){
  .cmp-bench-fig{ grid-template-columns:1fr; gap:4px; }
  .cmp-bench-honesty, .cmp-bench-cap{ max-width:none; }
}

/* ---- ranked columns: the at-a-glance view above each Elo benchmark ----
   Ordered best-first so position and height agree before any label is read. The axis is
   deliberately NOT zero-based (Elo has no meaningful zero) and the floor is stated in
   words directly beneath, which is what separates an honest truncated axis from a
   misleading one. No value is encoded by colour: height, order and printed text only. */
.cmp-cols{ display:flex; align-items:flex-end; gap:10px; list-style:none; margin:4px 0 0; padding:0; height:132px; }
.cmp-col{ flex:1 1 0; min-width:0; display:flex; flex-direction:column; justify-content:flex-end; align-items:center; height:100%; text-align:center; }
.cmp-col-v{ font-size:12px; font-weight:600; font-variant-numeric:tabular-nums; margin:0 0 4px; white-space:nowrap; }
.cmp-col-bar{ width:100%; max-width:64px; background:var(--accent); border-radius:3px 3px 0 0; min-height:6px; }
.cmp-col-n{ font-size:12px; margin:7px 0 0; width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.cmp-col-r{ font-size:11px; color:var(--text-3); font-variant-numeric:tabular-nums; }
.cmp-cols-axis{ font-size:11px; color:var(--text-3); margin:9px 0 0; max-width:62ch; }

@media (max-width:700px){
  .cmp-cols{ height:112px; gap:6px; }
  .cmp-col-v{ font-size:11px; }
  .cmp-col-n{ font-size:11px; }
}

/* ============================================================================
   COMPARE HUB (.cmph-*) — the four tabs of /compare.

   assets/compare-hub.js emits the markup and this block is the only place it is
   styled. It reuses .pill, .intg-tab*, .hub-search--strip, .btn-text and
   .tbl/.tbl-wrap rather than restating them; the only thing it overrides is the
   table look, and only for .cmph-lb.

   One rule here is load-bearing rather than cosmetic. It is marked.
   ============================================================================ */

/* 🔴 LOAD-BEARING. cmpHubSearch filters rows by toggling the [hidden] ATTRIBUTE,
   never style.display. Any author `display` on a table row outranks the UA
   sheet's `[hidden]{display:none}`, which is exactly how a filter silently stops
   filtering — this rule is what keeps it working. */
.cmph-lb tr[hidden]{ display:none !important; }

/* ---- furniture shared by the panels ---- */
.cmph-intro{ margin:0; font-size:14px; max-width:76ch; }
.cmph-empty{ margin:16px 0 0; }
.cmph-boards{ display:flex; flex-wrap:wrap; gap:6px; margin:16px 0 0; }
.cmph-cats{ display:flex; flex-wrap:wrap; gap:6px; margin:0 0 18px; }
/* The editorial grid trails the leaderboard panel; it needs to read as the next
   section rather than the end of the previous one. */
.cmph-editorial{ display:block; margin-top:40px; }

/* ---- tab 1: the leaderboard ----
   One card per model. The table keeps its semantics — a real caption, a real
   thead, one row per tool — and the cells draw the card edges themselves,
   because a run of <td>s cannot be wrapped in a box element. That is what
   border-collapse:separate buys: border-spacing then opens the gap between
   rows, and each row reads as a card sitting on the page ground. */
.tbl-wrap.cmph-wrap{ border:0; border-radius:0; margin-top:8px; }
.cmph-lb{ border-collapse:separate; border-spacing:0 10px; background:transparent; overflow:visible; }
.cmph-lb thead th{ padding:0 14px 2px; background:transparent; border-bottom:0; text-transform:none; letter-spacing:normal; font-size:11px; line-height:1.35; color:var(--text-3); vertical-align:bottom; }
.cmph-lb thead th small{ font-size:10px; }
.cmph-lb td{ padding:15px 14px; vertical-align:top; background:var(--surface); border-top:0.5px solid var(--border); border-bottom:0.5px solid var(--border); }
.cmph-lb td:first-child{ padding-left:18px; border-left:0.5px solid var(--border); border-top-left-radius:var(--r-md); border-bottom-left-radius:var(--r-md); }
.cmph-lb td:last-child{ padding-right:18px; text-align:right; border-right:0.5px solid var(--border); border-top-right-radius:var(--r-md); border-bottom-right-radius:var(--r-md); }
/* .tbl drops the last row's bottom border, which is right for a collapsed table
   and wrong here: every row is a card and needs all four edges. */
.cmph-lb tbody tr:last-child td{ border-bottom:0.5px solid var(--border); }
/* .tbl tints the whole row on hover. The card edge is the affordance here and
   the link colour below is the only hover cue. */
.cmph-lb tbody tr:hover{ background:transparent; }
.cmph-lb a:hover{ color:var(--accent); }

/* 260px is measured, not round: the widest rank pill ("#13 of 387"), the 22px logo,
   both 10px gaps and the longest model name in the catalogue ("Stable Diffusion")
   add up to ~257px. Below that the name wraps to a second line and the card rows
   stop sharing a rhythm, which is the whole point of the spaced-row treatment. */
.cmph-model{ display:flex; align-items:center; gap:10px; min-width:260px; }
.cmph-model-t{ display:flex; flex-direction:column; min-width:0; }
/* The ellipsis is a guard for a future long name, not something any current name
   reaches; nowrap is what actually holds the row to one line. */
.cmph-model-t a{ font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.cmph-logo{ width:22px; height:22px; border-radius:6px; object-fit:contain; flex:none; }
/* The medal circle (owner reference sheets, 2026-08-31). It prints the row's position
   in the ACTIVE board's published order -- a number that each cell's "#N of M" line
   repeats in text, so the colour is decoration, not information. The three tints are a
   sanctioned exception to the single-accent rule, per those reference sheets. */
.cmph-medal{ display:inline-flex; align-items:center; justify-content:center; min-width:26px; height:26px; padding:0 7px; border-radius:999px; border:0.5px solid var(--border); background:var(--surface); font-size:12.5px; font-weight:600; font-variant-numeric:tabular-nums; flex:none; }
.cmph-medal-1{ background:#f3c952; border-color:#e8ba38; }
.cmph-medal-2{ background:#dde1e7; border-color:#c9ced6; }
.cmph-medal-3{ background:#e5a96b; border-color:#d89a58; }
.cmph-medal-x{ font-weight:400; }
.cmph-cell-v{ font-weight:600; font-variant-numeric:tabular-nums; }
.cmph-cell-e{ display:block; font-size:11px; color:var(--text-3); overflow-wrap:anywhere; }
.cmph-lb td small{ display:block; margin-top:2px; font-size:11px; line-height:1.4; }
/* The reference's ghost "View details" control: hairline outline, dark label,
   accent on hover from the one rule above. */
.cmph-lb .btn-text{ display:inline-block; padding:6px 12px; border:0.5px solid var(--border); border-radius:999px; font-size:13px; color:var(--text); white-space:nowrap; }

.cmph-note, .cmph-full{ margin:16px 0 0; font-size:13px; max-width:76ch; }
.cmph-uncovered{ margin:16px 0 0; font-size:14px; }
.cmph-uncovered summary{ cursor:pointer; }

/* ---- tab 3: the categories ---- */
.cmph-cat{ border:0.5px solid var(--border); border-radius:14px; padding:18px 20px; margin-top:16px; }
/* cmpBenchGroupHtml carries an inline top rule and margin so its sections read as
   separate blocks inside one flowing column. Each group is its own card here, so
   that rule is a line drawn under nothing; an inline declaration takes an author
   !important to beat, and compare-bench.js is frozen. */
.cmph-cat > .cmp-bench-g{ margin-top:0 !important; padding-top:0 !important; border-top:0 !important; }
.cmph-cat-links{ margin:14px 0 0; font-size:13px; }
.cmph-cat-links a:hover{ color:var(--accent); }

/* ---- tab 4: the use cases ---- */
.cmph-uc{ display:grid; grid-template-columns:220px 1fr; gap:20px; margin-top:16px; align-items:start; }
.cmph-uc-list{ border:0.5px solid var(--border); border-radius:14px; padding:6px; }
.cmph-uc-list a{ display:block; padding:8px 10px; border-radius:8px; font-size:14px; }
.cmph-uc-list a:hover{ color:var(--accent); }
.cmph-uc-list a[aria-current]{ background:var(--bg-2); font-weight:600; color:var(--accent); }
/* The picks sit side by side the way the reference lays them out; the heading,
   blurb and disclosure run the full width above them. Two explicit tracks rather
   than auto-fit: an item spanning 1/-1 occupies every track, so auto-fit would
   have nothing left to collapse and would strand an empty column beside them. */
.cmph-uc-main{ display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:12px; align-content:start; }
.cmph-uc-h, .cmph-uc-blurb, .cmph-uc-note{ grid-column:1 / -1; margin:0; }
.cmph-uc-h{ font-size:20px; letter-spacing:-0.01em; }
.cmph-uc-blurb{ font-size:14px; }
.cmph-uc-note{ margin-bottom:4px; font-size:12.5px; max-width:76ch; }
.cmph-uc-card{ border:0.5px solid var(--border); border-radius:14px; padding:16px 18px; margin:0 0 12px; display:flex; flex-direction:column; }
/* One header row per the reference: medal, then name, then the badge pushed right. */
.cmph-uc-top{ margin:0; display:flex; align-items:center; gap:10px; }
.cmph-badge{ display:inline-block; padding:1px 8px; border:0.5px solid var(--border); border-radius:999px; font-size:12px; margin-left:auto; flex:none; }
.cmph-uc-name{ display:flex; align-items:center; gap:8px; font-size:16px; font-weight:600; min-width:0; }
.cmph-uc-name a{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.cmph-uc-name a:hover{ color:var(--accent); }
.cmph-uc-why{ margin:8px 0 0; font-size:14px; color:var(--text-2); }
/* The figure and the chips each close with the gap the footer rule needs, so the
   footer can take the leftover height and still never sit flush against them —
   that is what keeps the two cards in a row ending on the same line. */
.cmph-uc-fig{ margin:14px 0; }
.cmph-uc-big{ font-size:24px; font-weight:600; font-variant-numeric:tabular-nums; }
.cmph-uc-fig small{ display:block; margin-top:4px; font-size:11px; }
.cmph-uc-chips{ display:flex; flex-wrap:wrap; gap:6px; margin:0 0 14px; }
.cmph-uc-chip{ padding:5px 12px; gap:4px; font-size:12px; font-variant-numeric:tabular-nums; }
.cmph-uc-morechip{ padding:5px 12px; font-size:12px; }
.cmph-uc-foot{ margin:auto 0 0; padding-top:12px; border-top:0.5px solid var(--border); }
/* The reference's one filled control. Accent fill IS the single accent; no hover effect,
   because a filled button is already the affordance. */
.btn-cta{ display:inline-block; padding:8px 16px; border-radius:999px; background:var(--accent); color:#fff; font-size:13.5px; font-weight:500; }
.btn-cta:hover{ color:#fff; }
.cmph-uc-list-h{ margin:4px 10px 6px; font-size:12px; font-weight:600; text-transform:uppercase; letter-spacing:.04em; color:var(--text-3); }
.cmph-uc-all{ grid-column:1 / -1; text-align:center; margin:8px 0 0; font-size:14px; }
.cmph-uc-all a:hover{ color:var(--accent); }

@media (max-width:900px){
  .cmph-uc{ grid-template-columns:1fr; }
}
@media (max-width:700px){
  .cmph-uc-main{ grid-template-columns:1fr; }
}

/* ---- the light tab strip (owner reference sheets, 2026-08-31) ----
   WU-4: this block is gone. .intg-tabwrap itself now ships white + hairline with an --ink
   active tab (app.css), which is exactly what these seven overrides existed to provide
   back when the base strip was dark — and its --bg-2 well plus white/accent active tab
   contradicted comapre_01.png. The .cmph-tabs class stays on the markup as a hook; the
   compare strip and the guide strip are one object now, so nothing styles it twice. */

/* ---- tab 2: side by side (rail + table, owner reference sheets 2026-08-31) ----
   The old open-listbox picker and its CSS disclosure rule are gone: the hub was
   cmpPickerHtml's only call site and it draws its own checkbox rail now, so .cmp-picker
   markup no longer renders anywhere. The frozen compare-nway.js keeps exporting it,
   its other .cmp-* styling stays untouched below/above, and its own verifier still
   freezes the module itself. */
.cmph-sxs{ display:grid; grid-template-columns:260px minmax(0,1fr); gap:20px; margin-top:16px; align-items:start; }
.cmph-sxs-rail{ border:0.5px solid var(--border); border-radius:14px; padding:12px; }
.cmph-sxs-h{ margin:2px 6px 10px; font-size:14px; }
.cmph-sxs-row{ display:flex; align-items:center; gap:9px; padding:7px 8px; border-radius:8px; font-size:14px; color:var(--text); }
a.cmph-sxs-row:hover{ color:var(--accent); }
.cmph-sxs-row-t{ display:flex; flex-direction:column; min-width:0; }
.cmph-sxs-row-t small{ font-size:11px; }
.cmph-sxs-row.is-on{ background:color-mix(in srgb, var(--accent) 6%, transparent); }
.cmph-sxs-row.is-off{ opacity:.45; }
.cmph-cb{ width:16px; height:16px; flex:none; border:1.5px solid var(--border-2); border-radius:4px; background:var(--surface); position:relative; }
.is-on .cmph-cb{ background:var(--accent); border-color:var(--accent); }
.is-on .cmph-cb::after{ content:"\2713"; position:absolute; inset:0; display:flex; align-items:center; justify-content:center; color:#fff; font-size:11px; }
.cmph-sxs-more{ margin:8px 6px 0; font-size:13px; }
.cmph-sxs-more summary{ cursor:pointer; color:var(--text-2); }
.cmph-sxs-scroll{ max-height:250px; overflow:auto; margin-top:6px; }
.cmph-sxs-cap{ margin:10px 6px 2px; font-size:11.5px; }
.cmph-sxs-bar{ display:flex; align-items:center; justify-content:space-between; gap:12px; }
.cmph-sxs-h2{ margin:0; font-size:16px; }
.cmph-sxs-print{ padding:6px 12px; border:0.5px solid var(--border); border-radius:999px; background:var(--surface); font:inherit; font-size:13px; color:var(--text); cursor:pointer; white-space:nowrap; }
.cmph-sxs-print:hover{ color:var(--accent); }
/* Row heads are the card's left edge, the same way .cmph-lb's first td is elsewhere. */
.cmph-sxs-tbl th[scope="row"]{ padding:15px 14px 15px 18px; background:var(--surface); border-top:0.5px solid var(--border); border-bottom:0.5px solid var(--border); border-left:0.5px solid var(--border); border-top-left-radius:var(--r-md); border-bottom-left-radius:var(--r-md); text-align:left; font-size:13px; vertical-align:top; white-space:nowrap; }
.cmph-sxs-tbl thead th{ padding:0 14px 8px; }
.cmph-sxs-th{ display:flex; align-items:center; gap:8px; }
.cmph-sxs-th-t{ display:flex; flex-direction:column; min-width:0; }
.cmph-sxs-th-t a{ font-weight:600; font-size:14px; color:var(--text); white-space:nowrap; }
.cmph-sxs-th-t a:hover{ color:var(--accent); }
/* Best PUBLISHED rank on that one row's board -- the tint repeats what the cell's own
   "#N of M" line says in text. Accent-tinted, not green: single-accent palette. */
.cmph-sxs-tbl td.cmph-best{ background:#eef5fd; }
.cmph-sxs-empty{ border:0.5px solid var(--border); border-radius:14px; padding:18px 20px; }
.cmph-sxs-eh{ margin:0 0 6px; font-weight:600; }

@media (max-width:900px){
  .cmph-sxs{ grid-template-columns:1fr; }
}
@media print{
  .cmph-sxs-rail, .cmph-sxs-print{ display:none; }
}

/* ---- the model layer (.cmpm-*) — /compare on GET /api/models (WU-5) ----
   Same grammar as .cmph-* above it on purpose: white cards on hairlines, one hover
   affordance, tokens only, no shadow and no gradient. The two never share a selector
   because they never share data: .cmph-* draws hand-checked TOOL rows out of
   benchmarks.js, .cmpm-* draws MODEL rows out of the live feed, and a rule that styled
   both would be the first place they got confused for each other. */
.cmpm{ display:block; }

/* Category chips. Mockup + plan section 1: white with a hairline, --ink when active. */
.cmpm-chips{ display:flex; flex-wrap:wrap; gap:8px; margin:0 0 18px; }
.cmpm-chips .pill{ padding:8px 18px; }
.cmpm-chips .pill{ background:var(--surface); border-color:var(--border); }
.cmpm-chips .pill:hover{ border-color:var(--border-2); }
.cmpm-chips .pill-active{ background:var(--ink); border-color:var(--ink); color:var(--ink-text, #fff); }
.cmpm-chips .pill-active:hover{ color:var(--ink-text, #fff); border-color:var(--ink); }
/* The board. Rows are cards: separate borders, 10px of air, hairline all round -- the
   same construction .cmph-lb uses, so the two boards cannot drift apart visually. */
.tbl-wrap.cmpm-wrap{ border:0; border-radius:0; margin:0; overflow-x:auto; }
.cmpm-tbl{ width:100%; border-collapse:separate; border-spacing:0 18px; background:transparent; }
/* Fixed layout so the score columns are evenly spaced whatever the feed returns; the model
   column is the one that gets the room it needs. */
.cmpm-lb{ table-layout:fixed; }
.cmpm-c-model{ width:300px; }
.cmpm-tbl thead th{ padding:0 14px 4px; background:transparent; border:0; text-align:left;
  font-size:11px; font-weight:500; line-height:1.35; letter-spacing:normal; text-transform:none;
  color:var(--text-3); white-space:nowrap; vertical-align:bottom; }
.cmpm-tbl td{ padding:18px 8px; background:var(--surface); border-top:0.5px solid var(--border);
  border-bottom:0.5px solid var(--border); vertical-align:middle; white-space:nowrap; }
.cmpm-tbl td:first-child{ padding-left:20px; border-left:0.5px solid var(--border);
  border-top-left-radius:var(--r-lg); border-bottom-left-radius:var(--r-lg); }
.cmpm-tbl td:last-child{ padding-right:20px; border-right:0.5px solid var(--border);
  border-top-right-radius:var(--r-lg); border-bottom-right-radius:var(--r-lg); }
/* The mockup's cell: the category in small grey type, the figure under it in bold. The
   label is per cell because there is no header row - see cmpmLeaderboardHtml. */
/* The label wraps rather than truncating: the feed's real labels run to "Creative writing"
   and "Web development", and a column header that reads "Creative wri..." is worse than a
   two-line one. The figure under it never wraps. */
.cmpm-cl{ display:block; margin-bottom:5px; font-size:11px; line-height:1.2; color:var(--text-3);
  white-space:normal; }
.cmpm-tbl tr[hidden]{ display:none !important; }

.cmpm-model{ display:flex; align-items:center; gap:12px; min-width:220px; }
.cmpm-model-t{ display:flex; flex-direction:column; min-width:0; }
.cmpm-model-t small{ font-size:12px; }
.cmpm-name{ font-size:16px; font-weight:600; color:var(--text); overflow:hidden; text-overflow:ellipsis; }
/* Leaderboard rows: a long model name wraps at its hyphens instead of ellipsising to the
   same label as its siblings ("claude-opus-4-5-20…" twice); the full name is also in the
   title attribute (sweep 2026-09-13). */
.cmpm-model-t .cmpm-name{ white-space: normal; overflow: visible; text-overflow: clip; overflow-wrap: anywhere; }
.cmpm-v{ font-size:16px; font-weight:600; font-variant-numeric:tabular-nums; }
.cmpm-cost{ color:var(--accent); letter-spacing:.02em; }
/* The tile: .tool-logo's monogram-behind-image trick, on white instead of its grey card
   well, because these sit on a white row and the well read as a smudge at this size. */
.cmpm-logo{ width:38px; height:38px; border-radius:10px; flex:none;
  background:var(--surface); border:0.5px solid var(--border); }
.cmpm-logo img{ padding:4px; background:transparent; }
.cmpm-logo .tool-logo-fallback{ font-size:14px; color:var(--text-2); }
.cmpm-lb thead th:first-child{ padding-left:18px; }

/* Position in the current ranking. The first three carry the mockup's medal tints; the
   number is printed either way, so the column still reads in grayscale. */
.cmpm-rank{ display:inline-flex; align-items:center; justify-content:center; width:32px; height:32px;
  border-radius:999px; border:0.5px solid var(--border); background:var(--bg-2);
  font-size:13px; font-weight:600; font-variant-numeric:tabular-nums; flex:none; }
.cmpm-rank-1{ background:var(--medal-1); border-color:var(--medal-1-line); color:var(--medal-1-ink); }
.cmpm-rank-2{ background:var(--medal-2); border-color:var(--medal-2-line); color:var(--medal-2-ink); }
.cmpm-rank-3{ background:var(--medal-3); border-color:var(--medal-3-line); color:var(--medal-3-ink); }

.cmpm-src{ margin:24px 0 0; font-size:13px; text-align:center; }
.cmpm-src a{ color:var(--accent); }
.cmpm-src a:hover{ text-decoration:underline; }
.cmpm-none{ margin:12px 0 0; font-size:13px; }

/* ---- side by side: picker rail + table ---- */
.cmpm-sxs-wrap{ display:grid; grid-template-columns:260px minmax(0,1fr); gap:20px; align-items:start; }
.cmpm-pick{ border:0.5px solid var(--border); border-radius:var(--r-lg); background:var(--surface); padding:12px; }
.cmpm-pick-h{ margin:2px 6px 10px; font-size:14px; }
.cmpm-pick-scroll{ max-height:420px; overflow-y:auto; }
.cmpm-pick-row{ display:flex; align-items:center; gap:9px; padding:7px 8px; border-radius:10px;
  font-size:14px; color:var(--text); }
a.cmpm-pick-row:hover{ color:var(--accent); }
.cmpm-pick-row[hidden]{ display:none !important; }
.cmpm-pick-row.is-on{ background:var(--accent-tint); }
.cmpm-pick-row.is-off{ opacity:.45; }
.cmpm-pick-t{ display:flex; flex-direction:column; min-width:0; }
.cmpm-pick-t .cmpm-name{ font-size:14px; }
.cmpm-pick-t small{ font-size:11px; }
.cmpm-pick .cmpm-logo{ width:24px; height:24px; border-radius:7px; }
.cmpm-cb{ width:16px; height:16px; flex:none; border:1.5px solid var(--border-2); border-radius:4px;
  background:var(--surface); position:relative; }
.is-on .cmpm-cb{ background:var(--accent); border-color:var(--accent); }
.is-on .cmpm-cb::after{ content:"\2713"; position:absolute; inset:0; display:flex; align-items:center;
  justify-content:center; color:#fff; font-size:11px; }
.cmpm-pick-cap{ margin:10px 6px 2px; font-size:11.5px; }

.cmpm-sxs-bar{ display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:6px; }
.cmpm-sxs-h{ margin:0; font-size:16px; }
.cmpm-export{ padding:8px 16px; border:0.5px solid var(--border); border-radius:999px;
  background:var(--surface); font:inherit; font-size:13px; color:var(--text); cursor:pointer; white-space:nowrap; }
.cmpm-export:hover{ color:var(--accent); }
.cmpm-sxs th[scope="row"]{ padding:14px 14px 14px 18px; background:var(--surface); text-align:left;
  font-size:13px; font-weight:500; color:var(--text-2); white-space:nowrap;
  border-top:0.5px solid var(--border); border-bottom:0.5px solid var(--border); border-left:0.5px solid var(--border);
  border-top-left-radius:var(--r-md); border-bottom-left-radius:var(--r-md); }
.cmpm-sxs thead th{ padding:0 14px 8px; }
.cmpm-sxs-th{ display:flex; align-items:center; gap:8px; }
.cmpm-sxs-th .cmpm-logo{ width:28px; height:28px; border-radius:8px; }
.cmpm-sxs-th .cmpm-name{ font-size:14px; }
/* The row label carries a glyph from the site's own set; it is decorative, and the label
   beside it is what names the row. */
.cmpm-rowh{ display:flex; align-items:center; gap:8px; }
.cmpm-rowh svg{ width:15px; height:15px; flex:none; color:var(--text-3); }
/* Best and runner-up in that one row, among the models shown. Status colours, not decoration:
   --success for the best figure, --warning for the one behind it, and the numbers are printed
   either way so the table still reads in grayscale. Everything else stays on the accent. */
.cmpm-sxs td.cmpm-best{ background:color-mix(in srgb, var(--success) 10%, transparent); }
.cmpm-sxs td.cmpm-best .cmpm-v{ color:var(--success); }
.cmpm-sxs td.cmpm-second{ background:color-mix(in srgb, var(--warning) 10%, transparent); }
.cmpm-sxs td.cmpm-second .cmpm-v{ color:var(--warning); }
.cmpm-empty{ border:0.5px solid var(--border); border-radius:var(--r-lg); background:var(--surface); padding:18px 20px; }
.cmpm-empty-h{ margin:0 0 6px; font-weight:600; }

/* ---- category view: one card per category the feed returns ---- */
.cmpm-cats{ display:grid; grid-template-columns:repeat(4, minmax(0,1fr)); gap:14px; }
.cmpm-cat{ border:0.5px solid var(--border); border-radius:var(--r-lg); background:var(--surface);
  padding:18px 20px; display:flex; flex-direction:column; }
.cmpm-cat[hidden]{ display:none !important; }
.cmpm-cat.is-active{ border-color:var(--accent); }
.cmpm-cat-h{ margin:0; font-size:17px; letter-spacing:-0.01em; }
.cmpm-cat-note{ margin:4px 0 12px; font-size:12.5px; }
.cmpm-ledger{ list-style:none; margin:0; padding:0; }
.cmpm-ledger li{ display:flex; align-items:center; gap:10px; padding:9px 0;
  border-top:0.5px solid var(--border); font-size:14px; }
.cmpm-ledger li .cmpm-name{ flex:1 1 auto; min-width:0; font-size:14px; white-space:nowrap; }
.cmpm-ledger li .cmpm-v{ font-size:14px; }
.cmpm-ledger .cmpm-rank{ width:24px; height:24px; font-size:12px; }
.cmpm-cat-all{ margin:auto 0 0; padding-top:12px; font-size:13px; }
.cmpm-cat-all a:hover{ color:var(--accent); }

/* ---- use case view: the case rail, then the models that board ranks first ---- */
.cmpm-uc{ display:grid; grid-template-columns:240px minmax(0,1fr); gap:20px; align-items:start; }
.cmpm-uc-list{ border:0.5px solid var(--border); border-radius:var(--r-lg); background:var(--surface); padding:6px; }
/* Same title treatment as "Select models to compare" on the side-by-side tab. */
.cmpm-uc-list-h{ margin:8px 10px 10px; font-size:14px; font-weight:600; color:var(--text); }
.cmpm-uc-list a{ display:flex; align-items:center; gap:8px; padding:9px 10px; border-radius:10px; }
.cmpm-uc-t{ flex:1 1 auto; min-width:0; }
.cmpm-uc-go{ flex:none; display:inline-flex; color:var(--text-3); }
.cmpm-uc-go svg{ width:15px; height:15px; }
.cmpm-uc-list a:hover .cmpm-uc-go{ color:var(--accent); }
.cmpm-uc-list a small{ display:block; font-size:11.5px; line-height:1.35; }
.cmpm-uc-l{ display:block; font-size:14px; font-weight:500; }
.cmpm-uc-list a:hover .cmpm-uc-l{ color:var(--accent); }
.cmpm-uc-list a[aria-current]{ background:var(--accent-tint); }
.cmpm-uc-list a[aria-current] .cmpm-uc-l{ color:var(--accent); font-weight:600; }
.cmpm-uc-h{ margin:0; font-size:22px; letter-spacing:-0.01em; }
.cmpm-uc-sub{ margin:4px 0 16px; font-size:13.5px; }
.cmpm-wins{ display:grid; grid-template-columns:repeat(3, minmax(0,1fr)); gap:14px; align-items:start; }
.cmpm-win{ border:0.5px solid var(--border); border-radius:var(--r-lg); background:var(--surface); padding:18px 20px; }
.cmpm-win[hidden]{ display:none !important; }
.cmpm-win-top{ display:flex; align-items:center; gap:10px; margin:0 0 12px; }
.cmpm-badge{ margin-left:auto; padding:3px 10px; border-radius:999px; background:var(--accent-tint);
  color:var(--accent); font-size:11.5px; font-weight:500; white-space:nowrap; }
.cmpm-win-name{ margin:0; font-size:17px; font-weight:600; }
.cmpm-win-name small{ display:block; font-size:12.5px; font-weight:400; }
.cmpm-win-fig{ margin:14px 0 0; }
.cmpm-big{ font-size:30px; font-weight:600; font-variant-numeric:tabular-nums; letter-spacing:-0.02em; }
.cmpm-win-fig .muted{ display:block; margin-top:2px; font-size:12px; }
.cmpm-tiles{ display:flex; flex-wrap:wrap; gap:8px; margin:14px 0 0; }
.cmpm-tile{ display:flex; flex-direction:column; gap:2px; padding:8px 12px; border:0.5px solid var(--border);
  border-radius:var(--r-sm); min-width:74px; }
.cmpm-tile small{ font-size:11px; color:var(--text-3); }
.cmpm-tile b{ font-size:14px; font-weight:600; font-variant-numeric:tabular-nums; }
.cmpm-uc-all{ margin:16px 0 0; font-size:13.5px; }
.cmpm-uc-all a:hover{ color:var(--accent); }

@media (max-width:1100px){
  .cmpm-cats{ grid-template-columns:repeat(2, minmax(0,1fr)); }
  .cmpm-wins{ grid-template-columns:repeat(2, minmax(0,1fr)); }
}
@media (max-width:900px){
  .cmpm-sxs-wrap, .cmpm-uc{ grid-template-columns:1fr; }
  .cmpm-pick-scroll{ max-height:280px; }
}
/* The trail is four segments deep on /compare (Home / Library / Compare / <view>) and the
   pill is nowrap, so on a phone it ran under the right edge. It scrolls now rather than
   clipping - which also catches every crumbTail detail route, where the same four-segment
   trail was already being cut. */
@media (max-width:700px){
  .dash-float-crumbs{ max-width:100%; overflow-x:auto; scrollbar-width:none; }
  .dash-float-crumbs::-webkit-scrollbar{ display:none; }
}
/* Below 900px the leaderboard stops being a table and becomes one card per model: the
   name block on its own line, then the metrics as a wrapping grid (three per line at 375).
   The table's inline min-width is what would otherwise push the columns past the card, so
   it is overridden here - an author !important is the only thing that outranks it. A cell
   with no figure is dropped entirely rather than left as a hole in the grid. */
@media (max-width:900px){
  .tbl-wrap.cmpm-wrap{ overflow-x:visible; }
  .cmpm-lb{ display:block; table-layout:auto; min-width:0 !important; border-spacing:0; }
  .cmpm-lb tbody{ display:block; }
  .cmpm-lb tr{ display:grid; grid-template-columns:repeat(auto-fit, minmax(84px, 1fr));
    gap:14px 12px; padding:16px 18px; margin:0 0 14px; background:var(--surface);
    border:0.5px solid var(--border); border-radius:var(--r-lg); }
  .cmpm-lb td{ display:block; padding:0; background:transparent; border:0; white-space:nowrap; }
  .cmpm-lb td:first-child, .cmpm-lb td:last-child{ padding:0; border:0; border-radius:0; }
  .cmpm-lb td.cmpm-model-c{ grid-column:1 / -1; }
  .cmpm-lb td.cmpm-cell-off{ display:none; }
  .cmpm-lb col{ width:auto; }
  .cmpm-model{ min-width:0; }
}
@media (max-width:700px){
  .cmpm-cats, .cmpm-wins{ grid-template-columns:1fr; }
  .cmpm-tbl td{ padding:12px 8px; }
  .cmpm-big{ font-size:26px; }
}

/* ---- Community feed (/community) ----
   Logic in assets/community.js. Type and thin rules do the work: 0.5px borders instead
   of shadows, one hover affordance per element, no gradients, single accent. */
.cmty-feed{ display: flex; flex-direction: column; gap: 12px; margin-top: 16px; }

.cmty-composer{ display: flex; flex-direction: column; gap: 10px; }
.cmty-composer--out{ flex-direction: row; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.cmty-composer-foot{ display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.cmty-count{ font-size: 12px; font-variant-numeric: tabular-nums; }

.cmty-input{ width: 100%; box-sizing: border-box; padding: 10px 12px; border: .5px solid var(--border-2); border-radius: var(--r-sm); background: var(--surface); color: var(--text); font: inherit; font-size: 15px; line-height: 1.5; resize: vertical; }
.cmty-input:focus{ outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.cmty-input::placeholder{ color: var(--text-3); }

.cmty-post{ display: flex; flex-direction: column; gap: 8px; }
/* The deep-linked post. Border only - it is already scrolled to, so it does not need
   to shout as well. */
.cmty-post--pin{ border-color: var(--accent); }
.cmty-head{ display: flex; align-items: baseline; gap: 8px; }
.cmty-who{ font-weight: 600; font-size: 14px; color: var(--text); }
.cmty-when{ font-size: 12px; }
/* pre-wrap, not a markdown parser: bodies are stored and rendered as plain text, so line
   breaks survive with zero HTML-interpretation surface. */
.cmty-body{ font-size: 15px; line-height: 1.6; color: var(--text-2); white-space: pre-wrap; overflow-wrap: anywhere; }

.cmty-actions{ display: flex; gap: 4px; flex-wrap: wrap; margin-top: 2px; }
.cmty-n{ font-variant-numeric: tabular-nums; color: var(--text-3); }
.cmty-like.on{ color: var(--accent); }
.cmty-like.on .cmty-n{ color: var(--accent); }

.cmty-replies{ margin: 6px 0 0 4px; padding-left: 14px; border-left: .5px solid var(--border-2); display: flex; flex-direction: column; gap: 12px; }
.cmty-reply{ display: flex; flex-direction: column; gap: 6px; }
.cmty-reply .cmty-body{ font-size: 14px; }
.cmty-reply-out{ font-size: 13px; margin: 0; }
.cmty-replybox{ display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }

.cmty-state{ display: flex; flex-direction: column; align-items: flex-start; gap: 10px; padding: 24px; border: .5px solid var(--border-2); border-radius: var(--r-md); }
.cmty-state p{ margin: 0; }
.cmty-more{ display: flex; justify-content: center; padding: 4px 0 8px; }

/* Visually hidden but reachable: textareas need a real label, not a placeholder. */
.cmty-sr{ position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* "Posting as <Name> · Change" (WU-32). Ships hidden; community.js fills it. */
.cmty-as{ margin: 0; font-size: 13px; }
.cmty-as[hidden]{ display: none; }
.cmty-as-name{ font-weight: 600; color: var(--text); }
.cmty-as-change{ border: 0; background: none; padding: 0; font: inherit; color: var(--accent); cursor: pointer; }
.cmty-as-change:hover{ text-decoration: underline; }

/* The display-name dialog (WU-32): the .aad-seedpop overlay and card - scrim, .5px border,
   18px radius, no shadow, a plain fade that honours reduced motion. */
.cmty-dlg{ position: fixed; inset: 0; z-index: 2000; display: flex; align-items: center; justify-content: center; padding: 20px; }
.cmty-dlg-backdrop{ position: absolute; inset: 0; background: rgba(15,17,21,.42); }
.cmty-dlg-card{
  position: relative; z-index: 1; width: 100%; max-width: 420px; max-height: 88vh; overflow: auto; box-sizing: border-box;
  margin: 0; background: var(--surface, #fff); border: .5px solid var(--border-2); border-radius: 18px;
  padding: 26px 26px 22px; color: var(--text); animation: cmty-dlg-in 160ms ease-out;
}
@keyframes cmty-dlg-in{ from{ opacity: 0; } to{ opacity: 1; } }
@media (prefers-reduced-motion: reduce){ .cmty-dlg-card{ animation: none; } }
.cmty-dlg-x{
  position: absolute; top: 14px; right: 14px; width: 30px; height: 30px; border: 0; border-radius: 9px; cursor: pointer;
  background: transparent; color: var(--text-3); font-size: 20px; line-height: 1;
}
.cmty-dlg-x:hover{ color: var(--text); }
.cmty-dlg-title{ margin: 0 36px 16px 0; font-size: 19px; font-weight: 650; letter-spacing: -.01em; line-height: 1.3; }
.cmty-dlg-input{
  width: 100%; box-sizing: border-box; padding: 11px 14px; font: inherit; font-size: 15px;
  border: .5px solid var(--border-2); border-radius: var(--r-sm); background: var(--surface); color: var(--text);
}
.cmty-dlg-input:focus{ outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.cmty-dlg-hint{ margin: 8px 0 0; font-size: 13px; color: var(--text-3); }
.cmty-dlg-err{ margin: 6px 0 0; font-size: 13px; color: var(--danger, #b3261e); }
.cmty-dlg-err:empty{ display: none; }
.cmty-dlg-foot{ display: flex; justify-content: flex-end; margin-top: 18px; }

@media (max-width: 768px){
  .cmty-composer--out{ flex-direction: column; align-items: flex-start; }
  /* 16px stops iOS auto-zooming the page when the textarea takes focus. */
  .cmty-input, .cmty-dlg-input{ font-size: 16px; }
}

/* Install the app (WU-J, assets/install.js): the dialog is the .cmty-dlg card under its own
   prefix; the bar is a white pill that slides down under the floating nav. No shadow, .5px
   border, the accent only on the primary button, one hover affordance (the x's colour). */
.inst-dlg{ position: fixed; inset: 0; z-index: 2000; display: flex; align-items: center; justify-content: center; padding: 20px; }
.inst-dlg-backdrop{ position: absolute; inset: 0; background: rgba(15,17,21,.42); }
.inst-dlg-card{
  position: relative; z-index: 1; width: 100%; max-width: 420px; max-height: 88vh; overflow: auto; box-sizing: border-box;
  margin: 0; background: var(--surface, #fff); border: .5px solid var(--border-2); border-radius: 18px;
  padding: 26px 26px 22px; color: var(--text); animation: inst-dlg-in 160ms ease-out;
}
.inst-dlg-card:focus{ outline: none; }
@keyframes inst-dlg-in{ from{ opacity: 0; } to{ opacity: 1; } }
@media (prefers-reduced-motion: reduce){ .inst-dlg-card{ animation: none; } }
.inst-dlg-x{
  position: absolute; top: 14px; right: 14px; width: 30px; height: 30px; border: 0; border-radius: 9px; cursor: pointer;
  background: transparent; color: var(--text-3); font-size: 20px; line-height: 1;
}
.inst-dlg-x:hover{ color: var(--text); }
.inst-dlg-title{ margin: 0 36px 12px 0; font-size: 19px; font-weight: 650; letter-spacing: -.01em; line-height: 1.3; }
.inst-dlg-text{ margin: 0; font-size: 15px; line-height: 1.5; color: var(--text-2); }
.inst-dlg-how{ margin-top: 12px; color: var(--text); }
.inst-dlg-steps{ margin: 14px 0 0; padding-left: 22px; font-size: 15px; line-height: 1.7; color: var(--text); }
.inst-dlg-foot{ display: flex; justify-content: flex-end; margin-top: 18px; }

.inst-bar{
  position: fixed; top: 74px; left: 50%; z-index: 100; /* under the nav (101), drawers and every dialog */
  transform: translate(-50%, -160%); display: flex; align-items: center; gap: 10px;
  max-width: calc(100vw - 32px); box-sizing: border-box; padding: 6px 6px 6px 18px; border-radius: 999px;
  background: var(--surface, #fff); color: var(--text); border: .5px solid var(--border-2);
  font-size: 14px; line-height: 1.35; transition: transform .7s cubic-bezier(.22,.61,.36,1);
}
.inst-bar.is-in{ transform: translate(-50%, 0); }
body.dashboard-mode .inst-bar{ top: 12px; }
body.route-playground .inst-bar{ display: none; }
.inst-bar-text{ margin: 0; font-weight: 600; white-space: nowrap; }
.inst-bar-x{
  display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; flex: none;
  border: 0; border-radius: 50%; background: transparent; color: var(--text-3); cursor: pointer;
}
.inst-bar-x:hover{ color: var(--text); }
.inst-bar-x:focus-visible, .inst-dlg-x:focus-visible{ outline: 2px solid var(--accent); outline-offset: 1px; }
@media (prefers-reduced-motion: reduce){ .inst-bar{ transition: none; } }

/* ---- Dashboard hero panel (assets/dash-hero.js) ----
   Fixed chrome at the top of every side-panel page: an image slider carrying the page
   headline, with a floating control row straddling its bottom edge. It is a SIBLING BEFORE
   .dash-scroll inside the same fixed-height flex column, which is the whole reason only the
   cards scroll - see the .gd-wrap / .dash-subhead / .dash-scroll rules further up. */
.dash-hero-panel{ flex: 0 0 auto; display: flex; flex-direction: column; }
/* WU-4: the banner is a rounded --ink panel sitting on the --bg sheet, the same object as
   the rail beside it. overflow:hidden was already here for the slider and now also clips
   the image to the radius, so no second mask is needed. */
/* R2-4: the stage was a flat 190px at every width. The owner asked for "little bigger in
   height but not too big", so it ramps to 240px instead of gaining a breakpoint: 20vw between
   about 950px and 1200px, pinned at 190px below that and 240px from 1200px up. The ≤900px
   override further down still wins on phones (150px), which is where the lede is hidden and
   the headline needs the space more than the photograph does. `cover` scales these banners by
   WIDTH at every one of these sizes, so a taller stage only reveals MORE of the source — it
   cannot crop the right-third subject that object-position pins.
   WU-G (2026-10-02): the owner asked for the banner about 30% shorter on every hub, so the
   ramp is now 135px-168px at 14vw (was 190-240 at 20vw). The headline and sub-line still fit
   with margin at 1000px and up; the ≤900px 120px rule and the --story panel are unchanged. */
.dash-hero-stage{ position: relative; overflow: hidden; height: clamp(135px, 14vw, 168px); background: var(--ink); border-radius: var(--r-xl); }
.dash-hero-track{ display: flex; height: 100%; width: 100%; will-change: transform; }
/* Right-weighted: the WU-A banners were composed with their left two-thirds deliberately
   empty so the headline has somewhere to sit. object-position keeps the subject on the
   right as the panel narrows instead of centre-cropping it back under the type. */
.dash-hero-img{ flex: 0 0 100%; width: 100%; height: 100%; object-fit: cover; object-position: right center; }
/* WU-6: hero-guide.jpg is 2100x900 and the stage was a fixed 190px, so `cover` scales the
   image by WIDTH and we only ever see a horizontal slice of it — 31.6%-68.4% of the source
   at 1440, narrowing to 24.3% at 1920 because the stage keeps its height as the column
   widens. The woman's head ran 7.8%-34.5%, entirely above the slice, which is why the banner
   showed her hands and laptop and nothing else.
   Moving the slice up fixes 1100/1280/1440 but cannot fix 1920: a 26.7%-tall head does not
   fit a 24.3% window at any object-position. So the source was reframed to 75% (see
   image/masters/hero-guide-original.jpg for the untouched original) and the head now runs
   5.9%-25.9%, which fits every slice from 1100 to 1920 with margin at 5%.
   Scoped by src so the other fifteen hub banners, framed differently, keep `right center`.
   R2-4 raised the stage to 240px, which only WIDENS this window — 46.5% of the source at 1440
   (was 36.8%) and 33.3% at 1920 — so the 5.9%-25.9% head keeps its margin at every width. */
.dash-hero-img[src$="hero-guide.jpg"]{ object-position: 100% 5%; }
/* Flat scrim, not a gradient: the house rule bans background gradients, and a single even
   wash is calmer than a ramp while still guaranteeing white-on-photo contrast. */
/* WU-4: a left-weighted ink scrim, not the old flat .46 black veil over the whole frame.
   The mockups keep the right of the banner as a clean photograph and only darken the side
   the type sits on. Same technique the video cards already use (.ms-vcard-stage::before) —
   a legibility scrim over a photo, not one of the decorative gradient backgrounds BRAND.md
   bans. currentColor so the stops track --ink from one declaration. */
.dash-hero-scrim{
  position: absolute; inset: 0; pointer-events: none;
  color: var(--ink);
  background: linear-gradient(90deg, currentColor 0%, currentColor 34%, transparent 80%);
}

/* The headline is the page's own .page-head text block, moved here out of the toolbar, so
   these rules restyle markup authored elsewhere rather than defining new markup. */
/* Inset past the arrows (14px + 34px wide) so a long description cannot run under them. */
/* Type sits in the left two-thirds, clear of the photograph. `right` is a percentage, not
   the old symmetric clamp: the reserved strip has to scale with the panel, or a wide screen
   pushes the headline under the image. */
/* WU-G: centred in the part of the stage the crumb pill does not cover (.dash-float overlaps
   the bottom 22px), so the shorter banner never tucks the last line under the pill. */
.dash-hero-text{ position: absolute; left: clamp(24px, 3vw, 44px); right: 34%; top: calc(50% - 11px); transform: translateY(-50%); max-width: 44ch; z-index: 2; }
.dash-hero-text .eyebrow{ display: block; margin-bottom: 8px; color: rgba(255,255,255,.78); }
/* /compare/<id> pills land on the ink stage; --accent-ink is for light grounds and reads
   2.62:1 here, so the stage keeps the brighter --accent (3.38:1, the live look). */
.dash-hero-text .badge-accent{ color: var(--accent); }
.dash-hero-text h1{ margin: 0; color: #fff; font-family: var(--f-display); font-size: clamp(22px, 2.5vw, 34px); font-weight: 600; line-height: 1.12; letter-spacing: -.025em; }
/* Three lines, not two. The clamp is a safety net against a runaway deck, but at 2 it was
   cutting live hub copy off mid-word - /audit ended on "We don't ask fo..." at 1440. The
   block is absolutely centred in a 190-240px stage, and the longest deck at three lines
   still clears about 45px above and below, so the third line costs nothing but itself.
   Decks that already fit in two lines (/agents, /compare) are unchanged: the box is
   content-height, not clamp-height. Below 900px the deck is display:none, so this rule
   is desktop-only in practice. */
.dash-hero-text .sub,
.dash-hero-text p{ margin: 8px 0 0; max-width: 62ch; color: rgba(255,255,255,.84); font-size: 14px; line-height: 1.45; display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }

.dash-hero-arrow{ position: absolute; top: 50%; transform: translateY(-50%); z-index: 3; width: 34px; height: 34px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; border: .5px solid var(--ink-border-2); background: rgba(0,0,0,.34); color: #fff; cursor: pointer; transition: background var(--ms-dur-quick) var(--ms-ease-out), opacity var(--ms-dur-quick) var(--ms-ease-out); }
.dash-hero-arrow:hover{ background: rgba(0,0,0,.64); }
.dash-hero-arrow:focus-visible{ outline: 2px solid #fff; outline-offset: 2px; }
.dash-hero-arrow--prev{ left: 14px; }
.dash-hero-arrow--next{ right: 14px; }

/* 12px, not 11px: a11y-behaviour.mjs enforces a 12px floor on visible text and this
   button was the only thing on /tools below it. The pill is absolutely positioned with
   its own padding, so the extra pixel changes nothing else. */
.dash-hero-pause{ position: absolute; right: 14px; top: 12px; z-index: 3; padding: 3px 9px; border-radius: 999px; border: .5px solid var(--ink-border-2); background: rgba(0,0,0,.34); color: #fff; font-size: 12px; font-weight: 600; cursor: pointer; transition: background var(--ms-dur-quick) var(--ms-ease-out), opacity var(--ms-dur-quick) var(--ms-ease-out); }
.dash-hero-pause:hover{ background: rgba(0,0,0,.64); }
.dash-hero-pause:focus-visible{ outline: 2px solid #fff; outline-offset: 2px; }

/* WU-4: neither mockup shows the slider controls at rest, so they fade in on hover/focus.
   opacity, never display or visibility — both would drop the pause button out of the
   a11y tree, and WCAG 2.2.2 requires a reachable way to stop the rotation. :focus-within
   on the stage is what brings them back for a keyboard user the moment one is tabbed to. */
.dash-hero-arrow,
.dash-hero-pause{ opacity: 0; }
.dash-hero-stage:hover .dash-hero-arrow,
.dash-hero-stage:hover .dash-hero-pause,
.dash-hero-stage:focus-within .dash-hero-arrow,
.dash-hero-stage:focus-within .dash-hero-pause{ opacity: 1; }
@media (prefers-reduced-motion: reduce){
  .dash-hero-arrow,
  .dash-hero-pause{ transition: none; }
}
/* Coarse pointers never hover, so the controls would be unreachable behind it. */
@media (hover: none){
  .dash-hero-arrow,
  .dash-hero-pause{ opacity: 1; }
}
/* WU-6: the rule above is why the prev arrow lands ON the headline on a phone. It is
   permanently opaque there, and the arrows are vertically centred with `left:14px`, which at
   375px is inside the h1's box (arrow 24-52 x 71-99 vs h1 34-244 x 57-106) — measured, not
   assumed. Hiding them is not an option: WCAG 2.2.2 needs a reachable way to stop the
   rotation and a touch user has no hover to reveal it. So park both in the banner's
   bottom-right corner instead, clear of the headline and of the breadcrumb pill below.
   2026-10-05: 900px, not 700 - an iPad at 768 is touch too, and its centred prev arrow sat
   on the h1 while the pause overlapped the centred next one. prev is 14 + 44 + 8 from the
   edge: the 52 dated from 34px arrows, before the <=900 tap floor (:7351) made them 44. */
@media (max-width: 900px){
  .dash-hero-arrow{ top: auto; bottom: 10px; transform: none; }
  .dash-hero-arrow--prev{ left: auto; right: 66px; }
  .dash-hero-arrow--next{ right: 14px; }
}

/* Pulled up over the stage by a negative margin rather than absolutely positioned, so the
   panel's own height already accounts for the overhang: nothing below needs padding
   compensation and there is no z-index race with the toolbar. */
/* 24px to match .dash-subhead and .dash-scroll, so the breadcrumb, the toolbar and the
   cards below it share one left edge inside the banner's bleed. */
.dash-float{ position: relative; z-index: 4; margin: -22px 24px 0; display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }

/* WU-4: white --r-lg pill overlapping the banner's bottom-left, active segment an --ink
   pill (§1 active-chip rule: hub chrome goes ink, home/result chips go accent). */
.dash-float-crumbs{ display: inline-flex; align-items: center; gap: 2px; padding: 5px; border-radius: var(--r-lg); background: var(--surface); border: .5px solid var(--border); }
.dash-float-crumbs .crumb{ display: inline-flex; align-items: center; gap: 5px; padding: 7px 16px; border-radius: var(--r-sm); font-size: 13px; line-height: 1; color: var(--text-2); text-decoration: none; white-space: nowrap; transition: color var(--ms-dur-quick) var(--ms-ease-out); }
.dash-float-crumbs .crumb:hover{ color: var(--text); }
.dash-float-crumbs .crumb-current{ background: var(--ink); color: var(--ink-text); font-weight: 600; }
/* The back-arrow on "Home" is chrome the mockups do not draw. Hidden only inside the
   floating pill; the nav copy of the trail keeps it. */
.dash-float-crumbs .crumb-home svg{ display: none; }
.dash-float-crumbs .crumb svg{ width: 14px; height: 14px; flex: 0 0 auto; }
/* The trail's spacer spans are for text copy, not for a pill row. */
/* Hairline separators, as drawn in both mockups. The span holds a single space so the trail
   still reads correctly as text; the rule paints over it at zero width. */
.dash-float-crumbs .crumb-sep{ display: block; width: 1px; height: 16px; margin: 0 3px; overflow: hidden; text-indent: -99px; background: var(--border-2); }

/* R2-4: ONE shape for both floating boxes. This used to be a 999px capsule with 4px padding
   wrapping controls that carried their own chrome — trending's white --r-lg .intg-tabwrap
   inside it read as a box in a box, and every hubHead CTA as a blue pill inside a white pill.
   It is now the same component as .dash-float-crumbs above: --r-lg, 5px padding, hairline,
   white — and everything swept into it renders as a crumb-style item. */
.dash-float-actions{ display: inline-flex; align-items: center; gap: 2px; flex-wrap: wrap; padding: 5px; border-radius: var(--r-lg); background: var(--surface); border: .5px solid var(--border); }
.dash-float-actions > *{ margin: 0; }

/* The sweep (dash-hero.js:326-344) moves whatever a view left in .page-head in here, wrappers
   and all. Those wrappers keep their layout job and give up their skin: the box around them is
   the box now. */
.dash-float-actions .intg-tabwrap,
.dash-float-actions .intg-tabs,
.dash-float-actions .agents-view-pills,
.dash-float-actions .row{ gap: 2px; padding: 0; border: 0; background: transparent; box-shadow: none; }

/* Item = .dash-float-crumbs .crumb, reached through whatever class the donor view used. */
.dash-float-actions .btn,
.dash-float-actions .intg-tab,
.dash-float-actions .agents-view-pill{
  display: inline-flex; align-items: center; gap: 5px;
  height: auto; min-height: 0; padding: 7px 16px;
  border: 0; border-radius: var(--r-sm);
  background: transparent; box-shadow: none;
  font-size: 13px; font-weight: 400; line-height: 1;
  color: var(--text-2); white-space: nowrap;
}
/* Sized to the 13px line box, not the 16px/14px they ship with: an icon taller than the text
   stretches the flex line, which is what made the agents box measure 40px against the crumbs'
   39px and the compare "Compare →" button sit 1px above the pickers beside it. */
.dash-float-actions .agents-view-pill svg,
.dash-float-actions .btn svg{ width: 13px; height: 13px; }

/* Active segment / primary CTA = the --ink item, exactly as .crumb-current. */
.dash-float-actions .btn-primary,
.dash-float-actions .intg-tab.active,
.dash-float-actions .agents-view-pill.active{ background: var(--ink); color: var(--ink-text); font-weight: 600; }

/* ONE hover affordance, and it is the crumbs' one: colour. No fill, no border, no lift. */
.dash-float-actions .btn:hover,
.dash-float-actions .intg-tab:hover,
.dash-float-actions .intg-chev:hover:not(:disabled),
.dash-float-actions .agents-view-pill:hover{ background: transparent; border-color: transparent; color: var(--text); }
.dash-float-actions .btn-primary:hover,
.dash-float-actions .intg-tab.active:hover,
.dash-float-actions .agents-view-pill.active:hover{ background: var(--ink); color: var(--ink-text); }

/* .btn:focus-visible paints its ring with box-shadow, which the rest state above zeroes out —
   so the ring is re-declared here as an outline rather than left to disappear. */
.dash-float-actions .btn:focus-visible,
.dash-float-actions .intg-tab:focus-visible,
.dash-float-actions .agents-view-pill:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; box-shadow: none; }

/* /compare/:id is the one view whose swept controls are form fields rather than buttons: the
   two `<select class="input">` tool pickers (app.js:10109-10111). `.input` carries width:100%
   (app.css:551-557), so inside the float each picker claimed the whole box and the strip stacked
   three rows deep — 164px against the crumbs' 39px, with two white bordered fields inside the
   white bordered box. :has(select) scopes the single-row treatment to that box so the wrapping
   the other hubs rely on at 375 is untouched. */
/* min-width:0 as well as nowrap: without it the box keeps its automatic content minimum, so at
   375 it stayed 540px wide and the Compare button sat outside the viewport, hidden by the page's
   overflow-x: clip rather than reachable. */
.dash-float-actions:has(select){ flex-wrap: nowrap; min-width: 0; }
/* The box keeps the fill and the hairline; the inner row scrolls — the same division of labour
   as .intg-tabwrap > .intg-tabs, so a narrow screen reaches the Compare button by swiping
   instead of losing it off the edge. */
.dash-float-actions:has(select) .row{ flex-wrap: nowrap; min-width: 0; overflow-x: auto; scrollbar-width: none; }
.dash-float-actions:has(select) .row::-webkit-scrollbar{ display: none; height: 0; }
.dash-float-actions select{
  appearance: none; -webkit-appearance: none;
  width: auto; flex: 0 0 auto;
  height: auto; min-height: 0; padding: 7px 28px 7px 16px;
  border: 0; border-radius: var(--r-sm);
  background-color: transparent; box-shadow: none;
  font-size: 13px; font-weight: 400; line-height: 1;
  color: var(--text-2); cursor: pointer;
  /* appearance:none takes the native arrow with it, and a picker that looks like plain text is
     a picker nobody opens — so the control draws its own chevron. url() cannot read a custom
     property, so the stroke is --text-2 (#3c4a63) written out; `data:` is already in img-src
     (vercel.json:45, dev_server.py:105), so no external request and no CSP change. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%233c4a63' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 10px center; background-size: 11px 11px;
}
.dash-float-actions select:hover{ color: var(--text); }
/* .input:focus-visible already rings at 1px offset; 2px here so the ring clears the item the
   way it does on the buttons beside it. Only :focus-visible — .input:focus{outline:none} then
   still governs the cases where no ring is wanted, and Chromium matches :focus-visible on a
   select whether it was opened by pointer or by keyboard.
   NOTE for anyone measuring this: getComputedStyle reports 3px/solid/currentColor here because
   the shorthand carries a var(); the painted ring is 2px --accent. Read the pixels. */
.dash-float-actions select:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; box-shadow: none; }
/* The "vs" between the two pickers is body-sized copy; at 15px it was the only word in the
   strip that did not match the crumbs. */
.dash-float-actions .row .muted{ font-size: 13px; }
/* 13px above would have overridden the project's iOS anti-zoom guard (app.css:1243, the same
   768px block) — Safari zooms the page when a form control under 16px takes focus, and a select
   is a form control however it is skinned. So the pickers alone go back to 16px on phones; the
   crumb items around them stay 13px. The row already scrolls, so the wider control costs a few
   pixels of swipe rather than a second line. */
@media (max-width: 768px){
  .dash-float-actions select{ font-size: 16px; }
}

/* R2-1: /guide/:id gets the floating row with the breadcrumb alone and no banner behind it
   (dash-hero.js buildCrumbsOnly). The stage the row normally overhangs is not there, so the
   -22px pull becomes the page's own top margin on the same 24px gutter every hub shares. */
.dash-hero-panel--crumbs .dash-float{ margin: 14px 24px 0; }
/* Same measure the detail column is capped to, so the crumb pill starts on the video's left
   edge instead of 80px outside it. Both boxes centre inside the same content width. */
@media (min-width: 901px){
  /* width:100% is load-bearing — .dash-float is a flex ITEM of .dash-hero-panel, and an auto
     inline margin on a column-flex item shrink-wraps it to the pill instead of centring a
     full-width box. The gutter becomes padding so it survives when --gd-col stops binding
     on a tall screen; +48px is that gutter, so the pill lands on the wrap's left edge at
     every width. */
  .dash-hero-panel--crumbs .dash-float{
    width: 100%; max-width: calc(var(--gd-col) + 48px);
    padding-inline: 24px; margin-inline: auto;
  }
}
@media (max-width: 900px){
  .dash-hero-panel--crumbs .dash-float{ margin: 10px 18px 0; }
}

/* The trail moved into the panel, so the copy in the fixed top bar would be a duplicate. */
body.dashboard-mode.hero-crumbs .dash-crumbs{ display: none; }

/* Collapse handle on the sidebar seam, replacing the top-left burger. It rides
   grid-template-columns, which .shell already transitions, so it travels with the sidebar. */
.shell{ position: relative; }
/* WU-4: + --shell-inset. The fab is absolute against .shell's padding box, and the shell is
   padded now, so a bare var(--side-w) parks the circle inside the panel instead of on its
   seam. The 0px fallback keeps it correct if .shell ever renders outside dashboard mode,
   where --shell-inset is not declared. */
.side-collapse-fab{
  position: absolute; z-index: 6; top: 50%; left: calc(var(--side-w) + var(--shell-inset, 0px));
  transform: translate(-50%, -50%);
  width: 28px; height: 28px; padding: 0; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  border: .5px solid var(--border); background: var(--surface); color: var(--text-2);
  cursor: pointer;
  transition: left .2s var(--ease), color var(--ms-dur-quick) var(--ms-ease-out), opacity .14s var(--ease);
}
.side-collapse-fab:hover{ color: var(--accent); }
.side-collapse-fab:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
body.side-collapsed .side-collapse-fab{ left: calc(60px + var(--shell-inset, 0px)); }
body.side-collapsed .side-collapse-fab svg{ transform: rotate(180deg); }

/* Its twin on the scrollview: the way INTO the side panel.
   Hidden inside the panel, where .side-collapse-fab takes over.

   🔴 WU-4: it used to sit at left:-15px with an infinite nudge keyframe rocking it to -10px
   — a 36px control with a third of it permanently off-screen, and :focus-visible only slid
   it to -4px, so the focus ring was clipped by the viewport edge even when focused. It is
   fully on screen at 12px now.

   KEPT, not hidden, although the landing mockup draws no such control. Measured on home at
   1440: clicking the nav avatar opens the account popover, it does NOT navigate — its
   href="/dashboard" and data-action="enter-panel" are overridden by auth.js's popover
   binding, and its accessible name is "Account". So this button is the only one-click way
   into the panel from the landing page; the alternative is the footer's Account → Dashboard
   link at the bottom of a long scroll. Hide it only once the nav gains a real dashboard link.

   WU-4 removed the infinite nudge (§1 sanctions autoplay ambience for the landing sphere
   only). On 2026-10-05 the owner asked for a gentle sway again: side-enter-sway below,
   paused on hover/focus and off under reduced motion. */
.side-enter-fab{
  position: fixed; z-index: 60; top: 50%; left: 12px;
  transform: translateY(-50%);
  width: 42px; height: 42px; padding: 0; border-radius: 50%;   /* owner 2026-10-12: a bit bigger (37 -> 42) */
  display: none; align-items: center; justify-content: center;
  border: .5px solid var(--border); background: var(--surface); color: var(--text-2);
  /* Owner 2026-10-05 (mockup): about 2% bigger, a soft drop shadow, and a gentle left-right
     sway so the way into the panel is noticed. This reverses WU-4's "no nudge" on purpose. */
  box-shadow: 0 4px 12px rgba(0,0,0,.14);
  cursor: pointer;
  transition: color var(--ms-dur-quick, .15s) ease;
  animation: side-enter-sway 2.4s ease-in-out infinite;
}
/* translateY(-50%) is repeated in every frame: the animation owns transform while it runs. */
@keyframes side-enter-sway{
  0%, 100%{ transform: translateY(-50%) translateX(0); }
  50%     { transform: translateY(-50%) translateX(4px); }
}
/* Still under the cursor or keyboard focus, so it is easy to hit and its label reads. */
.side-enter-fab:hover,
.side-enter-fab:focus-visible{ animation-play-state: paused; }
body:not(.dashboard-mode) .side-enter-fab{ display: inline-flex; }
/* ONE hover affordance: the label beside it. The circle itself no longer moves — it has
   nowhere left to slide in from, and a control that shifts under the cursor is harder to
   hit, not friendlier. */
.side-enter-fab:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
.side-enter-fab > svg{ width: 18px; height: 18px; }   /* the chevron grows with the circle (16 -> 18) */

/* The label names the affordance. Hidden at rest; it fades in beside the circle on the same
   hover that slides the button in, so the two read as one gesture, not two. */
.side-enter-lbl{
  position: absolute; left: 47px; top: 50%;
  transform: translateY(-50%) translateX(-4px);
  white-space: nowrap;
  font-size: 12px; font-weight: 600; color: var(--text);
  background: var(--surface); border: .5px solid var(--border);
  border-radius: 7px; padding: 5px 9px;
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .15s ease, transform .15s ease, visibility 0s .15s;
}
.side-enter-fab:hover .side-enter-lbl,
.side-enter-fab:focus-visible .side-enter-lbl{
  opacity: 1; visibility: visible;
  transform: translateY(-50%) translateX(0);
  transition: opacity .18s ease .04s, transform .18s ease .04s, visibility 0s;
}

/* WU-4 removed @keyframes side-enter-nudge; the owner asked for movement again on 2026-10-05,
   now the gentle side-enter-sway above. */

@media (prefers-reduced-motion: reduce){
  .side-collapse-fab{ transition: none; }
  /* No sway and no hover colour fade for a reader who asked for no motion. */
  .side-enter-fab{ transition: none; animation: none; }
}

@media (max-width: 900px){
  .side-enter-fab{ display: none !important; }
}

/* U-mobile-2: the fixed chrome, scaled. Measured at 390 before this block, /agents spent
   384px of a 664px viewport before the first card: 10px inset + a 212px panel (150 stage
   + 10 margin + 52 float) + a 162px subhead (36 padding + 58 tab strip + 16 row gap + 52
   search). Every number below comes out of that stack. Nothing shrinks a HIT AREA — the
   44px floor from B4 is untouched; what goes is picture height, padding and gaps. */
@media (max-width: 900px){
  .dash-hero-stage{ height: 120px; }
  .dash-hero-text .sub,
  .dash-hero-text p{ display: none; }
  /* The headline has 30px less stage to sit in, so it comes down with it — same (0,1,1)
     as the base clamp at :5974, and later in the file, so it wins. */
  .dash-hero-text h1{ font-size: 20px; }
  .dash-hero-arrow{ width: 28px; height: 28px; }
  /* The sidebar is a drawer at this width and .dash-topbar's Menu button owns it. */
  .side-collapse-fab{ display: none; }
  /* Unpinned so the controls cannot sit over the picture on a short screen. */
  .dash-float{ margin: 8px 18px 0; }
  /* WU-G: the pill no longer overhangs the stage here, so the headline centres on all of it. */
  .dash-hero-text{ top: 50%; }
  .dash-float-crumbs,
  .dash-float-actions{ border: 0; background: transparent; padding: 0; }
  /* The subhead's 18px/18px band and 14px gap come from `body.dashboard-mode .gd-wrap
     .hub-subhead` (:3360), specificity (0,3,1) — a looser selector here would lose the
     cascade and change nothing, which is why both variants are named at that weight. The
     18px sides are the value the ≤900 rule at :3232 already sets, restated only because
     `padding` is a shorthand. */
  body.dashboard-mode .gd-wrap .dash-subhead,
  body.dashboard-mode .gd-wrap .hub-subhead{ padding: 8px 18px 0; gap: 8px; }
  /* Below 900px this row wraps (:3408), so the space between the tab strip and the search
     field under it is this ROW gap, not a margin — neither carries one (:3396 zeroes it).
     Halve the vertical half and leave the horizontal alone. */
  .hub-controls.intg-filters{ gap: 8px 16px; }
}

/* ============================================================
   R2-2 — ONE card language across every hub (owner brief, 2026-09-07)

   "Top area image, bottom text and buttons, no border; use this style across
   all the cards; when hovered, drop shadow more as if it is elevated."

   Replaces four per-hub card designs (.agent-card, .mcp-card, .tool-card and
   .card on /compare) with one shell. The legacy rules stay where they are in
   app.css and are overridden here, because this file loads after it.
   BRAND.md §4.4 and §5.5 are amended in the same commit: elevation is the ONLY
   hover affordance on .aad-card, so the house translate rule and the "cards are
   per type" rule both stop applying to this family.

   Two surfaces are DELIBERATELY NOT in this family, both by owner instruction
   on 2026-09-07: the /guide (Learn) video cards ("let the video card remain like
   before, only the learn page"), which keep their dark poster overlay and their
   own translateY(-1px) hover; and the home scrollview strips ("the scrollview
   remains the same"), which keep their lift untouched.
   ============================================================ */
.aad-card{
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 10px;
  min-height: 0;
  background: var(--surface);
  border: 0;
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
  color: inherit;
  text-decoration: none;
  text-align: left;
  cursor: pointer;
  transition: box-shadow .22s var(--ease);
}
/* Elevation, and nothing stacked on it: no lift, no rim, no colour change. */
/* transform:none neutralises the legacy .agent-card lift, which app.css still
   declares on the one legacy class this family keeps. */
.aad-card:hover{ box-shadow: var(--sh-3); transform: none; }
.aad-card:active{ box-shadow: var(--sh-1); transform: none; transition-duration: .08s; }
.aad-card:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce){ .aad-card{ transition: none; } }

/* ---- media: the photo inset, 3:2, on top ---- */
.aad-card-media{
  position: relative;
  /* Its own stacking context, below the card-wide .mcp-hit link (z-index 1) and
     the CTA (2) — otherwise the photograph swallows the clicks meant for them. */
  z-index: 0;
  flex: 0 0 auto;
  aspect-ratio: 3 / 2;
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--accent-tint);
}
.aad-card-media > img{
  position: absolute; inset: 0; z-index: 1;
  display: block; width: 100%; height: 100%;
  object-fit: cover;
}
/* The glyph tile sits UNDER the photo. A file that 404s removes its own <img>
   (onerror), so the tile is what shows — a broken-image icon never can. */
.aad-card-glyph{
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--accent);
  font-family: var(--f-display); font-weight: 600; font-size: 26px; letter-spacing: -.01em;
}
.aad-card-glyph svg{ width: 34px; height: 34px; }
/* Only a card with no <img> left shows its tile. onerror removes a file that 404s,
   which flips this selector and brings the glyph back. */
.aad-card-media:has(img) .aad-card-glyph{ display: none; }

/* Tools keep their logo: contained on a quiet ground, not cropped to fill. */
.aad-card-media--logo{ background: var(--bg-2); }
.aad-card-media--logo > img{
  inset: auto; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 40%; height: 40%;
  object-fit: contain;
}
.aad-card-media--logo .aad-card-glyph{ color: var(--text-2); }

/* Comparisons have no photograph — the two names ARE the picture. */
.aad-card-media--vs{ background: var(--bg-2); }
.aad-card-vs{
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 7px; padding: 12px;
}
.aad-vs-chip{
  max-width: 100%; padding: 7px 15px; border-radius: var(--r-pill);
  background: var(--surface); color: var(--text);
  font-size: 14px; font-weight: 600; letter-spacing: -.01em;
  box-shadow: var(--sh-1);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.aad-vs-x{
  font-family: var(--f-mono); font-size: 11px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--text-3);
}

/* ---- body ---- */
.aad-card-body{
  display: flex; flex-direction: column; gap: 3px;
  flex: 1 1 auto; min-width: 0;
  padding: 2px 4px 0;
}
.aad-card-title{
  font-family: var(--f-display);
  font-size: 15px; font-weight: 600; line-height: 1.32; letter-spacing: -.011em;
  color: var(--text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.aad-card-cat{
  font-size: 13px; line-height: 1.42; color: var(--accent);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.aad-card-desc{
  margin: 5px 0 0;
  font-size: 13px; line-height: 1.5; color: var(--text-3);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* ---- foot: black rectangular CTA left, one real metric right ---- */
.aad-card-foot{
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin-top: auto; min-width: 0;
  padding: 4px 4px 2px;
}
.aad-card-cta{
  position: relative; z-index: 2;   /* above .mcp-hit and any card-wide overlay */
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  flex: 0 0 auto;
  height: 34px; padding: 0 14px;
  border: 0; border-radius: 8px;
  background: var(--ink); color: var(--ink-text);
  font-family: inherit; font-size: 13px; font-weight: 600; line-height: 1;
  white-space: nowrap; text-decoration: none; cursor: pointer;
  transition: background .18s var(--ease);
}
.aad-card-cta:hover{ background: var(--ink-2); }
.aad-card-cta:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
.aad-card-cta[disabled]{ opacity: .55; cursor: default; }
.aad-card-cta.is-copied,
.aad-card-cta.is-done{ background: var(--success); }
.aad-card-cta svg{ width: 15px; height: 15px; flex: 0 0 auto; }
.aad-card-metric{
  display: inline-flex; align-items: center; gap: 5px;
  min-width: 0;
  font-size: 12.5px; color: var(--text-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.aad-card-metric svg{ width: 14px; height: 14px; flex: 0 0 auto; }
/* Up to three lines for the metric, so a price like "Included with ChatGPT Plus / API from
   $0.04/image" (three lines in the 152px foot) keeps its number instead of ellipsising it
   away (sweep 2026-09-13). 46 of 48 fit in two. */
.aad-card-metric{ white-space: normal; text-overflow: clip; }
.aad-card-metric > span{
  min-width: 0; display: -webkit-box; -webkit-line-clamp: 3; line-clamp: 3;
  -webkit-box-orient: vertical; overflow: hidden; line-height: 1.3; text-align: left;
}

/* A narrow card gets a shorter, tighter button. Container query where the
   browser has one; the class is the hook for a grid that opts in by hand. */
@supports (container-type: inline-size){
  .aad-card{ container-type: inline-size; }
}
@container (max-width: 240px){
  .aad-card-cta{ height: 30px; padding: 0 10px; border-radius: 6px; font-size: 12px; }
}
.aad-card--compact .aad-card-cta{ height: 30px; padding: 0 10px; border-radius: 6px; font-size: 12px; }

/* ---- the grid these cards live in ---- */
.grid.aad-grid{
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 16px;
}
.grid.aad-grid > *{ min-width: 0; }
/* Phones: two up. The 250px minimum resolves to a single column under 516px, so at 390 the
   hubs showed one 334x390 card at a time — a 664px viewport holding one and a half cards,
   which is what "the scrollview cards are very long" is describing. Two columns put a
   second card on the screen; the padding and type come down so the narrower card still
   reads. Hit areas do not move: .aad-card-cta keeps its 44px floor from the B4 block.

   /compare opts out. Its cards carry <select> pickers, which do not survive a 170px column.
   /guide is not in this family at all — it has its own .guide-grid and stays one up, which
   is also what the mockup's Learn screen shows. .aad-card--ed.is-expanded already spans
   both columns (grid-column: 1/-1), so an expanded /news story is unaffected. */
@media (max-width: 600px){
  .grid.aad-grid{ grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .aad-card{ padding: 10px; }
  .aad-card-title{ font-size: 14px; }
  .aad-card-desc{ font-size: 12.5px; -webkit-line-clamp: 3; line-clamp: 3; }
  .aad-card-cta{ width: 100%; }
  #compare-grid.aad-grid{ grid-template-columns: minmax(0, 1fr); }
}
/* /mcp/<id> facts strip (2026-10-02): install, repository owner, downloads. Three plain
   .card boxes, one column on a phone; the number in the display face, captions muted. */
.mcp-facts{
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px;
  margin-top: 32px;
}
.mcp-fact{ min-width: 0; padding: 18px 20px; }
.mcp-fact .gd-brief-install{ margin: 10px 0 0; }
.mcp-fact-num{
  font-family: var(--f-display); font-weight: 600; font-size: 20px; line-height: 1.25;
  margin: 8px 0 0; overflow-wrap: anywhere;
}
.mcp-fact-num a{ color: inherit; text-decoration: none; }
.mcp-fact-num a:hover{ color: var(--accent); }
.mcp-fact-line{ font-size: 14px; line-height: 1.55; color: var(--text-2); margin: 10px 0 0; }
.mcp-fact-cap{ font-size: 12.5px; line-height: 1.5; margin: 6px 0 0; overflow-wrap: anywhere; }
.mcp-fact-cap a, .mcp-fact-line a{ color: inherit; text-decoration: underline; text-decoration-thickness: .5px; text-underline-offset: 2px; }
.mcp-fact-cap a:hover, .mcp-fact-line a:hover{ color: var(--accent); }
.mcp-facts-gap{ font-size: 13px; line-height: 1.55; margin: 12px 0 0; }
@media (max-width: 767px){
  .mcp-facts{ grid-template-columns: minmax(0, 1fr); gap: 10px; margin-top: 24px; }
}
/* /editors-choice MCP shortlist (2026-10-07): two up; one column in mobile.css. The card keeps
   .card's own border hover; the name link changes colour on its own element. */
.mcp-picks-sec{ margin-top: 40px; }
.mcp-picks{ display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.mcp-pick{ min-width: 0; padding: 18px 20px; }
.mcp-pick-name{ margin: 0; font-size: 18px; font-weight: 600; letter-spacing: -0.01em; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.mcp-pick-name a{ color: inherit; text-decoration: none; }
.mcp-pick-name a:hover{ color: var(--accent); }
.mcp-pick .gd-brief-install{ margin: 12px 0 0; }
.mcp-pick-skip{ border-top: .5px solid var(--border); padding-top: 10px; margin-top: 12px; }

/* /compare wraps each card in a filter hook, so the wrapper has to stretch too. */
#compare-grid > [data-compare-card]{ display: flex; }
#compare-grid > [data-compare-card] > .aad-card{ width: 100%; }


/* ============================================================
   R2-10 — Weekly digest + weekly brief (mockup: final_mockup/weekly digest.png)

   Three new pieces, all of them shells for live data:
     .dash-hero-panel--story   the hub hero when it carries the week's top story
     .wde-*                    the digest, rebuilt as one editorial page (U-wdx)
     .wdx-sub / .wdx-credit    the subscribe strip and the picture credit line
     .sx-*                     the story that expands in place, on /news

   Nothing here paints a placeholder. A row with no picture gets a tile with the
   source's initial, a story with no summary shows the source's own words under a
   different label, and a page with no stories at all keeps its dark banner.
   ============================================================ */

/* ---- the hero, when it is a story ---- */
/* Taller than the other hubs (240px after R2-4): this one is the page, not a header. */
.dash-hero-panel--story .dash-hero-stage{ height: clamp(240px, 26vw, 300px); }
/* A photograph from a feed is framed for its own page, not for a 3:1 band. Centre it
   rather than pinning it right the way a designed banner is. */
.dash-hero-panel--story .dash-hero-img{ object-position: center; }
/* The three slides all carry the same story picture and the rotation is stopped, so the
   slider controls have nothing to control. */
.dash-hero-panel--story .dash-hero-arrow,
.dash-hero-panel--story .dash-hero-pause{ display: none; }
/* Bottom-anchored, not centred: this hero carries four lines instead of two, and the
   floating crumb row is pulled 22px up over the stage, so a centred block sat under it.
   38px = that overlap plus a gap. A feed headline can be any length, so it is clamped to
   two lines rather than allowed to grow off the top of a fixed-height stage. */
.dash-hero-panel--story .dash-hero-text{ right: 30%; top: auto; bottom: 38px; transform: none; }
.dash-hero-panel--story .dash-hero-text h1{
  display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2;
  -webkit-box-orient: vertical; overflow: hidden;
}
/* WU-G clamps the hub lede to two lines; the story stage did not shrink, so it keeps three. */
.dash-hero-panel--story .dash-hero-text p{ -webkit-line-clamp: 3; line-clamp: 3; }
.sxh-foot{ display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: 14px; }
.sxh-cta{
  display: inline-flex; align-items: center; justify-content: center;
  height: 36px; padding: 0 16px;
  border: 0; border-radius: 8px;
  background: var(--accent); color: #fff;
  font-family: inherit; font-size: 13px; font-weight: 600; line-height: 1;
  cursor: pointer;
  transition: background var(--ms-dur-quick) var(--ms-ease-out);
}
.sxh-cta:hover{ background: var(--accent-2); }
.sxh-cta:focus-visible{ outline: 2px solid #fff; outline-offset: 2px; }
.sxh-src{
  font-family: var(--f-mono); font-size: 11.5px; letter-spacing: .04em;
  color: rgba(255,255,255,.76);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
@media (prefers-reduced-motion: reduce){ .sxh-cta{ transition: none; } }
@media (max-width: 900px){
  /* 150px at phone width (U-mobile-2, was 180). The base rule at this breakpoint is 120px;
     the story hero keeps 30px more because its scrim carries an eyebrow above the title. */
  .dash-hero-panel--story .dash-hero-stage{ height: 150px; }
  /* The story headline stays 22px where the base phone rule takes every other hero to 20px:
     it is the feed's own title, two lines clamped, and it is the page. (0,2,2) beats the
     (0,1,1) base rule whatever the order. */
  .dash-hero-panel--story .dash-hero-text h1{ font-size: 22px; }
  /* Below 900px the float row is no longer pulled up over the stage, so the text only
     needs its own bottom padding back. */
  .dash-hero-panel--story .dash-hero-text{ right: clamp(16px, 5vw, 24px); bottom: 16px; }
  /* At this width the headline spans the full stage, so the base left-to-right scrim runs
     out under the last words of a long feed title. A second, bottom-up layer covers the
     band the text actually sits in. Only the story hero needs it: the designed banners
     carry a two-line headline that never reaches this far. */
  .dash-hero-panel--story .dash-hero-scrim{
    /* The bottom-up layer covers the whole stage (sweep 2026-09-13): the eyebrow sits at
       y~26 of a 204px stage and the first headline line reaches the right edge, both past
       where the left-to-right layer has faded, so over a light picture their last words
       fell to 1.5:1. 45% ink at the top keeps the picture readable. */
    background:
      linear-gradient(90deg, currentColor 0%, currentColor 34%, transparent 80%),
      linear-gradient(0deg, currentColor 0%, color-mix(in srgb, currentColor 60%, transparent) 52%, color-mix(in srgb, currentColor 45%, transparent) 100%);
  }
  /* .dash-hero-text p is display:none below 900px (base rule); the CTA is not. */
  .sxh-foot{ margin-top: 10px; gap: 10px; }
  .sxh-src{ font-size: 10.5px; }
}

/* ---- the single-page digest (U-wdx, 2026-09-14) ----
   /weekly is one editorial column now, not a hub: lead story, the week's workflows,
   every remaining story as an alternating two-column spread, then our take as four dark
   tiles. Thin rules and type do the work — no shadows, no gradients, one hover
   affordance per link. Every picture carries its credit line directly underneath it.
   The .wdx-* sections, trending rows and step accordions this replaced are gone; the
   subscribe strip below and .wdx-credit (shared with /news and the home paper) stay. */
.wde-page > section{ padding: 44px 0; border-top: .5px solid var(--border); }
.wde-page > section:first-child{ padding-top: 8px; border-top: 0; }

/* The darker accent, not --accent: at 11px on the page ground --accent measures 4.24:1,
   which is under AA, and a size that small cannot buy the exemption back with weight. */
.wde-eyebrow{
  margin: 0 0 10px;
  font-family: var(--f-mono); font-size: 11px; font-weight: 600; letter-spacing: .1em;
  text-transform: uppercase; color: var(--accent-press);
}
.wde-h{
  margin: 0 0 22px;
  font-family: var(--f-display); font-size: clamp(26px, 3vw, 40px);
  font-weight: 600; line-height: 1.1; letter-spacing: -.03em; color: var(--text);
}

/* ---- the lead story ---- */
.wde-lead-h{
  margin: 0 0 16px; max-width: 1000px;
  font-family: var(--f-display); font-size: clamp(32px, 4.4vw, 58px);
  font-weight: 600; line-height: 1.05; letter-spacing: -.035em; color: var(--text);
}
.wde-deck{
  margin: 0 0 26px; max-width: 880px;
  font-size: clamp(17px, 1.5vw, 21px); line-height: 1.5; color: var(--text-2);
}
.wde-body{ max-width: 840px; }
/* :not(.wde-eyebrow) because the eyebrow is a <p> in the same box: `.wde-text > p` would
   otherwise outrank `.wde-eyebrow` on specificity and paint it body grey. */
.wde-body p, .wde-text > p:not(.wde-eyebrow){ margin: 0 0 16px; font-size: 16px; line-height: 1.65; color: var(--text-2); }

/* ---- pictures: the box, then the credit line under it ---- */
.wde-media{ min-width: 0; margin: 0 0 24px; }
.wde-pic{
  overflow: hidden; background: var(--bg-2);
  border: .5px solid var(--border); border-radius: var(--r-lg);
}
.wde-pic > img{ display: block; width: 100%; height: 100%; object-fit: cover; }
.wde-lead .wde-pic{ aspect-ratio: 16 / 9; }
.wde-story .wde-pic{ aspect-ratio: 16 / 10; }
/* .wdx-credit is pulled up 4px for the card tile it was written for; under a framed
   picture it needs the gap back. */
.wde-media .wdx-credit{ margin-top: 8px; }
/* A picture that 404s takes its box and its credit with it (aadImgFail), which would
   otherwise leave an empty column holding the spread open. */
.wde-media:not(:has(.wde-pic)){ display: none; }

/* ---- why it matters ---- */
.wde-why{
  margin: 24px 0 0; max-width: 840px;
  padding: 18px 20px;
  border-left: 3px solid var(--accent);
  background: var(--bg-3);
  border-radius: 0 var(--r-md) var(--r-md) 0;
}
.wde-why b{ display: block; margin-bottom: 6px; font-size: 13px; font-weight: 600; color: var(--text); }
.wde-why p{ margin: 0; font-size: 15px; line-height: 1.6; color: var(--text-2); }

/* ---- the source, named under every story ---- */
/* --accent-press for the same reason as the eyebrow: --accent at 13.5px on the page
   ground measures 4.24:1, and axe does not flag it, but it is still under AA. */
.wde-src{
  display: inline-flex; align-items: center; margin-top: 18px;
  font-size: 13.5px; font-weight: 500; color: var(--accent-press); text-decoration: none;
}
.wde-src:hover{ text-decoration: underline; text-underline-offset: 3px; }
.wde-src:focus-visible{ outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px; }

/* ---- workflows this week ---- */
.wde-flow{ margin-top: 30px; }
.wde-flow:first-of-type{ margin-top: 0; }
.wde-flow-h{
  margin: 0 0 8px;
  font-family: var(--f-display); font-size: clamp(18px, 1.7vw, 23px);
  font-weight: 600; line-height: 1.25; letter-spacing: -.018em; color: var(--text);
}
.wde-flow-desc{ margin: 0 0 14px; max-width: 840px; font-size: 15px; line-height: 1.6; color: var(--text-2); }
.wde-tools{ display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 16px; padding: 0; list-style: none; }
.wde-tools li{
  padding: 4px 10px;
  border: .5px solid var(--border); border-radius: var(--r-pill);
  background: var(--surface);
  font-family: var(--f-mono); font-size: 11px; letter-spacing: .03em; color: var(--text-3);
}
/* auto-fit at 200px gives five across the 1440 scroller and three on a narrow laptop;
   640px drops it to one. The <ol> carries the order, .wde-step-n only prints it. */
.wde-steps{
  display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 10px; margin: 0; padding: 0; list-style: none;
}
.wde-step{
  padding: 16px 18px;
  border: .5px solid var(--border); border-radius: var(--r-md);
  background: var(--surface);
}
.wde-step-n{
  display: block; margin-bottom: 18px;
  font-family: var(--f-mono); font-size: 11px; font-weight: 600; letter-spacing: .08em;
  color: var(--accent);
}
.wde-step-h{ margin: 0 0 6px; font-size: 14.5px; font-weight: 600; line-height: 1.3; color: var(--text); }
.wde-step-d{ margin: 0; font-size: 13px; line-height: 1.55; color: var(--text-3); }

/* ---- stories 2..n: picture left, then right, then left ---- */
.wde-story{
  display: grid; grid-template-columns: minmax(0, 1.02fr) minmax(0, .98fr);
  gap: 44px; align-items: center;
}
.wde-story .wde-media{ margin: 0; }
.wde-story--flip .wde-media{ order: 2; }
.wde-text{ min-width: 0; }
.wde-text > p:not(.wde-eyebrow):last-of-type{ margin-bottom: 0; }
.wde-story-h{
  margin: 0 0 14px;
  font-family: var(--f-display); font-size: clamp(24px, 2.6vw, 36px);
  font-weight: 600; line-height: 1.1; letter-spacing: -.03em; color: var(--text);
}
.wde-story--nopic,
.wde-story:not(:has(.wde-pic)){ grid-template-columns: minmax(0, 1fr); }

/* ---- what we think this week ---- */
.wde-tiles{ display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
.wde-tile{
  margin: 0; padding: 22px;
  border-radius: var(--r-lg); background: var(--text); color: var(--ink-text);
  font-family: var(--f-display); font-size: 17px; line-height: 1.35; letter-spacing: -.012em;
}

@media (max-width: 900px){
  .wde-page > section{ padding: 34px 0; }
  .wde-story, .wde-story--flip{ grid-template-columns: minmax(0, 1fr); gap: 22px; }
  .wde-story--flip .wde-media{ order: 0; }
  .wde-tiles{ grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* The source link is the only control inside a story, so it carries the target. */
  .wde-src{ min-height: 44px; }
}
@media (max-width: 640px){
  .wde-page > section{ padding: 28px 0; }
  .wde-steps{ grid-template-columns: minmax(0, 1fr); }
  .wde-step{ padding: 14px 16px; }
  .wde-step-n{ margin-bottom: 10px; }
  .wde-why{ padding: 14px 16px; }
  .wde-tiles{ grid-template-columns: minmax(0, 1fr); }
  .wde-tile{ padding: 18px; font-size: 16px; }
}

/* ---- subscribe strip ---- */
/* newsletterBox(..., 'strip') restyled full-width: same form, same handler, laid out as
   a band. The strip layout omits the inline text-align/margin declarations the centred
   default carries (app.js), so every rule below wins by normal cascade — there is no
   !important anywhere in this block, and adding one would mean the variant has drifted. */
.wdx-sub{ margin-top: 40px; }
.wdx-sub .card{
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "head form" "sub form";
  align-items: center; gap: 4px 28px;
  margin-top: 0;
  text-align: left;
  padding: 26px 30px;
  border-radius: var(--r-lg);
}
.wdx-sub .h2{ grid-area: head; margin: 0; font-size: clamp(19px, 1.8vw, 24px); }
.wdx-sub .muted{ grid-area: sub; margin: 0; max-width: 52ch; font-size: 14px; }
.wdx-sub form{ grid-area: form; margin: 0; width: min(420px, 100%); }
.wdx-sub .btn-primary{ background: var(--ink); border-color: var(--ink); color: var(--ink-text); }
.wdx-sub .btn-primary:hover{ background: var(--ink-2); border-color: var(--ink-2); }
.wdx-sub .card > p:last-child{ display: none; }   /* the fine print reads as clutter in a band */
@media (max-width: 860px){
  .wdx-sub .card{ grid-template-columns: 1fr; grid-template-areas: "head" "sub" "form"; gap: 6px; }
  .wdx-sub form{ margin-top: 12px; width: 100%; }
  /* Stacked, the button reads as a stray pill unless it matches the field above it. */
  .wdx-sub form .btn-primary{ flex: 1 1 100%; }
}

/* ---- the story that expands in place (both pages) ---- */
.sx-panel{
  border: .5px solid var(--border); border-radius: var(--r-md);
  background: var(--surface);
  padding: 20px 22px;
  margin: 6px 0 14px;
  /* Opening animates opacity and transform only (house rule); the height change itself
     is not animated, so nothing reflows frame by frame. */
  opacity: 0; transform: translateY(-4px);
  transition: opacity .22s var(--ease), transform .22s var(--ease);
}
.sx-panel.is-open{ opacity: 1; transform: none; }
/* On /news a card expands rather than navigates, so the card itself is not a click
   target any more - its Read button is. The shared .aad-card cursor would say otherwise. */
#nh-live-wrap .aad-card,
#sx-hero-slot .aad-card{ cursor: default; }

/* Inside a card grid the panel takes the whole row: three paragraphs need the width. */
.sx-panel--grid{ grid-column: 1 / -1; }
.sx-panel .sx-img{
  display: block; width: 100%; max-height: 260px; object-fit: cover;
  border-radius: var(--r-sm); margin-bottom: 16px;
}
.sx-h{
  display: block;
  font-family: var(--f-display); font-size: clamp(18px, 1.9vw, 24px);
  font-weight: 600; line-height: 1.24; letter-spacing: -.018em;
  color: var(--text); text-decoration: none;
}
a.sx-h:hover{ color: var(--accent); }
a.sx-h:focus-visible{ outline: 2px solid var(--accent); outline-offset: 3px; }
.sx-meta{
  margin-top: 8px;
  font-family: var(--f-mono); font-size: 11px; letter-spacing: .05em;
  color: var(--text-3);
}
.sx-body{ margin-top: 14px; }
.sx-body p{ margin: 0 0 10px; font-size: 15px; line-height: 1.62; color: var(--text-2); max-width: 72ch; }
.sx-body p:last-child{ margin-bottom: 0; }
.sx-note{
  margin-top: 14px; padding-top: 12px; border-top: .5px solid var(--border);
  font-family: var(--f-mono); font-size: 10.5px; letter-spacing: .07em;
  text-transform: uppercase; color: var(--text-3);
}
/* Loading: three grey lines, not a spinner - it is text that is coming. */
.sx-sk{ display: flex; flex-direction: column; gap: 10px; }
.sx-sk span{ display: block; height: 12px; border-radius: 6px; background: var(--bg-2); }
.sx-sk span:nth-child(2){ width: 92%; }
.sx-sk span:nth-child(3){ width: 68%; }
@media (prefers-reduced-motion: reduce){
  .sx-panel{ transition: none; opacity: 1; transform: none; }
}
@media (max-width: 640px){
  .sx-panel{ padding: 16px; }
  .sx-body p{ font-size: 14.5px; }
}

/* ---- Sweep 2026-09-13: phone inputs ----
   iOS Safari zooms the page when a text input under 16px takes focus; the home hero form
   already sets 16px (.ms-hero-form above). Every other text input on phones and tablets
   follows, and the hero form's rule extends to 900px. */
@media (max-width: 900px){
  .nl-input, .nl-cleanse .nl-input, .hub-search-input, .intg-search, .gd-cmt-input,
  .auth-form input, #auth-email, .ms-hero-form input{ font-size: 16px; }
}

/* ---- W3: the published edition on /weekly and /news ----
   Credit lines are 12px --text-3 under every picture; on the hero they sit in the ink
   scrim's foot row, where --text-3 fails contrast, so they take the scrim's own text tone
   (same as .sxh-src). The panel picture gets a box of clamped height (a reservation, not
   a ratio: see .sx-media) because the pipeline stores no width/height, so nothing shifts
   when the file lands. */
.aad-card-title--link{ display: block; color: inherit; text-decoration: none; }
.aad-card-title--link:hover{ color: var(--accent); }
.aad-card-title--link:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.wdx-credit{ display: block; margin-top: -4px; font-size: 12px; line-height: 1.3; color: var(--text-3); }
.wdx-credit a{ color: inherit; text-decoration: none; }
.wdx-credit a:hover{ text-decoration: underline; }
.sxh-credit{
  font-size: 12px; color: rgba(255,255,255,.76);
  max-width: 100%; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sxh-credit a{ color: inherit; text-decoration: none; }
.sxh-credit a:hover{ text-decoration: underline; }
a.sxh-h{ color: inherit; text-decoration: none; }
a.sxh-h:hover{ text-decoration: underline; text-underline-offset: 3px; }
/* A definite height, not an aspect-ratio: max-height would transfer through the ratio
   and shrink the box to 607px wide at desktop. The height is what reserves the space. */
.sx-media{
  position: relative; height: clamp(150px, 24vw, 260px); overflow: hidden;
  border-radius: var(--r-md); background: var(--bg-2); margin-bottom: 8px;
}
.sx-media > img{ position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.sx-panel > .wdx-credit{ margin: 0 0 14px; }
/* Sweep 2026-09-13. The edition card meta ("Research · research.example.edu · Sep 15, 2026")
   wraps instead of losing its date to the ellipsis in 1440's 249px card. Credit and
   similar-coverage links take the site's ring instead of the UA default; the disclosed
   hero panel takes focus on open (app.js) and is ringed only for keyboard users. */
.aad-card--ed .aad-card-cat{ white-space: normal; overflow: visible; text-overflow: clip; }
.wdx-credit a:focus-visible, .sx-similar li a:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }
.sxh-credit a:focus-visible{ outline: 2px solid #fff; outline-offset: 2px; border-radius: 3px; }
.sx-panel:focus{ outline: none; }
.sx-panel:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
/* Category tabs: the UA ring straddles the pill's edge and .intg-tabs{overflow-x:auto}
   clipped its outer half, so the active (ink-filled) pill showed no focus at all. An
   inset ring survives the clip on every hub strip, the float strips included. */
.intg-tab:focus-visible, .dash-float-actions .intg-tab:focus-visible{ outline: 2px solid var(--accent); outline-offset: -2px; box-shadow: none; }
.sx-sub{
  margin: 18px 0 8px; font-family: var(--f-mono); font-size: 11px; letter-spacing: .07em;
  text-transform: uppercase; color: var(--text-3); font-weight: 500;
}
.sx-similar ul{ margin: 0; padding: 0; list-style: none; }
.sx-similar li{ padding: 8px 0; border-top: .5px solid var(--border); font-size: 14px; line-height: 1.5; }
.sx-similar li a{ color: var(--text); font-weight: 600; text-decoration: none; }
.sx-similar li a:hover{ color: var(--accent); }
.sx-sim-host{ margin-left: 8px; font-family: var(--f-mono); font-size: 11px; color: var(--text-3); }
.sx-sim-note{ display: block; color: var(--text-2); }
.sx-take p{ margin: 0; font-size: 15px; line-height: 1.62; color: var(--text-2); max-width: 72ch; }

/* ---- U-news 2026-09-14: the edition card expands in place ----
   An open card takes the whole grid row and carries the story itself, so there is no
   second block under it to read. Its picture gets more height than the panel's did: at
   full row width a 260px box under a 1400px card reads as a letterbox. The card stops
   being an elevation-on-hover surface while it is open — it is a page, not a target. */
.aad-card--ed.is-expanded{
  grid-column: 1 / -1;
  gap: 8px; padding: 12px 18px 18px;
  scroll-margin-top: 12px;
}
.aad-card--ed.is-expanded:hover{ box-shadow: var(--sh-1); }
.aad-card--ed.is-expanded .sx-media{ height: clamp(260px, 30vw, 420px); max-height: 420px; }
.aad-card--ed.is-expanded > .wdx-credit{ margin: 0; }
/* The hero's story has no compact card of its own, so its expanded card is built into
   the slot directly under the hero rather than into the grid. */
#sx-hero-slot > .aad-card--ed.is-expanded{ margin: 6px 0 14px; }
.aad-card-minbar{ display: flex; justify-content: flex-end; }
.aad-card-min{
  display: inline-flex; align-items: center; gap: 6px;
  height: 30px; padding: 0 10px;
  border: .5px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--text-3);
  font-family: inherit; font-size: 12.5px; font-weight: 600; line-height: 1;
  white-space: nowrap; cursor: pointer;
  transition: color .18s var(--ease);
}
.aad-card-min:hover{ color: var(--text); }
.aad-card-min:focus-visible{ outline: 2px solid var(--accent); outline-offset: 2px; }
.aad-card-min svg{ width: 14px; height: 14px; flex: 0 0 auto; }
@media (max-width: 900px){
  .aad-card--ed.is-expanded .sx-media{ height: 260px; max-height: 260px; }
  .aad-card-min{ min-height: 44px; }
}
@media (max-width: 640px){
  .aad-card--ed.is-expanded{ padding: 10px 14px 16px; }
}
@media (prefers-reduced-motion: reduce){ .aad-card-min{ transition: none; } }
@media (max-width: 900px){
  /* The credit is one more row in the scrim text; the 180px stage clips the eyebrow
     without it. Only a hero that carries a credit pays for the taller stage. */
  .dash-hero-panel--story .dash-hero-stage:has(.sxh-credit){ height: 204px; }
}

/* ============================================================
   U-phone - tap targets (owner decision B4) + collapse-on-scroll hub chrome
   (owner decision B3). Phones only; nothing here exists above 900px.
   Companion block for the app.css-owned controls: end of that file.
   ============================================================ */
@media (max-width: 900px){

  /* ---------- A. tap targets ----------
     Height only, so no label reflows and no row changes how many items fit.
     .dash-hero-arrow is the one exception: it is a 28px circle at this width,
     and growing a circle on one axis alone makes an ellipse, not a target. */

  /* min-height beats the `height` the @container (max-width: 240px) variant and
     .aad-card--compact set, so the narrow-card button grows with the rest and
     needs no second rule. */
  .aad-card-cta{ min-height: 44px; }

  .sxh-cta{ min-height: 44px; }

  .dash-hero-arrow{ min-width: 44px; min-height: 44px; }
  .dash-hero-pause{ display: inline-flex; align-items: center; min-height: 44px; }

  /* The floating row. :5997 sets `height: auto; min-height: 0` on the three
     swept-in control classes, so these have to be the same specificity AND
     later in the file to win - which is why they live here and not up there. */
  .dash-float-crumbs .crumb{ min-height: 44px; }
  .dash-float-actions .btn,
  .dash-float-actions .intg-tab,
  .dash-float-actions .agents-view-pill{ min-height: 44px; }
  /* The same pills outside the float (the /agents and /templates view switch
     when the sweep leaves them in place). */
  .agents-view-pill{ min-height: 44px; }
  /* The two tool pickers on /compare/:id, which the sweep moves into the same
     box. :6048 sets `height: auto; min-height: 0` on them at (0,1,1), so this
     has to match that specificity and sit later in the file - an app.css rule
     on `select.input` loses to it whatever it says. */
  .dash-float-actions select{ min-height: 44px; }

  /* /compare category chips. These are <a> links, so they are real targets;
     every other badge on the hubs is a plain <span> and is left alone. */
  .cmpm-chips .pill{ min-height: 44px; }

  /* Overview / How-to-decide tabs on /compare/:id and the guide + brief detail
     bars that share the class. 42.58px was the closest miss on the page. */
  .gd-tab{
    display: inline-flex; align-items: center; justify-content: center;
    min-height: 44px;
  }

  /* ---------- A2. the floating row is ONE scrolling line ----------
     Raising its items to 44px (above) turned a wrapping row into three stacked
     lines: 192px of chrome on /agents for four controls. One nowrap line that
     swipes sideways costs 52px instead, and the boxes already lost their fill
     and hairline at this width (:6202), so crumbs and actions read as a single
     strip rather than two boxes that happen to sit in a row.

     justify-content goes back to flex-start: `space-between` on an overflowing
     nowrap flex row pushes the overflow off the START edge, where no amount of
     scrolling reaches it. */
  .dash-float{
    flex-wrap: nowrap; justify-content: flex-start; gap: 8px;
    overflow-x: auto; overflow-y: hidden;
    -webkit-overflow-scrolling: touch; scrollbar-width: none;
    /* The focus ring is 2px at 2px offset, so a 44px item needs 4px of room
       above and below it or the ring is sheared off by overflow-y: hidden. The
       same 4px on the inline axis keeps the ring of the first and last item inside
       the horizontal clip when focus scrolls them flush to an edge. */
    padding-block: 4px; padding-inline: 4px; scroll-padding-inline: 4px;
  }
  .dash-float::-webkit-scrollbar{ display: none; height: 0; width: 0; }
  .dash-float > *,
  .dash-float-actions > *,
  .dash-float-actions .row > *,
  .dash-float-actions .agents-view-pills > *{ flex: 0 0 auto; }
  .dash-float-actions,
  .dash-float-actions .row,
  .dash-float-actions .agents-view-pills{ flex-wrap: nowrap; }
  /* The row itself is the scroller now, so the two inner scrollers that used to
     do this job have to stand down - a nested scroller inside a scrolling row
     just squeezes its own contents instead of letting the row slide. :5770 caps
     the crumbs at 100% and :6046 scrolls the compare pickers' .row; both are
     more specific than a bare class, hence the full selectors. */
  .dash-float-crumbs{ max-width: none; overflow-x: visible; }
  .dash-float-actions:has(select){ min-width: auto; }
  .dash-float-actions:has(select) .row{ overflow-x: visible; min-width: auto; }

  /* ---------- B. collapse the fixed chrome on scroll ----------
     `.is-collapsed` is put on the shell root (the .gd-wrap / .main that holds
     the panel, the subhead and the scroller) by bindCollapse() in
     assets/dash-hero.js, which only runs while this same query matches.

     The pictures go to zero height and are clipped - never display:none, never
     a src change - so collapsing starts no new request and cancels none in
     flight. What is left of the stage is a one-line title bar in the banner's
     own ink. Of the rest, only the category strip survives: the floating row
     and the search field below the tabs both go, which is the difference
     between 412px of chrome and 142px. */
  .is-collapsed .dash-hero-track{ height: 0; overflow: clip; }
  /* Three selectors because the story hero sets its stage height at (0,2,0)
     and again at (0,3,0) through :has(.sxh-credit); :has matches on presence,
     so a hidden credit still counts. */
  .is-collapsed .dash-hero-stage,
  .is-collapsed .dash-hero-panel--story .dash-hero-stage,
  .is-collapsed .dash-hero-panel--story .dash-hero-stage:has(.sxh-credit){ height: 38px; }
  .is-collapsed .dash-hero-text{
    top: 50%; bottom: auto; right: 16px; transform: translateY(-50%);
  }
  .is-collapsed .dash-hero-text h1{
    font-size: 18px;
    display: -webkit-box; -webkit-box-orient: vertical;
    -webkit-line-clamp: 1; line-clamp: 1; overflow: hidden;
  }
  .is-collapsed .dash-hero-text .eyebrow,
  .is-collapsed .dash-hero-text .sub,
  .is-collapsed .dash-hero-text p,
  .is-collapsed .dash-hero-text .sxh-foot{ display: none; }
  /* The slider controls go with the slider. WCAG 2.2.2 is about content that
     is moving in front of the user; nothing is visible to move while the track
     is zero-height, and scrolling back up returns pictures and controls
     together. */
  .is-collapsed .dash-hero-arrow,
  .is-collapsed .dash-hero-pause{ display: none; }

  /* The floating row goes with the banner. Its crumbs and actions belong to the
     page, not to the list being read, and scrolling back under 8px hands the
     whole row back at the top where a reader looks for it. */
  .is-collapsed .dash-float{ display: none; }
  /* The category strip stays - it filters what is on screen - but the search
     field below it does not: it is a second 51.5px line for something the
     reader is not doing while scrolling a list. /news has no search field here,
     so this is a no-op there and its pills are untouched. */
  .is-collapsed .dash-subhead .hub-search--strip{ display: none; }

  /* 180ms / --ms-ease-out, written out rather than through the tokens: --ms-*
     is declared on `body.mystyle` (mystyle.css:14) and a hub route's body is
     `dashboard-mode`, so var(--ms-dur-quick) resolves to nothing here and takes
     the whole shorthand down with it. Measured on /agents at 375: the token
     form computes to `all 0s`. (The same dead reference sits on
     .dash-hero-arrow at :5914 - pre-existing, left alone.)

     .dash-hero-track is deliberately NOT in this list: show() in dash-hero.js
     writes an inline `transition` on it every time the slider advances, and an
     inline style wins whatever is declared here. The visible box is the stage
     and that is what animates. */
  .dash-hero-stage,
  .dash-hero-text h1{
    transition: height 180ms cubic-bezier(.16, 1, .3, 1),
                font-size 180ms cubic-bezier(.16, 1, .3, 1);
  }
  @media (prefers-reduced-motion: reduce){
    .dash-hero-stage,
    .dash-hero-text h1{ transition: none; }
  }
}

/* ---------- site index: the crawlable bottom of the homepage ----------
   2026-09-17 SEO audit, item 1. Prose plus a plain link list to every hub and to the video
   guides, the comparisons and the ten richest skill pages. No new colours, no new
   decoration: it reuses the ms- type scale and the existing tokens, and the only rule that
   draws anything at all is one 0.5px hairline above the link columns. The single hover
   affordance is colour, nothing stacked. */
.ms-index-prose{
  max-width: 720px;
  margin: 0 0 56px;
}
.ms-index-prose p{
  font-family: var(--f-display);
  font-size: clamp(15px, 1.05vw, 17px);
  font-weight: 400;
  line-height: 1.65;
  color: var(--ms-ink-3);
  margin: 0 0 16px;
}
.ms-index-prose p:last-child{ margin-bottom: 0; }
.ms-index-prose strong{ font-weight: 500; color: var(--ms-ink); }

.ms-index-cols{
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 32px 28px;
  border-top: .5px solid var(--border);
  padding-top: 40px;
}
.ms-index-label{
  font-family: var(--f-display);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ms-ink-3);
  margin: 0 0 14px;
}
.ms-index-list{ list-style: none; margin: 0; padding: 0; }
.ms-index-list li{ margin: 0 0 9px; }
.ms-index-list a{
  font-family: var(--f-display);
  font-size: 14px;
  line-height: 1.45;
  color: var(--ms-ink);
  text-decoration: none;
}
.ms-index-list a:hover{ color: var(--ms-accent); }

/* ---- Audit-area role card (/will-ai-replace, /compare-peers; app.js roleCard()) ----
   The photo is the card, like .guide-card and .ms-vcard: a bottom-weighted scrim keeps the
   white title legible on any picture, two pills sit on top, one hover affordance (the lift). */
.role-card{ position:relative; display:block; aspect-ratio:16/10; border-radius:var(--r-sm); overflow:hidden; background:var(--text); color:#fff; text-decoration:none; transition:transform .18s var(--ease); }
.role-card:hover{ transform:translateY(-1px); }
.role-card:focus-visible{ outline:2px solid var(--accent); outline-offset:3px; }
.role-card-img{ position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.role-card::before{ content:""; position:absolute; inset:0; z-index:1; background:linear-gradient(180deg, rgba(0,0,0,.28) 0%, rgba(0,0,0,0) 30%, rgba(0,0,0,.35) 55%, rgba(0,0,0,.86) 100%); }
.role-card-top{ position:absolute; top:12px; left:12px; right:12px; z-index:2; display:flex; align-items:center; justify-content:space-between; gap:8px; }
.role-card-pill{ padding:4px 9px; border-radius:999px; background:rgba(0,0,0,.62); color:#fff; font-size:11px; font-weight:600; letter-spacing:.02em; white-space:nowrap; }
.role-card-pill--high,
.role-card-pill--med,
.role-card-pill--low{ display:inline-flex; align-items:center; gap:6px; background:rgba(255,255,255,.94); color:#1d1d1f; }
.role-card-pill--high::before,
.role-card-pill--med::before,
.role-card-pill--low::before{ content:""; width:7px; height:7px; border-radius:50%; }
.role-card-pill--high::before{ background:var(--danger); }
.role-card-pill--med::before{ background:var(--warning); }
.role-card-pill--low::before{ background:var(--success); }
.role-card-top > .role-card-pill:only-child{ margin-left:auto; }
.role-card-foot{ position:absolute; left:16px; right:16px; bottom:14px; z-index:2; display:flex; flex-direction:column; gap:3px; }
.role-card-title{ margin:0; font-family:var(--f-display); font-size:19px; font-weight:600; line-height:1.2; letter-spacing:-.01em; color:#fff; }
.role-card-sub{ font-size:13px; line-height:1.35; color:rgba(255,255,255,.8); }
@media (prefers-reduced-motion: reduce){
  .role-card{ transition:none; }
  .role-card:hover{ transform:none; }
}

/* Profession page template (redesign F4, app.js viewWillReplace): the answer line, the
   exposure sentence and the audit CTA beside the ring; the row's phrases as small static
   cards (no hover: they are not links); the 30-day plan as week cards. */
.wr-hero{ display: grid; grid-template-columns: minmax(0,1fr) auto; gap: 28px; align-items: start; }
.wr-answer{ margin: 0 0 10px; font-family: var(--f-display); font-size: 22px; font-weight: 600; letter-spacing: -.01em; color: var(--text); }
.wr-cards{ grid-template-columns: repeat(auto-fill, minmax(200px,1fr)); list-style: none; padding: 0; margin: 0; }
.wr-cards > .card{ padding: 16px 18px; font-size: 15px; color: var(--text); }
.wr-weeks > .card{ color: var(--text-2); }
.wr-sec{ margin-top: 36px; }
/* Brief section 29 hub links (tool -> /skills, skill -> /guides, weekly -> /will-ai-replace + /tools). */
.xlink{ display: flex; flex-wrap: wrap; gap: 4px 22px; margin: 32px 0 0; }
.xlink .btn-text{ padding-left: 0; }

/* Written-guide diagrams (owner, 2026-10-02). public-content.js guideDiagram(): the /guides
   card is an Editor's-picks .aad-card whose picture is the SVG thumb; the guide page draws the
   editions' workflow infographic (edition-render.js workflowGraph + stepsRow, edition.css
   .wfg-*) inside the figure. */
.aad-card-media--diagram{ background:var(--bg-2); }
.aad-card-media--diagram > .gdg-thumb{ position:absolute; inset:0; width:100%; height:100%; aspect-ratio:auto; }
.gdg-thumb{ display:block; width:100%; height:auto; aspect-ratio:320/160; font-family:var(--f-body, inherit); }
.gdg-thumb .gt-title{ font-size:13.5px; font-weight:600; fill:var(--text); }
.gdg-thumb .gt-box{ fill:var(--surface); stroke:var(--border-2); stroke-width:0.75; }
.gdg-thumb .gt-box--end{ stroke:var(--accent); }
.gdg-thumb .gt-lab{ font-size:12.5px; font-weight:500; fill:var(--text-2); }
.gdg-thumb .gt-arrow{ fill:none; stroke:var(--accent); stroke-width:1.25; stroke-linecap:round; stroke-linejoin:round; }
.gdg-thumb .gt-dot{ fill:var(--accent); }
.gdg-thumb .gt-dotnum{ font-size:10px; font-weight:600; fill:#fff; }
.gdg-thumb .gt-panel{ fill:var(--surface); stroke:var(--border-2); stroke-width:0.75; }
.gdg-thumb .gt-panel--b{ stroke:var(--accent); }
.gdg-thumb .gt-chip{ fill:var(--bg-2); }
.gdg-thumb .gt-chip--b{ fill:var(--accent-tint); }
.gdg-thumb .gt-chiplab{ font-size:11px; fill:var(--text-2); }

.gdg{ margin:28px 0 0; }
.gdg-title{ margin:0 0 18px; font-family:var(--f-display); font-weight:600; font-size:18px; color:var(--text); }
.gdg-verdict{ margin:18px 0 0; padding:12px 16px; border-radius:10px; background:var(--accent-tint); font-size:15px; font-weight:600; color:var(--text); text-align:center; }

/* /methodology (2026-10-11). Formula row, numbered steps, sources, change log.
   Stacked by default; the formula becomes a row at 768px. 0.5px lines, no shadow. */
.meth-formula, .meth-steps, .meth-sources, .meth-log{ list-style:none; margin:0 0 14px; padding:0; }
.meth-formula{ display:grid; gap:8px; }
.meth-formula li{ padding:12px 14px; border:0.5px solid var(--border); border-radius:12px; background:var(--surface); }
.meth-formula b{ display:block; font-size:14px; color:var(--text); }
.meth-formula span{ display:block; margin-top:4px; font-size:13px; line-height:1.45; color:var(--text-2); }
.meth-formula .meth-formula-end{ border-color:var(--accent); }
.meth-formula .meth-formula-end b{ color:var(--accent-ink); }
@media (min-width:768px){ .meth-formula{ grid-template-columns:repeat(5, minmax(0, 1fr)); } }
.meth-steps{ counter-reset:meth; }
.meth-steps li{ counter-increment:meth; position:relative; padding:0 0 14px 40px; }
.meth-steps li::before{ content:counter(meth); position:absolute; left:0; top:1px; width:26px; height:26px; border:0.5px solid var(--accent); border-radius:50%; color:var(--accent-ink); font-size:13px; font-weight:600; line-height:26px; text-align:center; }
.meth-steps b{ display:block; font-size:15px; color:var(--text); }
.meth-steps p{ margin:4px 0 0; }
.meth-sources li{ padding:14px 0; border-top:0.5px solid var(--border); }
.meth-sources li:last-child{ border-bottom:0.5px solid var(--border); }
.meth-sources a{ font-weight:600; color:var(--accent-ink); text-decoration:underline; text-underline-offset:2px; }
.meth-org{ display:block; margin-top:2px; font-size:13px; color:var(--text-3); }
.meth-sources p{ margin:6px 0 0; }
.meth-log li{ display:flex; flex-wrap:wrap; gap:4px 14px; padding:8px 0; border-top:0.5px solid var(--border); font-size:14px; color:var(--text-2); }
.meth-log time{ min-width:140px; color:var(--text-3); }
.meth-log span{ flex:1 1 240px; }

/* ---------- Home redesign (brief feature 2, 2026-10-11) ----------
   Audit, understand, learn, try. Same language as the 10-10 index cards: surface cards,
   .5px borders, no shadow, no gradient, one hover affordance (the border). Status colour
   appears only in the exposure meter. The new sections sit closer together than the older
   full-bleed ones so the first scroll reads as one story. */
.ms-hero .ms-hero-display{ font-size: clamp(36px, 4.6vw, 68px); max-width: 24ch; line-height: 1.06; text-wrap: balance; }
/* Owner reference 2026-10-11: one H1, two lines in two tones. */
.ms-hero-l1, .ms-hero-l2{ display: block; }
.ms-hero-l2{ font-weight: 400; color: var(--ms-ink-3); letter-spacing: -0.02em; }
@media (min-width: 768px){ .ms-hero .ms-hero-display{ max-width: none; } }
/* Owner 2026-10-12 (desktop screenshot): the search, chips, example button, stats and readers
   pill sit a little lower under the headline. The empty .ms-hero-gap carries the drop, so the
   block moves as one. :not() keeps the result state's display:none on the gap winning. */
@media (min-width: 768px){ .ms-hero:not(.ms-hero--result) .ms-hero-gap{ display: block; height: clamp(28px, 6vh, 72px); } }
@media (min-width: 768px) and (max-width: 1099px){
  .ms-hero-stat{ padding: 12px 12px 12px 10px; gap: 14px; }
}
.ms-hero .ms-hero-cycling{ font-size: clamp(17px, 1.45vw, 21px); font-weight: 400; line-height: 1.45; max-width: 46ch; margin-left: auto; margin-right: auto; text-align: center; text-wrap: balance; }
.ms-hero .ms-hero-cycling .ms-tryit{ display: inline; font-weight: 400; color: var(--ms-ink-2); }
/* Owner 2026-10-12: a shorter support line on a phone (mobile.css swaps the two). */
.ms-hero .ms-hero-cycling .ms-tryit--phone{ display: none; }
.ms-hero-trust{ margin: 10px 0 0; font-size: 13px; color: var(--ms-ink-3); letter-spacing: -0.003em; }
/* Reference 2026-10-11: the support line reads as one line once the hero is wide enough.
   Below 1280 it keeps the 46ch wrap. */
@media (min-width: 1280px){
  .ms-hero .ms-hero-cycling{ max-width: none; font-size: clamp(16px, 1.2vw, 18px); }
}

.ms-ex-section, .ms-journey-section, .ms-jp-section, .ms-next-section, .ms-meth-section, .ms-cat-section, .ms-fresh-section{
  padding-top: clamp(56px, 7vw, 112px);
  padding-bottom: 0;
}
.ms-fresh-section + .ms-daily-section,
.ms-fresh-section + .ms-daily-section[hidden] + .ms-wk-section{ padding-top: 32px; }

.ms-ex-card{
  background: var(--surface); border: .5px solid var(--border); border-radius: var(--r-md);
  padding: clamp(20px, 3vw, 36px); display: grid; gap: 24px;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: end;
}
.ms-ex-card:focus{ outline: 2px solid var(--accent); outline-offset: 3px; }
.ms-ex-card:focus:not(:focus-visible){ outline: none; }
.ms-ex-role{ font-family: var(--f-display); font-size: clamp(26px, 2.6vw, 36px); font-weight: 600; letter-spacing: -0.02em; margin: 0; color: var(--ms-ink); }
.ms-ex-note{ margin: 6px 0 0; font-size: 13px; color: var(--ms-ink-3); }
.ms-ex-num{ display: block; font-family: var(--f-display); font-size: clamp(40px, 4vw, 56px); font-weight: 600; letter-spacing: -0.03em; line-height: 1; color: var(--ms-ink); }
.ms-ex-lbl{ display: block; margin: 8px 0 10px; font-size: 14px; color: var(--ms-ink-2); }
.ms-ex-cols{ grid-column: 1 / -1; display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 20px 28px; border-top: .5px solid var(--border); padding-top: 20px; }
.ms-ex-h3{ margin: 0 0 8px; font-size: 13px; font-weight: 600; color: var(--ms-ink); }
.ms-ex-list{ list-style: none; margin: 0; padding: 0; }
.ms-ex-list li{ font-size: 14px; line-height: 1.5; color: var(--ms-ink-2); padding: 3px 0; }
.ms-ex-link{ color: var(--accent-ink); text-decoration: underline; text-underline-offset: 2px; }
.ms-ex-foot{ grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: center; gap: 12px 18px; }
.ms-ex-foot-note{ font-size: 13px; color: var(--ms-ink-3); }

/* Exposure meter: a single status tint, nothing else on the page is coloured. */
.ms-meter{ display: block; height: 6px; border-radius: 3px; background: var(--ms-line); overflow: hidden; }
.ms-meter-fill{ display: block; height: 100%; border-radius: 3px; background: var(--bar-2); }

.ms-journey, .ms-cat, .ms-meth{ list-style: none; margin: 0; padding: 0; display: grid; gap: 20px; grid-template-columns: repeat(4, minmax(0, 1fr)); }
.ms-journey-step, .ms-jp-card, .ms-cat-card{
  position: relative; background: var(--surface); border: .5px solid var(--border); border-radius: var(--r-md);
  transition: border-color var(--ms-dur-quick) ease;
}
.ms-journey-step:hover, .ms-jp-card:hover, .ms-cat-card:hover{ border-color: var(--border-2); }
.ms-journey-step{ padding: 10px 10px 18px; }
.ms-journey-media{ display: block; aspect-ratio: 16 / 10; overflow: hidden; border-radius: var(--r-sm); background: var(--ms-ink); margin: 0 0 14px; }
.ms-journey-media img, .ms-jp-media img{ display: block; width: 100%; height: 100%; object-fit: cover; object-position: center 30%; }
.ms-journey-num{ margin: 0 6px 4px; font-size: 12px; font-weight: 600; color: var(--accent-ink); }
.ms-journey-t, .ms-jp-t, .ms-cat-t{ margin: 0 6px 6px; font-family: var(--f-display); font-size: 18px; font-weight: 600; letter-spacing: -0.01em; color: var(--ms-ink); }
.ms-journey-link{ color: inherit; text-decoration: none; }
.ms-journey-link::after{ content: ""; position: absolute; inset: 0; border-radius: inherit; }
.ms-journey-link:focus-visible{ outline: none; }
.ms-journey-step:focus-within{ outline: 2px solid var(--accent); outline-offset: 2px; }
.ms-journey-p, .ms-jp-p, .ms-cat-p{ margin: 0 6px; font-size: 14px; line-height: 1.55; color: var(--ms-ink-3); }
/* Home "What to do next" (owner fix list 2026-10-13): three steps, thin lines, no shadow.
   One hover affordance: the card border takes the accent. */
.ms-next{ list-style: none; margin: 28px 0 0; padding: 0; display: grid; grid-template-columns: repeat(3, minmax(0,1fr)); gap: 14px; }
.ms-next-step{ position: relative; padding: 22px 20px 24px; background: var(--surface); border: .5px solid var(--border); border-radius: var(--r-md); transition: border-color .16s ease; }
.ms-next-step:hover{ border-color: var(--accent); }
.ms-next-num{ margin: 0 0 6px; font-size: 12px; font-weight: 600; color: var(--accent-ink); }
.ms-next-t{ margin: 0 0 6px; font-family: var(--f-display); font-size: 18px; font-weight: 600; letter-spacing: -0.01em; color: var(--ms-ink); }
.ms-next-link{ color: inherit; text-decoration: none; }
.ms-next-link::after{ content: ""; position: absolute; inset: 0; border-radius: inherit; }
.ms-next-link:focus-visible{ outline: none; }
.ms-next-step:focus-within{ outline: 2px solid var(--accent); outline-offset: 2px; }
.ms-next-p{ margin: 0; font-size: 14px; line-height: 1.55; color: var(--ms-ink-3); }
@media (max-width: 767px){
  .ms-next{ grid-template-columns: minmax(0,1fr); gap: 10px; }
}
/* The journey list is also a .ms-track (its phone layout, mobile.css); above phone width it
   stays a grid. Two classes, so these win over .ms-track. */
.ms-journey.ms-track{ display: grid; overflow: visible; margin: 0; padding: 0; scroll-snap-type: none; gap: 20px; grid-template-columns: repeat(4, minmax(0, 1fr)); }
.ms-dashes.ms-journey-dashes{ display: none; }

/* Pinned four steps (setupJourney). The run is a tall runway; the list sticks in it, steps on
   the left with a rail, the current step's picture on the right. Each step's rail segment
   turns accent once it is reached, so the rail fills as the reader scrolls. */
@media (min-width: 1100px) and (prefers-reduced-motion: no-preference){
  .ms-journey-run{ --ms-jr-stage: min(70vh, 580px); height: calc(var(--ms-jr-stage) + 150vh); }
  .ms-journey.ms-track{
    position: sticky; top: max(88px, calc((100vh - var(--ms-jr-stage)) / 2));
    height: var(--ms-jr-stage); grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr);
    grid-auto-rows: min-content; align-content: center; column-gap: 56px; row-gap: 0;
  }
  .ms-journey-step{ grid-column: 1; position: static; background: none; border: 0; border-left: 2px solid var(--border); border-radius: 0; padding: 18px 0 18px 28px; transition: border-color 300ms ease; }
  .ms-journey-step.is-on, .ms-journey-step.is-done{ border-left-color: var(--accent); }
  .ms-journey-step:focus-within{ outline-offset: 4px; }
  .ms-journey-link::after{ display: none; }
  /* Colour, not opacity, marks the step that is not current: the text keeps its contrast. */
  .ms-journey-step .ms-journey-t, .ms-journey-step .ms-journey-num{ color: var(--ms-ink-3); transition: color 300ms ease; }
  .ms-journey-step.is-on .ms-journey-t{ color: var(--ms-ink); }
  .ms-journey-step.is-on .ms-journey-num{ color: var(--accent-ink); }
  .ms-journey-step .ms-journey-t{ font-size: clamp(22px, 2vw, 28px); }
  .ms-journey-step .ms-journey-p{ max-width: 42ch; font-size: 15px; }
  .ms-journey-media{
    position: absolute; top: 0; bottom: 0; right: 0; width: calc((100% - 56px) * 1.1 / 2);
    aspect-ratio: auto; margin: 0; border-radius: var(--r-md);
    opacity: 0; transform: scale(1.04); transition: opacity 500ms ease, transform 1200ms var(--ms-ease-out);
  }
  .ms-journey-step:not(.is-on) .ms-journey-media{ pointer-events: none; }
  .ms-journey-step.is-on .ms-journey-media{ opacity: 1; transform: none; z-index: 1; }
}

.ms-cat-card{ padding: 22px 18px; }
.ms-cat-card .ms-ico{ display: block; margin: 0 6px 14px; color: var(--accent-ink); }

.ms-jp-card{ padding: 10px 10px 18px; display: flex; flex-direction: column; }
.ms-jp-media{ display: block; aspect-ratio: 16 / 9; overflow: hidden; border-radius: var(--r-sm); background: var(--ms-ink); margin: 0 0 14px; }
.ms-jp-level{ display: flex; justify-content: space-between; gap: 10px; margin: 0 6px 8px; font-size: 13px; color: var(--ms-ink-2); }
.ms-jp-pct{ font-variant-numeric: tabular-nums; color: var(--ms-ink); font-weight: 600; }
.ms-jp-card .ms-meter{ margin: 0 6px 12px; }
.ms-jp-p{ flex: 1; margin-bottom: 14px; }
.ms-jp-cta, .ms-cat-cta, .ms-jp-all{ margin: 0 6px; font-size: 14px; font-weight: 600; color: var(--accent-ink); text-decoration: none; }
.ms-jp-cta:hover, .ms-cat-cta:hover, .ms-jp-all:hover{ text-decoration: underline; text-underline-offset: 2px; }
/* The whole job card opens its page, like the journey cards: the CTA is the one real link and
   its ::after covers the card. */
.ms-jp-cta::after{ content: ""; position: absolute; inset: 0; border-radius: inherit; }
.ms-jp-cta:focus-visible{ outline: none; }
.ms-jp-card:focus-within{ outline: 2px solid var(--accent); outline-offset: 2px; }
/* Card strips (.ms-track): a snap row that bleeds into the section gutters, so the
   neighbouring cards peek in at both edges while the first card lines up with the copy.
   Margin and padding cancel, so the page never scrolls sideways. */
.ms-trackwrap{ position: relative; }
.ms-track{
  list-style: none; margin: 0 calc(var(--ms-pad-x) * -1); padding: 2px var(--ms-pad-x) 4px;
  display: flex; gap: 20px; overflow-x: auto; scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--ms-pad-x); overscroll-behavior-x: contain; scrollbar-width: none;
}
.ms-track::-webkit-scrollbar{ display: none; }
.ms-track > *{ flex: 0 0 calc((100% - 40px) / 3); scroll-snap-align: start; }
.ms-track-chev{ position: absolute; top: 34%; z-index: 2; width: 44px; height: 44px; font-size: 22px; line-height: 1; color: var(--ms-ink); background: var(--surface); border: .5px solid var(--border-2); }
.ms-track-chev:hover:not(:disabled){ background: var(--surface); border-color: var(--ms-ink-3); }
/* Inset, not hung outside the strip at -22px: between about 1000 and 1300px wide the fixed side
   button (.side-enter-fab, x 12-53) sat over the prev arrow, so a click meant for the strip
   opened the side panel (owner bug 2026-10-12). The button grew to 42px the same day: it spans
   x 12-54 and sways to 58, and the arrow sits at 5vw + 16 (61 at 901px, the narrowest width the
   button shows), so the two still clear by 3px. */
.ms-track-chev--prev{ left: 16px; }
.ms-track-chev--next{ right: 16px; }
.ms-dashes{ display: flex; justify-content: center; gap: 2px; margin-top: 14px; }
.ms-dashes button{ width: 40px; height: 24px; padding: 0; border: 0; background: none; cursor: pointer; display: grid; place-items: center; }
.ms-dashes button::before{ content: ""; width: 28px; height: 3px; border-radius: 2px; background: var(--border-2); transition: background-color var(--ms-dur-quick) ease; }
.ms-dashes button[aria-current]::before{ background: var(--ms-ink); }
.ms-dashes button[hidden]{ display: none; }
.ms-dashes button:focus-visible{ outline: 2px solid var(--accent); outline-offset: 1px; border-radius: 4px; }
.ms-jp-note{ margin: 18px 0 0; font-size: 13px; color: var(--ms-ink-3); }
.ms-jp-note .ms-jp-all{ margin: 0 0 0 4px; font-size: 13px; }

/* Methodology cards (owner reference 2026-10-12): card 0 carries the section heading where
   the others carry a line drawing; title and line sit under both. */
.ms-meth li{ display: flex; flex-direction: column; gap: 4px; padding: 0 0 18px; overflow: hidden; border: .5px solid var(--border); border-radius: var(--r-md); background: var(--surface); }
.ms-meth-art, .ms-meth-head{ display: block; height: 150px; margin: 0 0 14px; }
.ms-meth-art{ background: var(--bg-2); border-bottom: .5px solid var(--border); color: var(--ms-ink-2); }
.ms-meth-art svg{ display: block; width: 100%; height: 100%; }
.ms-meth-art text{ font-size: 12px; fill: currentColor; stroke: none; }
.ms-meth-art .ms-meth-acc{ color: var(--accent-ink); }
.ms-meth-art .ms-meth-thin{ opacity: .4; }
.ms-meth-head{ padding: 20px 18px 0; }
.ms-meth-head .ms-headline{ margin: 8px 0 0; font-size: clamp(26px, 2.3vw, 34px); line-height: 1.05; max-width: none; }
.ms-meth b{ margin: 0 18px; font-size: 15px; color: var(--ms-ink); }
.ms-meth-p{ margin: 0 18px; font-size: 13px; line-height: 1.5; color: var(--ms-ink-3); }
/* The drawing draws itself in once, when the list is revealed (catalog #9's moment). Every
   shape carries pathLength="1", so a dash of 1 is the whole stroke. Selected as svg > *, not
   [pathLength]: Chrome did not restyle on the .is-in change through the camelCase selector. */
@media (prefers-reduced-motion: no-preference){
  .js .ms-meth .ms-meth-art svg > *{ stroke-dasharray: 1; stroke-dashoffset: 1; }
  .ms-meth.is-in .ms-meth-art svg > *{ animation: ms-draw 1.4s var(--ms-ease-out) .25s forwards; }
  .ms-meth.is-in li:nth-child(3) .ms-meth-art svg > *{ animation-delay: .4s; }
  .ms-meth.is-in li:nth-child(4) .ms-meth-art svg > *{ animation-delay: .55s; }
}
@keyframes ms-draw{ to{ stroke-dashoffset: 0; } }
.ms-meth-foot{ margin-top: 20px; display: flex; flex-wrap: wrap; align-items: center; gap: 12px 24px; }
.ms-meth-src{ margin: 0; max-width: 70ch; font-size: 14px; line-height: 1.55; color: var(--ms-ink-2); }
.ms-meth-src a{ color: var(--accent-ink); text-decoration: underline; text-underline-offset: 2px; }

.ms-cat-card{ display: flex; flex-direction: column; }
.ms-cat-n{ font-weight: 500; color: var(--ms-ink-3); font-variant-numeric: tabular-nums; }
.ms-cat-p{ flex: 1; }
.ms-cat-fresh{ margin: 10px 6px 0; font-size: 12px; color: var(--ms-ink-3); }
.ms-cat-cta{ margin-top: 14px; }

/* Catalogue bento (owner 2026-10-12): Tools is the Editor's Choice film, tall on the left;
   Agents runs wide beside it with its picture; Skills and MCP sit under Agents. The first
   link's ::after covers the card, so a click anywhere opens it; the Tools card's second
   link sits above that cover. */
.ms-cat{ grid-template-columns: repeat(3, minmax(0, 1fr)); grid-auto-rows: minmax(220px, auto); }
.ms-cat-body{ display: flex; flex-direction: column; flex: 1; }
.ms-cat-cta::after{ content: ""; position: absolute; inset: 0; border-radius: inherit; }
.ms-cat-cta:focus-visible, .ms-cat-cta2:focus-visible{ outline: none; }
.ms-cat-card:focus-within{ outline: 2px solid var(--accent); outline-offset: 2px; }
.ms-cat-media{ display: block; overflow: hidden; border-radius: var(--r-sm); background: var(--bg-2); }
.ms-cat-media img{ display: block; width: 100%; height: 100%; object-fit: cover; }
.ms-cat-card--agent{ grid-column: span 2; flex-direction: row; align-items: stretch; gap: 24px; }
.ms-cat-card--agent .ms-cat-media{ order: 2; flex: 0 0 48%; }
.ms-cat-card--agent .ms-cat-body{ justify-content: center; }
.ms-cat-card--skill .ms-cat-media{ width: 52px; height: 52px; margin: 0 6px 14px; }
.ms-cat-card--skill .ms-cat-media img{ object-fit: contain; }
/* The film and its scrim sit at z-index -1 inside an isolated card, so the copy can stay
   unpositioned and the first link's ::after covers the whole card, not just the copy. */
.ms-cat-card--ec{ grid-row: span 2; justify-content: flex-end; min-height: 460px; padding: 0; overflow: hidden; isolation: isolate; background: var(--ms-dark); border-color: transparent; }
.ms-cat-card--ec .ms-build-ec-img{ z-index: -1; }
.ms-cat-card--ec::after{ content: ""; position: absolute; inset: 0; z-index: -1; background: rgba(8, 12, 20, .46); }
.ms-cat-ec-body{ padding: 24px 20px; }
.ms-cat-card--ec .ms-cat-t, .ms-cat-card--ec .ms-cat-n{ color: #fff; }
.ms-cat-card--ec .ms-cat-p, .ms-cat-card--ec .ms-cat-fresh{ color: rgba(255, 255, 255, .82); }
.ms-cat-ec-tag{ margin: 0 6px 10px; font-size: 12px; font-weight: 600; letter-spacing: .1em; color: rgba(255, 255, 255, .85); }
.ms-cat-ctas{ display: flex; flex-wrap: wrap; gap: 6px 18px; margin: 14px 6px 0; }
.ms-cat-ctas .ms-cat-cta{ margin: 0; color: #fff; }
.ms-cat-cta2{ position: relative; z-index: 2; font-size: 14px; font-weight: 600; color: rgba(255, 255, 255, .82); text-decoration: underline; text-underline-offset: 2px; }
.ms-cat-cta2:hover{ color: #fff; }

/* Card lift (BRAND.md §5 #5): the cards rise 2px and sharpen their hairline. `translate`,
   not `transform`, so the lift composes with the stagger reveal's transform animation. */
@media (hover: hover) and (prefers-reduced-motion: no-preference){
  .ms-jp-card, .ms-cat-card, .ms-meth li{ transition: translate var(--ms-dur-quick) var(--ms-ease-out), border-color var(--ms-dur-quick) ease; }
  .ms-jp-card:hover, .ms-cat-card:hover, .ms-meth li:hover{ translate: 0 -2px; border-color: var(--border-2); }
}
/* The four steps are cards only below 1100px; above it they are the pinned list. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) and (max-width: 1099px){
  .ms-journey-step{ transition: translate var(--ms-dur-quick) var(--ms-ease-out), border-color var(--ms-dur-quick) ease; }
  .ms-journey-step:hover{ translate: 0 -2px; }
}

@media (max-width: 1099px){
  .ms-journey.ms-track, .ms-cat, .ms-meth{ grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ms-cat-card--ec{ grid-row: auto; grid-column: 1 / -1; min-height: 340px; }
  .ms-ex-card{ grid-template-columns: 1fr; align-items: start; }
}

/* Under "Fresh this week" the daily and weekly headings are subheads (h3), sized as such. */
#ms-sec-daily .ms-section-head .ms-headline, #ms-sec-weekly .ms-section-head .ms-headline{ font-size: clamp(24px, 2.4vw, 34px); max-width: none; }

/* ============================================================
   Agent loader (owner, 2026-10-13; assets/agent-loader.js). The workspace sketch shown
   while an agent opens. Port of the owner's AgentLoader.module.css, held to the site rules:
   a 0.5px line instead of the shadow, no dot-grid background, the site accent for the
   button blocks, the pill and the ping. The filled look is painted on ::after and only
   its opacity animates.
   ============================================================ */
.al-root{ display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; gap:8px; min-height:60vh; padding:48px 20px; color:var(--text); }
.al-sheet{ position:relative; width:min(100%, 340px); aspect-ratio:4 / 3; border:.5px solid var(--border); border-radius:12px; background:var(--surface); overflow:hidden; margin-bottom:14px; }
.al-block{ position:absolute; box-sizing:border-box; border:1px dashed rgba(16,32,64,.18); border-radius:6px; transition:opacity .3s ease, transform .35s cubic-bezier(.2,.8,.2,1); }
.al-block::after{ content:""; position:absolute; inset:-1px; border-radius:inherit; opacity:0; transition:opacity .35s ease; }
.al-block[data-kind="chip"], .al-block[data-kind="button"]{ border-radius:999px; }
.al-block[data-kind="line"]{ border-radius:3px; }
.al-block[data-on]{ border-color:transparent; }
.al-block[data-on]::after{ opacity:1; }
.al-block[data-kind="bar"]::after{ background:var(--text); }
.al-block[data-kind="line"]::after{ background:var(--bg-2, #ececea); }
.al-block[data-kind="chip"]::after{ background:var(--surface); border:1px solid var(--text-2, #4a4a48); }
.al-block[data-kind="button"]::after{ background:var(--accent); }
.al-block[data-kind="card"]::after{ background:var(--surface); border:.5px solid var(--border); }
.al-block i{ position:absolute; z-index:1; left:11%; height:9%; border-radius:2px; opacity:0; transition:opacity .3s ease .15s; }
.al-block i:first-child{ top:20%; width:58%; background:var(--text-2, #4a4a48); }
.al-block i + i{ top:42%; width:38%; background:var(--bg-2, #ececea); }
.al-block[data-on] i{ opacity:1; }
.al-block[data-fresh]{ animation:al-pop .45s cubic-bezier(.2,.8,.2,1); }
.al-sheet[data-clearing] .al-block{ opacity:0; transform:scale(.94); }
.al-mark{ position:absolute; z-index:2; width:14px; height:14px; margin:-9px 0 0 -9px; border-radius:4px; background:var(--text); transition:left .34s cubic-bezier(.65,0,.35,1), top .34s cubic-bezier(.65,0,.35,1), opacity .3s ease; }
.al-mark::after{ content:""; position:absolute; inset:4.5px; border-radius:50%; background:var(--surface); }
.al-mark[data-hidden]{ opacity:0; }
.al-pill{ display:inline-flex; align-items:center; gap:6px; padding:4px 10px; border-radius:999px; font-size:11px; font-weight:600; letter-spacing:.08em; text-transform:uppercase; color:var(--accent); background:var(--accent-tint); }
.al-pill i{ width:4px; height:4px; border-radius:50%; background:currentColor; animation:al-dot 1.2s ease-in-out infinite; }
.al-pill i:nth-child(2){ animation-delay:.15s; }
.al-pill i:nth-child(3){ animation-delay:.3s; margin-right:2px; }
.al-title{ margin:4px 0 0; font-size:17px; font-weight:600; letter-spacing:-.01em; text-wrap:balance; }
.al-roller{ height:20px; overflow:hidden; }
.al-roller > div{ transition:transform .45s cubic-bezier(.65,0,.35,1); }
.al-roller span{ display:block; height:20px; line-height:20px; font-size:13px; color:var(--text-3, #8a8a87); }
.al-roller span[data-current]{ color:var(--text-2, #4a4a48); }
.al-target{ margin:0; font-size:13px; color:var(--text-3, #8a8a87); }
.al-target b{ font-weight:600; color:var(--text-2, #4a4a48); }
.al-sr{ position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; }
@keyframes al-pop{ 0%{ transform:scale(.9); } 60%{ transform:scale(1.03); } 100%{ transform:scale(1); } }
@keyframes al-dot{ 0%,100%{ opacity:.3; transform:translateY(0); } 40%{ opacity:1; transform:translateY(-2px); } }
@media (prefers-reduced-motion: reduce){
  .al-block, .al-block::after, .al-roller > div{ transition:none; }
  .al-block[data-fresh]{ animation:none; }
  .al-pill i{ animation:none; opacity:1; }
}
