/* =====================================================================   Prime Foundry — F2 "Daylight (brand)" design system, for the base apps.

   Sections 1-9 are COPIED VERBATIM from the cockpit's source of truth:
     Allonsygmbh/foundry  app/assets/stylesheets/tokens.css
                          app/assets/stylesheets/brand-fonts.css
                          app/assets/stylesheets/inter-font.css
   Do not "improve" those values here. If the cockpit's tokens change, re-copy.
   The base apps must read as the same product as the cockpit.

   Section 10 is the only original CSS: the handful of classes the boards need
   (stat trio, #N chip, to-do rows, prose bodies). It derives every colour,
   radius and font from the tokens above — no new palette, no ad-hoc values.
   ============================================================================ */

/* ---- 1. brand type: Source Serif 4 (display) + IBM Plex Mono (data) + Inter (body)
        The same Google Fonts import the cockpit's brand-fonts.css / inter-font.css use. ---- */
@import url('https://fonts.googleapis.com/css2?family=Source+Serif+4:opsz,wght@8..60,600;8..60,700&family=IBM+Plex+Mono:wght@400;500&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap');

/* ---- 2. palette + type tokens (verbatim) ---- */
:root {
  /* Palette — paper base, white surfaces, hairline neutrals, navy accent, status set */
  --pf-paper:     #FAF3E3;  /* app background */
  --pf-bg:        #FAF3E3;  /* alias — app background */
  --pf-surface:   #FFFFFF;  /* panels / cards */
  --pf-surface-2: #F2ECDF;  /* raised / hover / active nav */
  --pf-line:      #E7E3DA;  /* hairlines */
  --pf-line-2:    #D9D3C5;  /* stronger divider */
  --pf-ink:       #1C1E21;  /* primary text */
  --pf-muted:     #545A61;  /* secondary text */
  --pf-faint:     #8A9099;  /* tertiary / labels */
  --pf-navy:      #122F4D;  /* accent navy */
  --pf-navy-2:    #0C2138;  /* accent navy hover */
  --pf-accent:    #122F4D;  /* alias — primary action */
  --pf-accent-2:  #0C2138;  /* alias — accent hover */
  --pf-green:     #274F42;
  --pf-mint:      #B7DFD2;
  /* A6 anvil brand accents (2026-07): brass = secondary detail (never body
     text on cream), ember = pure signal (glow/working states, never text) */
  --pf-brass:     #BA9745;
  --pf-ember:     #E8641E;
  /* status: live/online = green, casting = amber, attention = red */
  --pf-ok:        #274F42;  /* online / authed / ready  (live) */
  --pf-warn:      #B4732A;  /* pending / casting        (cast) */
  --pf-bad:       #B23A3A;  /* offline / logged-out     (dead) */
  --pf-radius:    12px;
  --pf-radius-sm: 8px;
  --pf-disp: "Source Serif 4", Georgia, "Times New Roman", serif;
  --pf-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --pf-mono: "IBM Plex Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
  /* Desk / PF-Chat night panel — copied verbatim from the Hub's tokens.css so
     the Cockpit's Desk and the Hub's Desk are the same surface, not a
     lookalike. Brass + ember already live above; this completes the set. */
  --pf-night:       #0C2138;  /* desk panel background */
  --pf-night-ink:   #F2ECDC;  /* light text on night */
  --pf-night-muted: #93A0B0;  /* secondary text on night */
  --pf-night-line:  color-mix(in srgb, #FAF3E3 13%, transparent); /* hairline on night */
  --pf-night-well:  #081827;  /* code well inside perm card */
}

/* ---- 3. base (verbatim) ---- */
/* No reset ships with these pages, so the browser's default 8px body margin let
   the paper background show above and beside the full-bleed topbar — the header
   read as a floating white card with cream down both sides, and the brand mark
   sat 8px from the screen edge instead of on the content gutter (owner: "der
   Hammer sieht auf Mobile abgeschnitten aus ganz links", 2026-07-29). The Hub
   carried this line in its own overlay; it belongs to both. */
body.pf { margin: 0; }
.pf { background: var(--pf-paper); color: var(--pf-ink); font-family: var(--pf-sans);
      font-size: 14px; line-height: 1.55; -webkit-font-smoothing: antialiased; }
.pf-mono { font-family: var(--pf-mono); font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.pf-muted { color: var(--pf-muted); }
.pf-label { font-family: var(--pf-sans); font-size: 12px; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: var(--pf-navy); }

/* ---- 4. brand lockup: anvil badge + Source Serif wordmark (verbatim) ---- */
.pf-wrap { max-width: 1160px; margin: 0 auto; padding: 0 30px; }
.pf-brand { display: inline-flex; align-items: center; gap: 12px; text-decoration: none;
            font-family: var(--pf-disp); font-weight: 700; font-size: 19px; letter-spacing: -.01em; color: var(--pf-ink); }
.pf-brand .pf-word-2 { color: var(--pf-muted); font-weight: 600; }
.pf-brand .pf-mark, .pf-brand img.pf-mark { width: auto; height: 30px; flex: none; display: block; }

/* ---- 5. top-bar cockpit shell (verbatim) ---- */
.pf-topbar-wrap { background: var(--pf-surface); border-bottom: 1px solid var(--pf-line); }
.pf-bar { display: flex; align-items: center; justify-content: space-between; gap: 18px; height: 72px; }
/* Header C (copied from the Hub): the nav sits left, next to the brand, and the
   account chip is the only thing on the right. `margin-right: auto` is what
   pushes it there — the bar's space-between then has nothing left to spread. */
.pf-topnav { display: flex; gap: 2px; margin-right: auto; margin-left: 14px; }
.pf-topnav a { font-family: var(--pf-sans); font-size: 13px; font-weight: 500; color: var(--pf-muted);
               text-decoration: none; padding: 8px 13px; border-radius: 8px; }
.pf-topnav a:hover { color: var(--pf-ink); background: var(--pf-surface-2); }
.pf-topnav a.active { color: var(--pf-navy); background: var(--pf-surface-2); }
.pf-acct { position: relative; display: flex; align-items: center; gap: 12px; font-family: var(--pf-mono); font-size: 12px; color: var(--pf-muted); }
/* Vertical padding only — the horizontal inset comes solely from .pf-wrap, so
   the content column lines up with the topbar brand (the Hub's rule, verbatim). */
/* Longhand on purpose: <main class="pf-wrap pf-content"> carries BOTH
   classes, and the shorthand form of this rule (source order winner) was
   zeroing .pf-wrap's horizontal gutters — content sat flush against the
   screen edge on every phone (owner report, Hub settings, 2026-07-29). */
.pf-content { padding-top: 30px; padding-bottom: 60px; box-sizing: border-box; }
/* border-box above: both shells stretch .pf-content to width:100%, and a
   content-box 100% plus the gutters is a page that scrolls sideways. */

/* ---- the two doors out: Hub, Sign out ----
   This was an avatar chip over a popover, ported from the Hub. It hid the only
   link back to the Hub behind a click on an anonymous circle, and the owner
   duly failed to find it. Both actions are now labelled links in the bar, so
   there is no open state to style and no controller to run: a header that
   cannot be closed cannot lock anyone in.

   Both read at --pf-muted; Sign out separates itself on hover, where it turns
   --pf-bad. Leaving is the rarer intent, but it does not get to be quieter than
   the rest of the chrome to say so — --pf-faint is under the AA floor for text,
   and a door is the last thing to render at 2.91:1. A permanently red word in
   the bar reads as an error the page is reporting rather than an offer, so the
   colour waits for the pointer. */
/* Element-qualified on purpose: this skin sits ABOVE the button base in the
   file, and (0,1,1) is what lets a quiet bar action stay quiet under the
   later (0,1,0) base rule. */
a.pf-baract, button.pf-baract { gap: 7px; font-weight: 500; color: var(--pf-muted); min-height: 0;
             padding: 8px 12px; border-radius: 8px;
             border-color: transparent; background: none; box-shadow: none; }
.pf-baract svg { width: 16px; height: 16px; stroke: currentColor; fill: none; flex: none; }
.pf-baract:hover { color: var(--pf-ink); background: var(--pf-surface-2); }
.pf-baract-out:hover { color: var(--pf-bad); background: color-mix(in srgb, var(--pf-bad) 7%, transparent); }
/* The pair sits tighter than the chip did: two words need less air than an
   avatar, and .pf-acct's 12px gap between them reads as two separate widgets. */
.pf-acct-plain { gap: 4px; }
/* A door that wraps mid-word is a door that reads broken: the bar's brand and
   pills never wrap — the bar itself is what gives way (flex gaps shrink). */
.pf-brand > span { white-space: nowrap; }
.pf-baract { white-space: nowrap; }

/* ---- quiet footer (header C, copied from the Hub) ---- */
.pf-foot { border-top: 1px solid var(--pf-line); }
.pf-foot-inner { display: flex; align-items: center; justify-content: space-between; gap: 14px;
                 padding: 16px 0 18px; font-family: var(--pf-mono); font-size: 11.5px; color: var(--pf-muted); }
.pf-foot a { color: var(--pf-muted); text-decoration: underline; text-underline-offset: 2px; }
.pf-foot a:hover { color: var(--pf-muted); }

/* ---- mobile bottom tab bar (header C, copied from the Hub) ----
   Fixed app-style primary nav with >=44px targets and safe-area padding.
   Hidden on desktop; never rendered on the immersive Desk (the chat page's
   own back arrow is the way out there, exactly as in the Hub). */
.pf-tabbar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 40; display: none;
             background: var(--pf-surface); border-top: 1px solid var(--pf-line-2);
             padding: 6px 6px calc(6px + env(safe-area-inset-bottom));
             box-shadow: 0 -14px 30px -24px color-mix(in srgb, var(--pf-ink) 40%, transparent); }
.pf-tabbar-inner { display: flex; max-width: 560px; margin: 0 auto; }
.pf-tab { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
          min-height: 48px; text-decoration: none; color: var(--pf-muted); border-radius: 10px; }
.pf-tab svg { width: 22px; height: 22px; stroke: currentColor; fill: none; display: block; }
.pf-tab-label { font: 600 10px/1 var(--pf-sans); letter-spacing: .01em; }
.pf-tab.active { color: var(--pf-navy); }
.pf-tab:active { background: var(--pf-surface-2); }
/* The tab bar's own badge: same meaning as the nav pill's, sized for an icon. */
/* The tab bar's own badge. It cannot inherit the pill badge's look — that rule
   is scoped to `.pf-topnav a` — so it is stated here in full, as a count on the
   icon's shoulder rather than a chip after a label there is no room for. */
.pf-tab-ico { position: relative; display: block; }
.pf-tab .nav-badge { position: absolute; top: -5px; left: 12px; margin: 0;
                     min-width: 16px; height: 16px; padding: 0 4px; box-sizing: border-box;
                     border-radius: 999px; background: var(--pf-navy); color: var(--pf-paper);
                     font: 600 10px/16px var(--pf-sans); text-align: center;
                     border: 2px solid var(--pf-surface); }

/* ---- 6. primitives (verbatim) ---- */
.pf-card { background: var(--pf-surface); border: 1px solid var(--pf-line); border-radius: var(--pf-radius);
           padding: 18px 20px; box-shadow: 0 14px 30px -24px rgba(28,30,33,.35); }
/* ============================  THE BUTTON  ================================
   One component, every pill (owner order 2026-07-31, after two rounds of
   "identical" buttons rendering at different sizes). The BASE below owns the
   box model, layout, typography, floors, focus ring, press and busy states —
   for .pf-btn AND for every legacy family name that still appears in markup
   (aliased into the same selector). A family may keep a SKIN (colors, its
   own radius or padding) further down or in chat.css, but never its own box
   model or type again. If a new button is not a .pf-btn or one of these
   aliases, it is a bug by definition. The Hub renders this same component
   through ButtonComponent (ViewComponent): one recipe, two renderers. */
.pf-btn, .need-btn, .chat-starter, .perm-btn, .pf-baract,
.pf-voice-btn, .pf-model-btn, .chat-signedout-fix {
  box-sizing: border-box; appearance: none;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  min-height: 36px; padding: 8px 14px;
  border: 1px solid var(--pf-line-2); border-radius: var(--pf-radius-sm);
  background: linear-gradient(180deg,#fff,#f3f5f7); color: var(--pf-ink);
  font: 600 13px/1 var(--pf-sans); text-decoration: none; white-space: nowrap;
  cursor: pointer; box-shadow: inset 0 1px 0 #fff;
  transition: background-color .15s ease, border-color .15s ease,
              color .15s ease, transform .12s ease;
}
/* An icon inside a pill is a flex item, never inline text: display:block
   removes the baseline slot (and its descender gap) entirely, so an icon can
   only ever sit where align-items puts it — dead centre. */
.pf-btn > svg { display: block; flex: none; }
.pf-btn:hover { border-color: var(--pf-faint); }
.pf-btn:focus-visible, .need-btn:focus-visible, .chat-starter:focus-visible,
.pf-baract:focus-visible, .pf-voice-btn:focus-visible, .pf-model-btn:focus-visible {
  outline: none; border-color: var(--pf-navy);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--pf-navy) 15%, transparent);
}
.pf-btn:disabled, .need-btn:disabled, .pf-model-btn:disabled {
  opacity: .5; cursor: not-allowed; }
/* faces */
.pf-btn-primary { background: var(--pf-navy); border-color: var(--pf-navy); color: #fff;
                  box-shadow: 0 8px 20px -8px rgba(18,47,77,.55); }
.pf-btn-primary:hover { background: var(--pf-navy-2); border-color: var(--pf-navy-2); }
.pf-btn--ink { background: var(--pf-ink); border-color: var(--pf-ink); color: var(--pf-paper); box-shadow: none; }
.pf-btn--ink:hover { border-color: var(--pf-ink); transform: translateY(-1px); }
.pf-btn-danger { background: color-mix(in srgb, var(--pf-bad) 8%, #fff);
                 border-color: color-mix(in srgb, var(--pf-bad) 40%, transparent); color: var(--pf-bad); }
.pf-btn-danger:hover { border-color: var(--pf-bad); }
.pf-btn--bare { background: none; box-shadow: none; }
/* On-dark faces: a CTA sitting on a navy panel (the landing's contact card,
   dark surfaces generally). Paper = solid light on dark; its outline twin is
   the quiet second action beside it. */
.pf-btn--paper { background: #FAF3E3; border-color: transparent; color: var(--pf-navy); box-shadow: none; }
.pf-btn--paper:hover { background: #fff; border-color: transparent; }
.pf-btn--paper-outline { background: none; border-color: rgba(250,243,227,.35); color: #FAF3E3; box-shadow: none; }
.pf-btn--paper-outline:hover { border-color: rgba(250,243,227,.7); }
/* shapes + sizes */
.pf-btn--pill { border-radius: 999px; }
.pf-btn-sm { min-height: 30px; padding: 6px 10px; font-size: 12px; }
.pf-btn-lg { min-height: 48px; padding: 12px 24px; font-size: 14.5px; border-radius: 10px; }
.pf-btn-xs { min-height: 0; padding: 3px 9px; font-size: 11.5px; font-weight: 500; border-radius: 999px; }
/* press + async states (moved out of hub.css so both renderers share them) */
.pf-btn:active { transform: translateY(1px); box-shadow: inset 0 1px 3px rgba(0,0,0,.12); }
.pf-btn.is-busy { transform: translateY(1px); cursor: progress; color: var(--pf-muted);
  background: linear-gradient(180deg,#eef0f2,#e6e9ec); box-shadow: inset 0 1px 3px rgba(0,0,0,.14);
  border-color: #b8bdc4; }
.pf-btn.is-done { color: var(--pf-ok);
  border-color: color-mix(in srgb, var(--pf-ok) 32%, #c8ccd2);
  background: linear-gradient(180deg, #fdfffe, color-mix(in srgb, var(--pf-mint) 26%, #eef0f2)); }
.spin { width: 11px; height: 11px; border-radius: 50%; flex: none; display: inline-block;
  border: 1.5px solid rgba(18,47,77,.22); border-top-color: var(--pf-navy);
  animation: pf-spin .8s linear infinite; }
.pf-btn .spin { margin-right: 6px; vertical-align: -1px; }
@keyframes pf-spin { to { transform: rotate(360deg); } }
/* the thumb floor: EVERY pill of the family, one rule (xs chips exempt on
   purpose — a filter chip is not a primary action) */
@media (max-width: 760px) {
  .pf-btn, .need-btn, .chat-starter, .perm-btn, .pf-baract,
  .pf-voice-btn, .chat-signedout-fix { min-height: 44px; }
  .pf-btn-xs { min-height: 0; }
}
/* the model-swap chip: an xs pill, quiet by default */
.pf-model-btn { min-height: 0; padding: 3px 9px; border-radius: 999px;
                background: var(--pf-surface); color: var(--pf-muted);
                font: 500 11.5px/1.5 var(--pf-sans); box-shadow: none; }
.pf-model-btn:hover { border-color: var(--pf-faint); color: var(--pf-ink); }

.pf-badge { display: inline-flex; align-items: center; gap: 6px; padding: 3px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 600; }
.pf-badge::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.pf-badge-ok      { color: var(--pf-ok);   background: color-mix(in srgb, var(--pf-ok) 12%, transparent); }
.pf-badge-warn    { color: var(--pf-warn); background: color-mix(in srgb, var(--pf-warn) 14%, transparent); }
.pf-badge-bad     { color: var(--pf-bad);  background: color-mix(in srgb, var(--pf-bad) 12%, transparent); }

/* ---- 8. typography + layout helpers (verbatim) ---- */
.pf-h1 { font-family: var(--pf-disp); font-size: 32px; font-weight: 700; letter-spacing: -.02em; line-height: 1.06; margin: 0 0 6px; }
.pf-h2 { font-family: var(--pf-disp); font-size: 20px; font-weight: 700; letter-spacing: -.01em; margin: 0 0 12px; }
.pf-eyebrow { font-family: var(--pf-sans); font-size: 12px; font-weight: 600; letter-spacing: .18em; text-transform: uppercase; color: var(--pf-navy); }
.pf-lead { color: var(--pf-muted); margin: 0 0 18px; font-size: 15px; }
.pf-stack > * + * { margin-top: 14px; }
.pf-section + .pf-section { margin-top: 22px; }
.pf-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.pf-row-between { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.pf-grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 14px; }
.pf-hint { color: var(--pf-muted); font-size: 12px; }
.pf-link { color: var(--pf-navy); text-decoration: none; }
.pf-link:hover { text-decoration: underline; }

.pf-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.pf-field > label { font-size: 12px; font-weight: 600; color: var(--pf-muted); }
.pf-input, .pf-select, textarea.pf-input {
  width: 100%; box-sizing: border-box; padding: 9px 11px; border-radius: var(--pf-radius-sm);
  border: 1px solid var(--pf-line-2); background: var(--pf-surface); color: var(--pf-ink);
  font: 400 14px/1.4 var(--pf-sans); }
.pf-input:focus, .pf-select:focus, textarea.pf-input:focus {
  outline: none; border-color: var(--pf-navy); box-shadow: 0 0 0 3px rgba(18,47,77,.12); }
.pf-input.pf-mono { font-family: var(--pf-mono); }

.pf-empty { color: var(--pf-muted); border: 1px dashed var(--pf-line-2); border-radius: var(--pf-radius); padding: 22px; text-align: center; }

/* ---- 9. focus visibility + responsive + reduced motion (verbatim) ---- */
.pf a:focus-visible, .pf button:focus-visible, .pf-topnav a:focus-visible,
.pf-input:focus-visible, .pf-select:focus-visible, .forge:focus-visible {
  outline: 2px solid var(--pf-navy); outline-offset: 2px; }

@media (max-width: 760px) {
  .pf-wrap { padding: 0 18px; }
  .pf-content { padding-top: 22px; padding-bottom: 44px; }
  .pf-h1 { font-size: 26px; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* =====================================================================   10. Base-apps surfaces. The ONLY original CSS in this file. Everything below
   is composed from the tokens above — no new colours, radii or fonts.
   ============================================================================ */

/* ---- page head: eyebrow / two-tone display headline / lead ----
   Kept tight to the list below it: a big head over a far-away list reads
   top-heavy, and the board should feel composed as one block. */
.pf-head { margin-bottom: 20px; }
.pf-h1 .pf-h1-2 { display: block; color: var(--pf-muted); }
.pf-head .pf-lead { max-width: 46ch; }

/* The account slot used to be dropped on a phone: the old bar WRAPPED, and a
   third row of chrome for a single-owner box was not worth the height. Header C
   (2026-07) keeps the bar to one slim row and puts identity in a 44px chip, so
   it now fits — and it is the only control left up there once the nav has moved
   to the bottom tab bar. It stays. */
@media (max-width: 760px) {
  .pf-head .pf-stats { margin-top: 18px; }
}

/* ---- "How this board works" explainer card ----
   Quiet instructional card at the foot of a board: body-toned prose, roomy
   line-height, a real list. Sits below the content because it is read once —
   the board itself stays the hero. */
/* ---- "How this board works" — THE standard explainer component -----------
   One component on every board (todo, research, meetings, emails); only the
   prose differs. Markup contract, exactly this shape:

     <section class="pf-section pf-how">
       <details class="pf-card">
         <summary>How this board works</summary>
         <div class="pf-how-body"> ...board-specific prose... </div>
       </details>
     </section>

   Collapsed by default (it is read once; it does not get to be the first
   screen). The chevron is drawn HERE via a CSS mask so pages cannot drift,
   and the spacing to head above / board below is owned HERE — never by the
   pages. */
.pf-how { margin: 0 0 28px; }
.pf-how .pf-card { padding: 0; color: var(--pf-muted); font-size: 14px; line-height: 1.65; }
.pf-how summary { display: flex; align-items: center; justify-content: space-between; gap: 12px;
                  padding: 13px 20px; cursor: pointer; list-style: none;
                  font: 600 13px/1.4 var(--pf-sans); color: var(--pf-muted); }
.pf-how summary::-webkit-details-marker { display: none; }   /* Safari's own triangle */
.pf-how summary:hover { color: var(--pf-ink); }
.pf-how summary::after { content: ""; width: 14px; height: 14px; flex: none;
                  background: var(--pf-faint);
                  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 6l4 4 4-4' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
                  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 6l4 4 4-4' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
                  transition: transform .16s ease; }
.pf-how details[open] summary { border-bottom: 1px solid var(--pf-line); }
.pf-how details[open] summary::after { transform: rotate(180deg); }
.pf-how-body { padding: 16px 20px 18px; }
.pf-how-body strong, .pf-how-body em { color: var(--pf-ink); }
.pf-how-body p { margin: 0 0 10px; }
.pf-how-body p:last-child { margin-bottom: 0; }
.pf-how-body ul { margin: 0 0 10px; padding-left: 22px; }
.pf-how-body li { margin: 4px 0; }
.pf-how-body code { font-family: var(--pf-mono); font-size: 12.5px; background: var(--pf-surface-2);
  padding: 1px 5px; border-radius: 5px; }
.pf-how-body a { color: var(--pf-navy); }

/* ---- pending-count badge on a nav pill (today: Emails drafts) ----
   A small navy counter so unreviewed drafts are visible from every board.
   Sits inside the pill so the hover/active backgrounds keep wrapping both. */
.pf-topnav a .nav-badge { display: inline-block; margin-left: 6px; min-width: 18px;
  padding: 1px 5px; border-radius: 999px; background: var(--pf-navy); color: #fff;
  font-family: var(--pf-mono); font-variant-numeric: tabular-nums;
  font-size: 10.5px; font-weight: 500; line-height: 1.5; text-align: center;
  vertical-align: 1px; }

/* ---- stat trio: big mono numerals over tiny uppercase labels ---- */
.pf-stats { display: flex; gap: 30px; }
.pf-stat { text-align: right; }
.pf-stat-n { font-family: var(--pf-mono); font-variant-numeric: tabular-nums; font-size: 26px;
             font-weight: 500; line-height: 1; color: var(--pf-ink); }
.pf-stat-l { font-family: var(--pf-sans); font-size: 10.5px; font-weight: 600; letter-spacing: .14em;
             text-transform: uppercase; color: var(--pf-muted); margin-top: 6px; }
.pf-stat-ok   .pf-stat-n { color: var(--pf-ok); }
.pf-stat-warn .pf-stat-n { color: var(--pf-warn); }
.pf-stat-wip  .pf-stat-n { color: var(--pf-warn); }

/* ---- the stable item number: a ledger line-number, not a chip ----
   A slim mono figure that sits quietly just before the text it names. Tabular
   figures in a fixed, right-aligned column so #9 and #10 line up and the text
   column starts on a true vertical. Never boxed: a border here turns a
   reference mark into something that looks pressable. */
.num { flex: none; width: 2.4em; text-align: right;
       font-family: var(--pf-mono); font-variant-numeric: tabular-nums;
       font-size: 12.5px; font-weight: 400; letter-spacing: -.01em;
       color: var(--pf-muted); white-space: nowrap; user-select: none; }

/* ---- the reading column ----
   A list stretched to 1160px puts the task at the far left and its timestamp at
   the far right, with a lake of nothing between them — vertically dense but
   horizontally empty. Hold the boards to a measure, and hold the page head to
   the SAME measure so the stat trio lands on the ledger's right edge instead of
   floating off alone. The dashboard's tile grid (`#tiles`) spans the full shell
   and keeps its own wide head. */
#board, .pf-head-col { max-width: 46rem; }
/* ...except the Kanban, which is three columns and needs the whole shell. Held
   to 46rem it sat in a left-hand strip with the same lake of empty to its right
   that the measure above exists to prevent — only worse, because a board of
   columns reads as broken when it is squeezed into one. Its head goes wide too,
   so the stat trio lands on the board's right edge instead of floating mid-page. */
#board.board-kanban { max-width: none; }
.pf-head.board-kanban-head { max-width: none; }

/* ---- the ledger: ONE framed panel of hairline-divided rows ----
   Not a stack of floating cards. The frame is the card; the rows are content.
   `overflow: hidden` lets a row's hover tint reach the rounded corners. */
.ledger { background: var(--pf-surface); border: 1px solid var(--pf-line);
          border-radius: var(--pf-radius); box-shadow: 0 14px 30px -24px rgba(28,30,33,.35);
          overflow: hidden; }
.ledger > * + * { border-top: 1px solid var(--pf-line); }

/* A to-do row: [#N] [task text — the hero] [when]. One line, ~50px tall. */
.ledger-row { display: flex; align-items: baseline; gap: 14px; padding: 14px 18px;
              transition: background-color .12s ease; }
.ledger-row:hover { background: var(--pf-surface-2); }
.todo-text { flex: 1; min-width: 0; margin: 0;
             font-family: var(--pf-sans); font-size: 16.5px; font-weight: 500; line-height: 1.3;
             color: var(--pf-ink); overflow-wrap: anywhere; }
.todo-time { flex: none; font-family: var(--pf-mono); font-variant-numeric: tabular-nums;
             font-size: 11.5px; color: var(--pf-muted); }

/* Done recedes: paper-toned, tighter, struck, and a quiet tick rather than a
   badge. It is history, not work. */
.ledger-done { background: var(--pf-paper); }
.ledger-done .ledger-row { padding: 11px 18px; }
.ledger-done .todo-text { font-size: 15px; font-weight: 400; color: var(--pf-muted);
                          text-decoration: line-through; text-decoration-color: var(--pf-line-2); }
.tick { flex: none; color: var(--pf-ok); font-size: 12px; line-height: 1; }

/* In progress leans the other way from Done: it is the work in hand, so it is
   the loudest panel on the board, not the quietest. A warm hairline down the
   left edge marks the whole panel — cheaper than tinting every row, and it
   survives the row hover. The chip states it in words rather than a colour dot
   alone, so it still reads without colour. */
.ledger-doing { border-left: 2px solid var(--pf-warn); }
.wip { flex: none; font-family: var(--pf-sans); font-size: 10.5px; font-weight: 600;
       letter-spacing: .08em; text-transform: uppercase; color: var(--pf-warn);
       white-space: nowrap; }

/* A quiet "Done · 1" rule, rather than a second display heading competing with
   the page headline. */
.ledger-rule { display: flex; align-items: center; gap: 12px; margin: 24px 0 10px; }
.ledger-rule::after { content: ""; flex: 1; height: 1px; background: var(--pf-line); }
.ledger-rule-label { font-family: var(--pf-mono); font-size: 11px; letter-spacing: .04em;
                     color: var(--pf-muted); white-space: nowrap; }

/* ---- research + emails: same frame, same rhythm, each entry carries a body ----
   The header line IS a ledger row; the prose hangs beneath it, indented to the
   text column so the #N gutter stays true all the way down the panel. */
.ledger-entry { padding: 15px 18px 17px; transition: background-color .12s ease; }
.ledger-entry:hover { background: var(--pf-surface-2); }
.entry-head { display: flex; align-items: baseline; gap: 14px; }
.entry-title { flex: 1; min-width: 0; margin: 0;
               font-family: var(--pf-disp); font-size: 17px; font-weight: 700; letter-spacing: -.01em;
               line-height: 1.25; color: var(--pf-ink); overflow-wrap: anywhere; }
.entry-when { flex: none; font-family: var(--pf-mono); font-variant-numeric: tabular-nums;
              font-size: 11.5px; color: var(--pf-muted); }
.entry-sub { margin: 4px 0 0; font-family: var(--pf-mono); font-size: 11.5px; color: var(--pf-muted); }
.entry-sub + .prose { margin-top: 9px; }
.entry-head + .prose { margin-top: 8px; }
.prose + .entry-sub { margin-top: 10px; }
/* Emails carry a #N, so their body hangs from the text column: num + row gap.
   Research has no number and starts flush (an empty gutter reads as a mistake).
   The indent holds at every width — zeroing it on mobile put the body to the
   LEFT of the title it belongs to, which reads as a broken layout. */
.entry-indent { padding-left: calc(2.4em + 14px); margin-top: 8px; }

/* ---- the draft's action row ----
   There is no outbox: the only way a draft leaves this board is through the
   owner's clipboard, so these buttons ARE the feature, not a garnish. They sit
   under the body, in the text column, separated by a hairline so they read as
   an action on the draft above rather than the start of the next one.
   `is-done` / `is-bad` are the button's own receipt — pressed, it says what
   happened, in place. */
.entry-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px;
                 padding-top: 12px; border-top: 1px solid var(--pf-line); }
.entry-actions .pf-btn.is-done { color: var(--pf-ok); border-color: var(--pf-ok); }
.entry-actions .pf-btn-primary.is-done { background: var(--pf-ok); border-color: var(--pf-ok); color: #fff; }
.entry-actions .pf-btn.is-bad { color: var(--pf-bad); border-color: var(--pf-bad); }
.entry-actions .pf-btn-primary.is-bad { background: var(--pf-bad); border-color: var(--pf-bad); color: #fff; }
/* A button stays disabled while it shows its receipt, to swallow a double-click.
   The base `.pf-btn:disabled` dims to 50%, which would render "Copied" as a
   greyed-out control — i.e. as a failure. Keep it lit. */
.entry-actions .pf-btn:disabled { opacity: 1; }

/* Archived drafts recede like a struck to-do, but their buttons must stay
   legible: the panel tint is the signal, not a faded control. */
.ledger-done .entry-title { font-weight: 600; color: var(--pf-muted); }

@media (max-width: 760px) {
  .ledger-row, .ledger-entry { padding-left: 14px; padding-right: 14px; }
  /* At 390px four buttons in a row become four buttons on four lines with the
     gutter eating half the width. Drop the entry indent's gutter for the action
     row only, so they wrap into two clean pairs. */
  .entry-actions { margin-left: calc(-2.4em - 14px); }
}

/* ---- section header: serif h2 on the left, mono count on the right ---- */
.pf-section-head { display: flex; align-items: baseline; justify-content: space-between;
                   gap: 12px; margin: 0 0 12px; }
.pf-section-head .pf-h2 { margin: 0; }
.pf-count { font-family: var(--pf-mono); font-size: 11.5px; letter-spacing: .02em; color: var(--pf-muted); }

/* ---- dashboard tiles ---- */
a.pf-card { display: block; text-decoration: none; color: inherit;
            transition: border-color .12s ease, transform .12s ease, box-shadow .12s ease; }
a.pf-card:hover { border-color: var(--pf-navy); transform: translateY(-1px);
                  box-shadow: 0 18px 34px -22px rgba(18,47,77,.45); }
.tile-desc { color: var(--pf-muted); margin: 0 0 18px; min-height: 2.8em; }
.tile-foot { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.tile-stat { font-family: var(--pf-disp); font-size: 24px; font-weight: 700; color: var(--pf-navy);
             letter-spacing: -.01em; }

/* ---- prose bodies (research markdown, email drafts) ---- */
.prose { color: var(--pf-ink); font-size: 14px; line-height: 1.65; }
.prose > *:first-child { margin-top: 0; }
.prose > *:last-child { margin-bottom: 0; }
.prose h3, .prose h4, .prose h5, .prose h6 {
  font-family: var(--pf-disp); font-weight: 700; letter-spacing: -.01em;
  margin: 1.2rem 0 .4rem; color: var(--pf-ink); }
.prose p { margin: .6rem 0; }
.prose ul { margin: .6rem 0; padding-left: 1.25rem; }
.prose li { margin: .2rem 0; }
.prose a { color: var(--pf-navy); text-decoration: underline; text-underline-offset: 2px; }
.prose code { font-family: var(--pf-mono); font-size: .86em; background: var(--pf-surface-2);
              border: 1px solid var(--pf-line); border-radius: 5px; padding: .1em .34em; }
.prose strong { font-weight: 700; }

/* A long writeup scrolls inside its card rather than stretching the page. */
.prose-scroll { max-height: 26rem; overflow-y: auto; }

/* Email drafts are plain text: line breaks are content. */
.prose-pre { white-space: pre-wrap; }

/* The source path under a research card: mono, faint, wraps on mobile. */
.source-path { font-family: var(--pf-mono); font-size: 11px; color: var(--pf-muted);
               overflow-wrap: anywhere; }

/* ---- long tokens must never push a panel wider than the screen ----
   Model output and vault paths contain unbreakable strings (package specs,
   URLs, hashes). The ledger panel clips nothing, so the string has to break. */
.prose { overflow-wrap: anywhere; }
.prose code { overflow-wrap: anywhere; }
.prose-pre { overflow-wrap: anywhere; }
.entry-title, .todo-text { overflow-wrap: anywhere; }

/* =====================================================================   The mobile primary nav — the Hub's bottom tab bar, below 760px.

   The hamburger sheet that used to live here is gone (2026-07). The Hub's
   Cockpit answers the same question with a fixed bottom tab bar, and the owner
   asked for one product, not two: below 760px the inline `.pf-topnav` pills
   give way to `.pf-tabbar` (built by nav.js from the SAME entries), and the
   account chip is the only control left in the bar. `display` for both is
   decided here, in CSS, never by script — so no viewport can end up with both.
   ============================================================================ */

/* The account popover hangs off the bar, so the bar is the positioning context. */
.pf-topbar-wrap { position: relative; }

/* The cockpit shell is a column so the quiet footer sits at the bottom even on
   a short page; the tab-bar body padding keeps the last row above the fixed nav. */
body.pf-cockpit { display: flex; flex-direction: column; min-height: 100dvh; }
body.pf-cockpit .pf-content { flex: 1 0 auto; width: 100%; }

@media (max-width: 760px) {
  /* Header C: the bar stays one fixed slim row. */
  /* Vertical padding only. `padding: 0` also zeroed .pf-wrap's 18px gutter, so
     the brand started at the screen edge while every card below it started at
     18px — the misalignment that reads as a clipped logo. */
  .pf-bar { height: 56px; flex-wrap: nowrap; padding: 0 18px; gap: 12px; }
  .pf-brand { font-size: 17px; }
  .pf-brand .pf-mark, .pf-brand img.pf-mark { height: 26px; }
  .pf-topnav { display: none; }
  .pf-tabbar { display: block; }
  body.pf-cockpit.pf-has-tabbar { padding-bottom: calc(70px + env(safe-area-inset-bottom)); }
}

/* =====================================================================   11. THE COCKPIT — the dashboard's own components.

   The dashboard used to be three tiles of counts. A count is a question ("16
   entries" — of what?), and an executive should not have to click to find the
   answer. So the cockpit shows the WORK: what the Brain is doing right now, and
   the newest lines on each board, by name.

   Everything here derives from the tokens above — the same Source Serif 4 /
   Inter / IBM Plex Mono, the same paper, navy and status colours as the cockpit.
   The one new idea is the INSTRUMENT: a single dark bar, the only dark object on
   a light page, carrying the machine itself. Its pulse is not decoration — it
   animates only while a to-do is actually in progress, and sits still when the
   Brain is idle. If it moves, work is happening.
   ============================================================================ */

/* ---- the verdict: one sentence, the whole point of the page ---- */
.pf-verdict { padding: 30px 0 10px; }
/* Wide enough that the verdict lands on two lines, not four — it is the one
   sentence the owner reads, and a cramped column makes it look like a caption. */
.pf-verdict .pf-h1 { max-width: 26ch; }
@keyframes pf-breathe { 0%, 100% { opacity: 1 } 50% { opacity: .3 } }

/* The third door: voice. The pill only STARTS the thing — the performance
   happens on the machine itself (see .is-listening below), because the point
   of speaking is that the Brain hears you, not that a button lights up.
   `--lvl` is written by JS every frame: 0 (silence) → 1 (loud). Everything
   warm and moving here is driven by a real microphone, never by a timer. */
.pf-voice { display: flex; align-items: center; gap: 12px; margin: 12px 2px 0; flex-wrap: wrap; }
.pf-voice-btn { --lvl: 0;
                /* skin over THE BUTTON: position for the halo, pill shape,
                   its own gap — box model and type come from the base. */
                position: relative; gap: 8px; border-radius: 999px; }
.pf-voice-btn:hover { border-color: var(--pf-faint); transform: translateY(-1px); }
.pf-voice-btn:focus-visible { outline: 2px solid var(--pf-warn); outline-offset: 3px; }
/* the halo: a ring that breathes with your actual voice, not with a clock. */
.pf-voice-btn .pf-voice-halo { position: absolute; inset: -1px; border-radius: 999px; pointer-events: none;
                               box-shadow: 0 0 0 0 color-mix(in srgb, var(--pf-warn) 40%, transparent);
                               opacity: 0; transition: opacity .2s ease; }
.pf-voice-btn.is-recording { background: var(--pf-warn); border-color: var(--pf-warn); color: #fff; }
.pf-voice-btn.is-recording .pf-voice-halo {
  opacity: 1;
  box-shadow: 0 0 0 calc(3px + var(--lvl) * 11px) color-mix(in srgb, var(--pf-warn) calc(30% - var(--lvl) * 22%), transparent);
}
.pf-voice-btn.is-busy { color: var(--pf-muted); cursor: progress; }
.pf-voice-ico { display: inline-flex; }
.pf-voice-btn .pf-ico-stop { display: none; }
.pf-voice-btn.is-recording .pf-ico-mic  { display: none; }
.pf-voice-btn.is-recording .pf-ico-stop { display: inline; }
/* the clock only exists while it is counting — a frozen 0:00 is furniture. */
.pf-voice-time { font-family: var(--pf-mono); font-size: 11.5px; font-weight: 500;
                 padding-left: 8px; margin-left: 2px; border-left: 1px solid rgba(255,255,255,.35);
                 font-variant-numeric: tabular-nums; }
.pf-voice-status { font-size: 12.5px; color: var(--pf-muted); min-height: 1em; }
.pf-voice-status .pf-voice-hint { color: var(--pf-muted); }
.pf-voice-status kbd { font: 500 11px/1 var(--pf-mono); border: 1px solid var(--pf-line-2);
                       border-bottom-width: 2px; border-radius: 4px; padding: 2px 4px;
                       background: var(--pf-surface); color: var(--pf-muted); }
/* what it heard, in your own words. Quoted, so it reads as testimony. */
.pf-voice-said { margin: 10px 2px 0; font-family: var(--pf-disp); font-size: 15px; color: var(--pf-ink);
                 animation: pf-said-in .34s cubic-bezier(.2,.8,.25,1) both; }
.pf-voice-said::before { content: "“"; color: var(--pf-muted); }
.pf-voice-said::after  { content: "”"; color: var(--pf-muted); }
.pf-voice-said.is-sent { color: var(--pf-ok); }
@keyframes pf-said-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

.pf-braincard { display: flex; align-items: center; gap: 20px; margin: 18px 0 6px;
                padding: 16px 20px; background: var(--pf-surface);
                border: 1px solid var(--pf-line); border-radius: var(--pf-radius);
                box-shadow: 0 14px 30px -26px rgba(28,30,33,.35); }
.pf-bc-id { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.pf-bc-name { display: inline-flex; align-items: center; gap: 9px; text-decoration: none;
              font-family: var(--pf-disp); font-weight: 700; font-size: 17px;
              letter-spacing: -.01em; color: var(--pf-ink); }
a.pf-bc-name:hover { color: var(--pf-navy); }
.pf-bc-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--pf-muted);
             flex: none; transition: background-color .3s ease; }
.pf-bc-dot.is-working { background: var(--pf-ok);
                        box-shadow: 0 0 0 3px color-mix(in srgb, var(--pf-ok) 18%, transparent);
                        animation: pf-breathe 2.6s ease-in-out infinite; }
/* The ONE place the machine's state is written — the pulse owns it while idle
   or working, the voice UX borrows it while listening/transcribing. */
.pf-bc-state { font-size: 13px; color: var(--pf-muted);
               overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pf-bc-model { font-family: var(--pf-mono); font-size: 10.5px; letter-spacing: .1em;
               text-transform: uppercase; color: var(--pf-muted); }
.pf-braincard .pf-trace { margin-left: auto; }
.pf-bc-doors { display: flex; align-items: center; gap: 10px; flex: none; }
/* The doors are plain .pf-btn pills now — the box model, floors and focus
   ring come from THE BUTTON. (The 2026-07-30/31 "unequal twins" bug lived in
   exactly the bespoke rules this replaced: two hand-rolled box models.) Only
   the voice pill's card dress remains: bare face, pill shape. */
.pf-braincard .pf-voice { margin: 0; flex-wrap: nowrap; }
.pf-braincard .pf-voice-btn { border-radius: 999px;
  background: none; box-shadow: none; border-color: var(--pf-line-2); }
/* While recording the label is empty (icon + timer only) — an empty span must
   not hold a flex gap open. */
.pf-braincard #voice-label:empty { display: none; }
/* Narration lives in the state line; the pill-side status only carries the
   short mechanics (Esc hint, errors), and never widens the card. */
.pf-braincard .pf-voice-status { max-width: 200px; }
/* What it heard, quoted inside the card: under the state line, clamped to two
   lines so a long take cannot unfold the whole card. The [hidden] rule matters:
   a stylesheet `display` would otherwise override the hidden attribute and
   paint a pair of empty quotes into the card. */
.pf-braincard .pf-voice-said { margin: 2px 0 0; font-size: 13px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; }
.pf-braincard .pf-voice-said[hidden] { display: none; }

/* the trace. still when idle, alive when working — never the other way round. */
.pf-trace { display: flex; gap: 3px; align-items: center; height: 40px; }
.pf-trace i { width: 3px; border-radius: 2px; background: var(--pf-warn); opacity: .9; }
.pf-braincard.is-working .pf-trace i { animation: pf-pulse 1.5s ease-in-out infinite; }
/* :not(.is-hearing) — while it listens, the trace IS the microphone, and a
   flat idle bar would overrule the voice with an !important. */
.pf-braincard.is-idle:not(.is-hearing) .pf-trace i { background: var(--pf-line-2); height: 3px !important; }
@keyframes pf-pulse { 0%, 100% { transform: scaleY(.25) } 50% { transform: scaleY(1) } }

/* ---- the machine, listening ----
   The whole point: speaking does not light up a button, it makes the MACHINE
   react. While the mic is open, JS writes every bar's height from the live
   FFT — the trace on the card becomes your voice. No keyframe may compete
   with that, hence `animation: none` and the transform reset (the working
   pulse leaves a scaleY behind). */
.pf-braincard.is-hearing .pf-trace i {
  animation: none !important; transform: none !important;
  transition: height .06s linear, background-color .3s ease;
  will-change: height; }
.pf-braincard.is-listening .pf-trace i { background: var(--pf-warn); opacity: 1; }
/* transcribing: it stops hearing and starts thinking — cool, not warm, so the
   two states can never be confused at a glance. A sweep runs through the bars
   (also JS-driven) the way a scanner passes over what you just said. */
.pf-braincard.is-thinking .pf-trace i { background: var(--pf-mint); opacity: .85; }
/* it heard you: the machine flashes its own colour of "yes". */
.pf-braincard.is-heard .pf-trace i { background: var(--pf-mint); }
.pf-braincard.is-listening { border-color: color-mix(in srgb, var(--pf-warn) 60%, var(--pf-line));
                             box-shadow: 0 0 0 1px color-mix(in srgb, var(--pf-warn) 45%, transparent),
                                         0 14px 30px -26px rgba(28,30,33,.35); }
@media (prefers-reduced-motion: reduce) {
  .pf-braincard.is-hearing .pf-trace i { transition: none; }
  .pf-voice-btn.is-recording .pf-voice-halo { box-shadow: 0 0 0 3px color-mix(in srgb, var(--pf-warn) 30%, transparent); }
  .pf-voice-said { animation: none; }
}

/* On a phone the card stacks: identity, then the doors at thumb width. The
   trace goes — at 390px it is decoration fighting the doors for room. */
@media (max-width: 640px) {
  .pf-braincard { flex-wrap: wrap; gap: 12px; }
  .pf-braincard .pf-trace { display: none; }
  .pf-bc-doors { width: 100%; }
  .pf-bc-doors .pf-btn { flex: 1; }
  .pf-braincard .pf-voice { flex: 1; }
  .pf-braincard .pf-voice-btn { width: 100%; }
  .pf-braincard .pf-voice-status { display: none; }
}

/* ---- boards that carry content, not counts ---- */
.pf-board { display: block; text-decoration: none; color: inherit; background: var(--pf-surface);
            border: 1px solid var(--pf-line); border-radius: var(--pf-radius);
            padding: 22px 22px 6px; transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease; }
.pf-board:hover { transform: translateY(-2px); border-color: var(--pf-line-2);
                  box-shadow: 0 20px 38px -22px rgba(28, 30, 33, .24); }
.pf-board:focus-visible { outline: 2px solid var(--pf-navy); outline-offset: 3px; }
.pf-board-h { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.pf-board-h h2 { font-family: var(--pf-disp); font-weight: 600; font-size: 24px; letter-spacing: -.01em; }
.pf-board-c { font-family: var(--pf-mono); font-size: 11px; color: var(--pf-muted); white-space: nowrap; }
.pf-board-c.is-wanted { color: var(--pf-warn); font-weight: 500; }
.pf-lines { margin-top: 14px; }
.pf-line { display: flex; gap: 11px; align-items: baseline; padding: 11px 0;
           border-top: 1px solid var(--pf-line); }
.pf-line-s { width: 6px; height: 6px; border-radius: 50%; background: var(--pf-line-2); flex: none;
             transform: translateY(-1px); }
.pf-line-s.is-doing { background: var(--pf-warn); box-shadow: 0 0 0 3px rgba(180, 115, 42, .16); }
.pf-line-s.is-done { background: var(--pf-ok); }
.pf-line p { font-size: 14.5px; color: var(--pf-muted); line-height: 1.35; flex: 1; }
.pf-line time { font-family: var(--pf-mono); font-size: 10.5px; color: var(--pf-muted); flex: none; }
.pf-board-more { display: block; padding: 13px 0; border-top: 1px solid var(--pf-line);
                 font-size: 13px; color: var(--pf-muted); font-weight: 500; }

/* ── staleness ───────────────────────────────────────────────────────────────
   A board is published by the Brain; this says so when the last publish is old
   enough to matter (or never happened). It wears --pf-warn, the same colour the
   rest of the system uses for "this wants a human" — never an error red: a
   stale board is not broken, it is unattended, and the rows below it may still
   be perfectly true. The point is that the reader knows which he is looking at.

   Deliberately quiet on the dashboard card (one line under the heading) and
   explicit on the board page itself (a banner above the content), because the
   card is a glance and the page is where somebody acts on what he sees. */
.pf-stale { display: flex; align-items: baseline; gap: 9px; margin: 0 0 20px;
            padding: 11px 14px; border: 1px solid var(--pf-line-2); border-radius: var(--pf-radius);
            background: rgba(180, 115, 42, .06); }
.pf-stale-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--pf-warn);
                flex: none; transform: translateY(-1px); box-shadow: 0 0 0 3px rgba(180, 115, 42, .16); }
.pf-stale p { font-size: 13px; color: var(--pf-muted); line-height: 1.4; }
.pf-board-stale { display: flex; align-items: baseline; gap: 8px; margin-top: 6px; }
.pf-board-stale span { width: 5px; height: 5px; border-radius: 50%; background: var(--pf-warn);
                       flex: none; transform: translateY(-1px); }
.pf-board-stale p { font-family: var(--pf-mono); font-size: 10.5px; color: var(--pf-warn);
                    line-height: 1.35; }
.pf-board:hover .pf-board-more { color: var(--pf-navy); }

/* ---- apps: a shelf. places you go, not things that pile up. ---- */
.pf-shelf { background: var(--pf-surface); border: 1px solid var(--pf-line);
            border-radius: var(--pf-radius); overflow: hidden; }
.pf-app { display: flex; align-items: center; gap: 16px; padding: 15px 20px; text-decoration: none;
          color: inherit; border-bottom: 1px solid var(--pf-line); transition: background .14s ease, padding-left .14s ease; }
.pf-app:last-child { border-bottom: 0; }
.pf-app:hover { background: var(--pf-surface-2); padding-left: 24px; }
.pf-app:focus-visible { outline: 2px solid var(--pf-navy); outline-offset: -2px; }
/* The number the owner says out loud: "share app 2". A ledger line-number, not a
   chip — slim mono, tabular, never boxed. */
.pf-app-n { font-family: var(--pf-mono); font-variant-numeric: tabular-nums; font-size: 12px;
            color: var(--pf-muted); flex: none; width: 2.2em; text-align: right; }
.pf-app-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--pf-ok); flex: none; }
.pf-app h3 { font-size: 15.5px; font-weight: 600; letter-spacing: -.01em; min-width: 148px; }
.pf-app-path { font-family: var(--pf-mono); font-size: 12.5px; color: var(--pf-muted); flex: 1; }
.pf-app-vis { font-family: var(--pf-sans); font-size: 10.5px; text-transform: uppercase; letter-spacing: .1em;
              color: var(--pf-muted); background: var(--pf-surface-2); border-radius: 99px;
              padding: 3px 10px; font-weight: 500; }
.pf-app-go { color: var(--pf-muted); }
.pf-app:hover .pf-app-go { color: var(--pf-navy); }

@media (prefers-reduced-motion: reduce) {
  .pf-bc-dot, .pf-trace i { animation: none !important; }
  .pf-board, .pf-app, .pf-btn { transition: none !important; }
}
@media (max-width: 900px) {
  .pf-trace { height: 30px; }
}
@media (max-width: 560px) {
  .pf-app { flex-wrap: wrap; gap: 10px; }
  .pf-app h3 { min-width: 0; }
  .pf-app-path { flex-basis: 100%; order: 3; }
}

/* ---- SIGNATURE: the meetings board ----
   A meeting is a record; an unanswered question is a job. So the question is the
   loud thing here, and the meeting is the quiet frame around it. */
.mt { background: var(--pf-surface); border: 1px solid var(--pf-line); border-radius: var(--pf-radius);
      padding: 22px 24px; margin-bottom: 16px; }
.mt-h { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; }
.mt-h h2 { font-family: var(--pf-disp); font-weight: 600; font-size: 19px; letter-spacing: -.01em;
           color: var(--pf-ink); margin: 0; }
.mt-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; margin: 6px 0 0;
           font-family: var(--pf-mono); font-size: 11.5px; color: var(--pf-muted); }
.mt-meta code { color: var(--pf-muted); }
.mt-status { text-transform: uppercase; letter-spacing: .1em; }
.mt-goal { margin: 12px 0 0; color: var(--pf-muted); font-size: 14px; line-height: 1.6; }

/* the people in the room */
.mt-people { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 14px; }
.mt-person { font-size: 12px; font-weight: 500; color: var(--pf-ink);
             background: var(--pf-surface-2); border: 1px solid var(--pf-line);
             border-radius: 999px; padding: 4px 10px; }

/* the questions */
.mt-qs { list-style: none; margin: 16px 0 0; padding: 0; }
.mt-qs > li + li { margin-top: 12px; }
.mt-q { border-left: 2px solid var(--pf-line-2); padding: 2px 0 2px 14px; }
.mt-q.is-open { border-left-color: var(--pf-warn); }
.mt-q-h { display: flex; align-items: baseline; gap: 8px; }
.mt-q-h p { margin: 0; font-size: 14.5px; font-weight: 600; color: var(--pf-ink); line-height: 1.5; }
.mt-q-mark, .mt-a-mark { font-family: var(--pf-mono); font-size: 10.5px; font-weight: 500;
                         color: var(--pf-muted); flex: none; }
.mt-q.is-open .mt-q-mark { color: var(--pf-warn); }
.mt-by { display: block; margin-top: 3px; font-family: var(--pf-mono); font-size: 11px;
         color: var(--pf-muted); }
.mt-as { list-style: none; margin: 8px 0 0; padding: 0; }
.mt-as > li + li { margin-top: 6px; }
.mt-a { display: grid; grid-template-columns: auto 1fr; gap: 8px; align-items: baseline; }
.mt-a p { margin: 0; font-size: 14px; color: var(--pf-muted); line-height: 1.6; }
.mt-a .mt-by { grid-column: 2; margin-top: 0; }

/* =====================================================================   12. THE KANBAN  —  the to-do board as three columns you can move cards across
   ----------------------------------------------------------------------------
   The board was three stacked read-only panels. It is now three side-by-side
   columns — To-do / In progress / Done — and a card can be dragged, or nudged
   with its buttons, from one to the next. Each move POSTs to /todo/move, which
   runs `foundry todo` on the box: the Brain is still the writer, the click just
   pulls the same lever. Buttons exist alongside drag because drag does not work
   on a phone, and this board is read on one.
   ========================================================================== */
.kanban { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; align-items: start; }

.kan-col { background: var(--pf-paper); border: 1px solid var(--pf-line);
           border-radius: var(--pf-radius); padding: 10px; min-height: 120px;
           transition: background-color .14s ease, box-shadow .14s ease, border-color .14s ease; }
/* The live drop target while a card is held over it. */
.kan-col.is-drop { background: var(--pf-surface-2); border-color: var(--pf-navy);
                   box-shadow: inset 0 0 0 1px var(--pf-navy); }

.kan-col-head { display: flex; align-items: baseline; justify-content: space-between;
                gap: 8px; padding: 6px 8px 12px; }
.kan-col-title { font-family: var(--pf-sans); font-weight: 600; font-size: 13px;
                 letter-spacing: .04em; text-transform: uppercase; color: var(--pf-muted); }
.kan-col-n { font-family: var(--pf-mono); font-variant-numeric: tabular-nums;
             font-size: 12px; color: var(--pf-muted); }
/* The column's identity lives in one dot on its header, not a wash of colour. */
.kan-col-doing .kan-col-title { color: var(--pf-warn); }
.kan-col-done  .kan-col-title { color: var(--pf-ok); }

.kan-cards { display: flex; flex-direction: column; gap: 8px; min-height: 44px; }
/* The empty column still reads as a place a card can land. */
.kan-empty { border: 1px dashed var(--pf-line-2); border-radius: 9px; padding: 14px 12px;
             text-align: center; font-size: 12px; color: var(--pf-muted); }

.kan-card { background: var(--pf-surface); border: 1px solid var(--pf-line);
            border-radius: 10px; padding: 11px 12px 9px;
            box-shadow: 0 8px 20px -18px rgba(28,30,33,.5);
            cursor: grab; transition: box-shadow .12s ease, transform .12s ease, opacity .12s ease; }
.kan-card:hover { box-shadow: 0 12px 26px -18px rgba(28,30,33,.55); }
.kan-card.is-dragging { opacity: .4; cursor: grabbing; }
.kan-card:focus-visible { outline: 2px solid var(--pf-navy); outline-offset: 2px; }

.kan-card-top { display: flex; align-items: baseline; gap: 9px; }
.kan-card-text { flex: 1; min-width: 0; margin: 0; font-family: var(--pf-sans);
                 font-size: 15px; font-weight: 500; line-height: 1.32; color: var(--pf-ink);
                 overflow-wrap: anywhere; }
.kan-col-done .kan-card { box-shadow: none; background: var(--pf-paper); }
.kan-col-done .kan-card-text { font-weight: 400; color: var(--pf-muted); text-decoration: line-through;
                               text-decoration-color: var(--pf-line-2); }

.kan-card-foot { display: flex; align-items: center; justify-content: space-between;
                 gap: 8px; margin-top: 9px; }
.kan-card-time { font-family: var(--pf-mono); font-variant-numeric: tabular-nums;
                 font-size: 11px; color: var(--pf-muted); }
/* Move controls: one nudge each way. The label is set per-button in JS so it
   says where the card goes ("Start", "Done", "Reopen"), never a bare arrow. */
.kan-moves { display: flex; gap: 4px; }
.kan-move { font-family: var(--pf-sans); font-size: 11px; font-weight: 600;
            color: var(--pf-muted); background: var(--pf-surface-2);
            border: 1px solid transparent; border-radius: 7px; padding: 4px 9px;
            cursor: pointer; transition: background-color .12s ease, color .12s ease; }
.kan-move:hover { background: var(--pf-navy); color: #fff; }
.kan-move-done:hover { background: var(--pf-ok); }
.kan-move:disabled { opacity: .4; cursor: default; }
.kan-card.is-saving { opacity: .55; pointer-events: none; }

@media (prefers-reduced-motion: reduce) {
  .kan-col, .kan-card, .kan-move { transition: none !important; }
}
/* On a phone three columns cannot sit side by side and stay legible, so they
   stack — the same order, top to bottom. Drag across a stack is unreliable, so
   the move buttons carry the whole job here. */
@media (max-width: 760px) {
  .kanban { grid-template-columns: 1fr; gap: 12px; }
}
/* the meeting's handle — the number the owner says out loud ("close meeting 2") */
.mt-num { font-family: var(--pf-mono); font-size: 13px; font-weight: 500; color: var(--pf-muted);
          margin-right: 6px; }
/* a closed meeting is still the record, it just stops asking for anything */
.mt.is-done { background: var(--pf-paper); border-style: dashed; }
.mt.is-done h2 { color: var(--pf-muted); }
.mt.is-done .mt-q { border-left-color: var(--pf-line); }
.mt.is-done .mt-q-h p { font-weight: 500; color: var(--pf-muted); }
/* an archived meeting is off the active board but kept — quieter than done, and
   one click from restore */
.mt.is-archived { background: var(--pf-paper); border-style: dashed; opacity: .82; }
.mt.is-archived h2 { color: var(--pf-muted); }

/* further information attached after the fact — reads like a meeting answer */
.mt-notes { margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--pf-line); }
.mt-notes-h { margin: 0 0 8px; font-family: var(--pf-mono); font-size: 10.5px; font-weight: 500;
              letter-spacing: .12em; text-transform: uppercase; color: var(--pf-muted); }
.mt-note + .mt-note { margin-top: 8px; }
.mt-note p { margin: 0; font-size: 14px; color: var(--pf-ink); line-height: 1.6; }
.mt-note .mt-by { margin-top: 2px; }

/* the card's controls: archive / restore, and the add-note fold */
.mt-actions { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 8px;
              margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--pf-line); }
.mt-addnote > summary { list-style: none; cursor: pointer; display: inline-flex; align-items: center;
                        padding: 6px 10px; font-size: 12px; color: var(--pf-muted);
                        border: 1px solid var(--pf-line); border-radius: var(--pf-radius-sm); }
.mt-addnote > summary::-webkit-details-marker { display: none; }
.mt-addnote > summary::after { content: "▾"; margin-left: 6px; font-size: 11px; }
.mt-addnote[open] > summary::after { content: "▴"; }
.mt-addnote > summary:hover { color: var(--pf-ink); border-color: var(--pf-faint); }
.mt-addnote-body { display: flex; flex-direction: column; gap: 8px; margin-top: 8px;
                   width: 100%; max-width: 520px; }
.mt-note-input { width: 100%; resize: vertical; }
.mt-addnote-body .pf-btn { align-self: flex-start; }


/* =====================================================================   13. THE RESEARCH LIBRARY  —  a shelf you browse, and a page you read on.
   ----------------------------------------------------------------------------
   The Research board used to print all 15 writeups, in full, one under the next:
   an 8,000px page of prose where every entry was cut off at 1,200 characters and
   there was nowhere to go for the rest. It answered no question a reader arrives
   with. "What has my Brain researched?" needs a SHELF. "What did it find out?"
   needs a PAGE. Those are two different objects, so this is two screens.

   The library (index.html) is a shelf: one line per writeup, its question, and
   the answer it reached. The reading room (entry.html) is the writeup itself,
   whole, with its own table of contents and its sources listed as receipts.

   Everything here is composed from the tokens in sections 1-9. The one new idea
   is EDITORIAL RESTRAINT: a research library should look like a well-set journal,
   so the display serif does the work, hairlines do the dividing, and nothing is
   coloured that does not have to be.
   ========================================================================== */

/* The library spans the shell. It is a shelf, not a column of prose — held to the
   46rem reading measure it sat in a left-hand strip with a lake of empty beside
   it, which is the exact fault the measure exists to prevent. The head goes wide
   with it, so the two share ONE right edge. (This overrides the `#board,
   .pf-head-col` cap in section 10 — same reason `.board-kanban` does.) */
#board.board-library, .pf-head.board-library-head { max-width: none; }

/* ---- the head: a headline, and the shelf's own vital signs ---- */
.lib-head { display: flex; align-items: flex-end; justify-content: space-between;
            gap: 30px; flex-wrap: wrap; }
.lib-head .pf-lead { max-width: 52ch; margin-bottom: 0; }

/* ---- the on-demand visualization affordance --------------------------------
   A page-level NOTE, not a button: it tells the owner a capability the board
   otherwise hides — that the Brain will build a bespoke visual view of any
   writeup on request and deploy it to his own server. Navy-tinted so it reads
   as guidance rather than a warning (the amber `.pf-stale` reads as trouble),
   and composed entirely from tokens: the tint is a `color-mix` off the accent
   navy, so it introduces no new hex. It spans the wide library head. */
.pf-viz-cta { display: flex; align-items: flex-start; gap: 12px; margin: 0 0 26px;
              padding: 14px 16px; border: 1px solid var(--pf-line-2); border-radius: var(--pf-radius);
              background: color-mix(in srgb, var(--pf-navy) 4%, transparent); }
.pf-viz-cta svg { flex: none; width: 18px; height: 18px; margin-top: 1px; color: var(--pf-navy); }
.pf-viz-cta p { margin: 0; font-size: 13px; line-height: 1.55; color: var(--pf-muted); max-width: 74ch; }
.pf-viz-cta strong { color: var(--pf-ink); font-weight: 600; }
.pf-viz-cta a { color: var(--pf-navy); font-weight: 600; text-decoration: none; }
.pf-viz-cta a:hover { text-decoration: underline; }


/* ---- THE BRIEFING — a front page, not a table --------------------------------
   The board is read in about twenty seconds by someone who did not commission the
   research. A flat list of fifteen equal titles spends those seconds making that
   person hunt for the one good thing; a front page spends them PROVING there is a
   good thing. So the newest writeup is promoted to a lead story carrying its
   actual finding — not its title, the finding — and the other fourteen fall into
   a tight index beneath it.

   The rail on the left is the other half of the fix. With search and filters
   sitting above a full-bleed list, the reading column stretched to the shell and
   preview lines ran to ~120 characters, which is what made the spacing feel
   wrong: the measure was broken, not the padding. Filters live in the rail now,
   and the reading column keeps a measure it can be read at. */
.brief { display: grid; grid-template-columns: 200px 1fr; gap: 40px; align-items: start; }

/* the rail: sticky, because a filter you have to scroll back up to reach is a
   filter nobody uses on the twelfth row */
.rail { position: sticky; top: 24px; }
.rail-h { font-family: var(--pf-sans); font-size: 10.5px; font-weight: 600; letter-spacing: .14em;
          text-transform: uppercase; color: var(--pf-muted); margin: 0 0 10px; }
.rail-search { position: relative; margin-bottom: 22px; }
.rail-search .pf-input { width: 100%; padding-left: 30px; }
.rail-search svg { position: absolute; left: 9px; top: 50%; transform: translateY(-50%);
                   width: 14px; height: 14px; color: var(--pf-muted); pointer-events: none; }
.rail-tags { display: flex; flex-direction: column; gap: 1px; }
.rail-tag { display: flex; align-items: center; justify-content: space-between; gap: 8px;
            padding: 6px 9px; border: 0; border-radius: 7px; background: none; cursor: pointer;
            font: 500 12.5px/1.4 var(--pf-sans); color: var(--pf-muted); text-align: left;
            transition: background-color .12s ease, color .12s ease; }
.rail-tag:hover { background: var(--pf-surface-2); color: var(--pf-ink); }
.rail-tag[aria-pressed="true"] { background: var(--pf-navy); color: #fff; }
.rail-tag[aria-pressed="true"] .rail-c { color: rgba(255,255,255,.7); }
.rail-c { font-family: var(--pf-mono); font-size: 10.5px; color: var(--pf-muted);
          font-variant-numeric: tabular-nums; }

/* ---- the lead story ---- */
.lead-card { display: block; position: relative; background: var(--pf-surface);
             border: 1px solid var(--pf-line); border-radius: var(--pf-radius);
             padding: 30px 32px 26px; margin-bottom: 30px; text-decoration: none; color: inherit;
             box-shadow: 0 18px 40px -30px rgba(28,30,33,.4);
             transition: box-shadow .16s ease, border-color .16s ease; overflow: hidden; }
.lead-card::after { content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
                    background: linear-gradient(90deg, var(--pf-navy),
                                color-mix(in srgb, var(--pf-navy) 25%, transparent)); }
.lead-card:hover { border-color: var(--pf-line-2); box-shadow: 0 26px 50px -30px rgba(28,30,33,.5); }
.lead-card:focus-visible { outline: 2px solid var(--pf-navy); outline-offset: 2px; }
.lead-card:hover .lead-title { color: var(--pf-navy); }
.lead-flag { display: inline-flex; align-items: center; gap: 7px; margin-bottom: 14px;
             font: 600 10px/1 var(--pf-sans); letter-spacing: .16em; text-transform: uppercase;
             color: var(--pf-navy); }
.lead-flag::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--pf-ok); }
.lead-title { margin: 0 0 14px; font-family: var(--pf-disp); font-size: 30px; font-weight: 700;
              line-height: 1.14; letter-spacing: -.02em; max-width: 22ch;
              transition: color .16s ease; }
.lead-q { margin: 0 0 16px; font-family: var(--pf-disp); font-style: italic; font-size: 16px;
          line-height: 1.5; color: var(--pf-muted); max-width: 62ch; }
/* three lines of the FINDING. Not the filing note, not the methodology — the
   answer, which is the only part of a writeup that earns a click. */
.lead-body { margin: 0; font-size: 14px; line-height: 1.65; color: var(--pf-ink); max-width: 66ch;
             display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3;
             line-clamp: 3; overflow: hidden; }
.lead-foot { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--pf-line);
             display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
             font-family: var(--pf-mono); font-size: 11.5px; color: var(--pf-muted); }
.lead-foot > .m + .m::before { content: "·"; margin-right: 8px; color: var(--pf-line-2); }
.lead-foot .m-src { color: var(--pf-navy); font-weight: 500; }
.lead-go { margin-left: auto; font-family: var(--pf-sans); font-weight: 600; font-size: 12.5px;
           color: var(--pf-navy); }
.lead-card:hover .lead-go { text-decoration: underline; }

/* ---- the index beneath: hairline rows, like a journal's table of contents.
   The whole row is the link. Fifteen boxes read as fifteen unrelated things;
   fifteen lines read as one body of work. (A card-per-writeup grid was built and
   shown alongside this and turned down — the same "sparse, floating" look that
   was rejected for the reading boards once before. It does not live here as dead
   CSS.) */
.idx-h { display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
         margin: 0 0 12px; }
.idx-h h2 { margin: 0; font-family: var(--pf-disp); font-size: 15px; font-weight: 700;
            letter-spacing: -.01em; }
.idx-count { font-family: var(--pf-mono); font-size: 11px; color: var(--pf-muted); white-space: nowrap; }

/* ONE framed panel, divided by hairlines — never a .pf-card per row. A card per
   writeup gives each one a box and a shadow, and fifteen boxes read as fifteen
   unrelated things floating in space (the look that was tried here and turned
   down). The panel is what makes the index read as a single body of work.
   Pinned by tests/test_base_apps.py: the reading boards are one framed panel. */
.lib { background: var(--pf-surface); border: 1px solid var(--pf-line);
       border-radius: var(--pf-radius); box-shadow: 0 14px 30px -24px rgba(28,30,33,.35);
       overflow: hidden; }
.lib > * + * { border-top: 1px solid var(--pf-line); }

.idx-item { display: grid; grid-template-columns: 2.4rem 1fr auto; align-items: baseline;
            gap: 0 18px; padding: 17px 22px 16px;
            text-decoration: none; color: inherit; transition: background-color .14s ease; }
.idx-item:hover { background: var(--pf-surface-2); }
.idx-item:focus-visible { outline: 2px solid var(--pf-navy); outline-offset: -2px; }
.idx-item:hover .idx-title { color: var(--pf-navy); }
.idx-n { font-family: var(--pf-disp); font-weight: 700; font-size: 13px; color: var(--pf-muted);
         font-variant-numeric: tabular-nums; user-select: none; }
.idx-title { margin: 0 0 4px; font-family: var(--pf-disp); font-size: 17px; font-weight: 700;
             line-height: 1.25; letter-spacing: -.01em; max-width: 40ch;
             transition: color .14s ease; }
/* ONE line of preview, hard. A preview that runs to six lines is not a preview,
   it is the wall of text this board used to be. */
.idx-sub { margin: 0; font-size: 13px; line-height: 1.5; color: var(--pf-muted); max-width: 72ch;
           display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1;
           line-clamp: 1; overflow: hidden; }
.idx-sub.is-q { font-family: var(--pf-disp); font-style: italic; }
.idx-meta { margin-top: 7px; display: flex; gap: 8px; flex-wrap: wrap;
            font-family: var(--pf-mono); font-size: 10.5px; color: var(--pf-muted); }
.idx-meta > * + *::before { content: "·"; margin-right: 8px; color: var(--pf-line-2); }
.idx-meta .m-src { color: var(--pf-navy); }
.idx-right { text-align: right; align-self: center; }
.idx-date { font-family: var(--pf-mono); font-size: 11px; color: var(--pf-muted); white-space: nowrap; }

/* an in-progress writeup is still a writeup, but it says so */
.idx-wip, .lead-wip {
  display: inline-block; margin-top: 6px;
  font: 600 9.5px/1 var(--pf-sans); letter-spacing: .08em; text-transform: uppercase;
  color: var(--pf-warn); background: color-mix(in srgb, var(--pf-warn) 10%, transparent);
  padding: 3px 7px; border-radius: 999px;
}

/* nothing matched the filter — a dead end must still look like the board */
.idx-none { padding: 40px 22px; text-align: center; color: var(--pf-muted); }

/* the tag chips, still worn by the reading room's header (entry.html) */
.lib-chips { display: flex; gap: 5px; flex-wrap: wrap; margin-left: 4px; }
.lib-chip { font-family: var(--pf-sans); font-size: 10.5px; font-weight: 500; letter-spacing: .02em;
            color: var(--pf-muted); background: var(--pf-surface-2); border-radius: 4px;
            padding: 2px 6px; }
.lib-chips .lib-chip::before { content: none; }

/* ---- THE READING ROOM (entry.html) ----
   Prose on the left at a real measure, the writeup's own structure on the right.
   The sidebar is what turns a 2,000-word report into something you can stand in
   front of a CEO and navigate. */
.rd { display: grid; grid-template-columns: minmax(0, 1fr) 232px; gap: 44px; align-items: start; }
/* A grid item's default `min-width: auto` is its CONTENT's width, so the widest
   comparison table in the report set the floor for the whole column and pushed
   the page sideways on a phone — the table's own `overflow-x` never got a chance
   to do its job. This is the line that lets it. */
.rd > * { min-width: 0; }
.rd-back { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; font-weight: 500;
           color: var(--pf-muted); text-decoration: none; margin-bottom: 18px; }
.rd-back:hover { color: var(--pf-navy); }
.rd-back svg { transition: transform .12s ease; }
.rd-back:hover svg { transform: translateX(-3px); }

.rd-title { font-family: var(--pf-disp); font-size: 34px; font-weight: 700; letter-spacing: -.02em;
            line-height: 1.12; margin: 0; color: var(--pf-ink); max-width: 22ch; }
.rd-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 0 8px; margin-top: 14px;
           font-family: var(--pf-mono); font-size: 11.5px; color: var(--pf-muted); }
.rd-meta > * + *::before { content: "·"; margin-right: 8px; color: var(--pf-line-2); }
.rd-meta .lib-chips .lib-chip::before { content: none; }

/* the question, given the weight it deserves: this is the brief the Brain was
   handed, and the report below is the answer to exactly it. */
.rd-q { margin: 26px 0 0; padding: 16px 20px; border-left: 2px solid var(--pf-navy);
        background: var(--pf-surface); border-radius: 0 var(--pf-radius-sm) var(--pf-radius-sm) 0;
        font-family: var(--pf-disp); font-size: 17px; font-style: italic; line-height: 1.5;
        color: var(--pf-ink); }
.rd-q-k { display: block; font-family: var(--pf-sans); font-style: normal; font-size: 10.5px;
          font-weight: 600; letter-spacing: .16em; text-transform: uppercase;
          color: var(--pf-navy); margin-bottom: 7px; }

/* The MEASURE applies to prose, not to everything.
   Holding the whole report to 68ch squeezed its comparison tables into a column
   half their natural width, where the browser hyphen-less-ly broke "bedingt"
   into "beding t" to make a cell fit. Prose gets the measure; the objects that
   are as wide as they are — tables, code — get the whole column and carry their
   own horizontal scroll. */
.rd-body { margin-top: 30px; }
.rd-body .prose > * { max-width: 68ch; }
.rd-body .prose > .table-scroll,
.rd-body .prose > pre.code,
.rd-body .prose > hr { max-width: 100%; }
/* The report is the page now, not a passenger in a scroll box. `.prose-scroll`
   (a 26rem inner scroller) is deliberately NOT used here: a scrollbar inside a
   scrolling page is how the old board hid 90% of every writeup. */
.rd-body .prose { font-size: 15px; line-height: 1.72; }
.rd-body .prose h3 { font-size: 21px; margin: 2.1rem 0 .5rem; scroll-margin-top: 90px; }
.rd-body .prose h4 { font-size: 16.5px; margin: 1.5rem 0 .35rem; scroll-margin-top: 90px; }
.rd-body .prose h5, .rd-body .prose h6 { font-size: 14.5px; scroll-margin-top: 90px; }
.rd-body .prose hr { border: 0; border-top: 1px solid var(--pf-line); margin: 2rem 0; }
.rd-body .prose blockquote { margin: 1rem 0; padding: 2px 0 2px 16px;
                             border-left: 2px solid var(--pf-line-2); color: var(--pf-muted); }
.rd-body .prose ol { margin: .6rem 0; padding-left: 1.35rem; }
.rd-body .prose pre.code { background: var(--pf-surface-2); border: 1px solid var(--pf-line);
                           border-radius: var(--pf-radius-sm); padding: 14px 16px; overflow-x: auto;
                           font-family: var(--pf-mono); font-size: 12.5px; line-height: 1.6; }
.rd-body .prose pre.code code { background: none; border: 0; padding: 0; font-size: inherit; }

/* A comparison table IS the answer in most of these writeups. Set it like a
   table in a journal: hairlines, tabular figures, a shy header, and its own
   horizontal scroll so a wide one never pushes the page sideways. */
.table-scroll { overflow-x: auto; margin: 1.1rem 0; border: 1px solid var(--pf-line);
                border-radius: var(--pf-radius-sm); }
.prose table { border-collapse: collapse; width: 100%; font-size: 13.5px;
               font-variant-numeric: tabular-nums; }
.prose thead th { position: sticky; top: 0; background: var(--pf-surface-2); text-align: left;
                  font-family: var(--pf-sans); font-size: 11px; font-weight: 600; letter-spacing: .08em;
                  text-transform: uppercase; color: var(--pf-muted); padding: 10px 14px;
                  white-space: nowrap; border-bottom: 1px solid var(--pf-line); }
.prose tbody td { padding: 11px 14px; border-top: 1px solid var(--pf-line);
                  color: var(--pf-ink); vertical-align: top; line-height: 1.5; }
/* Section 10 sets `.prose { overflow-wrap: anywhere }` so a URL can never widen
   the panel. Inside a table cell that rule is too eager: it breaks ordinary German
   words mid-syllable to save a pixel. Break long words only when there is no other
   way, and at a word boundary first. */
.prose th, .prose td { overflow-wrap: break-word; }
.prose tbody tr:first-child td { border-top: 0; }
.prose tbody tr:hover { background: var(--pf-paper); }

/* ---- the rail: contents, then the receipts ---- */
.rd-rail { position: sticky; top: 22px; display: flex; flex-direction: column; gap: 22px; }
.rd-rail-k { font-family: var(--pf-sans); font-size: 10.5px; font-weight: 600; letter-spacing: .16em;
             text-transform: uppercase; color: var(--pf-muted); margin: 0 0 10px; }

.rd-toc { list-style: none; margin: 0; padding: 0; border-left: 1px solid var(--pf-line); }
.rd-toc li { margin: 0; }
.rd-toc a { display: block; padding: 6px 0 6px 14px; margin-left: -1px;
            border-left: 2px solid transparent; font-size: 13px; line-height: 1.4;
            color: var(--pf-muted); text-decoration: none;
            transition: color .12s ease, border-color .12s ease; }
.rd-toc a:hover { color: var(--pf-ink); }
/* the reader's place in the report, tracked by an IntersectionObserver */
.rd-toc a.is-here { color: var(--pf-navy); font-weight: 600; border-left-color: var(--pf-navy); }
.rd-toc .is-sub a { padding-left: 26px; font-size: 12.5px; color: var(--pf-muted); }
.rd-toc .is-sub a:hover, .rd-toc .is-sub a.is-here { color: var(--pf-navy); }

/* the sources: the writeup's receipts. A research report a CEO cannot trace back
   to its sources is an opinion. */
.rd-src { list-style: none; margin: 0; padding: 0; }
.rd-src li { padding: 7px 0; border-top: 1px solid var(--pf-line); }
.rd-src li:first-child { border-top: 0; }
.rd-src a { font-size: 12.5px; line-height: 1.4; color: var(--pf-muted); text-decoration: none;
            display: flex; gap: 7px; }
.rd-src a:hover { color: var(--pf-navy); }
.rd-src-n { font-family: var(--pf-mono); font-size: 11px; color: var(--pf-muted); flex: none; }
.rd-src-host { overflow-wrap: anywhere; }

/* attachments: named, not linked. The decks live in the vault on the Brain and
   the board is served from the web server, so this says WHAT exists and does not
   pretend it can hand it over. A dead download button would be the lie. */
.rd-files { list-style: none; margin: 0; padding: 0; font-family: var(--pf-mono); font-size: 11.5px;
            color: var(--pf-muted); }
.rd-files li { padding: 5px 0; overflow-wrap: anywhere; }
.rd-foot { margin-top: 36px; padding-top: 14px; border-top: 1px solid var(--pf-line);
           font-family: var(--pf-mono); font-size: 11px; color: var(--pf-muted); }

@media (max-width: 980px) {
  /* The rail stops being a rail: contents above the report, sources below it —
     ordered by DOM, so no reflow trickery is needed. */
  .rd { grid-template-columns: 1fr; gap: 24px; }
  .rd-rail { position: static; }
  .rd-title { font-size: 27px; max-width: none; }
}
@media (max-width: 900px) {
  /* The rail stops being a rail: search and topics sit above the stories as a
     normal bar. Sticky is dropped with it — a sticky block in normal flow on a
     phone just eats the top of the screen. */
  .brief { grid-template-columns: 1fr; gap: 24px; }
  .rail { position: static; }
  .rail-tags { flex-direction: row; flex-wrap: wrap; }
  .rail-tag { border: 1px solid var(--pf-line); border-radius: 999px; }
  .lead-card { padding: 24px 20px 20px; }
  .lead-title { font-size: 24px; max-width: none; }
  .lead-q { font-size: 15px; }
}
@media (max-width: 760px) {
  /* The date column folds under the title rather than squeezing it: on a 390px
     screen a third column leaves the title about twelve characters. */
  .idx-item { grid-template-columns: 2rem 1fr; gap: 0 12px; padding: 16px 14px; }
  .idx-right { grid-column: 2; text-align: left; margin-top: 8px; }
  .idx-title { font-size: 16px; max-width: none; }
  .idx-wip { margin-top: 0; margin-left: 8px; }
}
@media (prefers-reduced-motion: reduce) {
  .idx-item, .lead-card, .rd-back svg, .rail-tag { transition: none !important; }
}

/* =====================================================================   14. THE CHAT
   Retired 2026-07. The Cockpit's chat page is now the Hub's Desk, ported
   verbatim into apps/shared/chat.css — same class names, same visual system,
   loaded only by that page.

   The old section that stood here (its own .chat-msg, .chat-thread, .chat-pick
   and a `.chat [hidden]` block) was not merely dead: it loaded FIRST and set
   `display: flex` on `.chat-msg`, which quietly beat the UA sheet's
   `[hidden] { display: none }` and left every non-matching bubble on screen
   during a search. Two stylesheets owning one class name is not a style
   question, it is a bug generator — so it is gone rather than overridden.
   ============================================================================ */

/* =====================================================================   15. THE MOBILE OVERHAUL — "Needs you" (2026-07)
   ----------------------------------------------------------------------------
   Direction A from the July mobile review. The dashboard stops opening on an
   explanation of itself and opens on the work: a slim status strip, one serif
   lede, and the two-to-four things that actually want the owner as cards with
   their action reachable. The bottom bar drops from six destinations to three —
   Today · Ask · Boards — with Ask, the thing he does most, as the fat pill in
   the thumb's home position. Boards opens the six destinations as a sheet.

   Everything here is composed from the tokens in sections 1-9: no new hex, no
   new radius, no new font. Contrast note: --pf-faint (2.91:1) is retired as a
   TEXT colour across this file; timestamps, labels and meta lines read at
   --pf-muted (6.31:1). Faint survives only on hairlines, dots and disabled fills.
   ========================================================================== */

/* ---- the lede: one serif line, the answer the owner opened the app for ---- */
.pf-lede { padding: 20px 0 14px; }
.pf-lede-h { font-family: var(--pf-disp); font-size: 28px; line-height: 1.14; margin: 0;
             font-weight: 700; letter-spacing: -.01em; color: var(--pf-ink); max-width: 24ch; }
.pf-lede-h em { font-style: normal; color: var(--pf-muted); }
.pf-lede-sub { margin: 7px 0 0; font-size: 13.5px; color: var(--pf-muted); }

/* ---- the needs-you stack: one card per thing that wants a human ---- */
.pf-needs { display: flex; flex-direction: column; gap: 12px; }
.need { background: var(--pf-surface); border: 1px solid var(--pf-line);
        border-radius: var(--pf-radius); padding: 15px 16px 13px;
        box-shadow: 0 14px 30px -26px rgba(28,30,33,.35); }
/* The lead card wears the ember edge — the one place ember appears as signal,
   never as text: the single most-wanting thing on the page. */
.need--lead { border-left: 3px solid var(--pf-ember); }
.need-kicker { display: flex; align-items: center; gap: 8px; margin-bottom: 7px; }
.need-kicker > span:first-child { font-family: var(--pf-mono); font-size: 10.5px;
                 letter-spacing: .12em; text-transform: uppercase; color: var(--pf-muted);
                 overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.need-age { margin-left: auto; flex: none; font-family: var(--pf-mono); font-size: 10.5px;
            color: var(--pf-muted); }
.need-h { margin: 0; font-family: var(--pf-sans); font-size: 16px; line-height: 1.35;
          font-weight: 600; letter-spacing: -.005em; color: var(--pf-ink); overflow-wrap: anywhere; }
.need-h em { font-style: italic; }
.need-h code { font-family: var(--pf-mono); font-size: .9em; }
.need-p { margin: 6px 0 0; font-size: 13.5px; line-height: 1.45; color: var(--pf-muted);
          overflow-wrap: anywhere; }
/* actions: 44px minimum, reachable one-handed. */
.need-acts { display: flex; gap: 8px; margin-top: 12px; }
.need-btn { flex: 1; min-height: 44px; border-radius: 10px;
            background: var(--pf-surface); box-shadow: none;
            font-size: 14px; text-align: center; padding: 0 10px; line-height: 1.2; }
.need-btn--primary { background: var(--pf-navy); border-color: var(--pf-navy); color: var(--pf-night-ink); }
.need-btn--primary:hover { background: var(--pf-navy-2); border-color: var(--pf-navy-2); }
.need-btn--primary.is-done { background: var(--pf-ok); border-color: var(--pf-ok); }
.need-btn--ghost { flex: 0 0 auto; min-width: 84px; color: var(--pf-muted); }
.need-btn--ghost:hover { color: var(--pf-ink); border-color: var(--pf-muted); }
.pf-needs-more { margin: 12px 2px 0; font-family: var(--pf-mono); font-size: 11.5px;
                color: var(--pf-muted); }
.pf-needs-clear { border: 1px dashed var(--pf-line-2); border-radius: var(--pf-radius);
                  padding: 22px 18px; text-align: center; color: var(--pf-muted); font-size: 14px; }
.pf-needs-clear p { margin: 0; }

/* ---- running without you: the quiet secondary block ---- */
.pf-running { margin-top: 26px; }
.pf-running-h { font-family: var(--pf-mono); font-size: 10.5px; letter-spacing: .14em;
                text-transform: uppercase; color: var(--pf-muted); margin: 0 0 8px; font-weight: 500; }
.pf-running-rows { border: 1px dashed var(--pf-line-2); border-radius: var(--pf-radius);
                   overflow: hidden; }
.pf-running-row { display: flex; align-items: center; gap: 12px; min-height: 46px;
                  padding: 6px 14px; text-decoration: none; color: var(--pf-ink);
                  border-top: 1px solid var(--pf-line); transition: background-color .12s ease; }
.pf-running-row:first-child { border-top: 0; }
.pf-running-row:hover { background: var(--pf-surface-2); }
.pf-running-k { flex: none; font-family: var(--pf-mono); font-size: 10px; letter-spacing: .08em;
                text-transform: uppercase; color: var(--pf-muted); min-width: 92px; }
.pf-running-t { flex: 1; min-width: 0; font-size: 14px; color: var(--pf-ink);
                overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pf-running-go { flex: none; color: var(--pf-muted); }

/* =====================================================================   THE THREE-TAB BAR — Today · Ask · Boards
   ----------------------------------------------------------------------------
   The bar's display mechanics (hidden by default, shown below 760px, never on
   the Desk) are unchanged in section 5. What changes here is the CONTENT: three
   slots, not six, with Ask as a navy pill in the middle where the thumb rests.
   ========================================================================== */
.pf-tabbar-inner { display: grid; grid-template-columns: 1fr 1.3fr 1fr; align-items: center;
                   gap: 6px; }
/* Ask: the fat pill. It is a link to chat — the thing the owner does most, in
   the easiest place a thumb reaches. */
.pf-ask { min-height: 48px; margin: 0 4px; border-radius: 24px; background: var(--pf-navy);
          color: var(--pf-night-ink); display: flex; align-items: center; justify-content: center;
          gap: 8px; text-decoration: none; font: 600 14.5px/1 var(--pf-sans); }
.pf-ask:hover, .pf-ask:active { background: var(--pf-navy-2); }
.pf-ask svg { width: 19px; height: 19px; stroke: currentColor; fill: none; }

/* ---- the Boards sheet: the six destinations, one tap from the bar ---- */
.pf-sheet-backdrop { position: fixed; inset: 0; z-index: 45; background: rgba(12,33,56,.42);
                     opacity: 0; pointer-events: none; transition: opacity .2s ease; }
.pf-sheet-backdrop.is-open { opacity: 1; pointer-events: auto; }
.pf-sheet { position: fixed; left: 0; right: 0; bottom: 0; z-index: 46;
            background: var(--pf-surface); border-top-left-radius: 18px; border-top-right-radius: 18px;
            border-top: 1px solid var(--pf-line-2);
            padding: 8px 16px calc(20px + env(safe-area-inset-bottom));
            box-shadow: 0 -24px 48px -24px rgba(12,33,56,.5);
            transform: translateY(100%); transition: transform .24s cubic-bezier(.2,.8,.25,1); }
.pf-sheet.is-open { transform: translateY(0); }
.pf-sheet-grip { width: 40px; height: 4px; border-radius: 999px; background: var(--pf-line-2);
                 margin: 6px auto 12px; }
.pf-sheet-h { font-family: var(--pf-mono); font-size: 10.5px; letter-spacing: .14em;
              text-transform: uppercase; color: var(--pf-muted); margin: 0 4px 6px; }
.pf-sheet-row { display: flex; align-items: center; gap: 14px; min-height: 52px;
                padding: 0 6px; text-decoration: none; color: var(--pf-ink);
                border-top: 1px solid var(--pf-line); }
.pf-sheet-row:first-of-type { border-top: 0; }
.pf-sheet-ico { flex: none; width: 22px; height: 22px; color: var(--pf-muted); }
.pf-sheet-ico svg { width: 22px; height: 22px; stroke: currentColor; fill: none; display: block; }
.pf-sheet-label { flex: 1; font: 600 15.5px/1.2 var(--pf-sans); }
.pf-sheet-c { flex: none; font-family: var(--pf-mono); font-size: 11.5px; color: var(--pf-muted); }
.pf-sheet-c.is-wanted { color: var(--pf-warn); }
.pf-sheet-go { flex: none; color: var(--pf-muted); }

/* ---- tap targets: 44px on the two boards with the most actions ----
   Desktop keeps the compact controls — a mouse does not miss a 26px button —
   but on a phone every control the review flagged grows to 44px. */
@media (max-width: 760px) {
  .kan-move { min-height: 44px; padding: 8px 12px; font-size: 13px; }
  .kan-card-foot { flex-wrap: wrap; }
  .kan-moves { gap: 6px; }
  .entry-actions .pf-btn { min-height: 44px; padding: 10px 14px; }
}

/* ---- emails: one full-width primary action, the rest behind an overflow ---- */
.entry-actions .entry-primary { width: 100%; min-height: 44px; justify-content: center;
                                font-size: 13.5px; }
.entry-more { margin-top: 2px; }
.entry-more > summary { list-style: none; cursor: pointer; display: inline-flex; align-items: center;
                        min-height: 44px; padding: 0 2px; font: 600 12.5px/1 var(--pf-sans);
                        color: var(--pf-muted); }
.entry-more > summary::-webkit-details-marker { display: none; }
.entry-more > summary:hover { color: var(--pf-ink); }
.entry-more > summary::after { content: "▾"; margin-left: 6px; font-size: 11px; }
.entry-more[open] > summary::after { content: "▴"; }
.entry-more-row { display: flex; flex-wrap: wrap; gap: 8px; padding: 4px 0 2px; }
@media (max-width: 760px) {
  .entry-more-row .pf-btn { min-height: 44px; padding: 10px 14px; }
}

/* ---- the Boards tab is a <button> (it toggles the sheet); reset its chrome ---- */
button.pf-tab { background: none; border: 0; cursor: pointer; font: inherit; }
/* A single dot on the Boards tab when something on a board wants the owner. */
.pf-tab-boards { position: relative; }
.pf-tab-wants .pf-tab-ico::after { content: ""; position: absolute; top: -3px; right: 50%;
                 margin-right: -13px; width: 8px; height: 8px; border-radius: 50%;
                 background: var(--pf-warn); border: 2px solid var(--pf-surface); }

/* =====================================================================   PHONE POLISH PASS (owner order, 2026-07-29: "Paddings und so weiter,
   das muss alles top-notch funktionieren")
   ----------------------------------------------------------------------------
   Two rules applied everywhere, measured on the live pages at 390px first:

   1. Every field a thumb can type into is at least 16px — below that iOS
      Safari zooms the whole viewport on focus and does not zoom back. The
      chat search had this bug (fixed in 1.8.1); the research search had it
      too (#q at 14px). This closes the class for every .pf-input on every
      page, present and future.

   2. Every control a thumb must hit clears ~44px. The audit found the bar
      actions at 36px, the voice button at 33px, the Brain-strip link at
      18px. Padding does the growing; negative margins keep the visual
      rhythm exactly where it was.

   Append-only, token-only, no frozen selector renamed or removed.
   ========================================================================== */
@media (max-width: 760px) {
  .pf-input, .pf-select, textarea.pf-input {
    font-size: 16px;
    min-height: 44px;
  }
  .pf-baract { min-height: 44px; }
  .pf-brand { min-height: 44px; }
  .pf-voice-btn { min-height: 44px; }
}
