/* ===========================================================================
   WLR TEAM POLL — DESIGN SYSTEM v3 (LIGHT)

   [D49] THE GROUND INVERTS. Christian, 2026-08-18:
     "I don't think you should make the whole poll black. You should probably
      make it white. It's easier to see against black text."
   Note "the whole poll" — every screen, not just the results view. This
   supersedes the ground/ink halves of D1-D48; their reasoning survives, their
   hex values do not.

   [D20] TYPE FLOOR — 12px minimum, unchanged. The reference designs specify
         7/8/9/10px; we scale them up. Christian, same call: "the UI/UX around
         the questions need to be normal and accessible, otherwise you'll put
         something live and it'll be trash."
   [D21] CONTRAST — WCAG AAA (7:1) for every text/background pair, unchanged.
         The reference greys (#666 5.74:1, #777 4.48:1, #999 2.85:1) all fail
         on white and are not used.
   [D50] NO FULL CIRCLES. "never use full rounded corners. Like never make a
         full circle on something." border-radius:50%/999px is banned for
         DECORATIVE shapes. Form controls are exempt (decision S3) — a square
         radio reads as a checkbox and changes what the control means.
   [D51] ONE HUE, FOR ERRORS ONLY. "Don't use this color. You should probably
         use a red color or something when it's an error." The old amber
         (var(--error) on var(--error-bg)) is gone. The standing rule narrows from "colour
         never encodes meaning" to "...except to mark an error".

   EVERY VALUE BELOW IS MEASURED, NOT ESTIMATED. Worst text pair is
   --muted on --active: 7.57:1. Worst non-text pair is --line-hover on the
   ground: 3.03:1 (WCAG 1.4.11 wants 3:1 for UI boundaries).
   =========================================================================== */

:root{
  --sp-14:56px; --sp-20:80px;   /* [D101] the two upper rungs of the ladder */
  /* =========================================================================
     [D63] THE DESIGN SYSTEM BECOMES THE TOKEN SOURCE OF TRUTH.

     Source: "WLR Internal Tools Design System", built by Ziyad in Claude
     Design 2026-08-21, Update 3. Ziyad's ruling on intake, this session:
     page tone, type scale, layout dimensions and the auth composition all go
     to the design system. The AAA floor, the 12px floor, the one hue, the
     shape rule and the no-branding-icons rule were reconciled INTO the design
     system across three revisions, so there is no longer a conflict to
     arbitrate. See docs/12-design-system-intake.md.

     TIER 1 below is the design system's own semantic set, verbatim.
     TIER 2 is the legacy alias layer: the ~360 existing var() references in
     this stylesheet keep working, each pointing at its correct new semantic.
     Do not put a new literal value in a rule. New values belong here.

     EVERY RATIO BELOW IS MEASURED ON EVERY SURFACE THE TOKEN CAN LAND ON,
     not on white. A token validated only against white is not validated:
     that is the design system's section 13 rule, and it is the D57 lesson.
     ========================================================================= */

  /* --- surfaces. The ladder, five rungs. ------------------------------- */
  --surface-page:#E6E6E9;         /* the app ground. 1.25:1 under white */
  --surface-base:#FFFFFF;         /* sidebar, topbar, cards, document pages */
  --surface-sunken:#F6F6F8;       /* wells, table headers, progress tracks,
                                     search. STRUCTURAL ONLY: sunken says
                                     "this is a container", never "you chose
                                     this". Prohibited: sunken meaning
                                     selected. */
  --surface-interactive:#FAFAFB;  /* row and nav hover on white */
  --surface-selected:#E4E4EA;     /* active nav item, current step. 1.17:1
                                     from sunken. They measured 1.018:1
                                     before Update 3, i.e. the same colour. */
  --surface-inverse:#0B0B0C;      /* primary button + selected answer rows.
                                     Nothing else. */
  --surface-inverse-hover:#1C1C1F;

  /* --- ink ramp. Worst-surface ratios, measured. ------------------------ */
  --ink-primary:#0B0B0C;          /* 19.67 white .. 15.54 selected */
  --ink-secondary:#464650;        /* 9.32 white ..  7.36 selected  AAA */
  --ink-muted:#4A4A4F;            /* 8.81 white ..  7.07 page      AAA
                                     BARRED from --surface-selected, where it
                                     reaches only 6.96:1. Captions on a
                                     selected fill use --ink-secondary. */
  --ink-disabled:#B6B6BC;         /* exempt. Never the only copy of anything */
  --ink-inverse:#FFFFFF;          /* 19.67 on inverse */
  --ink-inverse-muted:rgba(255,255,255,.66);  /* 8.79 on inverse */

  /* --- the one hue. Error only. ----------------------------------------- */
  /* Christian, 2026-08-18: "Don't use this color. You should probably use a
     red color or something when it's an error." No amber, no green, no blue.
     8.77 white, 8.13 sunken, 7.04 page. BARRED from --surface-selected, where
     it reaches only 6.93:1 -- confirmed by the design system, Update 4. On a
     selected fill, error text sets in --ink-primary with the glyph and the
     error border carrying the state. The token deliberately does NOT move:
     darkening it to clear a surface it should not appear on would cost
     contrast everywhere it actually does. */
  --error:#9B0000;
  --error-bg:#FBF3F2;

  /* --- borders. Three weights: decorative, resting, hover. -------------- */
  --border-hairline:#EDEDEF;      /* decorative, exempt: card edges, rules.
                                     CALIBRATED FOR WHITE. It measures 1.11:1
                                     on #FFFFFF and 1.05:1 on the page tone,
                                     where it is invisible -- use the next
                                     token for any rule drawn on the ground. */
  /* [D138] A hairline that survives --surface-page. Ziyad, 2026-08-24: the
     divider under the masthead cards "is not visible right now", and it was
     not: #EDEDEF on #E6E6E9 is the same colour twice. 1.25:1 on the page tone,
     which is the separation --border-hairline gets on white. Decorative, so
     exempt from the text floor, and it exists so this cannot be solved again
     by reaching for an ink token. */
  --border-on-page:#CFCFD5;
  --border-resting:#7C7C82;       /* 4.15 white .. 3.28 selected, 3:1 min */
  --border-hover:#6E6E76;         /* 5.05 white .. 3.99 selected */
  --border-selected:#0B0B0C;      /* 1.5px, grows inward, no layout shift */
  --border-focus:#0B0B0C;         /* 2px, offset 2px, everywhere */

  /* --- data marks. Every series clears 3:1 on white AND on the page.
         Adjacent tones separate by as little as 1.46:1, so tone is NEVER the
         only channel: dash or hatch plus a direct label, always. ---------- */
  --data-1:#0B0B0C;               /* 19.67 white / 15.80 page */
  --data-2:#3D3D42;               /* 10.80 white /  8.67 page */
  --data-3:#5C5C63;               /*  6.63 white /  5.33 page */
  --data-4:#767676;               /*  4.54 white /  3.65 page */
  --grid:#EDEDEF;                 /* gridlines, decorative, exempt */

  /* --- type. The ten named styles. 14px floor, no exceptions. -----------
     [D143] THE FLOOR RISES FROM 12 TO 14. Ziyad, 2026-08-25: "Some places,
     fonts are 13, keep it 14... This goes for the whole mockup", and again in
     his summary: "Font sizes should be 14 everywhere at minimum."

     This RAISES D20's floor, so nothing that was legible stops being legible
     and every measured contrast pair is untouched -- ratio does not depend on
     size. What it does cost is WIDTH: the three styles below carried most of
     the dense furniture in the product (rail keys, KPI labels, chart ticks,
     chips), and 14px sets about 17% wider than 12px. Ziyad asked for the floor
     and for no wrapping in the same message, so the width has to be found
     rather than wished for -- see D150, which moves the evidence rail from
     23.7% to 30% of the ballot, and D154, which cuts the copy that only ever
     fitted because it was small.

     The three styles collapse to two distinct sizes. Caption and overline both
     land on 14, so "caption" and "overline" are now a WEIGHT and a TRACKING
     difference rather than a size difference. That is deliberate: three sizes
     inside a 2px band was a distinction nobody could see anyway. */
  --fs-display:32px;   --lh-display:38px;   --tr-display:-.025em;
  --fs-title-lg:24px;  --lh-title-lg:30px;  --tr-title-lg:-.02em;
  --fs-title:20px;     --lh-title:26px;     --tr-title:-.015em;
  --fs-heading:17px;   --lh-heading:24px;
  --fs-question:16px;  --lh-question:24px;  --tr-question:-.01em;
  --fs-body:15px;      --lh-body:22px;
  --fs-label:14px;     --lh-label:20px;
  --fs-caption:14px;   --lh-caption:20px;
  --fs-overline:14px;  --lh-overline:18px;  --tr-overline:.08em;
  --fs-kpi:22px;       --lh-kpi:26px;
  /* The floor itself, so a rule can be written against it and a test can read
     it back off the page rather than out of this file. */
  --fs-floor:14px;

  /* --- spacing, 4px base --- */
  --sp-1:4px;  --sp-2:8px;  --sp-3:12px; --sp-4:16px; --sp-5:20px;
  --sp-6:24px; --sp-8:32px; --sp-10:40px; --sp-12:48px; --sp-16:64px;

  /* --- radii. NOTHING IS A FULL CIRCLE OR A FULL-RADIUS PILL.
         One exception in the whole system: the radio control. A data-drawn
         circle is NOT exempt, so proportions are horizontal bars. 999 is not
         a token and no longer appears in this file. --------------------- */
  --r-marker:2px;  --r-xs:4px;  --r-sm:6px;  --r-md:10px;
  --r-lg:12px;     --r-xl:16px; --r-track:3px;

  /* --- elevation. Only for layers that genuinely float. In-flow content
         gets tone plus a hairline, never a shadow. ------------------------ */
  --elev-1:0 1px 2px rgba(11,11,12,.04);
  --elev-2:0 4px 12px rgba(11,11,12,.06);
  --elev-3:0 16px 40px rgba(11,11,12,.12);

  /* --- sizing --- */
  --h-control:40px; --h-control-sm:32px; --h-control-xs:28px; --h-control-lg:48px;
  --w-sidebar:248px; --w-rail:320px; --w-content:1120px;
  /* [D155] THE ANSWER MEASURE. One number for every control a question is
     answered with, so an option stack and the free-text box beside it cannot
     end up different widths. It was 560 for the options (D114) and 720 for the
     textarea, set in two places that never referenced each other. */
  --w-answer:560px;
  --w-ballot:720px;  --w-auth:440px;   --h-topbar:64px;

  /* --- motion. Nothing spins, because nothing round is left to spin. ---- */
  --dur-instant:0ms;
  --dur-fast:120ms;
  --dur-base:180ms;
  --dur-slow:240ms;
  --ease:cubic-bezier(.2,0,0,1);
  --ease-exit:cubic-bezier(.4,0,1,1);

  /* --- z layers --- */
  --z-sticky:100; --z-menu:200; --z-scrim:300; --z-modal:400;
  --z-popover:500; --z-toast:600;

  /* =========================================================================
     TIER 2 - LEGACY ALIASES. Each points at its semantic above. These exist
     so the stylesheet re-skins without rewriting 1,277 lines of selectors.
     RE-ALIASING IS NOT A LOCAL EDIT: --bg had 15 consumers across five
     screens and re-pointing it silently broke two of them. Every alias below
     was checked against its actual consumers before being written.
     ========================================================================= */
  --page:var(--surface-page);
  --bg:var(--surface-page);
  --surface:var(--surface-base);      /* content well + evidence rail: base */
  --sidebar-bg:var(--surface-base);   /* "Sidebar, topbar, cards" */
  --panel-bg:var(--surface-base);
  --surface-2:var(--surface-sunken);  /* wells */
  --raised:var(--surface-base);
  --card:var(--surface-base);
  --field:var(--surface-base);        /* inputs are base plus a resting edge */
  --control:var(--surface-base);
  --fill:var(--surface-base);
  --fill-hover:var(--surface-interactive);
  --active:var(--surface-selected);

  --ink:var(--ink-primary);
  --ink-2:var(--ink-secondary);
  --muted:var(--ink-muted);
  --text:var(--ink-primary);
  --on-ink:var(--ink-inverse);
  --on-ink-2:var(--ink-inverse-muted);
  --accent:var(--surface-inverse);
  --accent-hover:var(--surface-inverse-hover);
  --focus:var(--border-focus);

  /* --line is overwhelmingly a CONTAINER edge in this file (cards, topbar,
     rail, dividers, rules), so it maps to the hairline. The rules that are
     genuinely interactive boundaries take --line-control instead. */
  --line:var(--border-hairline);
  --line-subtle:var(--border-hairline);
  --hair:var(--border-hairline);
  --line-control:var(--border-resting);
  --line-hover:var(--border-hover);
  --line-strong:var(--border-hover);

  --data:var(--data-1);
  /* [D170] THE COMPARATOR SEPARATES FROM THE SUBJECT. Ziyad, 2026-08-27: "the
     sprint delivery legends as in the colors of the 'Team' vs '{Department}',
     they're pretty same in terms of the colors. Use a different color for one
     of them."

     He is right and the number is bad: --data-1 (#0B0B0C) against --data-2
     (#3D3D42) is **1.82:1**. On an 8px key swatch and a 2px tick that is one
     colour twice.

     Re-pointed to --data-4 (#767676): **4.33:1** against the subject's mark,
     while still reading 4.54:1 on white and 3.65:1 on the page tone, so it is a
     visible mark in its own right rather than a faded one.

     NO NEW HUE, and that is not a dodge. The one-hue rule is Christian's and
     --error is error-only, so "a different colour" inside this system means a
     different TONE plus a second channel -- which the design system already
     requires of data marks: "tone is NEVER the only channel: dash or hatch plus
     a direct label, always". The second channel is the DASH, and it is already
     this product's word for "team": the `Team avg` toggle draws one, and
     `.ratio-avg` draws one. The sprint tick and its key were the only places
     representing the comparator as a solid, which is why they were the only
     places it could be confused with the subject.

     One re-alias moves the comparator everywhere at once -- charts, ratio bars,
     sprint ticks and the toggle -- so they cannot disagree. */
  --data-avg:var(--data-4);
  /* [D152] PROGRESS TRACKS ARE NOT SUNKEN ANY MORE. Ziyad, 2026-08-25: "make
     it slightly more visible. (goes for every other bar not just this one)".
     --surface-sunken is #F6F6F8, which is 1.03:1 on a white card -- so an
     empty bar was not a bar at all, it was nothing, and a poll at 0 of 393
     showed no track to be at 0 OF. --border-on-page reads 1.55:1 on white,
     1.44:1 on sunken and 1.25:1 on the page tone, so the track is visible on
     all three grounds a bar is drawn on, and the black fill still measures
     12.68:1 against it. One re-alias moves every bar in the product. */
  --track:var(--border-on-page);
  --ring:var(--surface-base);
  --scroll-thumb:var(--border-resting);
  --error-border:var(--error);

  /* legacy type aliases, re-pointed at the named styles */
  --fs-xs:var(--fs-caption);          /* 12 -> 14 [D143] */
  --fs-sm:var(--fs-body);             /* 13 -> 15 */
  --fs-md:var(--fs-question);         /* 14 -> 16 */
  --fs-lg:var(--fs-heading);          /* 16 -> 17 */
  --fs-xl:var(--fs-title);            /* 20 */
  --dur:var(--dur-base);
}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms !important;animation-iteration-count:1 !important;
    transition-duration:.01ms !important;scroll-behavior:auto !important;
  }
}

*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;border:0;background:none;font:inherit;color:inherit;line-height:1;letter-spacing:normal;text-align:left;}
html{color-scheme:light;background:var(--bg);-webkit-text-size-adjust:100%;text-size-adjust:100%;}
html,body{min-height:100%;}
body{
  background:var(--bg);color:var(--ink);
  font-family:Inter,"Inter Variable","Segoe UI",Roboto,-apple-system,BlinkMacSystemFont,
              "Helvetica Neue",Arial,ui-sans-serif,system-ui,sans-serif;
  font-size:var(--fs-sm);line-height:1;-webkit-font-smoothing:antialiased;
}
svg{display:block;flex:none;}
/* Card-shaped links must not carry the browser's underline — it lands on every
   line of the card and reads as broken. */
a{text-decoration:none;color:inherit;}
button{cursor:pointer;-webkit-appearance:none;appearance:none;}
textarea{-webkit-appearance:none;appearance:none;resize:none;}
p,h1,h2,h3,h4{font:inherit;}
.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;}

/* [D20] the fixed 1229px canvas cannot hold 12px type. Fluid, with a ceiling. */
.canvas{width:min(1560px,100% - 36px);margin:0 auto;}

/* ------------------------------------------------------------------------- *
   SELECTION — two treatments, deliberately different                    [D1]
   ANSWERS invert to white: "the whole little element turns white and the text
   turns black." EVIDENCE CONTROLS only outline — filling five window chips
   plus a toggle competes with the answers for attention.
 * ------------------------------------------------------------------------- */
.opt.sel,.opt-s.sel,.recommendation-option.sel{
  background:var(--surface-inverse);color:var(--ink-inverse);
  border-color:var(--border-selected);
  box-shadow:inset 0 0 0 .5px var(--border-selected);
}
.tf-opt.sel,.team-toggle.sel{
  background:var(--control);color:var(--ink);border-color:var(--ink);
}

.opt,.opt-s,.recommendation-option,.tf-opt,.team-toggle,.nav-btn{
  transition:background-color var(--dur-fast) var(--ease),
             color var(--dur-fast) var(--ease),
             border-color var(--dur-fast) var(--ease),
             transform var(--dur-fast) var(--ease);
}
.opt:active,.opt-s:active,.recommendation-option:active,.tf-opt:active,.nav-btn:active{transform:translateY(1px);}

:focus-visible{outline:2px solid var(--ink);outline-offset:2px;}

/* --------------------------------------------------------------- header --- */

.header{height:52px;padding:0 32px;display:flex;align-items:center;background:var(--bg);}
/* The lockup is an <a> so it can take you home (see shellHeader/navRail in
   app.js). It must not LOOK like body copy that has been linked: no underline,
   no link colour, and a focus ring that is visible for keyboard users. */
.brand{display:flex;align-items:center;gap:8px;color:var(--ink-primary);flex:none;
  text-decoration:none;border-radius:4px;}
.brand:hover{opacity:.72;}
.brand:focus-visible,.rail-brand:focus-visible{outline:2px solid var(--ink-primary);outline-offset:3px;}
/* [D112] THE WORDMARK CENTRES ON THE MARK'S INK, NOT ON ITS BOUNDING BOX.

   D106 corrected the glyph-versus-box error and Ziyad said it was still wrong,
   which it was: that fix was worth 1px and the real error is nearly three
   times bigger and comes from the MARK.

   The mark is three ascending bars sharing a bottom edge -- 24, 38 and 31 tall
   in a 38-unit box. Its ink is therefore bottom-heavy: the area centroid sits
   at y = 21.97, while the bounding-box centre is at y = 19. That is 2.97 of 38,
   so the mark's visual mass lies 7.82% of its own height BELOW the box centre
   that flexbox aligns to. Centre the text on the box and the text reads high
   against the bars, at every size, which is exactly what was on screen.

   Each lockup therefore shifts its wordmark DOWN by 7.82% of ITS OWN mark
   height, and keeps the -0.05em glyph correction from D106. The calc is left
   unfolded so the derivation stays legible next to the number. */
.brand-word{
  font-size:var(--fs-caption);font-weight:700;letter-spacing:.11em;
  text-transform:uppercase;white-space:nowrap;line-height:1;
  transform:translateY(calc(17px * .0782 - .05em));   /* mark is 17 tall here */
}
/* [D95] The label was crowding the lockup. Design system inline spacing: a
   divider gets the 24 step either side, not 22/26 by eye. */
.header-divider{width:1px;height:20px;margin:0 var(--sp-6);background:var(--hair);}
.page-label{font-size:var(--fs-xs);font-weight:600;letter-spacing:.08em;color:var(--ink-2);}
.header-right{margin-left:auto;display:flex;align-items:center;gap:14px;}
.progress-label{font-size:var(--fs-xs);color:var(--muted);}
/* [D50 EXCEPTION] Progress/ratio BARS keep fully-rounded ends. Christian's
   instruction was "never make a full circle on something" — a circle is a 1:1
   element, and these are stadiums 4-8px tall. Reference image 2 specifies its
   own progress bar as 6px tall with a 3px radius, i.e. fully-rounded ends, and
   he generated that reference. Squaring these would deviate FROM the reference
   to satisfy a rule aimed at something else. Circles proper — avatars, the
   info glyph, the header icon, the slider thumb — are all squared above. */
.progress-track{width:180px;height:4px;border-radius:var(--r-track);background:var(--track);overflow:hidden;}
.progress-fill{display:block;height:100%;border-radius:var(--r-track);background:var(--data);transition:width var(--dur) var(--ease);}
.progress-counter{font-size:var(--fs-xs);font-weight:500;color:var(--ink-2);min-width:64px;}
/* [D7] "Confidential" badge removed. */

/* ----------------------------------------------------------------- body --- */

/* [D84] Air under the header. Every panel used to start flush against the
   topbar, so the whole page read as one welded block. */
/* [D150] THE BALLOT IS 30 / 70, AND IT CANNOT DRIFT.

   Ziyad, 2026-08-25: "The stats panel + the questions panel = 100% then stats
   panel should be 30% and the questions panel should be 70%. That's the
   breakdown, don't let it drift. Currently stats panel look too small."

   It was drifting because it was never a ratio in the first place: the rail was
   a FIXED 320px (`--w-rail`) and the question column took whatever was left, so
   the split was a different number at every viewport -- 23.67% at 1440, wider
   at 1280, and nobody had chosen either. A fixed width cannot hold a
   proportion.

   `grid-template-columns:30fr 70fr` states the proportion itself, and `fr`
   resolves AFTER the gap is subtracted, so the 12px gutter does not eat into
   one side. There is no width left to fall out of step. A test asserts 30% at
   both widths, because "don't let it drift" is a promise that needs an
   assertion rather than a comment. */
.body{
  display:grid;grid-template-columns:30fr 70fr;align-items:stretch;
  gap:var(--sp-3);padding:var(--sp-5) 20px 18px;height:calc(100vh - 70px);
}
.body.no-rail{grid-template-columns:minmax(0,1fr);}

/* [D89] NOTHING SCROLLS SIDEWAYS. Ziyad, 2026-08-22: "Dont use horizontal
   scroll for stats panel anywhere. Keep everything fixed in its space."
   `overflow-y:auto` on its own computes overflow-x to `auto` as well, so the
   rail had a live horizontal scrollbar (scrollWidth 337 against a 318 client
   width). Stated explicitly, and min-width:0 lets the children shrink
   instead of forcing the overflow in the first place. */

/* [D148] THE SCROLL MOVES INSIDE THE CARD, on both sides of the ballot.

   Ziyad, 2026-08-25: "someplaces the scroll is inside the card (for eg in
   stats panel) and in some places it's outside (for eg the questions panel),
   keep it consistent and keep it inside."

   The rail already scrolled inside itself. The question column did not: the
   scroller was `.right-column`, an unpainted box OUTSIDE the panel's border,
   so its scrollbar ran down the page tone beside the card. The scroller is now
   `.main-panel` -- the card itself -- which puts the bar inside the border on
   both sides.

   THIS ALSO FIXES THE STICKY HEADER'S CORNER, and that is not a coincidence.
   See the .subject-bar note below: the leftover Ziyad photographed was the
   panel's own white body showing through the bar's rounded corner, and it was
   only ever visible because the panel scrolled past a bar that was pinned to
   something else. */
.sidebar,.main-panel{overflow-y:auto;overflow-x:hidden;min-width:0;}
.right-column{min-width:0;}

/* -------------------------------------------------------------- sidebar --- */

/* [D57] The rail is layer 1. It sits BEHIND the question column in the visual
   stack: same tone as the well, a container edge rather than a card edge, and
   no elevation. It is reference material, not the thing you act on. */
.sidebar{
  /* [D150] No fixed width and no flex:none -- the 30fr grid column IS the
     width now. --w-rail is kept as a MINIMUM so a narrow viewport cannot
     squeeze the rail below the point where its KPI pairs stop fitting. */
  position:relative;min-width:var(--w-rail);display:flex;flex-direction:column;
  padding:22px 18px 18px;
  /* [FB2 2026-08-21] Background ONLY, reverted to the original white. D57 had
     put the rail on rung 1 of the ladder; the designer wants the original card
     colour back while keeping the layout and depth work. Nothing else on this
     rule changes: width, padding, border and radius are untouched. */
  border:1px solid var(--hair);border-radius:10px;background:var(--fill);
}
/* [2026-08-11] "make the picture larger, keep it as a square shape, but make
   it larger, and then you need to put their information beside the picture" */
.profile{display:flex;align-items:center;gap:16px;}
.avatar{width:104px;height:104px;flex:none;border-radius:6px;object-fit:cover;object-position:50% 30%;display:block;background:var(--control);}
.profile-id{min-width:0;}
.profile-name{font-size:var(--fs-xl);font-weight:600;line-height:25px;color:var(--ink);}
.profile-meta{margin-top:5px;font-size:var(--fs-sm);line-height:18px;color:var(--ink-2);}
.profile-meta + .profile-meta{color:var(--muted);}

.detail-list{margin-top:14px;border-top:1px solid var(--line);}
.detail-row{min-height:36px;display:flex;align-items:center;gap:10px;padding:6px 0;}
.detail-icon{color:var(--muted);flex:none;}
.detail-label{font-size:var(--fs-sm);color:var(--muted);}
.detail-values{margin-left:auto;display:flex;align-items:baseline;gap:8px;min-width:0;}
/* [D41] `text-overflow:ellipsis` REQUIRES `overflow:hidden`, and the global
   reset sets `line-height:1` — so the line box was exactly font-size tall and
   the hidden overflow sheared the descenders off p/q/y/g/j. Visible on every
   person and department step ("Africa/Lagos", "Praise Obah", "Ziyad Khan").
   Measured before the fix: 99 clipped nodes across 27 steps, and this class was
   the ONLY offender in the whole stylesheet. The row is `min-height:36px` with
   `align-items:center`, so a taller line box costs no layout. */
.detail-value{font-size:var(--fs-sm);line-height:18px;color:var(--ink-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.detail-value-2{font-size:var(--fs-xs);color:var(--muted);white-space:nowrap;}

/* [D6] no summary block  ·  [D7] no privacy badge */

/* ------------------------------------------------- rail controls [D12/D15] --- */

.rail-controls{
  position:sticky;top:0;z-index:5;margin-top:16px;padding:12px 0;
  background:var(--sidebar-bg);border-top:1px solid var(--line);border-bottom:1px solid var(--line);
}
.control-label{font-size:var(--fs-xs);letter-spacing:.07em;text-transform:uppercase;color:var(--muted);}
/* Window chips and the team toggle sit on ONE row — "the compare to team
   average button is too long. That should just be like a toggle button and it
   shouldn't take up the full width of the space." */
.rail-controls-row{display:flex;align-items:center;gap:8px;}
.timeframe{display:flex;gap:5px;flex:1;}
.tf-opt{
  flex:1;height:32px;display:flex;align-items:center;justify-content:center;
  border:1px solid var(--hair);border-radius:5px;background:var(--control);
  font-size:var(--fs-xs);font-weight:500;color:var(--ink-2);
}
.tf-opt:hover:not(.sel){border-color:var(--line-hover);color:var(--ink);}

.team-toggle{
  flex:none;height:32px;padding:0 11px;display:flex;align-items:center;gap:7px;
  border:1px solid var(--hair);border-radius:5px;background:var(--control);
  font-size:var(--fs-xs);color:var(--ink-2);white-space:nowrap;
}
.team-toggle:hover:not(.sel){border-color:var(--line-hover);}
.toggle-dash{width:16px;height:0;border-top:1px dashed var(--muted);flex:none;}
.team-toggle.sel .toggle-dash{border-top-color:var(--ink);}

/* ------------------------------------------------------------ rail cards --- */

.card{padding:13px;border:1px solid var(--line);border-radius:7px;background:var(--raised);margin-top:10px;}
.card-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;}
.card-title{font-size:var(--fs-md);font-weight:600;line-height:17px;color:var(--ink);}
/* [D13] computed from the active window, never hardcoded. */
.card-meta{font-size:var(--fs-xs);line-height:15px;color:var(--muted);text-align:right;white-space:nowrap;}

.kpi-grid{margin-top:10px;display:grid;grid-template-columns:1fr 1fr;gap:8px;}
.kpi{padding:10px 11px;border-radius:5px;background:var(--control);}
.kpi-value{font-size:var(--fs-xl);font-weight:600;line-height:23px;color:var(--ink);}
.kpi-label{margin-top:4px;font-size:var(--fs-xs);line-height:15px;color:var(--muted);}
.kpi-delta{margin-top:4px;font-size:var(--fs-xs);line-height:15px;color:var(--ink-2);}
.kpi-delta.up::before{content:"\25B2\00a0";}
.kpi-delta.down::before{content:"\25BC\00a0";}

/* ------------------------------------------------------------- charts --- */

.chart-wrap{position:relative;margin-top:10px;}
.chart{width:100%;overflow:visible;}

/* Gridlines sit behind everything and stay faint — reference, not content. */
.grid-line{stroke:var(--grid);stroke-width:1;}
/* SVG text. The viewBox is drawn ~1:1 against the rail width, so these are
   real rendered pixels and must clear the 12px floor like everything else. */
.axis-tick{fill:var(--muted);font-size:var(--fs-caption);}

.mark-bar{fill:var(--data);}
.mark-line{fill:none;stroke:var(--data);stroke-width:1.75;stroke-linejoin:round;stroke-linecap:round;}
.mark-area{fill:url(#areaFade);}
.mark-dot{fill:var(--data);}

/* [D15] The team reference stays dashed and behind — but it was drawn in the
   same grey family as the marks it sits under and simply disappeared against
   white bars. It now carries the one accent hue in the system. That does not
   break the monochrome rule: hue here distinguishes a REFERENCE from the
   person's own data, it never encodes identity. */
.mark-avg-halo{fill:none;stroke:var(--ring);stroke-width:5;stroke-linecap:round;stroke-opacity:.9;}
.mark-avg{fill:none;stroke:var(--data-avg);stroke-width:2;stroke-dasharray:6 4;stroke-linecap:round;}
.ratio-avg{border-left-color:var(--data-avg) !important;}
.toggle-dash{border-top-color:var(--data-avg);}
.team-toggle.sel{color:var(--ink);}
.team-toggle.sel .toggle-dash{border-top-color:var(--data-avg);}
.tooltip .tt-team{color:var(--data-avg);}

/* --- entry motion: bars grow, lines draw. Fires on every window change so
       the chart visibly rebuilds instead of silently swapping. --- */
@keyframes bar-grow{from{transform:scaleY(0);}to{transform:scaleY(1);}}
.animate .mark-bar{transform-box:fill-box;transform-origin:bottom;animation:bar-grow .34s var(--ease) both;}
@keyframes line-draw{from{stroke-dashoffset:var(--len);}to{stroke-dashoffset:0;}}
.animate .mark-line{stroke-dasharray:var(--len);animation:line-draw var(--dur-slow) var(--ease) both;}
@keyframes fade-in{from{opacity:0;}to{opacity:1;}}
.animate .mark-area,.animate .mark-avg,.animate .mark-avg-halo,.animate .mark-dot{animation:fade-in var(--dur-slow) var(--ease) both;}

/* Hover: an invisible full-height band per data point, so the pointer never
   has to find a 3px bar. */
.hit{fill:transparent;cursor:crosshair;}
.hit:hover{fill:rgba(255,255,255,.05);}
.hover-rule{stroke:var(--ink);stroke-width:1;opacity:.35;}

.tooltip{
  position:absolute;z-index:20;pointer-events:none;opacity:0;
  padding:8px 10px;border:1px solid var(--hair);border-radius:5px;
  background:var(--fill);box-shadow:0 4px 14px rgba(0,0,0,.10);
  font-size:var(--fs-xs);line-height:16px;color:var(--ink);white-space:nowrap;
  transition:opacity var(--dur-fast) var(--ease);
}
.tooltip.on{opacity:1;}
.tooltip b{font-weight:600;}
.tooltip .tt-team{color:var(--muted);}

.ratio-list{margin-top:10px;display:flex;flex-direction:column;gap:8px;}
.ratio-row{display:flex;align-items:center;gap:10px;font-size:var(--fs-sm);}
/* [D172] `nowrap` keeps the badge on the same line as the word it annotates.
   Without it the infotip can drop to a second line on a narrow card, which puts
   it 16px below its own label - measured, and caught by the D169 alignment
   test. It also keeps the row to one line (D171). */
.ratio-label{color:var(--ink-2);white-space:nowrap;}
.ratio-bar{margin-left:auto;position:relative;flex:0 1 120px;min-width:56px;height:6px;border-radius:var(--r-track);background:var(--track);overflow:visible;}
.ratio-fill{display:block;height:100%;border-radius:var(--r-track);background:var(--data);transition:width var(--dur) var(--ease);}
.ratio-avg{position:absolute;top:-3px;width:0;height:12px;border-left:1px dashed var(--muted);}
/* [D171] ONE LINE. Ziyad, 2026-08-27: "in the stats panel (for eg Attended
   section in Department Eval), it shows in 2 lines the '372 of 412'... Keep
   things in one line please in the stats panel."

   62px was measured against 12px type. At the 14px floor "372 of 412" needs
   about 72, so it wrapped -- and a value that wraps mid-phrase is worse than a
   narrower bar beside it. The value sizes to its content and refuses to wrap;
   the BAR gives up the width instead, which is the right thing to lose because
   its length is already redundant with the number next to it. */
.ratio-value{
  flex:none;text-align:right;white-space:nowrap;
  font-size:var(--fs-sm);line-height:var(--lh-body);color:var(--ink-2);
  font-variant-numeric:tabular-nums;
}

/* --- [D47] sprint delivery rates ------------------------------------------
   Replaces four KPI tiles carrying eight comparator numbers as prose. The
   DEPARTMENT tick is the prominent one and is listed first, because department
   is the primary comparator (D35) — completion rate is contaminated by recording
   convention, so team-first would read a Design person as failing. The team tick
   keeps the cyan already established for a reference series elsewhere in the
   build; it never encodes identity, so the monochrome rule holds. Both ticks
   carry a dark halo so they stay visible whether they land on the filled or the
   unfilled part of the track. */
.sprint-bars{margin-top:10px;display:flex;flex-direction:column;gap:14px;}
.sprint-bar-row{display:flex;flex-direction:column;gap:6px;}
.sb-head{display:flex;align-items:baseline;justify-content:space-between;gap:var(--sp-2);min-width:0;}
.sb-label{font-size:var(--fs-label);line-height:var(--lh-label);color:var(--ink-2);min-width:0;overflow-wrap:anywhere;}
.sb-value{font-size:var(--fs-body);font-weight:600;line-height:var(--lh-body);color:var(--ink);font-variant-numeric:tabular-nums;flex:none;}
.sb-track{position:relative;display:block;height:8px;border-radius:var(--r-track);background:var(--track);}
.sb-fill{display:block;height:100%;border-radius:var(--r-track);background:var(--data);
  transition:width var(--dur) var(--ease);}
/* [D170] The tick, and its HIT AREA. Ziyad: "hovering it is also too focused
   right now like i have to hover on the exact slim line so it's tough".

   It was 2px wide. The ink stays slim on purpose -- it marks a position on a
   bar, and a fat marker would lie about the precision -- so the target grows
   instead of the mark: a `::before` with negative insets gives roughly
   16 x 30px of pointer area around a 3px line. The tooltip is the element's own
   `title`, so enlarging the element is all that is needed.

   `.sb-track` is `overflow:visible`, which is what lets the target spill past
   the bar; if that ever becomes `hidden` the hit area silently shrinks back. */
.sb-tick{position:absolute;top:-3px;width:3px;height:14px;border-radius:1px;
  margin-left:-1.5px;box-shadow:0 0 0 1px var(--ring);}
.sb-tick::before{
  content:"";position:absolute;inset:-8px -7px;border-radius:var(--r-xs);
}
/* [D170] The DEPARTMENT comparator moves off --data-1 as well. It was the same
   ink as the bar's own fill, so on a person's screen the department tick
   disappeared into the filled part of the bar it was marking. --data-3 is
   2.97:1 from the fill and 1.53:1 from the team's --data-4 -- which would be
   too close on its own, and is why the two comparators also differ in SHAPE:
   department solid, team dashed. */
.sb-tick-dept{background:var(--data-3);}
/* [D170] The comparator is DASHED, not just a lighter solid. A repeating
   gradient rather than a border, because the tick is 14px tall and a dashed
   border would render two segments and a guess. */
.sb-tick-team{
  background:repeating-linear-gradient(
    to bottom, var(--data-avg) 0 3px, transparent 3px 5px);
}
/* [D48] ONE key for the whole card, replacing a written legend under every bar.
   The comparator VALUES moved to the tick tooltips — the key says which tick is
   which, the tick position says above or below, and the exact number is one
   hover away. Department is listed first because it is the primary comparator
   (D35). */
.sb-key-row{
  display:flex;flex-wrap:wrap;align-items:center;gap:4px 14px;
  margin-top:12px;padding-top:10px;border-top:1px solid var(--line);
}
.sb-leg{display:flex;align-items:center;gap:6px;font-size:var(--fs-xs);line-height:16px;color:var(--ink-2);}
.sb-leg-hint{margin-left:auto;color:var(--muted);font-style:italic;}
/* [D170] The key says which mark is which, so it has to BE the mark. A solid
   square for both was the legend telling you the two ticks look the same. */
.sb-key{width:8px;height:8px;flex:none;border-radius:2px;box-shadow:0 0 0 1px var(--ring);}
.sb-key-dept{background:var(--data-3);}
.sb-key-team{
  width:14px;height:0;border-radius:0;box-shadow:none;
  border-top:2px dashed var(--data-avg);
}

/* [D48] One dot per sprint, filed or not — replaces a comma-separated list of
   sprint labels ending in "+6 more", which was the densest text on the rail.
   Each dot names its own sprint on hover, so nothing is lost. */
.sb-dots{display:flex;flex-wrap:wrap;gap:4px;align-items:center;min-height:10px;}
.sb-dot{
  width:9px;height:9px;flex:none;border-radius:2px;
  border:1px solid var(--line-strong);background:transparent;
}
.sb-dot.is-filed{background:var(--data);border-color:var(--data);}

/* [D48] CSS-only infotip. Carries the caveat that used to be a paragraph. */
/* ===========================================================================
   [D169] THE GLYPH OUTGREW ITS BOX, AND THE BOX NEVER MOVED.

   Ziyad, 2026-08-27: "The info tip buttons, they don't look aligned right and
   the size also seems off."

   Both, and both measurable. D143 raised the type floor to 14px, which raised
   THIS glyph from 12 to 14 -- and left the box at 16x16. With a 1px border that
   is a 14px content box holding a 14px italic whose font box is **19px**:
   measured, the ink overflowed its own border by **2px at the top and 1px at
   the bottom**. The badge was not tight, it was too small for its contents.

   And it sat low. The box's centre measured **1.6px below** the neighbouring
   text's x-height centre, which is what `vertical-align:middle` is supposed to
   land on.

   20x20 holds a 14px glyph properly: an 18px content box against a 16.95px font
   box. `line-height` is set to the content box rather than to 1, so the font's
   own half-leading does the centring and the visible ink lands on the box's
   centre instead of riding its baseline. The remaining vertical-align
   correction is measured, not guessed -- see the note under it.
   =========================================================================== */
.infotip{
  display:inline-flex;align-items:center;justify-content:center;
  width:20px;height:20px;margin-left:var(--sp-2);
  /* A 1px OPTICAL RISE, and this is the wordmark and the tick again.
     `vertical-align:middle` lands the badge on the text's x-height centre,
     which is the correct GEOMETRIC place and reads low beside a capitalised
     label: the visual mass of "Measured to" sits between its baseline and its
     cap height, whose centre is higher than the x-height centre. Chosen by
     rendering middle, baseline, -1px and -1.5px at 4x device pixels and
     looking: middle and baseline read low, -1.5px reads high.
     NOTE -2px of margin buys 1px of rise, because vertical-align centres the
     MARGIN box and moving one edge shifts its midpoint by half.
     AND NOTE WHY THIS IS NOT A TRANSFORM: a transform makes an element a
     containing block for its absolutely positioned descendants, and the tip
     itself is one -- it would re-anchor to this 20px glyph and undo D104/D165.
     `position:relative` is out for the same reason. */
  vertical-align:middle;margin-top:-2px;
  border:1px solid var(--line-strong);border-radius:var(--r-xs);
  /* The 14px floor applies to the glyph too — it is real text, not an icon. */
  font-size:var(--fs-caption);line-height:18px;font-style:italic;font-weight:600;
  color:var(--ink-2);cursor:help;position:static;
}
/* [D104] Outside the rail the glyph is its own containing block, so the popover
   centres on it. Inside the rail it must NOT be: a 230px popover anchored to a
   16px glyph either overflows a 320 column or, capped at 100%, collapses to the
   width of the glyph. There, the card is the containing block instead. */
.q-copy .infotip,.q-title .infotip,.opt-s .infotip,.vote-block .infotip{position:relative;}
.infotip:hover,.infotip:focus{border-color:var(--ink);color:var(--ink);outline:none;}
.infotip::after{
  content:attr(data-tip);
  position:absolute;bottom:calc(100% + 8px);left:50%;transform:translateX(-50%);
  z-index:30;width:230px;padding:8px 10px;
  border:1px solid var(--hair);border-radius:5px;background:var(--fill);
  box-shadow:0 4px 14px rgba(0,0,0,.10);
  font-size:var(--fs-xs);line-height:16px;font-style:normal;font-weight:400;
  color:var(--ink);text-align:left;
  /* [D172] The tip ALWAYS wraps, whatever its ancestors say. `white-space`
     inherits, so a `nowrap` anywhere up the chain -- `.ratio-label` has one, to
     keep the badge on its label's line -- turned this paragraph into a single
     1346px line. The box does not grow (left and right are both set) so nothing
     looks wrong; the text just runs out of it and the rail gains a horizontal
     scroll. Caught by the D170 sideways-scroll assertion. */
  white-space:normal;
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity var(--dur-fast) var(--ease);
}
.infotip:hover::after,.infotip:focus::after{opacity:1;visibility:visible;}
/* The rail is a narrow column; a centred tip would clip on the left edge. */
/* [D104] The rail popover is anchored to its CARD, not to the 16px glyph.
   Anchoring to the glyph meant it either overflowed the 320 rail (and the rail
   grew a horizontal scrollbar) or, after being capped at 100%, collapsed to the
   width of the glyph itself -- 22px, which is what "looks broken" was.
   Spanning the card keeps it inside the rail from any anchor position. */

/* ===========================================================================
   [D165] ...BUT IT MUST SIT NEXT TO THE GLYPH, NOT ABOVE THE WHOLE CARD.

   Ziyad, 2026-08-26: "on the hover it does show the infotip correctly, just
   it's position doesn't seem accurate (it shows too above the infotip icon).
   Keep it close to the infotip icon please. This goes for all infotips."

   Measured: the "Boards filed" glyph sits at y=460 inside a `.sprint-bars`
   card running 355 to 493. The tip is positioned `bottom:calc(100% + 10px)`
   against that card, so it landed at y=345 -- **115px above the glyph it
   belongs to**, floating over unrelated rows. The `.rail-source` tip, whose
   host is a single-line paragraph, measured 24px above its glyph and looked
   fine. The fault was never the offset; it was that the ANCHOR was the whole
   card, so the further down a card a glyph sat, the further the tip flew.

   D104 chose the card for a good reason -- horizontal room -- and that reason
   still holds. The fix is to anchor to the glyph's own LINE instead: a
   line-level box spans exactly the same width as the card's content, so the
   tip keeps all the horizontal room D104 gave it, while `bottom:100%` now
   resolves against the row the glyph is actually on.

   These are ADDITIVE. `position:relative` resolves to the NEAREST positioned
   ancestor, so a glyph inside one of these lines anchors to the line, and any
   glyph without one falls back to the card exactly as before. A test measures
   the gap for every infotip on every ballot screen, so a new one in a new
   container cannot quietly go back to flying.
   =========================================================================== */
.sidebar .card,.sidebar .detail-list,.sidebar .sprint-bars,.sidebar .norecord,
.sidebar .rail-source{position:relative;}
.sidebar .sb-head,.sidebar .card-head,.sidebar .detail-row,.sidebar .kpi,
/* [D172] `.ratio-row` joins the list. A new infotip in a container that is not
   on it anchors to the whole CARD and flies back above it -- the D165 test
   caught this one 208px out, in the same session D165 shipped. If you add an
   infotip somewhere new, add its line here. */
.sidebar .norecord-rule,.sidebar .card-title,.sidebar .ratio-row{position:relative;}
.sidebar .infotip::after{
  left:8px;right:8px;width:auto;transform:none;
  bottom:calc(100% + 10px);
}

/* [D11] Same slot, same state, for anyone with no record. Never a silent gap. */
.no-record{
  margin-top:10px;padding:16px 12px;border:1px dashed var(--hair);border-radius:5px;
  font-size:var(--fs-sm);line-height:18px;color:var(--muted);text-align:center;
}
.rail-note{margin-top:10px;font-size:var(--fs-xs);line-height:17px;color:var(--muted);}
/* Incomplete-source warning. Uses --text rather than a hue: the monochrome
   rule exists so colour never encodes identity or judgement, and a missing
   export is neither. The left rule and weight carry the emphasis instead. */
.rail-note-warn{color:var(--text);border-left:2px solid var(--text);padding-left:9px;font-weight:500;}

/* --------------------------------------------------------- right column --- */

/* [D148] The column no longer scrolls; it lays the card and the action bar
   out and the CARD scrolls. min-height:0 on the panel is what lets it shrink
   inside the grid row instead of pushing the action bar off the bottom. */
.right-column{display:flex;flex-direction:column;min-height:0;}
.main-panel{flex:1;min-height:0;}

/* ===========================================================================
   [D149] ONE SCROLLBAR, AND IT IS PART OF THE DESIGN.

   Ziyad, 2026-08-25: "Use a different type of scroll, a thicker one especially
   the chevrons of the scroll, consistent with the design but some other
   variation." Confirmed the same day that "chevrons" means real arrow buttons
   at the two ends of the track.

   7px -> 13px, a --surface-sunken track, a --border-resting thumb at --r-track,
   and a chevron button at each end. Every value is a token; the two chevrons
   are the only inline SVG in this stylesheet and they are drawn in --ink-muted
   (#4A4A4F) to the same 1.6px stroke the icon set uses.

   THE ONE THING THAT WILL BREAK THIS IF SOMEBODY "TIDIES" IT: the standard
   `scrollbar-width` / `scrollbar-color` properties USED TO BE HERE, and in
   Chrome 121+ specifying either one makes the browser IGNORE every
   ::-webkit-scrollbar rule below. Not override -- ignore. The bar silently
   reverts to the thin default and the chevrons never appear, with no warning
   anywhere. They are deliberately absent. Do not put them back.
   =========================================================================== */
.sidebar::-webkit-scrollbar,
.main-panel::-webkit-scrollbar,
.doc-scroll::-webkit-scrollbar{width:13px;}

.sidebar::-webkit-scrollbar-track,
.main-panel::-webkit-scrollbar-track,
.doc-scroll::-webkit-scrollbar-track{
  background:var(--surface-sunken);
  border-left:1px solid var(--border-hairline);
}
.sidebar::-webkit-scrollbar-thumb,
.main-panel::-webkit-scrollbar-thumb,
.doc-scroll::-webkit-scrollbar-thumb{
  background:var(--scroll-thumb);
  border-radius:var(--r-track);
  border:3px solid var(--surface-sunken);   /* insets the thumb to 7px of ink */
  background-clip:padding-box;
  min-height:36px;
}
.sidebar::-webkit-scrollbar-thumb:hover,
.main-panel::-webkit-scrollbar-thumb:hover,
.doc-scroll::-webkit-scrollbar-thumb:hover{background:var(--border-hover);background-clip:padding-box;}

/* The chevrons. `display:block` is required -- the default is `none` and a
   button with no display never appears however it is coloured. */
.sidebar::-webkit-scrollbar-button,
.main-panel::-webkit-scrollbar-button,
.doc-scroll::-webkit-scrollbar-button{
  display:block;height:13px;
  background:var(--surface-sunken) center / 9px 9px no-repeat;
  border-left:1px solid var(--border-hairline);
}
.sidebar::-webkit-scrollbar-button:vertical:decrement,
.main-panel::-webkit-scrollbar-button:vertical:decrement,
.doc-scroll::-webkit-scrollbar-button:vertical:decrement{
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'><path d='M2.5 8L6 4.5L9.5 8' fill='none' stroke='%234A4A4F' stroke-width='1.6'/></svg>");
}
.sidebar::-webkit-scrollbar-button:vertical:increment,
.main-panel::-webkit-scrollbar-button:vertical:increment,
.doc-scroll::-webkit-scrollbar-button:vertical:increment{
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'><path d='M2.5 4.5L6 8L9.5 4.5' fill='none' stroke='%234A4A4F' stroke-width='1.6'/></svg>");
}
/* Chrome draws a SECOND pair of buttons at the far ends unless they are turned
   off explicitly. Two up-chevrons stacked is not a control anybody wants. */
.sidebar::-webkit-scrollbar-button:vertical:start:increment,
.sidebar::-webkit-scrollbar-button:vertical:end:decrement,
.main-panel::-webkit-scrollbar-button:vertical:start:increment,
.main-panel::-webkit-scrollbar-button:vertical:end:decrement,
.doc-scroll::-webkit-scrollbar-button:vertical:start:increment,
.doc-scroll::-webkit-scrollbar-button:vertical:end:decrement{display:none;}
.sidebar::-webkit-scrollbar-button:hover,
.main-panel::-webkit-scrollbar-button:hover,
.doc-scroll::-webkit-scrollbar-button:hover{background-color:var(--surface-interactive);}
/* [D57] The main well is layer 1 too, but it is what the cards sit ON, so the
   cards above it must read as lifted. Its own edge is a container hairline. */
/* [D85 REVERTED 2026-08-22] The panel keeps its full column width. Narrowing
   it to the 720 ballot measure did reduce the option-row aspect ratio, but it
   shrank every question section on every screen, which Ziyad did not want:
   "You shrinked the box altogether." The radius is the design system's 10px
   and stays; the width goes back. */
.main-panel{
  padding:0 28px 20px;border:1px solid var(--hair);border-radius:var(--r-md);
  background:var(--surface);
}

.poll-header{min-height:92px;display:flex;align-items:center;gap:16px;padding:20px 0;}
.poll-header-icon{
  width:44px;height:44px;flex:none;display:flex;align-items:center;justify-content:center;
  border:1px solid var(--line-strong);border-radius:8px;color:var(--ink);
}
/* [D42] `.poll-header-avatar` removed 2026-08-17 — the subject's face is already
   on the rail at 104px and this was the same person twice on one screen. The
   identity text beside it is retained; see app.js `viewPoll`. */
.poll-title{font-size:var(--fs-xl);font-weight:600;line-height:24px;color:var(--ink);}
.poll-subtitle{margin-top:5px;font-size:var(--fs-sm);line-height:17px;color:var(--muted);}
.poll-step{margin-left:auto;font-size:var(--fs-sm);color:var(--muted);white-space:nowrap;}

/* ------------------------------------------------------- section headers --- */

/* [D2] the category is a group header shown ONCE, not the heading of every card. */
/* [D57] A section label is the QUIETEST text on the screen: it orients, it is
   not read. Small, uppercase, tracked, muted. It was previously the same
   weight as a question title, which flattened the two together. */
/* [D101] The group label: 12px uppercase overline with a hairline ABOVE it,
   56px clear of the group before. */
.section-head{
  display:flex;align-items:center;gap:var(--sp-3);
  margin:var(--sp-14, 56px) 0 var(--sp-5);padding-top:var(--sp-5);
  border-top:1px solid var(--border-hairline);
}
.section-head:first-child{margin-top:0;padding-top:0;border-top:0;}
/* [D115] Ziyad, 2026-08-23: the gap between a group overline and the first
   question under it "is too much". It measured 60px, because the head's own
   20px bottom margin stacked on top of the list gap and the card's 32 step.
   The head's margin goes.

   [D122] 40 -- matching the question-to-question step -- was still too much:
   an overline BELONGS TO the question under it, so it should read as attached
   to that question rather than floating equidistant between two of them.
   Rendered at 30, 20 and 16 and compared; Ziyad chose 20. The value is set
   ONCE, here, so every group overline on every screen carries the same gap. */
.question-list > .section-head{margin-bottom:0;}
.question-list > .section-head + .q-card{margin-top:calc(var(--sub-gap, 20px) - 8px);}
.section-title{font-size:var(--fs-overline);line-height:var(--lh-overline);font-weight:600;letter-spacing:var(--tr-overline);text-transform:uppercase;color:var(--ink-muted);white-space:nowrap;}
.section-rule{flex:1;height:1px;background:var(--line);}
.section-note{font-size:var(--fs-xs);color:var(--muted);white-space:nowrap;}

/* -------------------------------------------------------- question cards --- */

.question-list{display:flex;flex-direction:column;gap:8px;}

/* [D28] EVERY card is stacked: question on top, answers full-width below.
   The inline layout gave the answer column only ~310px, so a five-option row
   was 62px per tile — one-word options sat next to three-line ones and the
   tiles came out ragged. Below the question there is ~700px, which is enough
   for equal tiles that hold their label on one or two lines. */
/* [D57] A question is layer 2: lighter than the well it sits on, a defined
   edge, and generous internal padding so the question and its answers read as
   ONE unit rather than as a heading floating above some buttons. Elevation is
   a 1px border plus a 2px near-invisible shadow, not a drop shadow. */
/* [D101] NO CARD AROUND A QUESTION. Design system section 12: groups are
   "3 to 5 questions under a 12px uppercase overline naming what the group asks
   about, hairline above the overline, NO CARDS AND NO BOXES -- the questions
   are already bounded by their option rows and a second frame doubles the
   borders."

   With the frame gone, SPACING carries the hierarchy, and the system fixes the
   four values: "12px between option rows, 32px between questions, 56px between
   groups, 80px before the verdict. Those four values are the only vertical
   gaps on the screen, and the 56/32 ratio is what makes a group read as a
   group without a container." */
.q-card{
  display:grid;grid-template-columns:56px 1fr;align-items:start;
  padding:0;margin-top:var(--sp-8);        /* 32 between questions */
  border:0;border-radius:0;background:none;box-shadow:none;
}
.q-card:first-child{margin-top:0;}
.q-card[hidden]{display:none;}

@keyframes card-enter{
  from{opacity:0;transform:translateY(10px);}
  to{opacity:1;transform:none;}
}
.q-card.enter,.section-head.enter{animation:card-enter var(--dur-slow) var(--ease) both;}

.q-num{
  width:38px;height:38px;margin-left:18px;flex:none;
  display:flex;align-items:center;justify-content:center;
  border-radius:5px;background:var(--control);
  font-size:var(--fs-md);font-weight:500;color:var(--ink-2);
}
/* The chip is 38px tall and the question's first line is 19px. Half the
   difference centres that first line against the chip — without it the text
   sits high and reads as misaligned. Multi-line questions align to their
   first line, which is what you want. */
.q-copy{padding-top:9px;padding-right:24px;}
/* [D2] the question sentence takes the primary slot. */
/* [D57] The question is the primary thing on the card and now reads that way:
   one step up the scale, tighter tracking, full ink. Options are secondary
   text at --fs-sm. Previously both sat at the same size and weight, which is
   most of why the screen read flat. */
.q-title{font-size:var(--fs-lg);font-weight:650;line-height:23px;letter-spacing:-.15px;color:var(--ink);}
.q-sub{margin-top:6px;font-size:var(--fs-sm);line-height:17px;color:var(--muted);}

/* Answers are row 2 of column 2 — placed explicitly, or they fall into the
   number gutter and the card balloons. */
.q-answer{grid-column:2;margin-top:16px;padding-right:24px;}

/* --- tiles: scale / choice / binary --------------------------------------- */

/* [D19] BASELINE BUG: .rating-group was repeat(5,1fr) with six scale entries,
   so N/A wrapped and broke the card. Columns follow the option count. */
.opt-row{display:grid;gap:10px;align-items:stretch;}
.opt-row.n6{grid-template-columns:repeat(6,1fr);}
.opt-row.n5{grid-template-columns:repeat(5,1fr);}
.opt-row.n4{grid-template-columns:repeat(4,1fr);}
.opt-row.n3{grid-template-columns:repeat(3,1fr);}
.opt-row.n2{grid-template-columns:repeat(2,1fr);}

/* stretch makes every tile the height of the tallest, so a wrapping label
   never leaves its neighbours short. */
.opt-cell{display:flex;flex-direction:column;align-items:stretch;}
/* [D57] A resting option now has a VISIBLE edge. It previously had
   `border:1px solid transparent`, so nothing on screen said it was a control
   until you hovered it. That is the single biggest reason the answers read as
   flat text blocks. */
.opt{
  width:100%;flex:1;min-height:48px;display:flex;align-items:center;justify-content:center;
  /* [FB2 2026-08-21] The fill is REMOVED, not swapped: "Do not replace the
     removed background with another background, gradient, shadow, or
     decorative treatment." The border, radius, padding and every interaction
     state stay exactly as they were. */
  /* [D123] Same corner as .opt-s: these are the same control family and a
     ballot with two different answer-row radii is worse than either value. */
  padding:10px 12px;border:1px solid var(--border-resting);border-radius:var(--r-sm);background:none;
  font-size:var(--fs-md);font-weight:500;color:var(--ink);text-align:center;line-height:17px;
  transition:border-color var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease),
             transform var(--dur-fast) var(--ease);
}
.opt-row.words .opt{
  font-size:var(--fs-body);font-weight:400;
  /* [D72] With a control present the label can no longer be centred: the
     control anchors left and the text reads from it. */
  justify-content:flex-start;gap:10px;text-align:left;
}
.opt-row.words .opt .opt-text{min-width:0;}
/* [D156] Same two fixes on the n-across variant. */
.opt.sel .opt-ctl{background:transparent;border-color:var(--ink-inverse);border-width:2px;}
.opt.sel .opt-ctl.dot::after{
  content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:8px;height:8px;border-radius:50%;background:var(--ink-inverse);
}
/* Hover comes FORWARD: darker edge, a 1px lift, and a faint ring. Focus gets
   the same treatment plus a real outline, so keyboard and mouse read alike. */
/* ===========================================================================
   [D157] `:not(.sel)` — AND THIS IS THE "STRETCHED" BUG.

   Ziyad has reported the answer controls as looking "stretched" or "like
   ovals" in three separate rounds (D121, D123, and again 2026-08-25), each
   time saying it shows on the SELECTED ones and shows when you zoom in. Twice
   the box was narrowed and twice reverted; once the corner went 10 -> 6. None
   of it was the cause, and he said so at the time: *"It's not due to the
   box."*

   `.opt:hover` and `.opt.sel` carry the SAME specificity (0,2,0) and hover is
   declared later, so **hover wins over selected**. Measured on a freshly
   clicked row:

     pointer still on it   border rgb(110,110,118)  drop shadow  translateY(-1px)
     pointer moved away    border rgb(11,11,12)     inset ring   none

   So a selected row is a BLACK fill wearing a GREY 1px outline, a drop shadow,
   and a 1px lift. The grey outline traces the rounded rectangle away from the
   fill it is supposed to bound, and on a 12.17:1 row that outline is the whole
   shape — which is exactly what reads as a stretched lozenge.

   AND IT IS ALWAYS ON, because you select a row by clicking it and the pointer
   is still there afterwards. Every previous investigation of this moved the
   mouse away, or measured from a script, and saw the correct black-on-black
   state. That is why it survived three rounds.

   The diagnosis already existed in this file — `.recommendation-option` was
   fixed with `:not(.sel)` and carries a comment explaining precisely this —
   but the fix was never carried to the other three families. It is now.

   Stated as a rule: HOVER IS A PREVIEW OF SELECTABILITY. A control that is
   already selected has nothing to preview, so it does not take a hover state.
   =========================================================================== */
.opt:hover:not(.sel){border-color:var(--line-hover);transform:translateY(-1px);
  box-shadow:0 2px 4px rgba(16,24,40,.06);}
.opt:focus-visible,.opt-s:focus-visible{outline:2px solid var(--focus);outline-offset:2px;}
.opt-label{margin-top:8px;font-size:var(--fs-xs);line-height:15px;text-align:center;color:var(--muted);}

/* [D57] The horizontal row is grouped the same way as the stack, so a scale
   and a list read as the same kind of control. */
/* [D75] NO FILL ON THE GROUP. Ziyad, 2026-08-22: "Options currently have a bg
   color as sunken, that's not right, remove that bg color please, looks bad."
   The design system agrees on the reason: sunken is STRUCTURAL and "can never
   say you chose this". The option rows already bound the question; a tinted
   tray behind them is a second frame doing nothing. */
.opt-row{padding:0;border:0;border-radius:0;background:none;}

/* A selected tile pops once. Short enough to feel like a response, not a show. */
@keyframes pick{0%{transform:scale(1);}45%{transform:scale(1.035);}100%{transform:scale(1);}}
.opt.sel,.opt-s.sel,.recommendation-option.sel{animation:pick .24s var(--ease);}

/* --- stacked lists: choice-stacked / multi -------------------------------- */

/* [D57] Ziyad: "Answer options should feel like a single grouped interaction
   rather than six unrelated floating boxes."

   The options now sit INSIDE a shared well, recessed into the card. The well
   is the grouping; the individual options are white and lifted within it. That
   reads as one control with several states rather than several controls.

   A shared-border list group was the other candidate and was rejected: the
   selected state animates with a 1.035 scale (see @keyframes pick), which
   `overflow:hidden` would clip, and removing the animation to gain the border
   trick would cost the clearest feedback the poll has. */
/* [D114] THE OPTION ROWS WERE STRETCHED. Ziyad, 2026-08-23: they "still look
   stretched to the right (both selected and unselected)".

   At the full 720 ballot measure a 44px row is 16.4:1, and the words end after
   a fifth of it -- "Always" is 60px of a 720px control, so the rest is a long
   empty runway with a rounded corner at the far end.

   D85 tried to fix this by narrowing the whole panel and was REVERTED: "You
   shrinked the box altogether." The right target was never the panel, it is
   the control. The question text and the panel keep the full measure; only the
   answer rows narrow. 560 holds the longest option in the poll (58 characters,
   ~496px including the control and padding) on one line with room to spare. */
/* [D121 REVERTED 2026-08-23] The stack briefly sized to its own longest option
   (max-content, 320-560). Ziyad: "Don't clip the options box, that's not right.
   The length was nice previously." The 560 measure from D114 stands, and the
   stretched control he is describing is NOT the box -- see D123. */
.opt-stack{
  display:flex;flex-direction:column;gap:var(--sp-3);max-width:var(--w-answer);
  padding:0;border:0;border-radius:0;background:none;
}
/* [D123] THE ANSWER ROW'S CORNER, 10 -> 6. Ziyad, 2026-08-23: "the radii
   buttons specifically look stretched and they'll be visible when you zoom on
   them clearly. It's not due to the box" -- the width is fine, the SHAPE is
   not.

   Zoomed to 10x, both ends of a 560x44 row are geometrically correct quarter
   arcs at 10px. The problem is proportional, not a rendering fault: a 12.7:1
   rectangle with a corner that generous reads as a stretched lozenge -- a pill
   pulled sideways -- rather than as a rectangle with soft corners. The longer
   the row, the more the eye reads the ends as the shape.

   Rendered at 10, 8, 6 and 4 and compared at 3x. 4 is hard-edged; 6 is the
   point where the row reads as a rectangle again while staying soft. It is
   --r-sm, already a token, and it is the same corner the badges and the doc
   buttons use, so the ballot gets no bespoke value.

   This also sits better with the standing shape rule: the further a control is
   from a stadium, the further it is from the full-radius pill Christian ruled
   out. */
.opt-s{
  min-height:44px;padding:12px 14px;display:flex;align-items:center;gap:10px;
  /* [FB2 2026-08-21] Fill removed, same as `.opt`. */
  border:1px solid var(--border-resting);border-radius:var(--r-sm);background:var(--surface-base);
  font-size:var(--fs-body);line-height:var(--lh-body);color:var(--ink);text-align:left;width:100%;
  transition:border-color var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease);
}
.opt-s:hover:not(.sel){border-color:var(--line-hover);box-shadow:0 2px 4px rgba(16,24,40,.06);}
.opt-s[disabled]{opacity:.42;cursor:not-allowed;}
.cap-note{margin-bottom:8px;font-size:var(--fs-xs);color:var(--muted);}


/* ===========================================================================
   [D72] THE OPTION CONTROL IS DRAWN, NOT IMPLIED.

   Design system section 09, "Selection controls". Until now an option was a
   bare bordered button: no radio, no checkbox, nothing on screen to say whether
   the question took one answer or several. The system is explicit:

     "Checkbox: square, radius 6, 18px, resting border #6E6E76. Square means
      'choose any number'."
     "Radio: circular, 18px, resting border #6E6E76, exactly one -- the one
      deliberate circle in the system."

   That circle is the ONE exception to the no-full-circles rule, and the rule
   itself gives the reason: a square radio reads as a checkbox and changes what
   the control means.

   ON A SELECTED (inverted) ROW the control inverts with it. The system spells
   out the multi-select case: the box "takes a 1.75px white border and fills
   with an 8px white square at radius 2 -- the square counterpart of the radio's
   dot, so single- and multi-select read as the same gesture in two shapes and
   NO TICK IS NEEDED."

   So there is no checkmark glyph here, and that is the design system's own
   decision rather than an omission: the filled square IS the checked state.
   =========================================================================== */
/* ===========================================================================
   [D156] THE RADIO READS AS A CIRCLE, AND ITS DOT IS ACTUALLY CENTRED.

   Ziyad, 2026-08-25: "Unselected radii they look like ovals (could be just me),
   selected radii the selected thing in the middle is not centred and is acting
   weirdly."

   MEASURED FIRST, because the obvious reading has been wrong twice on this
   project. The control was a true circle: 18.00 x 18.00 at radius 50%, at both
   1280 and 1440. So "oval" was not geometry. What it was is WEIGHT -- a 1px
   ring on an 18px circle is two device pixels of ink around a large hole, and
   Chrome antialiases the left and right arcs heavier than the top and bottom,
   which is exactly what an ellipse looks like. The fix for a circle that reads
   as an oval is a thicker, more even ring, not a rounder box.

   THE SECOND HALF WAS REAL AND MEASURABLE. The selected ring was declared at
   `border-width:1.75px`. getComputedStyle reported it back as **1px**: a
   fractional border on a device-pixel grid snaps, and the dot was centred with
   `inset:0; margin:auto`, which resolves against the PADDING box -- a box whose
   size depends on that same snapping border. So the dot's offset was computed
   from a number the browser had already changed underneath it, and it landed
   half a pixel out. That is the "acting weirdly".

   Both halves are now integers: a 20px control with a 2px ring, and a dot
   centred with a transform rather than with margins, so it does not care what
   the border does. 20 - 2*2 = 16, less an 8px dot, leaves exactly 4px of ring
   gap all round -- every number in the control is a whole pixel.
   =========================================================================== */
.opt-ctl{
  /* `display:block` is not cosmetic. This is a SPAN, and width/height do not
     apply to a non-replaced INLINE element -- so the control is only 20x20
     because every one of its parents happens to be a flex container. Drop one
     of these into a plain block and it collapses to an inline box that the 50%
     radius draws as a tall thin ELLIPSE. Verified by cloning one out of its
     row: it rendered exactly that. Nothing in the shipped ballot does this
     today (a sweep of every screen found 0 controls off 20x20), and this line
     is what keeps it that way. */
  display:block;flex:none;width:20px;height:20px;position:relative;
  border:2px solid var(--border-hover);background:var(--surface-base);
  transition:border-color var(--dur-fast) var(--ease),
             background var(--dur-fast) var(--ease);
}
.opt-ctl.dot{border-radius:50%;}            /* the one deliberate circle */
.opt-ctl.box{border-radius:var(--r-sm);}    /* square: choose any number */
.opt-text{min-width:0;}

/* Selected: the row inverts, so the control inverts with it. */
/* [D156] 1.75 -> 2, and the dot centres by transform. The ring keeps the same
   weight it has at rest, so selecting a row changes its COLOUR and adds the
   dot -- it does not also change the ring's thickness, which was the thing
   that made the control jump. */
.opt-s.sel .opt-ctl{background:transparent;border-color:var(--ink-inverse);border-width:2px;}
.opt-s.sel .opt-ctl.dot::after{
  content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:8px;height:8px;border-radius:50%;background:var(--ink-inverse);
}
.opt-s.sel .opt-ctl.box::after{
  content:"";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:8px;height:8px;border-radius:var(--r-marker);background:var(--ink-inverse);
}
.opt-s[disabled] .opt-ctl{opacity:.4;}

/* --- roster picks --------------------------------------------------------- */

/* [D18] "who is the most efficient person on the design team? You would have
   to have the values for each of the design team members." A pick, not a
   rating — the result is a count of names, which is the most defensible form
   the removal signal can take. */
.roster-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;max-width:900px;}
.roster-tile{
  display:flex;flex-direction:column;align-items:flex-start;gap:7px;
  padding:11px;border:1px solid transparent;border-radius:5px;background:var(--fill);
  text-align:left;transition:background-color var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease),
    transform var(--dur-fast) var(--ease);
}
.roster-tile img{width:30px;height:30px;border-radius:4px;object-fit:cover;object-position:50% 30%;background:var(--control);}
.roster-name{font-size:var(--fs-sm);font-weight:500;line-height:16px;color:var(--ink);}
.roster-role{font-size:var(--fs-xs);line-height:15px;color:var(--muted);}
.roster-tile:hover:not(.sel){border-color:var(--line-hover);transform:translateY(-1px);}
.roster-tile:active{transform:translateY(1px);}
.roster-tile.sel{background:var(--ink);border-color:var(--ink);animation:pick .24s var(--ease);}
.roster-tile.sel .roster-name{color:var(--on-ink);}
.roster-tile.sel .roster-role{color:var(--on-ink-2);}

/* "No one" tile. Same size and selected treatment as a person - it is a real
   answer, not an opt-out, and styling it as lesser would discourage the honest
   response the option exists to capture. The dashed edge distinguishes it
   without demoting it. */
.roster-none{border-style:dashed;border-color:var(--line-hover);}
.roster-none-mark{
  display:flex;align-items:center;justify-content:center;
  width:30px;height:30px;border-radius:4px;background:var(--control);color:var(--ink);
}
.roster-none.sel .roster-none-mark{background:var(--on-ink);color:var(--ink);}
.opt-none{border-style:dashed;}

/* --- sum-to-100 allocation ------------------------------------------------ */

.alloc{max-width:720px;}
.alloc-head{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:10px;}
.alloc-total{font-size:var(--fs-sm);font-weight:600;color:var(--muted);}
.alloc-total.ok{color:var(--ink);}
.alloc-total.over{color:var(--error);}
/* [D45] Row is label · stepper · slider. The slider took the column the old
   read-only `.alloc-bar` occupied, so the grid keeps its shape. */
.alloc-row{display:grid;grid-template-columns:1fr auto 150px;align-items:center;gap:12px;margin-bottom:9px;}
.alloc-label{font-size:var(--fs-sm);line-height:17px;color:var(--ink);}

/* The − / + buttons sit OUTSIDE the field, framing it — "have the plus minus
   buttons outside of the current selector". */
.alloc-stepper{display:flex;align-items:center;gap:6px;}
.alloc-step{
  width:30px;height:30px;flex:none;display:flex;align-items:center;justify-content:center;
  border:1px solid var(--hair);border-radius:5px;background:var(--control);
  font-size:var(--fs-md);line-height:1;color:var(--ink);cursor:pointer;
  transition:background var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease);
}
.alloc-step:hover{background:var(--fill-hover);border-color:var(--focus);}
.alloc-step:focus-visible{outline:2px solid var(--line-strong);outline-offset:2px;}
.alloc-input{
  width:66px;height:40px;padding:0 9px;border:1px solid var(--hair);border-radius:5px;
  background:var(--control);font-size:var(--fs-md);color:var(--ink);text-align:right;
  transition:border-color var(--dur-fast) var(--ease);
}
.alloc-input:focus{outline:none;border-color:var(--focus);}
.alloc-pct{font-size:var(--fs-sm);color:var(--muted);}

/* Range styled explicitly in both engines — the native track is a system blue
   that would be the only saturated hue in the build and would read as meaning. */
.alloc-range{width:100%;height:20px;background:none;cursor:pointer;-webkit-appearance:none;appearance:none;}
.alloc-range:focus{outline:none;}
.alloc-range:focus-visible{outline:2px solid var(--line-strong);outline-offset:3px;border-radius:4px;}
.alloc-range::-webkit-slider-runnable-track{height:6px;border-radius:var(--r-track);background:var(--track);}
.alloc-range::-moz-range-track{height:6px;border-radius:var(--r-track);background:var(--track);}
.alloc-range::-webkit-slider-thumb{
  -webkit-appearance:none;appearance:none;width:16px;height:16px;margin-top:-5px;
  border-radius:4px;background:var(--data);border:1px solid var(--line-strong);
}
.alloc-range::-moz-range-thumb{
  width:16px;height:16px;border-radius:4px;background:var(--data);border:1px solid var(--line-strong);
}

/* --- department rail ------------------------------------------------------ */

.dept-mark{
  width:56px;height:56px;display:flex;align-items:center;justify-content:center;
  border:1px solid var(--hair);border-radius:6px;background:var(--control);color:var(--ink-2);
}
/* Direct reports — wraps, never truncates. Having them is what makes someone a
   Team Lead, so every name has to be visible however long the list gets. */
/* [D162] NO DIVIDER, and [D167] no block either.

   D162 removed the rule above this thing because it read as a different KIND of
   thing from the rows around it. It was still a different SHAPE: a 50px,
   two-line block with a 20px margin, sitting beside 36px rows with a 0px gap.
   Ziyad, 2026-08-27: "it's in two lines and has inconsistent spacing between
   the previous stat."

   `.reports-block`, `.reports-head` and `.reports-count` are gone with the
   markup they styled. The control is a `.detail-row` now and takes that rule's
   height, gap, label column and value slot -- so it cannot be inconsistent with
   its neighbours without them all changing together. */
/* ===========================================================================
   [D158] DIRECT REPORTS: FACES ON ONE LINE, NAMES ON HOVER.

   Christian, 2026-08-26: "pictures of the people in the direct reports as
   small icons in a single line if we can accommodate that", "which on hover
   shows the whole list".

   `.reports-list` / `.report-chip` / `.report-name` / `.report-tier` are gone
   with the markup they styled: a named chip per person, wrapping. At fifteen
   reports that was three stacked lines carrying one fact.

   `flex-wrap:nowrap` is load-bearing, not decoration -- it is the whole
   instruction. A test asserts every face shares one y, so this cannot quietly
   become two lines again if the rail narrows or the roster grows.
   =========================================================================== */
/* [D167] It sits in the row's value slot, so it is right-aligned by the
   `.detail-values` margin above it and carries no margin of its own. */
.reports-faces{
  position:relative;
  display:inline-flex;align-items:center;flex-wrap:nowrap;
  width:max-content;max-width:100%;
  border-radius:var(--r-sm);cursor:default;
}
/* The value slot centres its children; every other row aligns on the baseline
   of text, and this row's value is a strip of faces. */
.reports-row .detail-values{align-items:center;}
/* Square, like every other avatar in the product -- the no-circles rule has no
   exception for a small one. The 2px ring is what separates overlapping faces,
   and it is --surface-base because the rail is --surface-base: the ring is the
   rail showing through, not a border drawn on the photo. */
.report-face{
  width:22px;height:22px;flex:none;
  border-radius:var(--r-xs);object-fit:cover;object-position:50% 30%;
  background:var(--surface-sunken);
  box-shadow:0 0 0 2px var(--surface-base);
}
/* -6 on a 22px face is a 16px step. Twelve faces = 198px, which clears the
   narrowest the rail is allowed to be. See REPORTS_SHOWN in app.js. */
.report-face + .report-face{margin-left:-6px;}
.report-face-more{
  flex:none;margin-left:var(--sp-2);
  min-height:22px;display:inline-flex;align-items:center;padding:0 7px;
  border:1px solid var(--border-resting);border-radius:var(--r-xs);
  background:var(--surface-base);
  font-size:var(--fs-caption);font-weight:600;color:var(--ink-secondary);
  font-variant-numeric:tabular-nums;
}
.reports-faces:hover .report-face-more,
.reports-faces:focus-visible .report-face-more{border-color:var(--border-hover);color:var(--ink-primary);}
.reports-faces:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px;}

.member-list{margin-top:14px;display:flex;flex-direction:column;gap:2px;}
.member{display:flex;align-items:center;gap:10px;padding:7px 0;}
.member img{width:26px;height:26px;border-radius:4px;object-fit:cover;object-position:50% 30%;background:var(--control);}
.member-name{font-size:var(--fs-sm);color:var(--ink-2);}
.member-role{margin-left:auto;font-size:var(--fs-xs);color:var(--muted);}

/* Screens with no measurable evidence (leadership, nominations, policy) drop
   the rail entirely rather than padding it with numbers that do not bear on
   the question. The question column simply takes the full width. */
/* [D159] THE SECTION WIDTH IS A MEASURE AGAIN, NOT WHATEVER THE CONTENT WANTED.

   Ziyad, 2026-08-26: "The width of different sections of the team poll like
   'Nominations' section and 'Leadership Evaluation' section, keep the width
   consistent to 'Company Direction' section."

   Measured, the no-rail sections were all different: Leadership 665, Nominations
   740, Policy 740, Company Direction 974. **This was D150's doing, the day
   before.** `.body` used to be FLEX, where `.right-column{flex:1}` forced the
   column to fill regardless of anything else. D150 made `.body` a GRID to hold
   the 30/70 ratio, and in grid an AUTO MARGIN in the inline axis absorbs the
   free space -- which overrides `justify-items:stretch` and leaves the item
   sized to its CONTENT. So `margin:0 auto` silently turned a fixed 1060 measure
   into shrink-to-fit, and every section became as wide as its widest question
   happened to be.

   `justify-self:center` centres the column without that side effect, so the
   1060 measure holds and every section is the same width again. A test asserts
   all four no-rail sections match. */
.body.no-rail .right-column{width:100%;max-width:1060px;justify-self:center;margin:0;}

/* --- free text ------------------------------------------------------------ */

/* [D155] THE TEXT BOX IS THE WIDTH OF THE OPTIONS. Ziyad, 2026-08-25: "keep
   the text input box that pops up, the size of the box consistent with the
   size of the options in terms of width." It measured 720 against the options'
   560 -- a 160px step between two controls answering the same question, on the
   same screen, one under the other.

   Its EDGE joins them too. The box was on --border-hairline, which is 1.11:1
   on white, while every option row is on --border-resting at 4.15:1: the two
   controls did not look like the same kind of thing because one of them barely
   had an outline. Same border, same --r-sm corner, same measure. */
.textarea-wrap{position:relative;max-width:var(--w-answer);}
.textarea{
  width:100%;height:104px;padding:12px 13px;
  border:1px solid var(--border-resting);border-radius:var(--r-sm);background:var(--control);
  font-size:var(--fs-sm);line-height:19px;color:var(--ink);
  transition:border-color var(--dur-fast) var(--ease);
}
.textarea::placeholder{color:var(--muted);}
.textarea:focus{outline:none;border-color:var(--focus);}
.textarea.invalid{border-color:var(--error);}

/* The counter stays out of the way until there is something to count — an
   empty field showing "0 / 1000 · 40 min" is noise before you have written
   anything. It fades in on the first keystroke. */
.char-counter{
  position:absolute;right:10px;bottom:8px;padding-left:6px;
  font-size:var(--fs-xs);color:var(--muted);background:var(--control);
  opacity:1;transition:opacity var(--dur) var(--ease),color var(--dur-fast) var(--ease);
}
/* [D67] Always visible: the .on fade-in gating is gone. A length rule you
   only learn about after typing is the same bug in a slower form. */
.char-counter.near{color:var(--ink-2);}
.char-counter.over{color:var(--error);}

/* Warnings appear on blur, not while typing — being told you are 37 characters
   short on character 3 is not useful. */
.field-warn{
  display:none;margin-top:9px;padding:9px 12px;
  border:1px solid var(--error-border);border-radius:5px;background:var(--error-bg);
  font-size:var(--fs-sm);line-height:17px;color:var(--error);
}
.field-warn.on{display:block;animation:card-enter var(--dur) var(--ease) both;}

/* [D17] Conditional required text opens inside the card that triggered it,
   with a height transition so the link to the answer just given is visible. */
.followup{
  overflow:hidden;max-height:0;opacity:0;margin-top:0;padding-top:0;
  border-top:1px solid transparent;
  transition:max-height var(--dur) var(--ease),opacity var(--dur) var(--ease),
             margin-top var(--dur) var(--ease),padding-top var(--dur) var(--ease);
}
.followup.is-open{max-height:230px;opacity:1;margin-top:14px;padding-top:14px;border-top-color:var(--line);}
.followup-label{display:block;font-size:var(--fs-sm);font-weight:600;line-height:17px;color:var(--ink);margin-bottom:9px;}
.followup-req{margin-left:7px;font-weight:400;color:var(--muted);}

/* ------------------------------------------------------------- the vote --- */

/* The binding vote carries more weight than a question card — brighter border,
   larger title — because it is the one answer that decides something. */
.recommendation{
  min-height:104px;margin-top:14px;padding:18px 20px;
  border:1px solid var(--line);border-radius:7px;background:var(--surface-2);
}
.recommendation-title{font-size:var(--fs-lg);font-weight:600;line-height:20px;color:var(--ink);}
.recommendation-options{margin-top:14px;display:grid;grid-template-columns:repeat(5,1fr);gap:7px;}
/* [D43] Horizontal padding 10px → 7px. The five verdict labels must each sit on
   ONE line at the narrowest supported width (1280), where each tile is ~136px.
   At 10px the text box was 116px and the conditional-keep rung measured 114px —
   a 2px margin, which font fallback would eat. 7px gives 122px and an 8px
   margin on the longest label. Vertical padding untouched. */
.recommendation-option{
  min-height:52px;display:flex;align-items:center;justify-content:center;padding:9px 7px;
  border:1px solid var(--hair);border-radius:5px;background:var(--control);
  font-size:var(--fs-sm);line-height:17px;color:var(--ink-2);text-align:center;
}
/* :not(.sel) matters. `.recommendation-option:hover` and
   `.recommendation-option.sel` carry the SAME specificity (0,2,0), and hover is
   declared later — so without this it won over the selected state and set white
   text on the white selected fill, making the chosen option unreadable exactly
   when the pointer was on it. */
.recommendation-option:hover:not(.sel){border-color:var(--line-hover);color:var(--ink);}

/* ---------------------------------------------------------- bottom bar --- */

/* The "Confidence in your feedback" block is removed. Rater weighting comes
   from the contact-tier routing question alone. */
.action-panel{
  min-height:72px;margin:10px 0 0;padding:0 28px;
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  border:1px solid var(--hair);border-radius:10px;background:var(--surface);
}
.action-status{font-size:var(--fs-sm);color:var(--muted);}
.nav{display:flex;align-items:center;gap:10px;}
/* ===========================================================================
   [D147] ONE BUTTON SPEC, AND IT IS THE ONE ON HISTORICAL RESULTS.

   Ziyad, 2026-08-25: "'Begin' button's arrow seems too close to the text.
   Ensure that the spacing is consistent across the mockup v3. The button I want
   every other button consistent with would be the button on 'Historical
   Results' page's 'View Results' button... the font, font size, button's
   border, spacing between arrow and the text of the button, every other
   component of its design."

   `.doc-open` is that button, and `.btn-quiet` already matched it. `.nav-btn`
   did not match on a single property: it was 15px/500 on a #EDEDEF hairline at
   radius 5 with --ink-secondary text and NO GAP AT ALL between label and
   arrow. The arrow "too close to the text" was literally a missing `gap` --
   two flex children with nothing between them.

   Everything now comes from the reference: --fs-label / 600 / --ink-primary,
   a --border-resting edge at --r-sm, --sp-4 of side padding, a 7px gap, and
   the same two-property hover. HEIGHT IS THE ONE EXCEPTION, on Ziyad's call
   (2026-08-25): the action bar is 72px tall and a 32px control inside it reads
   as an afterthought, so primary flow buttons stay at 40 and the reference
   stays at 32. Nothing else differs.

   Gap 7px is not on the 4px ladder. It is the reference button's own value and
   the point of this delta is to match the reference, not to improve it. */
.nav-btn{
  height:40px;padding:0 var(--sp-4);
  display:inline-flex;align-items:center;justify-content:center;gap:7px;
  border:1px solid var(--border-resting);border-radius:var(--r-sm);
  background:var(--surface-base);
  font-size:var(--fs-label);font-weight:600;color:var(--ink-primary);
  transition:border-color var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease);
}
.nav-btn:hover{border-color:var(--border-hover);background:var(--surface-interactive);}
.nav-btn.primary{background:var(--accent);color:var(--on-ink);border-color:var(--accent);}
.nav-btn.primary:hover{background:var(--accent-hover);border-color:var(--accent-hover);color:var(--on-ink);}

/* ===================================================== the application ===== */

.whoami{display:flex;align-items:center;gap:9px;font-size:var(--fs-sm);color:var(--ink-2);}
.whoami img{width:26px;height:26px;border-radius:4px;object-fit:cover;object-position:50% 30%;}

/* ---- sign in ---- */
.auth{display:flex;justify-content:center;padding:36px 20px 60px;}
.auth-card{width:100%;max-width:860px;padding:34px 36px 36px;border:1px solid var(--line);
  border-radius:10px;background:var(--field);}
.auth-icon{display:inline-flex;width:52px;height:52px;align-items:center;justify-content:center;
  border:1px solid var(--hair);border-radius:8px;background:var(--control);color:var(--ink-2);}
.auth-title{margin-top:18px;font-size:26px;font-weight:600;line-height:31px;color:var(--ink);}
.auth-sub{margin-top:10px;max-width:620px;font-size:var(--fs-md);line-height:22px;color:var(--muted);}
.auth-grid{margin-top:26px;display:grid;grid-template-columns:repeat(4,1fr);gap:8px;}
.auth-person{display:flex;flex-direction:column;align-items:flex-start;gap:8px;padding:13px;
  border:1px solid var(--hair);border-radius:6px;background:var(--control);text-align:left;
  transition:border-color var(--dur-fast) var(--ease),transform var(--dur-fast) var(--ease),
             background-color var(--dur-fast) var(--ease);}
.auth-person img{width:34px;height:34px;border-radius:5px;object-fit:cover;object-position:50% 30%;}
.auth-name{font-size:var(--fs-sm);font-weight:500;line-height:17px;color:var(--ink);}
.auth-dept{font-size:var(--fs-xs);line-height:15px;color:var(--muted);}
.auth-person:hover{border-color:var(--ink);transform:translateY(-2px);background:var(--line);}
@media (max-width:900px){ .auth-grid{grid-template-columns:repeat(2,1fr);} }

/* ---- dashboard ---- */
.home{width:min(1060px,100% - 40px);margin:0 auto;padding:34px 0 64px;}
.home-title{font-size:26px;font-weight:600;line-height:31px;color:var(--ink);}
.home-sub{margin-top:9px;font-size:var(--fs-md);line-height:21px;color:var(--muted);}
.home-section{margin:32px 0 12px;font-size:var(--fs-xs);letter-spacing:.09em;
  text-transform:uppercase;color:var(--muted);}
/* [D146] THE CARD DOES NOT MOVE UNDER THE POINTER. Ziyad, 2026-08-25: "Remove
   the hover effect when I hover on cards on dashboard homepage cards (not the
   ones within the team poll questions and the hover effect on buttons, no
   removing them)."

   Scoped exactly as he scoped it: this is the HOME card only. Answer rows keep
   their hover, because there the hover is the only thing telling you a row is
   selectable before you commit to an answer, and every button keeps its two-
   property hover from D147. What goes is the 2px lift and the border change on
   a card whose only affordance is already a labelled "Start" control.

   The transition stays on the property list for the dormant/live swap; there
   is simply nothing left that changes on hover. */
.tool-card{display:block;padding:var(--sp-6);border:1px solid var(--line);border-radius:9px;
  background:var(--field);}
.tool-top{display:flex;align-items:center;gap:12px;margin-bottom:14px;}
.tool-icon{width:38px;height:38px;display:flex;align-items:center;justify-content:center;
  border-radius:6px;background:var(--ink);color:var(--on-ink);}
/* [D152] THE CHIP GETS AN EDGE YOU CAN ACTUALLY SEE. Ziyad, 2026-08-25: "the
   clips 'Not open yet' just make the border more visible."

   He was right and the numbers are worse than they look. The locked chip sat
   on --surface-sunken INSIDE a dormant card that is also --surface-sunken,
   with a --border-hairline edge: #EDEDEF on #F6F6F8 is 1.08:1, the same colour
   twice. The live chip was barely better at 1.11:1 on white.

   NOTE WHY THE INVISIBLE-RULE SWEEP (D138) DID NOT CATCH THIS: that test only
   considers elements with exactly one border side and no fill of their own, on
   the reasoning that a filled box draws its own boundary. A chip whose fill is
   the SAME TONE as its container is the case that reasoning misses. Both chips
   now take --border-resting: 4.15:1 on white, 3.84:1 on sunken, comfortably
   past the 3:1 that WCAG 1.4.11 wants of a UI boundary. */
.tool-badge{padding:5px 11px;border:1px solid var(--border-resting);border-radius:var(--r-sm);
  font-size:var(--fs-xs);color:var(--ink-2);}
.tool-name{font-size:var(--fs-lg);font-weight:600;line-height:21px;color:var(--ink);}
.tool-desc{margin-top:9px;max-width:640px;font-size:var(--fs-sm);line-height:19px;color:var(--muted);}
.tool-foot{margin-top:20px;display:flex;align-items:center;gap:14px;}
.tool-progress{flex:1;max-width:280px;height:6px;border-radius:var(--r-track);background:var(--track);overflow:hidden;}
.tool-progress-fill{display:block;height:100%;border-radius:var(--r-track);background:var(--ink);
  transition:width var(--dur) var(--ease);}
.tool-meta{font-size:var(--fs-sm);color:var(--muted);}
/* [D161] THE CARD'S ACTION IS A BUTTON, and it is the same button as every
   other. Ziyad, 2026-08-26: "The button of 'Start ->' or 'Continue ->' on Team
   Poll card is not consistent with 'View Results' button on Historical Results
   page. Keep the buttons consistent with that of 'View Results' button."

   It was bare text with an arrow -- 15px/500, no border, no radius, no height,
   measured 15px tall -- sitting in the same place on the same kind of card as
   a 32px bordered control. D147 brought `.nav-btn` onto the reference spec and
   missed this one, because it does not look like a button in the markup: it is
   a span inside the card, and the whole card is the link. It reads as one now.

   Same six properties as `.doc-open`: --fs-label / 600 / --ink-primary, a
   --border-resting edge at --r-sm, --sp-4 of side padding and the 7px arrow
   gap. Height 32, matching the reference exactly -- this one is not in the
   action bar, so the D147 height exemption does not apply to it. */
.tool-go{
  margin-left:auto;flex:none;
  height:var(--h-control-sm);padding:0 var(--sp-4);
  display:inline-flex;align-items:center;justify-content:center;gap:7px;
  border:1px solid var(--border-resting);border-radius:var(--r-sm);
  background:var(--surface-base);
  font-size:var(--fs-label);font-weight:600;color:var(--ink-primary);
  transition:border-color var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease);
}
.tool-card.is-live:hover .tool-go{border-color:var(--border-hover);background:var(--surface-interactive);}
/* [D95] The not-open polls stack, and they need air between them. They were
   6px apart inside a shared tray, which read as one striped block rather than
   three separate polls.
   [D108] The rule was dead -- nothing carried .tool-grid -- so they rendered at
   0 until it was wired up.
   [D113] 24 was too much once it was actually applied: three dormant cards a
   full block-step apart read as three unrelated regions rather than one list of
   things that are not open yet. 12 separates them and still groups them. */
.tool-grid{display:grid;grid-template-columns:1fr;gap:var(--sp-3);}
/* [D83] A POLL THAT IS NOT OPEN MUST READ AS UNAVAILABLE. Ziyad, 2026-08-22:
   "those polls look open due to their colors, use the disabled colors for those
   polls that are not open."

   NOTE this overrides the design system, which says "Never grey the whole card
   out: it is not disabled, it is not yet available." The system's concern is
   that a locked card should not look broken; Ziyad's is that it should not look
   clickable. His call, and the card keeps its full frame and readable title
   rather than being dimmed into illegibility -- the surface recedes, the words
   do not. */
.tool-card.is-dormant{
  background:var(--surface-sunken);border-color:var(--border-hairline);
  cursor:default;box-shadow:none;
}
.tool-card.is-dormant .tool-name{color:var(--ink-secondary);}
.tool-card.is-dormant .tool-desc{color:var(--ink-muted);}
.tool-badge.is-locked{
  background:var(--surface-sunken);border-color:var(--border-resting);
  color:var(--ink-muted);
}
.tool-card.is-dormant .tool-name{font-size:var(--fs-md);}
@media (max-width:900px){ .tool-grid{grid-template-columns:1fr;} }

/* ---- intro + submit ---- */
.intro-list{display:grid;grid-template-columns:repeat(2,1fr);gap:10px;margin-bottom:14px;}
.intro-item,.intro-scope{padding:18px 20px;border:1px solid var(--line);border-radius:7px;background:var(--field);}
.intro-h{font-size:var(--fs-md);font-weight:600;line-height:19px;color:var(--ink);}
.intro-p{margin-top:8px;font-size:var(--fs-sm);line-height:19px;color:var(--muted);}
.scope-list{margin-top:10px;padding-left:18px;list-style:disc;}
.scope-list li{margin-top:6px;font-size:var(--fs-sm);line-height:19px;color:var(--muted);}
@media (max-width:900px){ .intro-list{grid-template-columns:1fr;} }

.submit-card,.done-card{padding:26px 28px;border:1px solid var(--line);border-radius:8px;
  background:var(--field);margin-bottom:14px;}
.done-card{text-align:center;color:var(--ink);}
.done-card svg{margin:0 auto 14px;color:var(--ink);}
.done-card h2{font-size:var(--fs-xl);font-weight:600;line-height:25px;}
.done-card p{margin-top:10px;font-size:var(--fs-sm);line-height:20px;color:var(--muted);}
.submit-line{font-size:var(--fs-md);line-height:21px;color:var(--ink);}
.submit-ok{margin-top:9px;font-size:var(--fs-sm);color:var(--muted);}
.submit-warn{margin-top:9px;font-size:var(--fs-sm);line-height:19px;color:var(--error);}
.submit-note{margin-top:9px;font-size:var(--fs-sm);color:var(--muted);}
.nav-btn.big{margin-top:18px;height:46px;padding:0 26px;font-size:var(--fs-md);}

.summary-grid{display:flex;flex-direction:column;gap:8px;}
.summary-row{display:flex;align-items:center;gap:14px;padding:13px 16px;
  border:1px solid var(--line);border-radius:6px;background:var(--field);}
.summary-name{flex:1;font-size:var(--fs-sm);color:var(--ink);}
.summary-bar{width:180px;height:6px;border-radius:var(--r-track);background:var(--track);overflow:hidden;}
.summary-fill{display:block;height:100%;border-radius:var(--r-track);background:var(--ink);}
.summary-count{width:78px;text-align:right;font-size:var(--fs-sm);color:var(--muted);}

/* The own-department swap, announced rather than silent. */
.own-banner{display:flex;align-items:flex-start;gap:10px;margin:0 0 12px;padding:13px 16px;
  border:1px solid var(--line);border-radius:6px;background:var(--surface-2);
  font-size:var(--fs-sm);line-height:19px;color:var(--ink-2);}
.own-banner svg{flex:none;margin-top:1px;}

a.nav-btn{text-decoration:none;}

/* Walkthrough affordance only — not part of the product. */
/* [D147] The demo control joins the family on radius, border and type. It
   keeps its low opacity: it is the one control on screen that is not part of
   the product, and it should stay quieter than everything that is. */
.reset-btn{
  position:fixed;right:14px;bottom:14px;z-index:60;height:30px;padding:0 var(--sp-3);
  border:1px solid var(--border-resting);border-radius:var(--r-sm);background:var(--control);
  font-size:var(--fs-caption);color:var(--muted);opacity:.55;
  transition:opacity var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease);
}
.reset-btn:hover{opacity:1;border-color:var(--line-hover);color:var(--ink-2);}

/* ------------------------------------------------------------- gating ----- */

.tier-block[hidden]{display:none;}
.tier-note{
  margin:10px 0;padding:13px 16px;border:1px dashed var(--hair);border-radius:5px;
  font-size:var(--fs-sm);line-height:18px;color:var(--muted);
}

/* ===========================================================================
/* ===========================================================================
   [D66, corrected 2026-08-22] SIGN-IN, MATCHED TO THE DESIGN SYSTEM ARTBOARD.

   Design system section 17, "Auth surface: the one branded moment", shell B in
   section 8. Ziyad, 2026-08-21: "The auth login is picture perfect to what I
   need from the auth login." Then, 2026-08-22: "The auth login page doesn't
   look right. Especially the continue button. Match to the design system."

   WHAT WAS WRONG THE FIRST TIME, and it was one mistake repeated:
   I mapped this screen onto the APP type scale (12 / 13 / 15) when the auth
   artboard deliberately runs ABOVE it. The system says so in the same
   paragraph: "It is the only place with type above the app scale." So the
   card title, the sub, the labels, the buttons, the divider, the secure line
   and the copyright were all one or two steps too small, and the Continue
   button was 500 weight where the artboard is 600. Every value below is taken
   from the artboard itself rather than translated through the token scale.

   THIS REVERSES D62 (the dark left panel). --surface-inverse is restricted to
   "Primary button and selected answer rows. Nothing else."

   ⚠️ MEASURED and accepted by Ziyad: the left panel and the card are both
   #FFFFFF, 1.0:1. The halves differ only by the #F7F7F8 ground (1.07:1) and
   the hairline (1.17:1). Do not "fix" it by tinting the left panel.

   ⚠️ ONE INTERNAL INCONSISTENCY IN THE SYSTEM, resolved toward the artboard:
   the auth prose says the wordmark is "0.16em uppercase"; the auth artboard
   renders it at 0.11em. The artboard is what Ziyad is looking at, so 0.11em.
   Raised with the designer.
   =========================================================================== */

.signin{
  min-height:100vh;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  background:#F7F7F8;
}

/* --- left: the brand panel. Lockup pinned to the top on a 40px inset; the
       headline, purpose line and commitments centred in the space beneath. --- */
/* [D97] THE BRAND PANEL, SCALED UP AND CENTRED. Ziyad, 2026-08-22: the logo,
   title and supporting text are "too small and does not match the reference
   closely enough", and all three should be "centred within the left-side
   panel". The composition is unchanged -- lockup above, headline, purpose line,
   commitments -- it is scaled and centred as one column. */
/* [D105] The lockup pins TOP-LEFT; the copy block is centred in the space
   beneath it but its text stays LEFT-ALIGNED. Ziyad, 2026-08-23: "Keep the text
   left aligned, just the text's position at the centre... Keep the logo on the
   top left with proper spacing." */
.signin-left{
  padding:var(--sp-10) var(--sp-8) var(--sp-12);
  display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;
  min-width:0;background:var(--surface-base);
  border-right:1px solid var(--border-hairline);
  text-align:left;
}
.signin-lockup{display:flex;align-items:center;gap:var(--sp-4);min-width:0;flex:none;color:var(--ink-primary);}
.signin-mark{display:block;flex:none;}
/* One line, uppercase, spelled out. Never "WLR", never stacked. */
/* [D107] 20% down from the D105 size, on Ziyad's call 2026-08-23: the lockup
   had grown 35% for the auth panel and was carrying more weight than the
   headline beneath it. 20px -> 16px, with the mark trimmed by the same 20%. */
.signin-wordmark{
  font-size:16px;font-weight:700;letter-spacing:.11em;text-transform:uppercase;
  white-space:nowrap;line-height:1;min-width:0;
  transform:translateY(calc(34px * .0782 - .05em));  /* [D112], mark 34 tall */
}
.signin-left-body{
  flex:1;align-self:center;
  display:flex;flex-direction:column;justify-content:center;
  gap:var(--sp-10);max-width:44ch;text-align:left;
}
.signin-heading{font-size:44px;font-weight:700;letter-spacing:-.03em;line-height:50px;color:var(--ink-primary);text-align:left;}
.signin-lockup-copy{display:block;}
.signin-lede{margin-top:var(--sp-4);max-width:36ch;font-size:18px;line-height:28px;color:var(--ink-secondary);text-align:left;}
/* Plain text. The circular icon tiles are gone: they were the badges Christian
   overruled, and the design system removed them from its own auth spec. */
.signin-commits{display:flex;flex-direction:column;gap:var(--sp-6);text-align:left;}
/* The glyph sits beside the label, at the text's own weight -- no tile, no
   fill, no circle. Not the badges Christian overruled. */
.signin-commit{display:flex;align-items:flex-start;gap:var(--sp-3);}
.signin-commit-ic{flex:none;display:flex;color:var(--ink-primary);margin-top:1px;}
.signin-commit-copy{min-width:0;}
.signin-commit-title{font-size:16px;font-weight:600;letter-spacing:-.005em;color:var(--ink-primary);text-align:left;}
.signin-commit-desc{margin-top:2px;font-size:15px;line-height:22px;color:var(--ink-secondary);text-align:left;}

/* --- right: a 400px card on the #F7F7F8 ground --------------------------- */
.signin-right{
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:24px;padding:48px 32px;min-width:0;
}
.signin-col{width:100%;max-width:400px;}
.signin-card{
  padding:32px 28px;
  border:1px solid var(--border-hairline);border-radius:var(--r-xl);
  background:var(--surface-base);
}
.signin-cardhead{text-align:center;margin-bottom:26px;}
.signin-title{font-size:26px;font-weight:700;letter-spacing:-.025em;color:var(--ink-primary);text-align:center;}
/* [D129] 6 -> 12. Ziyad, 2026-08-24: "a little more spacing between the two
   lines". 6 was also OFF the 4px ladder, which is how it got to be a value
   nobody chose; 12 is the ladder's own step and reads as two lines rather than
   one wrapped sentence. */
.signin-sub{margin-top:var(--sp-3);font-size:14px;color:var(--ink-secondary);text-align:center;}

.signin-label{display:block;margin-bottom:8px;font-size:var(--fs-label);font-weight:600;color:var(--ink-primary);}
.signin-field{
  display:flex;align-items:center;gap:12px;min-width:0;
  height:48px;padding:0 14px;
  border:1px solid var(--border-resting);border-radius:var(--r-md);
  background:var(--surface-base);
  margin-bottom:18px;
}
.signin-field svg{color:var(--ink-muted);flex:none;}
.signin-input{
  flex:1;min-width:0;height:100%;border:0;background:none;
  font-size:15px;color:var(--ink-primary);
}
.signin-input::placeholder{color:var(--ink-muted);}
.signin-field:focus-within{outline:2px solid var(--border-focus);outline-offset:-2px;border-color:var(--border-focus);}
.signin-input:focus{outline:none;}

.signin-remember{
  display:flex;align-items:center;gap:12px;margin-bottom:22px;
  font-size:14px;color:var(--ink-primary);cursor:pointer;min-width:0;
}
/* Square, radius 6 — 20px here rather than the app's 18px, because this screen
   runs above the app scale. Filled ink when checked, with a white check. */
.signin-checkbox{
  appearance:none;-webkit-appearance:none;
  flex:0 0 20px;width:20px;height:20px;
  border:1px solid var(--border-hover);border-radius:var(--r-sm);
  background:var(--surface-base);cursor:pointer;position:relative;
  transition:background var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease);
}
.signin-checkbox:checked{background:var(--surface-inverse);border-color:var(--surface-inverse);}
/* [D143b] THE TICK CENTRES ITSELF. Ziyad, 2026-08-25: "Check mark doesn't look
   centered on the auth login page in 'Remember me' checkmark box."

   It was not, and it was off by hand-written pixels: `left:6px; top:2.5px` put
   the glyph's bounding box at (9.5, 8.5) inside a 20px control whose centre is
   (10, 10) -- half a pixel left and a pixel and a half HIGH. Any change to the
   box size or the border width would have moved it again.

   It is now centred by the box rather than by arithmetic: 50/50 with a
   translate, so the glyph re-centres itself whatever the control becomes.

   THE -1px IS DELIBERATE AND IT IS THE WORDMARK LESSON AGAIN. A tick is not
   its bounding box: the ink is two strokes along the lower-left/upper-right
   diagonal, and its centroid computes 1.3px BELOW the box centre once the
   glyph is rotated 45 degrees. Centring the BOX therefore leaves the INK
   sitting low, which is the same class of mistake as centring the WLR mark on
   its box when its bars are bottom-aligned. -1px lifts the ink most of the way
   without letting the box read high. */
.signin-checkbox:checked::after{
  content:"";position:absolute;left:50%;top:50%;width:5px;height:10px;
  border:solid var(--ink-inverse);border-width:0 2px 2px 0;
  transform:translate(-50%,-50%) translateY(-1px) rotate(45deg);
}
.signin-checkbox:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px;}

/* THE CONTINUE BUTTON. 48px, radius 10, no border, 15px at weight 600.
   The 500 weight was the thing that read wrong. */
.signin-btn{
  width:100%;height:48px;border:0;border-radius:var(--r-md);
  /* [D78] The global reset sets text-align:left on every element, so this
     button was rendering "Continue" hard against its left edge. */
  text-align:center;display:flex;align-items:center;justify-content:center;
  background:var(--surface-inverse);color:var(--ink-inverse);
  font-size:15px;font-weight:600;cursor:pointer;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  transition:background var(--dur-fast) var(--ease);
}
.signin-btn:hover{background:var(--surface-inverse-hover);}
.signin-btn:active{background:#000000;}

.signin-or{display:flex;align-items:center;gap:14px;margin:22px 0;}
.signin-or::before,.signin-or::after{content:"";flex:1;height:1px;background:var(--border-hairline);}
.signin-or span{font-size:14px;color:var(--ink-secondary);}

/* The identity provider control stays SECONDARY: a foreign-coloured mark on a
   filled accent button is prohibited. Mark first, then the label. */
.signin-alt{
  display:flex;align-items:center;justify-content:center;gap:12px;min-width:0;
  width:100%;height:48px;padding:0 16px;
  border:1px solid var(--border-resting);border-radius:var(--r-md);
  background:var(--surface-base);
  font-size:15px;font-weight:600;color:var(--ink-primary);cursor:pointer;
  transition:border-color var(--dur-fast) var(--ease);
}
.signin-alt:hover{border-color:var(--border-hover);}
.signin-alt .google-mark{flex:none;order:1;}
.signin-alt span{order:2;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}

.signin-secure{
  display:flex;align-items:center;justify-content:center;gap:8px;
  margin-top:24px;font-size:var(--fs-label);color:var(--ink-secondary);
}
.signin-secure svg{color:var(--ink-muted);flex:none;}
/* [D129] The copyright sat hard against the card -- a measured 0px, with only
   the line box making it look separated. It is a footer, not part of the card,
   and now reads as one: 32, the ladder's block step. */
.signin-copy{margin-top:var(--sp-8);font-size:var(--fs-label);color:var(--ink-secondary);text-align:center;}

/* --- the walkthrough. Not part of the product; it replaces the card body
       once a sign-in path is chosen, and says so on screen. --------------- */
.signin-demo{margin-top:26px;padding-top:22px;border-top:1px solid var(--border-hairline);}
.signin-demo-label{font-size:var(--fs-label);font-weight:600;color:var(--ink-primary);}
.signin-demo-hint{margin-top:4px;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);}
.signin-demo-grid{
  margin-top:14px;display:grid;grid-template-columns:repeat(2,1fr);gap:6px;
  max-height:260px;overflow-y:auto;
}
.signin-demo-person{
  display:flex;align-items:center;gap:9px;padding:8px 10px;
  border:1px solid var(--border-hairline);border-radius:var(--r-md);
  background:var(--surface-base);text-align:left;cursor:pointer;
  transition:border-color var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease);
}
.signin-demo-person:hover{border-color:var(--border-hover);background:var(--surface-interactive);}
.signin-demo-person img{
  width:26px;height:26px;flex:none;border-radius:var(--r-sm);
  object-fit:cover;object-position:50% 30%;background:var(--surface-sunken);
}
.signin-demo-name{font-size:var(--fs-caption);font-weight:600;color:var(--ink-primary);}
.signin-demo-dept{font-size:var(--fs-caption);color:var(--ink-muted);}

@media (max-width:1023px){
  /* Below 1024 the brand panel collapses to a 96px header band. */
  .signin{grid-template-columns:1fr;}
  .signin-left{height:96px;padding:0 24px;justify-content:center;
    border-right:0;border-bottom:1px solid var(--border-hairline);}
  .signin-left-body{display:none;}
}

/* ===========================================================================
   [D56] HOME — reference image 2, rebuilt light.

   Fixed sidebar + topbar + a two-column body. Departures:
     - [D52] THE GREETING IS GONE. "I don't think that you should do this
       'good to see you' whatever information at the top... That's just AI
       slop." That is the reference's own page header; he rejected it in the
       same call in which he generated the image. The screen leads with the
       work instead.
     - [D50] the reference's circular progress RING is a full circle and is
       replaced by a bar. Same rule that replaced its circular badges.
     - [D20/D21] 7-10px type raised to the floor; reference greys replaced.
     - No close date — see the closing card in app.js.
   =========================================================================== */
.shell{min-height:100vh;display:grid;grid-template-columns:var(--w-sidebar) 1fr;background:var(--bg);}
.rail{
  position:sticky;top:0;align-self:start;height:100vh;display:flex;flex-direction:column;
  padding:20px 14px;border-right:1px solid var(--line);background:var(--sidebar-bg);
}
.rail-brand{display:flex;align-items:center;gap:8px;min-width:0;
  text-decoration:none;border-radius:4px;}
.rail-brand:hover{opacity:.72;}
/* [D69] The lockup, matched to sign-in and to the design system: ascending
   bar mark plus the wordmark on ONE line, 0.11em uppercase, spelled out. The
   old rail set "WLR" beside a stacked WHITE/LABEL/RESELL, which broke both
   halves of the rule. */
.rail-mark{display:block;flex:none;color:var(--ink-primary);}
.rail-wordmark{
  font-size:var(--fs-caption);font-weight:700;letter-spacing:.11em;text-transform:uppercase;
  line-height:1;color:var(--ink-primary);
  min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  transform:translateY(calc(19px * .0782 - .05em));  /* [D112], mark 19 tall */
}
/* [D109] "TEAM EVALUATION" IS NOT PART OF THE LOCKUP. Ziyad, 2026-08-23: it
   "looks too close to the logo, have proper spacing there, it's separate from
   the logo." Measured at 14px below the mark while the gap BELOW it, to the
   nav, was already 24 -- so the label read as a second line of the wordmark
   rather than as the product name. Both gaps are now the 24 step, which is the
   same correction D95 made to the header divider. */
.rail-section{margin-top:var(--sp-6);font-size:var(--fs-caption);font-weight:700;letter-spacing:.3px;color:var(--ink-2);}
.rail-nav{margin-top:24px;display:flex;flex-direction:column;gap:2px;}
.rail-item{
  display:flex;align-items:center;gap:12px;height:40px;padding:0 11px;
  border-radius:5px;border-left:2px solid transparent;
  font-size:var(--fs-label);font-weight:500;color:var(--ink-2);text-decoration:none;
  transition:background var(--dur-fast) var(--ease);
}
.rail-item:hover{background:var(--fill);}
.rail-item.is-active{background:var(--active);border-left-color:var(--ink);color:var(--ink);font-weight:600;}
.rail-item.is-locked{color:var(--muted);cursor:default;}
.rail-item.is-locked:hover{background:none;}
.rail-item svg{flex:none;}
.rail-badge{
  margin-left:auto;padding:3px 7px;border-radius:5px;
  background:var(--ink);color:var(--on-ink);font-size:var(--fs-caption);font-weight:600;
}
.rail-lock{margin-left:auto;color:var(--muted);}
/* [D145] .rail-foot / .rail-privacy* removed with the markup they styled. */

.topbar{
  height:var(--h-topbar);display:flex;align-items:center;justify-content:flex-end;gap:18px;
  padding:0 26px;border-bottom:1px solid var(--line);background:var(--fill);
}
.topbar-bell{position:relative;color:var(--ink-2);display:flex;}
.topbar-count{
  position:absolute;top:-5px;right:-6px;min-width:15px;height:15px;padding:0 4px;
  border-radius:4px;background:var(--ink);color:var(--on-ink);
  font-size:var(--fs-caption);font-weight:600;display:flex;align-items:center;justify-content:center;
}
.topbar-me{display:flex;align-items:center;gap:10px;}
.topbar-me img{width:32px;height:32px;border-radius:var(--r-sm);object-fit:cover;object-position:50% 30%;background:var(--fill);}
.topbar-name{font-size:var(--fs-label);font-weight:600;color:var(--ink);}
.topbar-role{margin-top:1px;font-size:var(--fs-caption);color:var(--muted);}

.home-body{padding:var(--sp-6) var(--sp-8) var(--sp-8);display:grid;grid-template-columns:1fr var(--w-rail);gap:var(--sp-6);align-items:start;}
.home-col{min-width:0;}
/* [D130] THE SIDE CARDS ALIGN WITH THE CARD, NOT WITH THE LABEL ABOVE IT.
   Ziyad, 2026-08-24: "Align the card 'Poll snapshot' with the team poll card
   please. It's currently aligned with 'Open for you' text... this goes for
   other screens as well not just limited to here."

   The left column opens with a group label and the right column does not, so
   both started at the same y and the CARDS were 30px out. The offset is
   derived from the label's own tokens -- its line box plus its bottom margin --
   so it cannot drift if either changes. It applies to Home, People and
   Historical Results alike, which all share this shell. */
.home-col + .home-col{padding-top:calc(var(--lh-label) + var(--sp-3));}
/* [D79] A GROUP LABEL NEEDS AIR ABOVE IT. Ziyad, 2026-08-22: "the 'Not open
   yet' sticks directly under the 'Team Poll' box entirely. There's no relevant
   spacing at all." The old rule only spaced a label that followed ANOTHER
   label, so a label following a card had none. The design system sets the
   value: "labelled groups by 32 with a 13px label above". */
.home-label{
  font-size:var(--fs-label);line-height:var(--lh-label);font-weight:600;
  color:var(--ink-primary);margin-bottom:var(--sp-3);
}
.home-col > * + .home-label{margin-top:var(--sp-8);}

.card-lite{border:1px solid var(--line);border-radius:9px;background:var(--fill);box-shadow:0 2px 7px rgba(0,0,0,.025);}
.side-card{padding:15px;}
/* [D108] 10px is not on the 4px ladder. The right column now steps by 12. */
.side-card + .side-card{margin-top:var(--sp-3);}
/* ===========================================================================
   [D160] A LABEL SITS --sp-1 UNDER ITS VALUE, AND THE TEXT HAS LEADING.

   Ziyad, 2026-08-26: "spacing between texts like 'Poll snapshot' and 'Q3 2026',
   '20' and 'people evaluated' and so on. Keep the spacing more and consistent
   across things like these coz I see spacing issues around this across the
   homepage (even on Historical Results page's cards)."

   Measured before this: value to label **1px**, title to sub **2px**. Two
   different values, both effectively nothing -- and the reason they read as
   nothing is not really the margin, it is that the global reset sets
   `line-height:1`, so 14px text had ZERO leading and the two lines were
   touching at their ink. Nobody chose 1 or 2; they are what was left after the
   leading was removed.

   Every pair in every card on Home and Historical Results now takes --sp-1
   between the two, and each line carries its own line-height token, which adds
   3px of half-leading either side. One value, one rule, both screens.
   =========================================================================== */
.side-title{font-size:var(--fs-label);line-height:var(--lh-label);font-weight:700;color:var(--ink);}
.side-sub{margin-top:var(--sp-1);font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--muted);}
/* [D79] No glyph on a stat row: "Stat tile: value text-kpi, caption
   beneath, no icon, no borders inside." */
.side-stat{display:block;padding:var(--sp-3) 0;border-bottom:1px solid var(--line-subtle);}
.side-stat-v{font-variant-numeric:tabular-nums;}
.side-stat:last-child{border-bottom:0;padding-bottom:0;}
.side-stat svg{color:var(--ink-2);}
.side-stat-v{font-size:var(--fs-label);line-height:var(--lh-label);font-weight:700;color:var(--ink);}
.side-stat-l{margin-top:var(--sp-1);font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--muted);}
.side-pct{margin-top:14px;font-size:30px;font-weight:700;color:var(--ink);line-height:1;letter-spacing:-1px;}
.side-pct-sub{margin-top:var(--sp-1);font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--muted);}
.side-bar{margin-top:12px;height:8px;border-radius:var(--r-track);background:var(--track);overflow:hidden;}
.side-bar span{display:block;height:100%;border-radius:var(--r-track);background:var(--data);}
.side-rows{margin-top:14px;padding-top:12px;border-top:1px solid var(--line-subtle);}
.side-row{display:flex;justify-content:space-between;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--muted);}
.side-row strong{color:var(--ink);font-weight:600;}
.side-note{margin-top:var(--sp-2);font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--muted);}

/* ===========================================================================
   [D57] DEPTH, part 3: the components added for Ziyad's 2026-08-19 feedback.
   =========================================================================== */

/* --- numeric field (the exclusivity-pay question) ------------------------- */
.num-wrap{
  display:inline-flex;align-items:center;gap:0;max-width:320px;
  border:1px solid var(--line);border-radius:8px;background:var(--fill);
  transition:border-color var(--dur-fast) var(--ease);
}
.num-wrap:focus-within{border-color:var(--focus);box-shadow:0 0 0 3px rgba(17,17,17,.08);}
.num-prefix{padding:0 4px 0 14px;font-size:var(--fs-lg);font-weight:600;color:var(--muted);}
.num-input{
  flex:1;min-width:0;height:50px;padding:0 10px;background:none;
  font-size:var(--fs-lg);font-weight:600;color:var(--ink);letter-spacing:-.2px;
}
.num-input:focus{outline:none;}
.num-unit{padding:0 14px 0 4px;font-size:var(--fs-sm);color:var(--muted);white-space:nowrap;}

/* --- "Something else" free-text on a multi -------------------------------- */
.other-wrap{
  overflow:hidden;max-height:0;opacity:0;margin-top:0;
  transition:max-height var(--dur) var(--ease),opacity var(--dur) var(--ease),
             margin-top var(--dur) var(--ease);
}
.other-wrap.is-open{max-height:80px;opacity:1;margin-top:8px;}
.other-input{
  width:100%;max-width:720px;height:44px;padding:0 14px;
  border:1px solid var(--line);border-radius:7px;background:var(--fill);
  font-size:var(--fs-sm);color:var(--ink);
}
.other-input::placeholder{color:var(--muted);}
.other-input:focus{outline:none;border-color:var(--focus);box-shadow:0 0 0 3px rgba(17,17,17,.08);}

/* --- topbar identity: name and role were running together ----------------- */
.topbar-id{display:flex;flex-direction:column;gap:3px;line-height:1;}
.topbar-name{display:block;font-size:var(--fs-label);font-weight:600;color:var(--ink);}
.topbar-role{display:block;font-size:var(--fs-caption);color:var(--muted);}

/* ===========================================================================
   [D58] "BEFORE YOU START", rebuilt.

   Ziyad, 2026-08-19: "The four explanatory sections at the top currently look
   like four unrelated cards in a 2x2 grid. They have too much equal visual
   weight. Make them feel like parts of one cohesive information section rather
   than four independent floating cards."

   And Christian called the old version UGLY, disliking text-heavy screens and
   "a bunch of boxes on a single screen".

   So: ONE card, with internal dividers. Four rows, not four boxes. The count
   can grow without the layout changing shape, which the 2x2 grid could not do
   without leaving a hole.
   =========================================================================== */
/* [D126 / A3] THREE POINTS, GLYPH + TITLE + ONE LINE, AND NO TILES.

   The numbered black squares were the icon-tile grammar Christian overruled,
   wearing a number instead of a glyph. The glyph now sits at the text's own
   weight with no fill, no tile and no circle -- the same treatment D97 gave the
   sign-in commitments. The card frame goes too: D101 already settled that
   spacing carries this hierarchy and a second frame doubles the borders. */
.intro-block{display:flex;flex-direction:column;gap:var(--sp-6);margin-top:var(--sp-2);}
.intro-row{display:grid;grid-template-columns:20px 1fr;gap:var(--sp-4);align-items:start;}
.intro-glyph{margin-top:2px;display:flex;color:var(--ink-secondary);}
.intro-h{font-size:var(--fs-heading);line-height:var(--lh-heading);font-weight:600;color:var(--ink-primary);}
.intro-p{margin-top:3px;max-width:82ch;font-size:var(--fs-body);line-height:var(--lh-body);color:var(--ink-secondary);}

/* [D135] THE DISCLOSURE IS ONE LINE NOW, at the foot of the screen. It had its
   own block with a 3px ink rule; Ziyad asked for a single line with no section
   around it. It keeps the ink rule at a hairline weight so it still reads as a
   statement of terms rather than a caption, and it is the last thing on the
   screen before the Begin button -- which is where a condition of consent
   belongs. */
.disc-line{
  margin-top:var(--sp-10);padding-left:var(--sp-4);
  border-left:2px solid var(--ink-primary);
  font-size:var(--fs-body);line-height:var(--lh-body);color:var(--ink-secondary);
  max-width:96ch;
}
.disc-line b{color:var(--ink-primary);font-weight:700;}

/* --- "What you will be asked about" ---------------------------------------
   The count is a FACT ABOUT THE BALLOT, so it takes the same treatment as the
   facts on the results masthead rather than a black tile. Ziyad's original
   ask -- "vertically stacked visual rows... visual rhythm and hierarchy
   without looking like four independent cards" -- is met by the shared rules
   and one alignment, without four filled bars. */
.scope-block{margin-top:var(--sp-12);}

/* [D132] FOUR CARDS ACROSS, not four stacked rows. Ziyad, 2026-08-24. The four
   counts are one fact about the ballot's shape, and a row reads as one thing
   where a stack reads as four. Equal tracks so the cards are identical widths
   whatever the numbers are, and the description is capped at a phrase -- four
   cards side by side have room for a line, not a sentence. */
.scope-cards{
  margin-top:var(--sp-4);
  display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--sp-3);
}
.scope-card{
  display:flex;flex-direction:column;
  padding:var(--sp-4) var(--sp-5) var(--sp-5);
  border:1px solid var(--border-hairline);border-radius:var(--r-md);
  background:var(--surface-base);
}
.scope-n{font-size:var(--fs-display);line-height:var(--lh-display);letter-spacing:var(--tr-display);font-weight:700;color:var(--ink-primary);}
.scope-l{
  margin-top:2px;font-size:var(--fs-overline);line-height:var(--lh-overline);
  letter-spacing:var(--tr-overline);text-transform:uppercase;font-weight:600;color:var(--ink-muted);
}
.scope-d{margin-top:var(--sp-3);font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-secondary);}

@media (max-width:900px){
  .disc-grid{grid-template-columns:1fr;}
  .scope-cards{grid-template-columns:repeat(2,minmax(0,1fr));}
}

/* ===========================================================================
   [D59] LEADERSHIP EVIDENCE RAIL — the stats panel Ziyad asked for.
   =========================================================================== */
.lead-missing{
  margin-top:10px;padding:14px 16px;
  border:1px dashed var(--hair);border-radius:8px;background:var(--surface);
}
.lead-missing-h{font-size:var(--fs-sm);font-weight:650;color:var(--ink);}
.lead-missing-p{margin-top:6px;font-size:var(--fs-sm);line-height:19px;color:var(--muted);}

/* ===========================================================================
   [D65] THE SUBJECT BLOCK.

   Design system section 12. The ballot's real unit is neither a single
   question nor a section: it is a repeating block of questions about one named
   subject. A person block is 12 questions plus a verdict, repeated for every
   other member; a leadership block is 13 questions, repeated per leader.
   Together they are 273 of 381 answers, so this component decides whether the
   ballot gets finished.

   [D70, 2026-08-22] ONE SCREEN PER SUBJECT, REVERSED BACK.

   Ziyad: "I need all questions for one individual, one department, one section
   of each category all questioned in one screen like the previous mockup."

   ⚠️ THIS OVERRIDES THE DESIGN SYSTEM, which is explicit the other way:
   section 12, "One question per view. The only grouping permitted is the
   subject block above, and it is still one question per step — a 'tight group'
   is never a licence to stack questions on a page." The system's stated reason
   was that "a scrolling block hides its own length, loses the reader's place on
   resume". Ziyad's call, made with that quoted to him.

   WHAT SURVIVES from D65, because none of it depended on the split: the sticky
   subject header, the subject-owned evidence rail, and "Save and exit" on every
   view. WHAT GOES: the per-question steps and the boundary interstitial, which
   was a second screen per person and so could not survive "one screen".

   The bar now reports THIS SUBJECT's completion rather than position in a
   sequence, because on a single screen position is not a thing — and it is the
   only figure on screen that moves while the reader stands still.
   =========================================================================== */

/* The subject header never scrolls away. It is the answer to "who am I
   judging", and the question column scrolls independently of the evidence
   rail, so across a 19-person loop this is the only persistent name. */
/* [D88 -> D148] THE STICKY HEADER, AND THE LEFTOVER BEHIND ITS CURVE.

   Ziyad, 2026-08-25, with a screenshot: "at the top of the questions' sticky
   element, there's some remains that remain outside the border radius... Either
   remove the border radius altogether or keep the border radius with nothing
   behind that element."

   I MAGNIFIED IT RATHER THAN GUESSING, and the cause was not the radius.
   Measured while scrolled: the bar sat at y=72 and `.main-panel` at y=-288.
   The panel had scrolled 360px up, so at the bar's corner the panel was a
   FLAT white edge, 288px below its own curve -- and the bar's 10px radius cut
   a notch out of itself and showed that flat white through it. At rest the two
   curves coincided and nothing was visible, which is exactly why this survived
   nine review rounds: it only existed once you scrolled.

   D88 gave the bar its own border and radius so the curve would not vanish on
   scroll. That was solving the right problem the hard way -- by making the bar
   impersonate the card's edge. It IS the card's edge now: the card is the
   scroller (D148), so its own border-radius clips the bar's corners for free,
   and there is nothing behind the curve to leak because the clip removes it.

   So the bar keeps no radius, no border and no negative offset of its own. It
   is a plain white band; the shape comes from the box it is clipped by. What
   Ziyad asked for -- the radius, with nothing behind it -- and what D88 asked
   for -- a curve that survives scrolling -- are the same thing here. */
.subject-bar{
  position:sticky;top:0;z-index:var(--z-sticky);
  margin:0 -28px var(--sp-6);padding:0 28px;
  background:var(--surface-base);
  border-bottom:1px solid var(--border-hairline);
}
/* [D102] 88px at rest, collapsing ONCE to 48px past 120px of scroll. Design
   system section 12: "64px above a long scroll is the worst of both: too tall
   to be free at question eleven, too short to hold the relation line." */
.subject-head{
  height:88px;display:flex;align-items:center;gap:var(--sp-3);
  transition:height var(--dur-base) var(--ease);
}
.subject-bar.is-compact .subject-head{height:48px;}
.subject-bar.is-compact .subject-rel{display:none;}
.subject-bar.is-compact .subject-name{font-size:15px;line-height:20px;}
.subject-bar.is-compact .subject-count{font-size:var(--fs-caption);}
.subject-bar.is-compact{box-shadow:var(--elev-1);}
.subject-id{min-width:0;flex:1;}
.subject-name{
  font-size:var(--fs-title);line-height:var(--lh-title);font-weight:600;
  letter-spacing:var(--tr-title);color:var(--ink-primary);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.subject-rel{
  margin-top:2px;font-size:var(--fs-caption);line-height:var(--lh-caption);
  color:var(--ink-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* Three denominators, narrowest first, exactly as the system specifies.
   Tabular so the numbers do not jitter as they advance. */
.subject-pos{
  flex:none;display:flex;flex-direction:column;align-items:flex-end;gap:2px;
  font-variant-numeric:tabular-nums;
}
.subject-count{
  font-size:var(--fs-label);line-height:var(--lh-label);font-weight:500;
  color:var(--ink-primary);
}
.subject-q{
  font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);
}
/* The bar shows the BLOCK only. A bar creeping 0 to 100% across an hour is
   not feedback. Radius 3 on the track, 2 on the fill -- no full-radius pill. */
/* [D168] THE PROGRESS BAR IS NOT PART OF THE BORDER. Ziyad, 2026-08-27: "the
   progress bar at the top that runs, there needs to be some sort of padding or
   spacing because currently it sticks to the bottom of the header."

   Measured: the track sat **0px** below the head and **1px** above the bar's
   bottom edge -- and that 1px was the bar's own border. A 4px bar welded
   between the header's text and a hairline does not read as a progress
   indicator; it reads as a thicker border. It needs to be a thing sitting in
   the bar, not the bar's edge.

   --sp-2 above and below. The bar's height goes 93 -> 109 at rest and 53 -> 69
   compact; `.subject-head` keeps its 88/48, so D102's one-collapse behaviour
   and the tests that assert those two numbers are untouched. */
.subject-track{
  display:block;height:4px;margin:var(--sp-2) 0;border-radius:var(--r-track);
  background:var(--surface-sunken);overflow:hidden;
}
.subject-fill{
  display:block;height:100%;border-radius:var(--r-marker);
  background:var(--data-1);transition:width var(--dur-base) var(--ease);
}

/* ===========================================================================
   [D92] THE "+n" OVERFLOW CHIP, and [D93]/[D94] the two compact rail states.
   =========================================================================== */

/* Design system section 09: "Everything past the line collapses into a '+n'
   chip; hovering or focusing it opens the popover below." Keyboard-reachable,
   because a hover-only affordance hides information from anyone using a
   keyboard -- and the system is explicit that a disclosure "is always a
   labelled control".

   [D158] `.report-more` itself is GONE: the disclosure is the whole faces row
   now (`.reports-faces`), and the overflow count is `.report-face-more`, which
   sits inside it rather than being the trigger. The PATTERN is unchanged and
   the rules that implement it -- `.report-pop` and `.report-pop-row` -- are
   directly below, still shared. */
/* [D158] Anchored LEFT now, because the faces row starts at the rail's left
   edge; `right:0` would have hung it off the short row's right-hand end.
   It carries EVERY report, so it needs a ceiling and its own scroll: fifteen
   rows at the 14px floor is over 400px, which is most of the rail. */
.report-pop{
  /* [D167] Anchored RIGHT again. The faces now sit at the right-hand end of a
     detail row, so a popover opening rightwards from their left edge would run
     straight off the rail -- which is overflow-x:hidden, so it would be cut
     rather than overflow. Opening leftwards from the right edge keeps it inside
     from any row position. (This also puts it back out of reach of the overflow
     sweep, which does not count left-side overflow; the exclusion and its
     paired direct assertion both stay, because the anchor may move again.) */
  position:absolute;top:calc(100% + 6px);right:0;z-index:var(--z-popover);
  min-width:210px;max-width:264px;max-height:320px;overflow-y:auto;padding:var(--sp-3);
  display:flex;flex-direction:column;gap:var(--sp-1);
  border:1px solid var(--hair);border-radius:var(--r-md);
  background:var(--surface-base);box-shadow:var(--elev-2);
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity var(--dur-fast) var(--ease);
}
/* `:focus` as well as `:focus-visible`, and that is not belt-and-braces: a
   TOUCH tap focuses without matching :focus-visible, and there is no hover on
   a touch screen at all -- so without this the whole list is unreachable on a
   phone. */
.reports-faces:hover .report-pop,
.reports-faces:focus .report-pop,
.reports-faces:focus-visible .report-pop,
.reports-faces:focus-within .report-pop{opacity:1;visibility:visible;}
/* [D162] AIR. Ziyad, 2026-08-26: "have some spacing there, the names and the
   list's content look too close together right now."
   Three values moved, and they were all the same mistake -- a 14px face-and-
   name row was being laid out on 8px logic: the gap between the face and the
   name 8 -> 12, the row's own padding 4 -> 6/8, and the popover's padding
   8 -> 12. The face grows 18 -> 22 to match the row it came from. */
.report-pop-row{
  display:flex;align-items:center;gap:var(--sp-3);padding:6px var(--sp-2);
  font-size:var(--fs-caption);font-weight:400;line-height:var(--lh-caption);
  color:var(--ink-primary);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis;
}
.report-pop-row img{
  width:22px;height:22px;flex:none;border-radius:var(--r-xs);
  object-fit:cover;object-position:50% 30%;background:var(--surface-sunken);
}

/* [D93] NO RECORD IS A STATE, NOT AN ESSAY.
   It was two paragraphs of explanation. The design system's absence rule is a
   label and a cause: "Not recorded... Always these words, in muted ink, never
   a dash or a blank cell." The hatch carries it visually, so the copy does not
   have to. */
.norecord{
  margin-top:14px;padding:18px 16px;
  border:1px dashed var(--border-resting);border-radius:var(--r-md);
  background:var(--surface-sunken);text-align:center;
}
.norecord-mark{
  height:44px;margin-bottom:14px;border-radius:var(--r-xs);
  /* A hatch, not a chart. It reads as "there is nothing to draw here" without
     pretending to be data -- and it carries no hue, so it states nothing. */
  background-image:repeating-linear-gradient(
    -45deg, var(--border-hairline) 0 6px, transparent 6px 12px);
  border:1px solid var(--border-hairline);
}
.norecord-label{
  font-size:var(--fs-label);line-height:var(--lh-label);font-weight:600;
  color:var(--ink-primary);text-align:center;
}
.norecord-why{
  margin-top:4px;font-size:var(--fs-caption);line-height:var(--lh-caption);
  color:var(--ink-muted);text-align:center;
}
.norecord-rule{
  margin-top:10px;padding-top:10px;border-top:1px solid var(--border-hairline);
  font-size:var(--fs-caption);line-height:var(--lh-caption);
  color:var(--ink-secondary);text-align:center;
}

/* [D94] The provenance line: one fact, the rest behind the infotip. */
.rail-source{
  margin-top:14px;padding-top:12px;border-top:1px solid var(--border-hairline);
  display:flex;align-items:center;gap:6px;flex-wrap:wrap;
  font-size:var(--fs-caption);line-height:var(--lh-caption);
  color:var(--ink-secondary);
}
.rail-source-k{color:var(--ink-muted);}
.rail-source-flag{
  margin-left:auto;padding:1px 7px;
  border:1px solid var(--border-resting);border-radius:var(--r-sm);
  font-size:var(--fs-caption);color:var(--ink-secondary);
}

/* ===========================================================================
   [D98] ONE NAVIGATION TRANSITION, EVERYWHERE.

   Ziyad, 2026-08-22: clicking Team Poll "jumps immediately to the Before You
   Start screen. Add a smooth transition... Apply the same transition pattern
   consistently to navigation throughout the mockup. This should feel similar
   to the transition between evaluating different people."

   Every screen re-renders by replacing its root element, so a CSS animation on
   that root replays on each navigation without a single line of JavaScript --
   and it is therefore identical on sign-in, the dashboard, the ballot and the
   submit screen. No exceptions to keep in sync.

   The vocabulary is the design system's own and nothing else is permitted:
   "Opacity, 4px translate, and progress width." dur-base, ease-standard.

   ⚠️ The system also says "No page transitions on the ballot: a several-hundred
   question flow cannot afford animation on every step." That concern is cost,
   and this is a 180ms opacity fade with a 4px rise on ONE element -- cheaper
   than the per-card stagger the ballot already ran. Reduced motion removes it
   entirely via the global override.
   =========================================================================== */
@keyframes view-in{
  from{opacity:0;transform:translateY(4px);}
  to{opacity:1;transform:none;}
}
.canvas,.signin,.shell{animation:view-in var(--dur-base) var(--ease) both;}

/* ===========================================================================
   [D110] HISTORICAL RESULTS, AND THE RESULTS DOCUMENT
   ===========================================================================

   Nothing below introduces a literal value that is not already a token, with
   two stated exceptions: the verdict ramp reuses --data-1..4 plus an outline
   for the fifth band, and the hatch reuses the missing-record hatch from D93.

   The document deliberately drops the app sidebar. Christian, 1620: "the
   results page needs to be more like you're not in a dashboard, you're going
   through like a single page experience... think of it as if it was like a
   DocuSign or like a DocHub." A results document has nowhere to navigate to
   except back, so the rail would be furniture that does nothing.
   =========================================================================== */

/* ---- the index -------------------------------------------------------- */

/* [D131] The facts and the action share one row: the action is pushed to the
   end rather than given a band of its own. Baseline alignment would drop the
   button below the values, so the row centres. */
.poll-facts{
  display:flex;flex-wrap:wrap;align-items:center;gap:var(--sp-4) var(--sp-8);
  margin-top:var(--sp-5);padding-top:var(--sp-5);
  border-top:1px solid var(--border-hairline);
}
.poll-facts .doc-open{margin-left:auto;}
/* [D160] The same pair, on Historical Results. It was 2px; it is --sp-1 now,
   for the same reason and out of the same token. */
.poll-fact{display:flex;flex-direction:column;gap:var(--sp-1);}
.poll-fact-k{font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);}
.poll-fact-v{font-size:var(--fs-label);line-height:var(--lh-label);font-weight:600;color:var(--ink-primary);}
/* [D131] A compact empty state. It replaced a full .tool-card that stood
   156px tall to say one sentence -- an empty shelf should not take as much
   room as a full one. */
.empty-strip{
  display:flex;gap:var(--sp-4);align-items:baseline;
  padding:var(--sp-4) var(--sp-5);
  border:1px dashed var(--border-hairline);border-radius:var(--r-md);
  background:var(--surface-sunken);
  font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);
}
.empty-strip b{color:var(--ink-primary);}
.empty-strip-k{
  flex:none;padding:2px var(--sp-2);border:1px solid var(--border-hairline);
  border-radius:var(--r-xs);background:var(--surface-base);
  font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);white-space:nowrap;
}
.empty-strip div{max-width:86ch;}
.doc-open{
  margin-left:auto;display:inline-flex;align-items:center;gap:7px;
  height:var(--h-control-sm);padding:0 var(--sp-4);
  border:1px solid var(--border-resting);border-radius:var(--r-sm);
  font-size:var(--fs-label);font-weight:600;color:var(--ink-primary);text-decoration:none;
  transition:border-color var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease);
}
.doc-open:hover{border-color:var(--border-hover);background:var(--surface-interactive);}
/* [D134] One line per rule. The title-plus-sentence pair said the same thing
   twice, the second time longer. A hairline between rules carries the
   separation that the second line used to. */
.side-list{margin-top:var(--sp-3);list-style:none;padding:0;}
.side-rule{
  padding:var(--sp-3) 0;font-size:var(--fs-label);line-height:var(--lh-label);
  color:var(--ink-primary);
}
.side-rule + .side-rule{border-top:1px solid var(--border-hairline);}
.side-rule:first-child{padding-top:0;}
.side-rule:last-child{padding-bottom:0;}

/* ---- the document shell ------------------------------------------------ */

.doc-canvas{min-height:100vh;background:var(--surface-page);animation:view-in var(--dur-base) var(--ease) both;}

.doc-top{
  position:sticky;top:0;z-index:var(--z-sticky);
  display:flex;align-items:center;gap:var(--sp-4);
  height:var(--h-topbar);padding:0 var(--sp-8);
  background:var(--surface-base);border-bottom:1px solid var(--border-hairline);
}
.doc-back{
  display:inline-flex;align-items:center;gap:7px;
  font-size:var(--fs-label);font-weight:600;color:var(--ink-primary);text-decoration:none;
}
/* The arrow glyph is the forward one, turned. One icon, two directions. */
.doc-back svg{transform:rotate(180deg);}
.doc-back:hover{text-decoration:underline;}
.doc-top-title{
  font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);
  padding-left:var(--sp-4);border-left:1px solid var(--border-hairline);
}
.doc-actions{margin-left:auto;display:flex;align-items:center;gap:var(--sp-2);flex-wrap:wrap;}
.doc-btn{
  height:var(--h-control-sm);padding:0 var(--sp-4);
  border:1px solid var(--border-resting);border-radius:var(--r-sm);
  background:var(--surface-base);color:var(--ink-primary);
  font-size:var(--fs-caption);font-weight:600;cursor:pointer;
  transition:border-color var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease);
}
.doc-btn:hover{border-color:var(--border-hover);background:var(--surface-interactive);}
.doc-btn.is-primary{background:var(--surface-inverse);border-color:var(--surface-inverse);color:var(--ink-inverse);}
.doc-btn.is-primary:hover{background:var(--surface-inverse-hover);border-color:var(--surface-inverse-hover);}

/* PERMANENT. Carries no hue: the one hue in this system is the error red, and
   a demonstration notice is not an error. It reads as weight and a hard left
   marker instead. */
.doc-notice{
  display:flex;gap:var(--sp-3);align-items:flex-start;
  padding:var(--sp-4) var(--sp-8);
  background:var(--surface-sunken);border-bottom:1px solid var(--border-hairline);
}
.doc-notice-mark{flex:none;width:3px;align-self:stretch;background:var(--ink-primary);border-radius:var(--r-marker);}
.doc-notice b{display:block;font-size:var(--fs-label);line-height:var(--lh-label);color:var(--ink-primary);}
.doc-notice span{display:block;margin-top:2px;max-width:110ch;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-secondary);}

.doc{width:min(var(--w-content),100% - var(--sp-16));margin:0 auto;padding:var(--sp-12) 0 var(--sp-20);}

.doc-masthead{padding-bottom:var(--sp-8);border-bottom:1px solid var(--border-on-page);}
.doc-h1{font-size:var(--fs-display);line-height:var(--lh-display);letter-spacing:var(--tr-display);font-weight:700;color:var(--ink-primary);}
.doc-period{
  margin-top:var(--sp-1);display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--sp-3);
  font-size:var(--fs-body);line-height:var(--lh-body);color:var(--ink-secondary);
}
/* States, not counts. They read as a line of text rather than sitting at 22px
   in a row of numbers. */
.doc-meta{
  /* [D138] On the page tone, like the masthead rule above it. */
  padding-left:var(--sp-3);border-left:1px solid var(--border-on-page);
  font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);
}
/* [D136] EQUAL CARDS, matching the intro's scope row, so the two screens that
   both summarise the ballot's shape summarise it the same way. Five items at
   five different widths on a bare row was the mess this replaces. */
.doc-facts{
  margin-top:var(--sp-6);
  display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--sp-3);
}
.doc-fact{
  display:flex;flex-direction:column;gap:2px;
  padding:var(--sp-4) var(--sp-5);
  border:1px solid var(--border-hairline);border-radius:var(--r-md);
  background:var(--surface-base);
}
.doc-fact b{font-size:var(--fs-title-lg);line-height:var(--lh-title-lg);letter-spacing:var(--tr-title-lg);font-weight:700;color:var(--ink-primary);}
.doc-fact span{font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);}

.doc-jump{display:flex;flex-wrap:wrap;gap:var(--sp-2);margin-top:var(--sp-6);}
.doc-jump-a{
  padding:6px var(--sp-3);border:1px solid var(--border-hairline);border-radius:var(--r-sm);
  background:var(--surface-base);cursor:pointer;
  font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-secondary);text-decoration:none;
  transition:border-color var(--dur-fast) var(--ease),color var(--dur-fast) var(--ease);
}
.doc-jump-a:hover{border-color:var(--border-hover);color:var(--ink-primary);}
/* [D118] The 80 step separates one SECTION from the next. Between the jump row
   and the section it jumps to, it read as a hole. The masthead block and its
   first section belong together. */
.doc-jump + .doc-sec{margin-top:var(--sp-10);}

/* The ladder, unchanged from the ballot: 80 before a major break. */
/* [D139] 80 -> 56. Ziyad, 2026-08-24: "Spacing between showing results between
   sections of the poll is too much." 80 is the ladder's step before a MAJOR
   break, and it earns that on the ballot where it separates a group of
   questions from the verdict. Here every section is the same kind of thing --
   one more block of results in a document you scroll -- so 56, the step
   between groups, is the right rung. */
.doc-sec{margin-top:var(--sp-14,56px);scroll-margin-top:calc(var(--h-topbar) + var(--sp-5));}
/* [D117] ONE SURFACE FOR RESULTS, ALL THE WAY DOWN. Sections 1 to 3 put their
   rows inside a bordered white panel; from Leadership onward the question
   blocks sat bare on the page tone, so the document visibly changed design
   language halfway through. Ziyad: "from the leadership section onwards, the
   design system is inconsistent with the previous section of this page."
   Same panel as .vlist, same border, radius and surface. */
.doc-panel{
  margin-top:var(--sp-6);padding:0 var(--sp-6) var(--sp-8);
  border:1px solid var(--border-hairline);border-radius:var(--r-md);
  background:var(--surface-base);
}
/* Inside a panel the first overline takes the panel's own step, not the 56
   that separates one group from the next. */
.doc-panel > .doc-group:first-child > .section-head{margin-top:var(--sp-6);}
.doc-panel > .sealed:first-child{margin-top:var(--sp-6);}
.doc-sec-head{margin-bottom:var(--sp-5);}
/* [D136] A quiet prefix, not a bordered square with a digit in it. */
.doc-sec-n{
  display:inline-block;min-width:1.6em;
  font-variant-numeric:tabular-nums;color:var(--ink-muted);font-weight:600;
}
.doc-sec-t{font-size:var(--fs-title);line-height:var(--lh-title);letter-spacing:var(--tr-title);font-weight:700;color:var(--ink-primary);}
.doc-sec-s{margin-top:var(--sp-1);max-width:78ch;font-size:var(--fs-body);line-height:var(--lh-body);color:var(--ink-secondary);}

/* ---- the verdict ramp -------------------------------------------------- */

/* Five bands, four data tones and an outline. Dark to light, Remove first:
   a pale bar is a person the team is content with, and trouble reads as ink
   gathering at the LEFT EDGE of twenty stacked rows. The system's rule that
   "tone is NEVER the only channel" is met by the fixed order, the legend that
   names every band, the outcome word on every row, and the counts one click
   away. */
/* [D136] The legend is the list's header, inside the same panel. */
/* [D142] THE LEGEND STICKS. Ziyad, 2026-08-24. Twenty rows of five-band bars
   are unreadable once the key has scrolled away -- the tones are the one thing
   on this page you cannot work out from the row itself.

   It sticks BELOW the document's own top bar, so `top` is the topbar's height
   rather than 0, and it sits one layer under it so it slides beneath rather
   than over. Both values derive from the tokens the topbar itself uses, so
   changing the topbar height cannot leave the legend floating in the wrong
   place. */
.vlist-head{
  position:sticky;top:var(--h-topbar);z-index:calc(var(--z-sticky) - 1);
  display:flex;flex-wrap:wrap;gap:var(--sp-5);
  padding:var(--sp-3) var(--sp-4);
  border-bottom:1px solid var(--border-hairline);background:var(--surface-sunken);
}
.vlegend-i{display:inline-flex;align-items:center;gap:var(--sp-2);font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-secondary);}
.vkey{width:14px;height:10px;flex:none;border-radius:var(--r-marker);}
.vkey.v0{background:var(--data-1);}
.vkey.v1{background:var(--data-2);}
.vkey.v2{background:var(--data-3);}
.vkey.v3{background:var(--data-4);}
.vkey.v4{background:var(--surface-base);border:1px solid var(--border-resting);}
.vkey.vna{background:var(--surface-base);border:1px dashed var(--border-resting);}

/* [D136] A footnote under the list, not a full-width white bar above it with
   six hundred pixels of nothing beside one line of text. */
.vrule{
  margin-top:var(--sp-3);max-width:100ch;
  font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);
}
.vrule b{color:var(--ink-primary);}

/* [D142] `overflow:clip`, NOT `overflow:hidden`. `hidden` makes the element a
   scroll container, and a sticky child sticks to its nearest scrolling
   ancestor -- so the legend would have stuck inside a box that never scrolls,
   which looks exactly like sticky being ignored. `clip` clips the rows to the
   rounded corners identically without establishing that scrollport. */
.vlist{margin-top:var(--sp-5);border:1px solid var(--border-hairline);border-radius:var(--r-md);background:var(--surface-base);overflow:clip;}
.vrow + .vrow{border-top:1px solid var(--border-hairline);}
.vrow-head{
  width:100%;display:flex;align-items:center;gap:var(--sp-4);
  padding:var(--sp-3) var(--sp-4);background:none;border:0;cursor:pointer;text-align:left;
  transition:background var(--dur-fast) var(--ease);
}
.vrow-head:hover{background:var(--surface-interactive);}
.vrow-face{flex:none;border-radius:var(--r-sm);object-fit:cover;}
/* [D143] 210 -> 244. At 14px the widest subtitle in this column, "2 people,
   led by Muhammad Muneeb", measures 233 and was being ellipsed. The width
   comes out of `.stack`, which is flex:1 and has 120px of its own minimum. */
.vrow-id{flex:none;width:244px;min-width:0;display:flex;flex-direction:column;gap:1px;}
.vrow-name{font-size:var(--fs-label);line-height:var(--lh-label);font-weight:600;color:var(--ink-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.vrow-dept{font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.vrow-out{flex:none;width:150px;display:flex;flex-direction:column;gap:1px;text-align:right;}
.vrow-word{font-size:var(--fs-label);line-height:var(--lh-label);font-weight:600;color:var(--ink-primary);}
.vrow-n{font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);}
.vrow-chev{flex:none;display:flex;color:var(--ink-muted);transition:transform var(--dur-fast) var(--ease);}
.vrow-head[aria-expanded="true"] .vrow-chev{transform:rotate(180deg);}

.vrow-body{padding:var(--sp-2) var(--sp-4) var(--sp-8) calc(var(--sp-4) + 34px + var(--sp-4));background:var(--surface-sunken);}
/* [D140] The counts are the SUMMARY of the row you just opened; everything
   under them is the detail behind it. Without a rule the two ran together and
   the first question read as a sixth count. */
.vrow-counts{
  display:flex;flex-wrap:wrap;gap:var(--sp-3);
  padding-bottom:var(--sp-4);margin-bottom:var(--sp-2);
  border-bottom:1px solid var(--border-on-page);
}

/* ---- the stacked bar --------------------------------------------------- */

/* [D116] NO TRACK. It had a sunken fill, a hairline border and 2px of padding,
   which drew a container around a bar that already fills its own width -- the
   segments always sum to the turnout, so the "empty" part of a track never
   exists. Ziyad: "the stacked bars seem to have some sort of a background
   colour and even a visible border, no need of that." The segments are the
   bar. */
.stack{
  flex:1;min-width:120px;display:flex;gap:2px;height:14px;overflow:hidden;
}
.stack .seg{display:block;height:100%;border-radius:0;min-width:2px;}
.stack .seg.v0{background:var(--data-1);}
.stack .seg.v1{background:var(--data-2);}
.stack .seg.v2{background:var(--data-3);}
.stack .seg.v3{background:var(--data-4);}
.stack .seg.v4{background:var(--surface-base);box-shadow:inset 0 0 0 1px var(--border-resting);}
/* "No view" is not a point on the scale and must not read as one. */
.stack .seg.vna{background:var(--surface-base);box-shadow:inset 0 0 0 1px var(--border-resting);opacity:.55;}

.distline{display:flex;flex-wrap:wrap;gap:var(--sp-3);margin-top:var(--sp-2);}
.dl{font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);}
.dl b{margin-left:5px;font-weight:700;color:var(--ink-secondary);}
.dl.is-modal{color:var(--ink-primary);}
.dl.is-modal b{color:var(--ink-primary);}
.dl.is-abstain{font-style:italic;}

/* ---- a question result ------------------------------------------------- */

.qres{margin-top:var(--sp-8);}
.doc-group > .qres:first-of-type{margin-top:var(--sp-5);}
/* `.section-head:first-child{margin-top:0}` is right inside a ballot panel and
   wrong here: the group overline is the first child of its .doc-group, so it
   collapsed hard against the section's own subtitle. Every group overline in
   the document takes the 56 step, first one included.
   NOTE the selector: `:first-of-type` matches by ELEMENT type, not by class,
   and .doc-sec-head is also a div -- so the obvious `.doc-group:first-of-type`
   never matched anything and the first overline stayed collapsed. */
.doc-sec .doc-group > .section-head{margin-top:var(--sp-14,56px);}
.qres-q{margin-bottom:var(--sp-3);font-size:var(--fs-body);line-height:var(--lh-body);font-weight:600;color:var(--ink-primary);}
.qres-note{margin-top:var(--sp-2);font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);}
.qres .stack{display:flex;}

.doc-group + .doc-group{margin-top:var(--sp-14,56px);}

/* ---- allocations ------------------------------------------------------- */

.alloc{display:flex;flex-direction:column;gap:var(--sp-2);}
.alloc-row{display:flex;align-items:center;gap:var(--sp-3);}
.alloc-label{flex:none;width:230px;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-secondary);}
/* [D153] THE DATA BARS ARE SQUARE. Ziyad, 2026-08-25: "keep the showcasing of
   the bars sharp for eg for the question 'Which departments should have a team
   lead they currently do not?' the bar is rounded, keep it sharp, no radius."

   Scoped to the results document's MEASURING bars -- allocations, name tallies
   and the verdict stack. A rounded end on a bar whose length is the number
   shortens the reading of it at both ends; a square end stops exactly where the
   value stops. Progress tracks elsewhere keep --r-track: they are chrome
   reporting how far through you are, not a quantity being compared. */
.alloc-track{flex:1;height:12px;border-radius:0;background:var(--surface-sunken);border:1px solid var(--border-hairline);overflow:hidden;}
.alloc-fill{display:block;height:100%;background:var(--data-1);}
.alloc-pct{flex:none;width:44px;text-align:right;font-size:var(--fs-caption);line-height:var(--lh-caption);font-weight:700;color:var(--ink-primary);}

/* ---- name tallies ------------------------------------------------------ */

.names{display:flex;flex-direction:column;gap:var(--sp-2);}
.names-row{display:flex;align-items:center;gap:var(--sp-3);}
.names-face{flex:none;border-radius:var(--r-xs);object-fit:cover;}
.names-name{flex:none;width:200px;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.names-track{flex:1;height:12px;border-radius:0;background:var(--surface-sunken);border:1px solid var(--border-hairline);overflow:hidden;}
.names-fill{display:block;height:100%;background:var(--data-2);}
/* 28px fitted two digits and clipped the third. Twenty-one voters means a
   count can be two digits today and a bigger team makes it three. */
.names-n{flex:none;width:34px;text-align:right;font-size:var(--fs-caption);line-height:var(--lh-caption);font-weight:700;color:var(--ink-primary);}
/* An option label needs more room than a person's name. */
.names-name.is-wide{width:300px;white-space:normal;}

/* ---- the sealed state -------------------------------------------------- */

/* The same hatch as D93's missing record, and for the same reason: it reads as
   "there is nothing to draw here" without pretending to be data, and it
   carries no hue so it states nothing. */
.sealed{
  display:flex;gap:var(--sp-3);align-items:stretch;
  padding:var(--sp-4);border:1px dashed var(--border-hairline);border-radius:var(--r-sm);
  background:var(--surface-sunken);
}
.sealed.is-big{margin-top:var(--sp-6);}
.sealed + .sealed{margin-top:var(--sp-3);}
.sealed-hatch{
  flex:none;width:34px;border-radius:var(--r-xs);
  background:repeating-linear-gradient(135deg,var(--border-hairline) 0 5px,var(--surface-base) 5px 10px);
}
.sealed-copy b{display:block;font-size:var(--fs-label);line-height:var(--lh-label);color:var(--ink-primary);}
.sealed-copy span{display:block;margin-top:3px;max-width:88ch;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);}

/* ---- the footer -------------------------------------------------------- */

.doc-foot{margin-top:var(--sp-20);padding-top:var(--sp-8);border-top:1px solid var(--border-hairline);}
.doc-foot-t{font-size:var(--fs-heading);line-height:var(--lh-heading);font-weight:600;color:var(--ink-primary);margin-bottom:var(--sp-4);}
.doc-foot .doc-actions{margin-left:0;}
.doc-foot-p{margin-top:var(--sp-4);max-width:84ch;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);}

/* Print is a real export path (doc 10 section 10a asks for PDF), so the
   document has to survive it: the chrome goes, the content stays. */
@media print{
  .doc-top,.doc-jump{display:none;}
  .doc-canvas{background:#fff;}
  .doc{width:100%;padding:0;}
  .vrow-body{display:block !important;}
  .doc-sec{margin-top:var(--sp-12);break-inside:avoid;}
}

@media (max-width:1100px){
  .doc-facts{grid-template-columns:repeat(2,minmax(0,1fr));}
  .vrow-id{width:170px;}
  .vrow-out{width:130px;}
  .alloc-label{width:170px;}
  .names-name{width:150px;}
}

/* ===========================================================================
   [D125] THE PEOPLE PAGE
   ===========================================================================
   Reuses the home furniture wholesale -- .home-body, .home-col, .home-label,
   .card-lite, .side-card, .vlist -- so the directory is visibly the same
   product as the dashboard rather than a second design. Nothing below
   introduces a value that is not already a token.
   =========================================================================== */

/* A count beside a group label, so "The team" carries its size without a
   second line. */
.home-label{display:flex;align-items:baseline;gap:var(--sp-2);}
.label-count{font-size:var(--fs-caption);line-height:var(--lh-caption);font-weight:400;color:var(--ink-muted);}

/* ---- your own profile -------------------------------------------------- */

.prof{padding:var(--sp-6);}
.prof-main{display:flex;gap:var(--sp-6);align-items:flex-start;}
/* [D127] .prof-face and its button are redefined below: the photo became
   the control and the separate button is gone. */
.prof-id{min-width:0;flex:1;}
.prof-name{font-size:var(--fs-title);line-height:var(--lh-title);letter-spacing:var(--tr-title);font-weight:700;color:var(--ink-primary);}
.prof-role{
  margin-top:2px;display:flex;align-items:center;gap:var(--sp-2);flex-wrap:wrap;
  font-size:var(--fs-body);line-height:var(--lh-body);color:var(--ink-secondary);
}
.prof-rows{
  margin-top:var(--sp-5);display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:var(--sp-4) var(--sp-8);
}
.prof-rows dt{font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);}
.prof-rows dd{margin-top:2px;font-size:var(--fs-label);line-height:var(--lh-label);font-weight:600;color:var(--ink-primary);word-break:break-word;}
/* [D151] .prof-foot / .prof-note removed with the sentence they styled. */

/* ---- buttons, shared --------------------------------------------------- */

.btn-quiet{
  height:var(--h-control-sm);padding:0 var(--sp-4);
  border:1px solid var(--border-resting);border-radius:var(--r-sm);
  background:var(--surface-base);color:var(--ink-primary);cursor:pointer;
  font-size:var(--fs-label);font-weight:600;
  transition:border-color var(--dur-fast) var(--ease),background var(--dur-fast) var(--ease);
}
.btn-quiet:hover{border-color:var(--border-hover);background:var(--surface-interactive);}
.btn-quiet.sm{height:var(--h-control-xs);padding:0 var(--sp-3);font-size:var(--fs-caption);}
.btn-solid{
  height:var(--h-control);padding:0 var(--sp-5);
  border:1px solid var(--surface-inverse);border-radius:var(--r-sm);
  background:var(--surface-inverse);color:var(--ink-inverse);cursor:pointer;
  font-size:var(--fs-label);font-weight:600;
  transition:background var(--dur-fast) var(--ease);
}
.btn-solid:hover{background:var(--surface-inverse-hover);}

/* ---- chips ------------------------------------------------------------- */

/* Rounded rectangles, no fill that could read as a state on a data row. */
/* HEIGHT-LOCKED to the label line it sits on. Left to its own padding the
   chip stood 23px against an 18px name line, so any row carrying one came out
   5px taller and the directory had three different row heights. */
.chip{
  height:18px;display:inline-flex;align-items:center;
  padding:0 var(--sp-2);border:1px solid var(--border-hairline);border-radius:var(--r-xs);
  font-size:var(--fs-caption);line-height:1;font-weight:600;color:var(--ink-secondary);
  background:var(--surface-sunken);white-space:nowrap;
}
.chip-admin{color:var(--ink-primary);}
.chip-you{background:var(--surface-inverse);border-color:var(--surface-inverse);color:var(--ink-inverse);}

/* ---- the directory ----------------------------------------------------- */

/* [D151] The Invite button sits CLOSER to the search. Ziyad, 2026-08-25:
   "Spacing between Invite button on People page and the search bar need to be
   lesser." It measured 32px, not the 16 the gap says: an empty .dir-count span
   sits between the two and an empty flex child still collects a gap on BOTH
   sides. Collapsing it when it has nothing to say, and dropping the step to
   --sp-3, takes the pair from 32px apart to 12. */
.dir-tools{display:flex;align-items:center;gap:var(--sp-3);margin-bottom:var(--sp-3);}
.dir-count:empty{display:none;}
.dir-search{
  flex:1;max-width:420px;height:var(--h-control);padding:0 var(--sp-4);
  border:1px solid var(--border-resting);border-radius:var(--r-sm);
  background:var(--surface-base);color:var(--ink-primary);
  font-size:var(--fs-body);font-family:inherit;
}
.dir-search::placeholder{color:var(--ink-muted);}
.dir-search:focus{outline:2px solid var(--border-focus);outline-offset:2px;border-color:var(--border-hover);}
.dir-count{font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);}

/* A GRID, NOT A FLEX ROW. As flex with one flexible cell, the name column
   collapsed to about 200px at a 784px list, so long names wrapped and row
   heights came out at 61, 78, 79 and 97 -- a directory that does not line up.
   Explicit tracks make every row identical by construction, and the name
   ellipsizes rather than wrapping. */
/* [D154] THE COLUMNS ARE RE-CUT FOR 14px TEXT. The widths below were measured
   against 12px strings, so "Existing, Web Development" and "United Kingdom"
   both started truncating the moment the type floor rose (D143) -- and an
   ellipsis in a directory is the same failure as a wrap: the reader does not
   get the fact.

   The money comes from the STATE column, which no longer needs 112px now that
   its glyph is gone (D151): "Can sign in" is the longest string in it. Role
   160 -> 190, location 88 -> 108, state 112 -> 80. The name column is
   minmax(0,1fr) and absorbs the 34px difference; it has the most slack because
   most names are far shorter than the longest one. */
.dir-row{
  /* The gutter drops 16 -> 12 as well: six gutters across the row is 24px that
     the address column needs at 1280, and 12 still separates the columns
     cleanly at this type size. */
  display:grid;align-items:center;gap:var(--sp-3);
  grid-template-columns:34px minmax(0,1fr) 190px 108px 80px;
  padding:var(--sp-3) var(--sp-4);min-height:58px;
}
.dir-row.has-act{grid-template-columns:34px minmax(0,1fr) 190px 108px 80px 56px;}
.dir-row + .dir-row{border-top:1px solid var(--border-hairline);}
.dir-row[hidden]{display:none;}
.dir-face{flex:none;border-radius:var(--r-sm);object-fit:cover;background:var(--surface-sunken);}
.dir-face.is-blank{
  width:34px;height:34px;display:block;border-radius:var(--r-sm);
  background:repeating-linear-gradient(135deg,var(--border-hairline) 0 5px,var(--surface-base) 5px 10px);
}
/* Height-locked for the same reason as .chip: the name line and the address
   line under it round differently per string, which left rows at 60 and 61.
   36 is 18 + 1 + 17 exactly, so every row is 60 including its padding. */
.dir-id{min-width:0;overflow:hidden;height:36px;display:flex;flex-direction:column;justify-content:center;gap:1px;}
.dir-name{
  display:flex;align-items:center;gap:var(--sp-2);min-width:0;overflow:hidden;
  font-size:var(--fs-label);line-height:var(--lh-label);font-weight:600;color:var(--ink-primary);
}
/* The name itself truncates; the chips never do. */
.dir-name > span:first-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.dir-name .chip{flex:none;}
.dir-mail{font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.dir-cell{min-width:0;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.dir-place{color:var(--ink-muted);}
/* [D151] The 7px gap went with the glyph it separated. */
.dir-state{display:flex;align-items:center;}
.dir-act{display:flex;justify-content:flex-end;}

/* [D151] .dot-ok / .dot-wait removed. The state was already in words beside
   them; the marker was the same fact a second time. */

.dir-empty{padding:var(--sp-5) var(--sp-4);}
.dir-empty b{display:block;font-size:var(--fs-label);line-height:var(--lh-label);color:var(--ink-primary);}
.dir-empty span{display:block;margin-top:3px;max-width:80ch;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);}
.dir-note{margin-top:var(--sp-3);font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);}
.dir-note b{color:var(--ink-secondary);font-weight:600;}

/* ---- the invite form --------------------------------------------------- */

.invite{padding:var(--sp-6);}
.invite-lede{max-width:78ch;font-size:var(--fs-body);line-height:var(--lh-body);color:var(--ink-secondary);}
.invite-grid{
  margin-top:var(--sp-5);display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:var(--sp-4) var(--sp-6);
}
.fld{display:flex;flex-direction:column;gap:6px;min-width:0;}
.fld-k{font-size:var(--fs-caption);line-height:var(--lh-caption);font-weight:600;color:var(--ink-secondary);}
.fld-i{
  height:var(--h-control);padding:0 var(--sp-3);
  border:1px solid var(--border-resting);border-radius:var(--r-sm);
  background:var(--surface-base);color:var(--ink-primary);
  font-size:var(--fs-body);font-family:inherit;width:100%;
}
.fld-i::placeholder{color:var(--ink-muted);}
.fld-i:focus{outline:2px solid var(--border-focus);outline-offset:2px;border-color:var(--border-hover);}

.invite-photo{
  margin-top:var(--sp-5);padding-top:var(--sp-5);border-top:1px solid var(--border-hairline);
  display:flex;gap:var(--sp-4);align-items:flex-start;
}
.invite-photo-face{
  flex:none;width:56px;height:56px;border-radius:var(--r-md);
  background:repeating-linear-gradient(135deg,var(--border-hairline) 0 5px,var(--surface-base) 5px 10px);
  background-size:cover;background-position:center;
}
.invite-photo-note{margin-top:6px;max-width:64ch;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);}
.invite-photo .fld-k{margin-bottom:6px;display:block;}
.invite-foot{margin-top:var(--sp-5);display:flex;align-items:center;gap:var(--sp-4);}
/* The one hue, used for the one thing it is for. */
.invite-err{font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--error);font-weight:600;}

/* ---- admins panel ------------------------------------------------------ */

.admin-list{margin-top:var(--sp-3);display:flex;flex-direction:column;gap:var(--sp-3);}
.admin-row{display:flex;align-items:center;gap:var(--sp-3);}
.admin-row img{flex:none;border-radius:var(--r-xs);object-fit:cover;}
.admin-name{font-size:var(--fs-label);line-height:var(--lh-label);font-weight:600;color:var(--ink-primary);}
.admin-role{font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);}

/* 1360, not 1200. At 1280 the admin view carries an extra 56px action column,
   which left about 90px for a name and address -- measured as real overflow on
   five rows. Location is the first thing worth losing: it is the only column
   the poll never acts on. */
@media (max-width:1360px){
  .dir-place{display:none;}
  /* [D154] Re-cut for 14px, same trade as the wide layout: the state column
     gives up 32px it only needed for a glyph that no longer exists, and the
     role column takes it -- "Existing, Web Development" is the longest string
     in the row and was being cut at 150. The padding drops a rung too, which
     is the last 8px the address column needs to render in full. */
  .dir-row{grid-template-columns:34px minmax(0,1fr) 182px 80px;padding-left:var(--sp-3);padding-right:var(--sp-3);}
  .dir-row.has-act{grid-template-columns:34px minmax(0,1fr) 182px 80px 56px;}
}
@media (max-width:1000px){
  .prof-rows,.invite-grid{grid-template-columns:1fr;}
}

/* ===========================================================================
   [D127] EDIT AFFORDANCES, THE INVITE DIALOG, AND THE ACCOUNT MENU
   =========================================================================== */

/* ---- the profile card's two edits ------------------------------------- */

/* Ziyad, 2026-08-24: no "Change photo" button and no "Edit name and photo"
   button. The affordance now sits ON the thing it edits -- the photo is the
   control, and the card's own edit is a bare pencil in its top right. */
.prof{position:relative;}
.prof-edit{
  position:absolute;top:var(--sp-4);right:var(--sp-4);
  width:var(--h-control-xs);height:var(--h-control-xs);
  display:flex;align-items:center;justify-content:center;
  border:1px solid transparent;border-radius:var(--r-sm);
  background:none;color:var(--ink-muted);cursor:pointer;
  transition:color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease),
             background var(--dur-fast) var(--ease);
}
.prof-edit:hover{color:var(--ink-primary);border-color:var(--border-resting);background:var(--surface-interactive);}
.prof-edit:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px;}

/* The photo IS the button. The veil sits over it at low opacity on hover and
   on keyboard focus, so the affordance is not mouse-only. */
.prof-face{
  position:relative;width:88px;height:88px;flex:none;padding:0;
  border:0;border-radius:var(--r-md);background:none;cursor:pointer;
  display:block;overflow:hidden;
}
.prof-face img{width:100%;height:100%;border-radius:var(--r-md);object-fit:cover;display:block;background:var(--surface-sunken);}
.prof-face-veil{
  position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  border-radius:var(--r-md);color:var(--ink-inverse);
  background:rgba(11,11,12,.42);
  opacity:0;transition:opacity var(--dur-fast) var(--ease);
}
.prof-face:hover .prof-face-veil,
.prof-face:focus-visible .prof-face-veil{opacity:1;}
.prof-face:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px;}

/* ---- the invite control ------------------------------------------------ */

/* The ONE filled control on the page. Everything else here looks at people;
   this is the only thing that adds one, and it should not look like the Edit
   buttons beside it. */
.btn-invite{
  flex:none;height:var(--h-control);padding:0 var(--sp-5);
  display:inline-flex;align-items:center;gap:var(--sp-2);
  border:1px solid var(--surface-inverse);border-radius:var(--r-sm);
  background:var(--surface-inverse);color:var(--ink-inverse);cursor:pointer;
  font-size:var(--fs-label);font-weight:600;white-space:nowrap;
  transition:background var(--dur-fast) var(--ease);
}
.btn-invite:hover{background:var(--surface-inverse-hover);}
.btn-invite:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px;}
.btn-invite-plus{font-size:17px;line-height:1;font-weight:400;margin-top:-1px;}
.dir-tools{align-items:center;}

/* ---- the dialog -------------------------------------------------------- */

.modal{position:fixed;inset:0;z-index:var(--z-modal);display:flex;align-items:center;justify-content:center;padding:var(--sp-6);}
.modal[hidden]{display:none;}
.modal-scrim{position:absolute;inset:0;background:rgba(11,11,12,.45);}
.modal-panel{
  position:relative;width:min(660px,100%);max-height:calc(100vh - var(--sp-12));
  display:flex;flex-direction:column;
  border:1px solid var(--border-hairline);border-radius:var(--r-lg);
  background:var(--surface-base);box-shadow:var(--elev-3);
  animation:view-in var(--dur-base) var(--ease) both;
}
.modal-head{
  display:flex;gap:var(--sp-4);align-items:flex-start;
  padding:var(--sp-6) var(--sp-6) var(--sp-5);border-bottom:1px solid var(--border-hairline);
}
.modal-title{font-size:var(--fs-title);line-height:var(--lh-title);letter-spacing:var(--tr-title);font-weight:700;color:var(--ink-primary);}
.modal-sub{margin-top:4px;max-width:70ch;font-size:var(--fs-body);line-height:var(--lh-body);color:var(--ink-secondary);}
.modal-x{
  flex:none;margin-left:auto;width:var(--h-control-sm);height:var(--h-control-sm);
  display:flex;align-items:center;justify-content:center;
  border:1px solid transparent;border-radius:var(--r-sm);
  background:none;color:var(--ink-muted);cursor:pointer;
  transition:color var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease);
}
.modal-x:hover{color:var(--ink-primary);border-color:var(--border-resting);}
.modal-body{padding:var(--sp-6);overflow:auto;}
.modal-body .invite-grid{margin-top:0;}
.modal-foot{
  display:flex;align-items:center;gap:var(--sp-3);justify-content:flex-end;
  padding:var(--sp-5) var(--sp-6);border-top:1px solid var(--border-hairline);
}
.modal-foot .invite-err{margin-right:auto;}

/* ---- the account menu -------------------------------------------------- */

.topbar-wrap{position:relative;margin-left:auto;}
.topbar-me{
  display:flex;align-items:center;gap:10px;padding:5px 8px 5px 5px;
  border:1px solid transparent;border-radius:var(--r-sm);
  background:none;cursor:pointer;text-align:left;font-family:inherit;
  transition:background var(--dur-fast) var(--ease),border-color var(--dur-fast) var(--ease);
}
.topbar-me:hover{background:var(--surface-interactive);border-color:var(--border-hairline);}
.topbar-me:focus-visible{outline:2px solid var(--border-focus);outline-offset:2px;}
.topbar-chev{display:flex;color:var(--ink-muted);transition:transform var(--dur-fast) var(--ease);}
.topbar-me[aria-expanded="true"] .topbar-chev{transform:rotate(180deg);}

.who-menu{
  position:absolute;top:calc(100% + var(--sp-2));right:0;z-index:var(--z-menu);
  min-width:250px;padding:var(--sp-2);
  border:1px solid var(--border-hairline);border-radius:var(--r-md);
  background:var(--surface-base);box-shadow:var(--elev-2);
}
.who-menu[hidden]{display:none;}
.who-menu-head{padding:var(--sp-3) var(--sp-3) var(--sp-4);border-bottom:1px solid var(--border-hairline);margin-bottom:var(--sp-2);}
.who-menu-name{font-size:var(--fs-label);line-height:var(--lh-label);font-weight:600;color:var(--ink-primary);}
.who-menu-mail{margin-top:2px;font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--ink-muted);}
.who-item{
  width:100%;display:flex;align-items:center;gap:var(--sp-3);
  height:var(--h-control);padding:0 var(--sp-3);
  border:0;border-radius:var(--r-sm);background:none;cursor:pointer;
  font-size:var(--fs-label);font-weight:500;color:var(--ink-primary);
  text-align:left;text-decoration:none;font-family:inherit;
  transition:background var(--dur-fast) var(--ease);
}
.who-item:hover{background:var(--surface-interactive);}
.who-item:focus-visible{outline:2px solid var(--border-focus);outline-offset:-2px;}
.who-item svg{flex:none;color:var(--ink-muted);}

/* ===========================================================================
   [REAL] Styles for the parts the mock did not have.

   Everything here is built from the existing tokens -- no new colours, no new
   type sizes. The one hue in this system is --error, reserved for errors, and
   it is used below only where something has genuinely failed.
   =========================================================================== */

/* ---- inline failures ---- */

/* Sign-in and submit both fail in place rather than through an alert: the
   field that has to change is on screen, and a dialog hides it. */
.signin-error,
.submit-error,
.adm-msg {
  margin: 10px 0 0;
  color: var(--error);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
}
.signin-error:empty,
.submit-error:empty,
.adm-msg:empty { display: none; }

/* ---- the results document, before it resolves ---- */

.doc-loading {
  padding: 64px 24px;
  text-align: center;
  color: var(--ink-muted);
  font-size: var(--fs-body);
}

/* THE SEAL. Not an error state -- no hue. It is a rule being stated, so it
   reads as a page of its own rather than a warning bolted onto an empty one. */
.doc-sealed {
  max-width: 560px;
  margin: 72px auto;
  padding: 40px;
  background: var(--surface-base);
  border: 1px solid var(--border-hairline);
  border-radius: 10px;
  text-align: center;
}
.doc-sealed-mark { color: var(--ink-muted); margin-bottom: 16px; }
.doc-sealed h1 {
  margin: 0 0 12px;
  font-size: var(--fs-title);
  line-height: var(--lh-title);
  letter-spacing: var(--tr-title);
}
.doc-sealed p {
  margin: 0 0 12px;
  color: var(--ink-secondary);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}
.doc-sealed-why { color: var(--ink-muted); }

/* ---- written answers, published ---- */

/* Quotes are set as blocks with a rule at the left, not in quotation marks:
   these are people's own words about colleagues, and typographic quotation
   marks read as reported speech -- as if someone were characterising them. */
.quotes { display: flex; flex-direction: column; gap: 10px; margin: 12px 0 8px; }
.quote {
  margin: 0;
  padding: 12px 14px;
  background: var(--surface-sunken);
  border-left: 2px solid var(--border-on-page);
  border-radius: 0 6px 6px 0;
  color: var(--ink-primary);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  white-space: pre-wrap;   /* people write in paragraphs; keep their breaks */
  overflow-wrap: anywhere; /* an unbroken URL must not widen the document */
}

/* ---- admin ---- */

/* The admin screen is reachable signed out, so the shell renders without the
   nav rail and the top bar. Without this the content sits in a grid column
   that no longer has a sibling to balance it. */
.shell.is-bare { grid-template-columns: 1fr; }

.adm-msg { margin-bottom: 10px; }

/* The setup form reuses the sign-in field, which was built for one-line
   inputs; the decision rule is a paragraph. */
.signin-field textarea.signin-input {
  min-height: 76px;
  padding: 10px 0;
  resize: vertical;
  font-family: inherit;
}

/* ===========================================================================
   [REAL] POLL ADMIN

   Rebuilt because the first pass explained itself in paragraphs: prose sat
   between every label and its field, collided with the label above it, and
   buried the two actions the screen exists for. An operator here wants the
   state, the numbers and the next move.

   Hints are ONE LINE each, sit under the label and above the field they
   describe, and are set at caption size in muted ink so they read as
   annotation rather than instruction.
   =========================================================================== */

.adm {
  max-width: 720px;
  margin: 0 auto;
  padding: 28px 24px 64px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.adm-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}
.adm-title {
  margin: 0;
  font-size: var(--fs-title);
  line-height: var(--lh-title);
  letter-spacing: var(--tr-title);
}
.adm-sub {
  margin: 4px 0 0;
  color: var(--ink-muted);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
}

/* The status is a word, not a colour: the one hue in this system is reserved
   for errors, and an open poll is not an error. Weight and a hairline carry it. */
.adm-status {
  flex: none;
  padding: 4px 10px;
  border: 1px solid var(--border-on-page);
  border-radius: 999px;
  font-size: var(--fs-caption);
  font-weight: 600;
  white-space: nowrap;
}
.adm-status.is-open   { border-color: var(--ink-primary); color: var(--ink-primary); }
.adm-status.is-closed { background: var(--surface-inverse); border-color: var(--surface-inverse); color: var(--ink-inverse); }
.adm-status.is-idle   { color: var(--ink-muted); }

.adm-card {
  padding: 20px;
  background: var(--surface-base);
  border: 1px solid var(--border-hairline);
  border-radius: 10px;
}
.adm-h2 {
  margin: 0 0 14px;
  font-size: var(--fs-heading);
  line-height: var(--lh-heading);
}

/* Numbers first. This row is the answer to "where is the poll up to". */
.adm-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: 12px;
}
.adm-stat { display: flex; flex-direction: column; gap: 2px; }
.adm-stat-n {
  font-size: var(--fs-title);
  line-height: var(--lh-title);
  font-weight: 600;
  letter-spacing: var(--tr-title);
}
.adm-stat-l {
  color: var(--ink-muted);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
}

/* ---- form ---- */

.adm-field { margin-bottom: 18px; }
.adm-label {
  display: block;
  font-size: var(--fs-label);
  line-height: var(--lh-label);
  font-weight: 600;
}
/* The hint sits BETWEEN label and field, with its own margins. The previous
   version let a paragraph run into the label above it. */
.adm-hint {
  margin: 2px 0 8px;
  color: var(--ink-muted);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
}
.adm-hint code {
  padding: 1px 5px;
  background: var(--surface-sunken);
  border-radius: 4px;
  font-size: .92em;
}

.adm-input {
  width: 100%;
  padding: 10px 12px;
  background: var(--surface-base);
  border: 1px solid var(--border-resting);
  border-radius: 8px;
  color: var(--ink-primary);
  font-family: inherit;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
}
.adm-input:hover { border-color: var(--border-hover); }
.adm-input:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
  border-color: var(--border-selected);
}
.adm-textarea { min-height: 84px; resize: vertical; }
/* A quorum is at most three digits; a full-width box invites a paragraph. */
.adm-number { max-width: 140px; }

.adm-actions { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }

/* Closing is irreversible, so it is NOT the primary button and never sits
   where a reflex click lands. It is a secondary control that says what it does. */
.adm-danger { border-color: var(--error); color: var(--error); }
.adm-danger:hover { background: var(--error-bg); }

.adm-foot {
  margin: 12px 0 0;
  color: var(--ink-muted);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
}
.adm-msg, .adm-warn { color: var(--error); font-size: var(--fs-caption); line-height: var(--lh-caption); }
.adm-msg { margin: 0 0 10px; }
.adm-msg:empty { display: none; }
.adm-warn { margin: 0; }
.adm-ok { margin: 0; font-size: var(--fs-caption); line-height: var(--lh-caption); }

@media (max-width: 640px) {
  .adm { padding: 20px 16px 48px; }
  .adm-head { flex-direction: column; }
}

/* ===========================================================================
   SMALL SCREENS — added by the audit of 2026-08-27.

   THE SHELL HAD NO BREAKPOINT AT ALL. `.shell` is a two-column grid whose
   first column is a fixed 248px rail, so on a 390px phone the content column
   was handed 142px and everything inside it pushed straight off the right-hand
   edge: measured at 656px of scroll width on the dashboard, 802px on the
   ballot. The page scrolled sideways on every screen, and the progress bar and
   the person's own name sat outside the viewport entirely.

   That matters more here than it usually would. Twenty-one people are being
   asked to sit through a long ballot, most of them on a phone, and a form you
   have to scroll sideways to read is one people abandon half-finished.

   THE DESKTOP DESIGN IS UNTOUCHED. Everything below is inside a max-width
   query, so the approved 1280px layout renders exactly as before. Under 860px
   the rail stops being a column and becomes a horizontal band above the
   content -- same links, same order, same active treatment moved from the left
   border to the bottom one -- and the header lets its right-hand cluster wrap
   instead of forcing a width nothing can honour.
   =========================================================================== */
@media (max-width: 860px) {
  /* One column -- and minmax(0, 1fr), not 1fr.

     A `1fr` track is really `minmax(auto, 1fr)`, and that `auto` floor means
     the TRACK refuses to be narrower than its widest item's min-content. So
     the rail stayed 408px wide on a 390px screen no matter what min-width or
     overflow it was given: the item was willing to shrink, the track was not.
     minmax(0, ...) removes the floor and lets the rail's own overflow-x do the
     work. */
  .shell { grid-template-columns: minmax(0, 1fr); }

  .rail {
    position: static; height: auto; align-self: stretch;
    flex-direction: row; align-items: center; gap: 14px;
    padding: 10px 16px;
    border-right: 0; border-bottom: 1px solid var(--line);
    /* The nav scrolls WITHIN the band rather than making the page scroll.
       min-width:0 is what makes that work: a grid item defaults to
       min-width:auto, which means "never shrink below your content", so
       overflow-x alone left the rail 408px wide inside a 390px screen and the
       PAGE scrolled instead of the band. */
    min-width: 0;
    overflow-x: auto; overflow-y: hidden;
  }
  /* "Team Evaluation" is a column heading for a column that no longer exists. */
  .rail-section { display: none; }
  .rail-brand { flex: none; }
  .rail-nav { margin-top: 0; flex-direction: row; gap: 4px; }
  .rail-item {
    height: 34px; padding: 0 10px; white-space: nowrap; flex: none;
    border-left: 2px solid transparent; border-bottom: 2px solid transparent;
  }
  /* Active moves from the left edge to the bottom, which is where a horizontal
     nav reads it. */
  .rail-item.is-active { border-left-color: transparent; border-bottom-color: var(--ink); }

  /* The ballot header. .header-right was 433px wide inside a 390px viewport
     because nothing was allowed to wrap. */
  .header { height: auto; min-height: 52px; padding: 8px 16px; flex-wrap: wrap; row-gap: 8px; }
  .header-divider, .page-label { display: none; }
  .header-right { flex-wrap: wrap; gap: 10px; row-gap: 6px; min-width: 0; }
  .progress-track { width: 88px; }

  .topbar { height: auto; min-height: 56px; padding: 10px 16px; gap: 12px; }
  /* A long name must ellipsis rather than push the topbar wider than the
     screen. */
  .topbar-me { min-width: 0; }
  .topbar-name, .topbar-role {
    display: block; max-width: 46vw;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }

  /* Wide content keeps its own scroller instead of scrolling the page. */
  .adm, .main-panel { min-width: 0; }
  table { display: block; overflow-x: auto; max-width: 100%; }
}

/* ---------------------------------------------------------------------------
   The three remaining fixed-width layouts, at the widths where they stop
   fitting. Each was measured rather than guessed: 360px is the common Android
   width, 320px the narrowest phone still in use.
   ------------------------------------------------------------------------- */
@media (max-width: 700px) {
  /* THE DASHBOARD'S SIDE COLUMN IS A FIXED 320px (--w-rail) NEXT TO A 1fr.
     Below about 700 that pair cannot fit, and the side cards hung 16px past
     the right edge. Stacked, the side cards simply follow the main column. */
  .home-body { grid-template-columns: minmax(0, 1fr); padding: var(--sp-6) 16px var(--sp-8); }
  /* The top padding on the second column exists to align its heading with the
     first column's. Stacked, there is nothing to align to. */
  .home-col + .home-col { padding-top: 0; }
}

@media (max-width: 860px) {
  /* THE PEOPLE DIRECTORY IS A SIX-COLUMN GRID adding up to more than a phone
     is wide. It is a table in all but name, so it gets a table's answer:
     scroll it inside its own box rather than stretching the page. The row keeps
     its columns, so nothing about how it reads changes. */
  #dir-list { overflow-x: auto; }
  .dir-row { min-width: 560px; }
}

@media (max-width: 380px) {
  /* THE BALLOT FOOTER: Back and Continue side by side need about 350px with
     their padding. Below that they wrap onto two rows rather than push the
     page sideways -- Continue stays full width and first to the thumb. */
  .nav { flex-wrap: wrap; gap: 8px; }
  .nav-btn { flex: 1 1 auto; min-width: 0; padding: 0 14px; justify-content: center; }
}

/* The autosave indicator in the ballot header. Quiet by default -- it says
   "Saving" for a moment and "Saved" for a moment more -- and in the error
   colour when a save has failed, because that is the only state the person
   needs to act on. See saveIndicator() in app.js. */
.save-state{
  font-size:var(--fs-caption);line-height:var(--lh-caption);color:var(--muted);
  white-space:nowrap;
}
.save-state[hidden]{display:none;}
.save-state.is-error{color:var(--error);font-weight:600;}
