/* ============================================================
   SHARED COMPONENT LIBRARY  —  hub-v1 foundation
   ------------------------------------------------------------
   Reads ONLY the token contract in tokens.css. Never hardcode a
   brand color here — use var(--accent*) so any theme repaints it.
   Load order in every page:  tokens.css → components.css → <user>/theme.css
============================================================ */

/* Viewport primitives are mechanics, not member identity. Consumers can use
   these without guessing whether the browser chrome or a display cutout is
   stealing part of the usable screen. */
:root {
    --safe-area-top: env(safe-area-inset-top, 0px);
    --safe-area-right: env(safe-area-inset-right, 0px);
    --safe-area-bottom: env(safe-area-inset-bottom, 0px);
    --safe-area-left: env(safe-area-inset-left, 0px);
    --viewport-block: 100vh;
    --control-primary: 44px;
    --control-dense: 32px;
}
@supports (height: 100dvh) {
    :root { --viewport-block: 100dvh; }
}

/* ============================================================
   A11Y HELPERS   (Wave 1a — access-warden)
   ------------------------------------------------------------
   Cross-page primitives for the per-page Wave-1a control passes:
   an invisible ≥32px tap expander for controls that must stay
   visually tiny, and screen-reader-only text. The per-page ✕/✎/
   status controls reach the ≥32px + focus-visible + reveal standard
   via grouped rules in their OWN stylesheet (loads after this file →
   wins the cascade). NB: `.icon-btn` below is the header-tools
   button — a different control; don't reuse that name here.
============================================================ */
/* invisible ≥32px hit expander for a control that must stay visually
   tiny (dense row, a chip corner). Element keeps its small box; the
   tap/click area grows to 32px centered on the glyph. Needs the
   element positioned (relative/absolute). */
.hit-expand { position: relative; }
.hit-expand::before {
    content: ''; position: absolute; top: 50%; left: 50%;
    width: 32px; height: 32px; min-width: 100%; min-height: 100%;
    transform: translate(-50%, -50%);
}
/* screen-reader-only real text, for glyph-only controls */
.visually-hidden {
    position: absolute !important; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0);
    white-space: nowrap; border: 0;
}

/* One drawn checkbox grammar for every engine. Native accent-color leaves the
   unchecked boundary too faint in WebKit, so the boundary, check and focus
   ring are all explicit. Call sites may override only --hub-check-accent. */
.hub-check {
    -webkit-appearance: none;
    appearance: none;
    box-sizing: border-box;
    display: inline-block;
    position: relative;
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    margin: 0;
    vertical-align: middle;
    /* ⚠ --hub-check-border, not --text-muted directly: this class lands on FOUR
       palettes and --text-muted is not the same distance from the ground on all
       of them. Measured on each member's own goals page against a RENDERED pixel:
       sami 5.74 · austin 5.27 · jerry 6.92 · jp 2.80 — jp's #978c7d on his
       #f0ece4 base is UNDER WCAG 1.4.11's 3.0 floor for a control boundary, and
       he is the only one. The synthetic harness could not see it: with only
       tokens.css + components.css loaded the var falls through to the literal
       below and reads 7.57, which is a fact about the harness. Gate arm X4. */
    border: 2px solid var(--hub-check-border, var(--text-muted, #6b7280));
    border-radius: 5px;
    background: transparent;
    cursor: pointer;
}
.hub-check:checked {
    background: var(--hub-check-accent, var(--accent));
    border-color: var(--hub-check-accent, var(--accent));
}
.hub-check:checked::after {
    content: "";
    position: absolute;
    left: 5px;
    top: 1px;
    width: 5px;
    height: 10px;
    border: solid var(--hub-check-mark, var(--accent-contrast, #fff));
    border-width: 0 2px 2px 0;
    transform: rotate(45deg);
}
.hub-check:focus-visible {
    outline: 2px solid var(--hub-check-focus, var(--hub-check-accent, var(--focus-ring)));
    outline-offset: 2px;
}
.hub-check:disabled { cursor: default; }

/* ============================================================
   LAYOUT  —  header, page shell, grid
============================================================ */
.page {
    width: 100%;
    max-width: 1200px;
    margin: 0 auto;
}

header.hub-header {
    width: 100%;
    max-width: 1200px;
    margin: 0 auto var(--space-xl);
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding-bottom: var(--space-lg);
    border-bottom: 1px solid var(--border-subtle);
}

.header-logo { display: flex; align-items: center; gap: var(--space-md); }

.logo-mark {
    width: 36px; height: 36px;
    background: linear-gradient(135deg, var(--accent), var(--accent-dim));
    border-radius: 8px;
    display: flex; align-items: center; justify-content: center;
    font-size: 1.1rem;
    box-shadow: 0 0 20px var(--accent-glow);
    flex-shrink: 0;
}

.header-title h1 {
    font-family: var(--font-display);
    font-size: 1.55rem;
    font-weight: 800;
    letter-spacing: 0.18em;
    color: var(--accent-text);
    text-transform: uppercase;
    text-shadow: 0 0 24px var(--accent-glow);

    /* The masthead is no longer a literal we authored — [data-empire-name]
       substitutes a name the MEMBER typed, up to ONBOARDING_EMPIRE_MAX (48).
       Measured at that ceiling it wrapped to two lines, grew the header from
       71px to 110px and orphaned the clock's date onto its own row. Truncate
       instead of wrapping, so an over-long name costs its own tail and never
       the header's shape. max-width is in em so it tracks the font-size drop
       in the phone breakpoint below rather than needing a second value. */
    max-width: 30em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.page-title { font-family: var(--font-display); }

/* The card-header type jobs — the half of the 2.3 "B" wiring that never
   shipped. `--font-display` reached the masthead h1, `.page-title` and the
   one-thing banner and stopped there, so on the three Archivo members the
   display face was absent from the thing a member actually reads down a page:
   every card header, on every page. The mockup's own WIRE block pointed
   `.hc-title / .hc-head / .hc-slide-head` at it and its docstring said why —
   "setting --font-display alone changes almost nothing on Jerry's and Austin's
   pages, which IS the defect".
   Wired on the CONTAINERS, as the mockup wired them, so `.hc-link` and
   `.hc-metric-label` inherit instead of each needing a rule of its own.
   Both mode sheets restyle `.hc-title`, but only grid sets font-family and it
   sets this same var, so no mode fights this. */
.hc-head, .hc-slide-head, .hc-title { font-family: var(--font-display); }

/* ============================================================
   DAILY BANNER — shared structure, member-owned tokens
============================================================ */
.daily-banner {
    position: relative;
    max-width: 1340px;
    margin: 12px auto 0;
    padding: 15px 26px 14px;
    background: var(--bg-card, var(--bg-inset));
    border: 1px solid var(--border-muted);
    border-left: 4px solid var(--accent);
    border-radius: var(--radius-lg);
}
.daily-banner-kick {
    margin-bottom: 6px;
    color: var(--daily-banner-label);
    font-size: 0.6rem;
    font-weight: 800;
    letter-spacing: 0.18em;
    text-transform: uppercase;
}
.daily-banner-task {
    color: var(--text-primary);
    font-family: var(--font-display);
    font-size: 2.1rem;
    font-weight: 700;
    line-height: 1.15;
    word-break: break-word;
    cursor: pointer;
}
.daily-banner-task:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 3px;
    border-radius: var(--radius-sm);
}
.daily-banner-task.is-empty {
    display: inline-block;
    padding-bottom: 2px;
    color: var(--text-muted);
    border-bottom: 1px dashed var(--border-active);
    font-size: 1.4rem;
    font-weight: 400;
}
.daily-banner-why {
    max-width: 680px;
    margin-top: 8px;
    color: var(--text-secondary);
    font-size: 0.86rem;
    line-height: 1.45;
}
.daily-banner-why.is-empty { color: var(--text-muted); opacity: 0.7; }
.daily-banner-why-tag {
    margin-right: 7px;
    color: var(--daily-banner-label);
    font-size: 0.6rem;
    font-weight: 800;
    letter-spacing: 0.14em;
    text-transform: uppercase;
}
.daily-banner-edit {
    position: absolute;
    top: 12px;
    right: 14px;
    min-width: 32px;
    min-height: 32px;
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--daily-banner-label);
    cursor: pointer;
    opacity: 0.45;
}
.daily-banner:hover .daily-banner-edit,
.daily-banner-edit:hover,
.daily-banner-edit:focus-visible {
    background: var(--bg-inset);
    opacity: 1;
}
.daily-banner-edit:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
/* 44px HIT AREA, 32px BOX — the Arc 2 tap-target bar, paid the way this file
   already pays it for `.wb-loop-edit`. This button is absolutely positioned
   ON TOP of the task line, so growing the box to 44 would reach 12px further
   across the text it floats over and enlarge the hover plate for a pointer
   that never needed it. A transparent pseudo costs neither, and costs zero
   layout width. Base rule rather than the phone block below, because a touch
   is a touch at any viewport and this is free at all of them.
   ⚠ A rect-based tap-target count will keep reporting 32x32 here and that is
   CORRECT — the hit test is the arm that answers for this control, exactly as
   recorded for `.wb-loop-edit` / `.wb-loop-del` further down this file. */
.daily-banner-edit::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: var(--control-primary, 44px);
    height: var(--control-primary, 44px);
    transform: translate(-50%, -50%);
}
.daily-banner-form { display: none; flex-direction: column; gap: 10px; margin-top: 12px; }
.daily-banner-form .f-input { width: 100%; }
.daily-banner-form-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.daily-banner.editing .daily-banner-form { display: flex; }
.daily-banner.editing .daily-banner-task,
.daily-banner.editing .daily-banner-why { display: none; }

@media (hover: none) {
    .daily-banner-edit { opacity: 0.6; }
}

@media (max-width: 720px) {
    .daily-banner {
        margin-left: var(--space-md);
        margin-right: var(--space-md);
        padding: 13px 16px 12px;
    }
    .daily-banner-task { font-size: 1.55rem; }
    .daily-banner-task.is-empty { font-size: 1.15rem; }
}

/* ⚠ DESKTOP ROW ONLY — deliberately NOT global, and the phone case is why.
   A flex item's min-width defaults to `auto`, refusing to shrink below its
   content; zeroing it is what lets the h1 clip instead of pushing the header
   wide. But it also lets the title absorb ANY squeeze — and Sami's phone
   layout already overflows (documentElement scrollWidth 508 in a 390 viewport,
   present with these rules reverted, so it predates them). Applied globally it
   collapsed his title to 36px against a 144px natural width: "SEAL STUDIOS"
   rendered as roughly "S…". Above the phone breakpoint there is no such
   squeeze, and below it `max-width` alone still does the truncating. */
@media (min-width: 721px) {
    header.hub-header > .header-logo,
    .header-logo > .header-title { min-width: 0; }

    /* The clock/tools cluster is the one that must NOT absorb the squeeze.
       Measured at a 48-char name: the title took its full cap, the cluster was
       pushed narrow, and `.clock-date` wrapped 16px → 32px — so the 16px the
       header grew was the DATE wrapping, never the h1, which stayed one line
       throughout. The title can lose its tail gracefully; the date cannot. */
    header.hub-header > .header-tools { flex-shrink: 0; }
}

.clock-container { text-align: right; }
.clock-time {
    font-size: 1.5rem; font-weight: 700;
    color: var(--text-primary);
    font-family: var(--font-mono);
    letter-spacing: 0.05em;
}
.clock-date {
    font-size: 0.78rem; color: var(--accent-muted);
    font-weight: 600; letter-spacing: 0.04em;
    text-transform: uppercase; margin-top: 2px;
}

.dashboard-grid {
    width: 100%; max-width: 1200px; margin: 0 auto;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
    gap: var(--space-xl);
    align-items: start;
}

/* Section heading between card bands */
.band-label {
    width: 100%; max-width: 1200px; margin: var(--space-2xl) auto var(--space-md);
    font-size: 0.72rem; font-weight: 800;
    letter-spacing: 0.18em; text-transform: uppercase;
    color: var(--text-muted);
    display: flex; align-items: center; gap: var(--space-md);
}
.band-label::after {
    content: ''; flex: 1; height: 1px;
    background: var(--border-subtle);
}

/* ============================================================
   CARD SYSTEM
============================================================ */
.card {
    background-color: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    padding: var(--space-lg);
    box-shadow: 0 4px 32px rgba(0,0,0,0.45);
    display: flex; flex-direction: column;
    gap: var(--space-md);
    transition: border-color var(--t-med) ease, box-shadow var(--t-med) ease, transform var(--t-med) var(--ease-out);
}
.card:hover {
    border-color: var(--border-muted);
    box-shadow: 0 6px 40px rgba(0,0,0,0.55);
}

.card-header {
    display: flex; justify-content: space-between; align-items: center;
    padding-bottom: var(--space-md);
    border-bottom: 1px solid var(--border-subtle);
}
.card-title {
    font-size: 0.78rem; font-weight: 800;
    letter-spacing: 0.1em; text-transform: uppercase;
    color: var(--accent-muted);
    display: flex; align-items: center; gap: var(--space-sm);
}

/* ---- Clickable card (navigates to a deeper page) ---- */
.card-link { text-decoration: none; color: inherit; cursor: pointer; }
.card-link:hover .card { border-color: var(--accent-muted); transform: translateY(-3px); }

/* ============================================================
   METRIC PREVIEW CARD  —  home-page live data tile
   States: default · empty (no data yet) · neglect (overdue)
============================================================ */
.metric-card { position: relative; overflow: hidden; }

.metric-accent-bar {
    position: absolute; top: 0; left: 0; width: 100%; height: 3px;
    background: var(--metric-accent, var(--accent));
    opacity: 0.9;
}

.metric-headline {
    display: flex; align-items: baseline; gap: var(--space-sm);
}
.metric-value {
    font-size: 2.4rem; font-weight: 800;
    font-family: var(--font-mono); line-height: 1;
    color: var(--metric-accent, var(--accent-text));
}
.metric-unit { font-size: 0.9rem; color: var(--text-secondary); font-weight: 600; }
.metric-sub {
    font-size: 0.82rem; color: var(--text-secondary);
    line-height: 1.5;
}
.metric-delta { font-size: 0.78rem; font-weight: 700; }
.metric-delta.positive { color: var(--success); }
.metric-delta.negative { color: var(--danger); }

/* Empty state: data layer present but nothing logged yet */
.metric-card.is-empty .metric-value { color: var(--text-muted); }
.metric-card.is-empty .metric-headline::after {
    content: 'awaiting first entry';
    font-size: 0.72rem; color: var(--text-muted);
    font-style: italic; align-self: center;
}

/* Neglect state: the page is "alive" and nudges when overdue */
.metric-card.is-neglected {
    border-color: rgba(245, 158, 11, 0.35);
    animation: neglect-pulse 2.6s ease-in-out infinite;
}
.metric-card.is-neglected .metric-accent-bar { background: var(--warning); }
@keyframes neglect-pulse {
    0%, 100% { box-shadow: 0 4px 32px rgba(0,0,0,0.45); }
    50%      { box-shadow: 0 4px 32px rgba(0,0,0,0.45), 0 0 22px rgba(245,158,11,0.22); }
}
.neglect-tag {
    display: none;
    font-size: 0.7rem; font-weight: 700; color: var(--warning);
    text-transform: uppercase; letter-spacing: 0.06em;
}
.metric-card.is-neglected .neglect-tag { display: inline; }

/* tiny sparkline row */
.spark-row { display: flex; align-items: flex-end; gap: 3px; height: 34px; margin-top: var(--space-xs); }
.spark-bar { flex: 1; background: var(--metric-accent, var(--accent)); opacity: 0.55; border-radius: 2px 2px 0 0; min-height: 3px; }

/* ============================================================
   BUOY  —  always-ready surface quick-access (never neglected)
============================================================ */
.buoy-row {
    width: 100%; max-width: 1200px; margin: 0 auto;
    display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: var(--space-md);
}
.buoy {
    background: var(--bg-inset);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: var(--space-md) var(--space-lg);
    display: flex; align-items: center; gap: var(--space-md);
    text-decoration: none; cursor: pointer;
    transition: all var(--t-fast) var(--ease-out);
}
.buoy:hover {
    border-color: var(--accent-muted);
    background: var(--accent-glow);
    transform: translateY(-2px);
}
.buoy-icon { font-size: 1.4rem; flex-shrink: 0; }
.buoy-text { display: flex; flex-direction: column; gap: 1px; }
.buoy-name {
    font-weight: 800; font-size: 0.8rem; letter-spacing: 0.06em;
    text-transform: uppercase; color: var(--text-primary);
}
.buoy-status { font-size: 0.72rem; color: var(--text-muted); }
.buoy-badge {
    margin-left: auto;
    background: var(--accent); color: var(--accent-contrast);
    font-size: 0.66rem; font-weight: 800;
    padding: 2px 7px; border-radius: 999px;
    display: none;
}
.buoy.has-badge .buoy-badge { display: inline-block; }

/* ============================================================
   STAT BLOCKS  (reused on detail pages)
============================================================ */
.stat-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-md); }
.stat-block {
    background: var(--bg-inset);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: var(--space-md); text-align: center;
}
.stat-value {
    font-size: 2.1rem; font-weight: 800;
    color: var(--accent-text); font-family: var(--font-mono); line-height: 1;
}
.stat-label {
    font-size: 0.7rem; color: var(--text-muted);
    text-transform: uppercase; letter-spacing: 0.08em;
    margin-top: var(--space-xs); font-weight: 700;
}

/* ============================================================
   BUTTONS
============================================================ */
.btn-primary {
    background: linear-gradient(135deg, var(--accent), var(--accent-fill, var(--accent-muted)));
    color: var(--accent-contrast);
    border: none; border-radius: var(--radius-sm);
    padding: var(--space-md);
    font-size: 0.88rem; font-weight: 800;
    cursor: pointer; text-transform: uppercase; letter-spacing: 0.06em;
    transition: all var(--t-med) ease;
    box-shadow: 0 4px 18px var(--accent-glow);
    min-height: var(--control-primary);
}
.btn-primary:hover { filter: brightness(1.12); transform: translateY(-1px); box-shadow: 0 6px 24px var(--accent-glow); }
.btn-primary:disabled { opacity: 0.6; cursor: not-allowed; transform: none; }

.btn-ghost {
    background: none;
    border: 1px solid var(--border-muted);
    color: var(--text-secondary);
    padding: var(--space-xs) var(--space-md);
    border-radius: var(--radius-sm);
    font-size: 0.75rem; font-weight: 700;
    cursor: pointer; text-transform: uppercase; letter-spacing: 0.05em;
    transition: all var(--t-fast) ease;
    min-height: var(--control-dense);
}
.btn-ghost:hover { border-color: var(--accent-muted); color: var(--text-primary); background: var(--bg-inset); }

.btn-add {
    background-color: var(--accent);
    color: var(--accent-contrast);
    border: none; border-radius: var(--radius-sm);
    padding: var(--space-sm) var(--space-md);
    font-weight: 800; font-size: 0.78rem;
    cursor: pointer; text-transform: uppercase; letter-spacing: 0.06em;
    transition: background-color var(--t-fast);
    min-height: var(--control-primary);
}
.btn-add:hover { background-color: var(--accent-fill-hi, var(--accent-bright)); }

/* ---- .exp-btn — the compact action button on an expanded card ----
   PROMOTED HERE 2026-08-12 (Batch 7A) from jerry/theme.css, where it was the
   only declaration in the repo. shared/plan-funnel.js emits it (every card
   action row: ✎ Edit · Turn into a plan → · Mark Active · Delete), so leaving
   it in one member's theme meant the shared funnel rendered UNSTYLED BUTTONS
   on the other three the moment it mounted — the same class of hole as the
   funnel CSS itself. Declarations are jerry's verbatim; they were already
   100% token-driven, so this is a move, not a re-design, and his pages read
   the identical values from one file earlier in the cascade.
   The 32px clamp and focus ring came from that theme's a11y block and travel
   WITH the component — an operability guarantee is not a member's property. */
.exp-btn {
    background: none; border: 1px solid var(--border-muted); color: var(--accent-muted);
    border-radius: var(--radius-sm); padding: 5px 11px; font-size: 0.7rem; font-weight: 800;
    text-transform: uppercase; letter-spacing: 0.04em; cursor: pointer;
    min-height: 32px; display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box;
}
.exp-btn:hover { border-color: var(--accent); color: var(--accent-text); }
.exp-btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-contrast); }
.exp-btn.primary:hover { filter: brightness(1.08); }
.exp-btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* ---- .funnel-empty — the honest empty line under a list ----
   PROMOTED HERE 2026-08-12 (Batch 7A) from jerry/theme.css for the same reason
   as .exp-btn, and it very nearly went to shared/plan-funnel.css instead: the
   NAME says funnel, but jerry/trading.html renders one for "No trades logged
   yet." Filing it with the funnel would have left that sentence unstyled on a
   page this batch never opened. 🔑 A class name is a claim about where it is
   used, and it is not evidence — grep the class, don't read it. */
.funnel-empty { font-size: 0.78rem; color: var(--text-muted); font-style: italic; padding: 8px 2px; }


/* ============================================================
   FORM INPUTS
============================================================ */

/* ── the form label, de-forked 2026-08-17 (lot L2) ──────────────
   Was declared FIVE times, byte-identically: jerry/theme.css:423,
   austin/theme.css:524, jp/theme.css:435, and — the two the lot
   item did not know about — inline in sami/fitness.html and in
   jerry/constellation.html.

   ⚠ L2 WAS FILED AS A TASTE CALL ("promoting this restyles two
   shipped pages, so it needs Sami's eyes"). It does not. Both of
   those pages already carried the identical rule in their own
   <style>, so the promotion is a visual no-op — measured across
   117 labels on 10 pages, every computed property unchanged
   (tests/gate_l2_flabel_defork.mjs). The earlier claim came from
   grepping sami/theme.css, which never had it; the rule lived in
   the page. Grepping the file a rule is EXPECTED to live in cannot
   tell "missing" from "declared somewhere else".

   Scoped variants deliberately NOT absorbed, because they are real
   overrides rather than copies: jerry/theme.css:663
   `.tt-trade-row .f-label` (0.56rem) and the `.pf-hero .f-label`
   pair in shared/plan-brief.css + jerry/constellation.html. Both
   out-specify this rule, so cascade order cannot disturb them.
   ────────────────────────────────────────────────────────────── */
.f-label { font-size: 0.6rem; font-weight: 800; color: var(--accent-muted); text-transform: uppercase; letter-spacing: 0.08em; display: block; margin-bottom: 0.22rem; }

/* Native controls draw their POPUP and their internal widgets from the UA's
   colour scheme, not from our tokens — so a <select>'s option list, a date
   picker's calendar and number spinners all render system-light in this dark
   hub no matter how thoroughly the closed control itself is themed. Sami hit
   this on the fitness dropdowns (page walk #8, 2026-07-31); it was never a
   fitness bug — 30 selects across all four member hubs had it.

   Declared once here rather than on .f-input because 8 of those selects carry
   no shared class at all (#mtInterval, #jnFontFamily, #jnFontSize,
   #mtIntervalUnit, .te-interval …). Measured, not assumed: every live page
   that contains a <select> loads this stylesheet — the five that don't
   (the three constellation redirect stubs, jp/journal.html, community/
   meeting.html) contain no native controls.

   ⚠ Scoped to form controls, NOT :root. A root declaration would also restyle
   scrollbars and every other UA surface hub-wide, which is a bigger change
   than the one that was asked for and would need its own look. If a light
   mode is ever added, this is a line that has to become mode-aware. */
select, input, textarea { color-scheme: dark; }

.text-input {
    width: 100%;
    background-color: #111120;
    border: 1px solid var(--border-muted);
    border-radius: var(--radius-sm);
    padding: var(--space-sm) var(--space-md);
    color: var(--text-primary);
    font-size: 0.85rem; font-family: var(--font-sans);
    transition: border-color var(--t-fast);
}
.text-input:focus { outline: none; border-color: var(--accent-muted); }
/* ⛔ THE LINE ABOVE IS AN ACTIVE SUPPRESSION, not a missing ring — it killed the
   browser default on EVERY text input on all four hubs and replaced it with a
   border tint a keyboard user cannot reliably see. Restored as :focus-visible so
   a mouse click still gets no ring (the house behaviour) while tabbing does.
   MUST stay AFTER the :focus rule: both are (0,2,0), so source order decides. */
.text-input:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
textarea.text-input { resize: vertical; min-height: 70px; line-height: 1.5; }

/* ============================================================
   SUGGESTION BOX  —  on every member hub page
============================================================ */
.suggest-box { gap: var(--space-md); }
.suggest-tags { display: flex; gap: var(--space-sm); flex-wrap: wrap; }
.suggest-tag {
    background: var(--bg-inset);
    border: 1px solid var(--border-muted);
    color: var(--text-secondary);
    font-size: 0.7rem; font-weight: 700;
    padding: 4px 10px; border-radius: 999px;
    cursor: pointer; text-transform: uppercase; letter-spacing: 0.05em;
    transition: all var(--t-fast) ease;
}
.suggest-tag.active {
    border-color: var(--accent);
    color: var(--accent-text);
    background: var(--accent-glow);
}

/* ============================================================
   CALENDAR  —  activity hub (day-at-a-glance on hover)
============================================================ */
.calendar {
    width: 100%; max-width: 1200px; margin: 0 auto;
    background-color: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    padding: var(--space-lg);
    box-shadow: 0 4px 32px rgba(0,0,0,0.45);
}
.calendar-head {
    display: flex; justify-content: space-between; align-items: center;
    margin-bottom: var(--space-md);
}
.calendar-month { font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; color: var(--accent-muted); font-size: 0.9rem; }
.calendar-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px; }
.cal-dow {
    text-align: center; font-size: 0.66rem; font-weight: 800;
    color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.06em;
    padding-bottom: var(--space-xs);
}
.cal-day {
    position: relative;
    aspect-ratio: 1 / 1;
    background: var(--bg-inset);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    font-size: 0.8rem; color: var(--text-secondary);
    cursor: default;
    transition: all var(--t-fast) ease;
}
.cal-day.empty { background: none; border: none; cursor: default; }
.cal-day.today { border-color: var(--accent); color: var(--text-primary); font-weight: 800; }
.cal-day.has-activity { color: var(--text-primary); }
.cal-day:not(.empty) { cursor: pointer; }
.cal-day:hover { border-color: var(--accent-muted); background: var(--accent-glow); z-index: 5; }

/* activity dots under the date number.
   J4 (jerry, 2026-07-05: "on calender, dots are too small"). 4px reads as
   grit on a phone; 6px with a 3px gap is still a dot and is actually
   distinguishable. Wraps rather than overflowing the day cell on a day that
   collects several markers, since .cal-day centres its column either way. */
.cal-dots { display: flex; flex-wrap: wrap; justify-content: center; gap: 3px; margin-top: 3px; min-height: 7px; max-width: 100%; }
.cal-dot { width: 6px; height: 6px; border-radius: 50%; box-sizing: border-box; }

/* day-at-a-glance popover */
.cal-pop {
    position: absolute; bottom: calc(100% + 6px); left: 50%; transform: translateX(-50%);
    width: 190px;
    background: #0e0e18;
    border: 1px solid var(--accent-muted);
    border-radius: var(--radius-md);
    padding: var(--space-sm) var(--space-md);
    box-shadow: 0 12px 40px rgba(0,0,0,0.7);
    opacity: 0; pointer-events: none;
    transition: opacity var(--t-fast) ease;
    z-index: 20; text-align: left;
}
.cal-day:hover .cal-pop { opacity: 1; }
.cal-pop-title { font-size: 0.72rem; font-weight: 800; color: var(--accent-text); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: var(--space-xs); }
.cal-pop-row { font-size: 0.74rem; color: var(--text-secondary); display: flex; justify-content: space-between; gap: var(--space-sm); padding: 1px 0; }
.cal-pop-empty { font-size: 0.72rem; color: var(--text-muted); font-style: italic; }

/* named things parked on a day — short text under the date number.
   J4 (jerry, 2026-07-05: "theres no text for events"). This line was
   Flight-Plan-only: an event got a dot and nothing else, so a day with a real
   appointment on it looked the same as a day with a logged weigh-in. Events now
   render here too, and lead, because an event has a name a person put there.
   Colour still separates the two so the line does not read as one kind of thing. */
.cal-day-keyword {
    font-size: 0.58rem; color: var(--cat-flight, var(--accent-muted)); text-align: center;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 100%;
    padding: 0 2px; margin-top: 1px; line-height: 1.15;
}
.cal-day-keyword .cdk-ev { color: var(--cat-events, var(--accent-text)); }
.cal-day-keyword .cdk-fl { color: var(--cat-flight, var(--accent-muted)); }
.cal-day-keyword .cdk-sep { color: var(--text-muted); }
/* filled dot = happened; hollow ring = planned/ahead (same 6px outer box as a
   filled dot — box-sizing above means the border eats into the width) */
.cal-dot.is-planned { background: transparent; border: 1.5px solid currentColor; width: 6px; height: 6px; }

/* grouped legend — category sub-headers, each with icon+color entries
   (icons pair with color so meaning never rides on hue alone). Each group is
   its own bordered chip with a colored left edge — plain text run together
   read as one continuous list (Sami, 2026-07-02), this gives real separation. */
.cal-legend { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: var(--space-md); }
.cal-legend-group {
    display: flex; flex-direction: column; gap: 3px;
    padding: 7px 12px; border-radius: var(--radius-sm);
    background: rgba(255,255,255,0.03); border: 1px solid var(--border-subtle);
    border-left: 3px solid var(--cal-legend-accent, var(--border-subtle));
}
.cal-legend-cat {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 0.6rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--text-muted);
}
/* the category's own color swatch — the "what does this color mean" ask made
   literal, since the label text alone (however it's tinted) reads as decoration */
.cal-legend-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.cal-legend-items { display: flex; gap: 10px; flex-wrap: wrap; }
.cal-legend-item { display: inline-flex; align-items: center; gap: 4px; font-size: 0.7rem; color: var(--text-secondary); }
.cal-legend-item .cli-ic { font-size: 0.78rem; line-height: 1; }

/* ------------------------------------------------------------
   WEEK VIEW  —  month/week toggle + 7-day hourly grid.
   Structure only; color comes from --accent and --impact-*.
   Logic lives in shared/calendar.js. (CP-W1)
------------------------------------------------------------ */
.cal-vt-row { display: flex; gap: 4px; margin-bottom: var(--space-md); }
.cal-vt {
    flex: 0 0 auto;
    font-size: 0.66rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--text-muted);
    background: var(--bg-inset);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm);
    padding: 5px 14px; cursor: pointer;
    transition: all var(--t-fast) ease;
}
.cal-vt:hover { color: var(--text-secondary); border-color: var(--accent-muted); }
.cal-vt.active {
    color: var(--text-primary);
    border-color: var(--accent);
    background: var(--accent-glow);
}
/* compact M/W pill (header, replaces the two full-width Month/Week buttons) */
.cal-vt-compact { margin-bottom: 0; }
.cal-vt-compact .cal-vt { padding: 4px 9px; font-size: 0.6rem; }

/* week-nav header (◀  date range  ▶) */
.cw-head { display: flex; align-items: center; justify-content: center; gap: var(--space-md); margin-bottom: var(--space-md); }
.cw-range { font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase; color: var(--accent-muted); font-size: 0.82rem; min-width: 150px; text-align: center; }
.cw-nav {
    background: var(--bg-inset); border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
    color: var(--text-secondary); font-size: 0.8rem; line-height: 1; cursor: pointer;
    padding: 5px 10px; transition: all var(--t-fast) ease;
}
.cw-nav:hover { color: var(--text-primary); border-color: var(--accent-muted); background: var(--accent-glow); }

/* the grid: hour gutter + 7 day columns (horizontally scrollable when tight) */
.cw-scroll { overflow-x: auto; }
.cw-grid {
    display: grid;
    grid-template-columns: 46px repeat(7, minmax(72px, 1fr));
    min-width: 620px;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    overflow: hidden;
}
.cw-corner { background: var(--bg-inset); border-bottom: 1px solid var(--border-subtle); }
.cw-colhead {
    text-align: center; font-size: 0.62rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--text-muted);
    background: var(--bg-inset);
    padding: 6px 2px;
    border-bottom: 1px solid var(--border-subtle);
    border-left: 1px solid var(--border-subtle);
}
.cw-colhead b { display: block; font-size: 0.82rem; color: var(--text-secondary); }
.cw-colhead.today { color: var(--accent-text); }
.cw-colhead.today b { color: var(--text-primary); }

/* hour gutter — labels sit at the top line of each hour */
.cw-gutter { background: var(--bg-inset); }
.cw-hour { position: relative; }
.cw-hour span {
    position: absolute; top: -1px; right: 5px;
    font-size: 0.56rem; color: var(--text-muted); line-height: 1;
    transform: translateY(0);
}

/* day body — stacked hour cells, blocks positioned absolutely over them */
.cw-col { border-left: 1px solid var(--border-subtle); }
.cw-col.today { background: var(--accent-glow); }
.cw-colbody { position: relative; }
.cw-cell { border-top: 1px solid var(--border-subtle); box-sizing: border-box; }
.cw-cell:first-child { border-top: none; }

.cw-block {
    position: absolute; left: 2px; right: 2px;
    border-radius: var(--radius-sm);
    font-size: 0.6rem; font-weight: 700; color: #07121c;
    padding: 2px 4px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
    box-shadow: 0 1px 4px rgba(0,0,0,0.4);
    box-sizing: border-box;
}
/* ── THE ALL-DAY BAND (2026-08-24) — row 2 of .cw-grid ──────────────
   Only rendered when the week actually holds an all-day or multi-day
   event, so an ordinary week loses no vertical space to an empty rail.
   .cw-allday is a nested 7-column grid: one <div class="cw-allday-lane">
   per stacking lane, bars placed by grid-column inside it. The nesting
   is what makes overlapping bars cheap — lanes are just more rows —
   and it keeps the outer grid's auto-flow (gutter + 7 columns) intact. */
.cw-allday-lbl {
    grid-column: 1; align-self: stretch;
    display: flex; align-items: center; justify-content: flex-end;
    padding: 3px 5px 3px 0;
    font-size: 0.48rem; font-weight: 800; letter-spacing: 0.08em;
    color: var(--text-muted); background: var(--bg-inset);
    border-bottom: 1px solid var(--border-subtle);
}
.cw-allday {
    grid-column: 2 / -1;
    display: flex; flex-direction: column; gap: 2px;
    padding: 3px 0;
    border-bottom: 1px solid var(--border-subtle);
    background: var(--bg-inset);
}
.cw-allday-lane { display: grid; grid-template-columns: repeat(7, 1fr); }
/* ⛔ ONE RECORD, ONE BAR — never a pin per day. A five-day trip drawn as
   five marks reads as a rendering fault and argues against the feature
   ("I shouldn't have to make five events"). */
.cw-span {
    display: flex; align-items: center; gap: 4px;
    margin: 0 3px; padding: 2px 6px; min-height: 17px; box-sizing: border-box;
    border: 1px solid var(--cat-events);
    border-radius: var(--radius-sm);
    background: color-mix(in srgb, var(--cat-events) 26%, transparent);
    color: var(--text-primary);
    font-size: 0.54rem; font-weight: 700; line-height: 1.1;
    overflow: hidden; white-space: nowrap;
}
/* ⛔ A SPAN STAYS IN --cat-events. The first build tinted it --cat-flight
   for contrast against single-day pins, borrowing the mockup's orange —
   and --cat-flight is a CATEGORY in this system, the Flight Plan's own
   ink, named as such in the calendar legend. On jerry's theme the result
   was a blue multi-day event sitting beside a red single-day one, so the
   two halves of one category read as two categories and the legend was
   wrong about both. A trip differs from an appointment in SHAPE — a
   continuous bar across days — not in hue. */
.cw-span.is-span {
    background: linear-gradient(90deg,
        color-mix(in srgb, var(--cat-events) 34%, transparent),
        color-mix(in srgb, var(--cat-events) 18%, transparent));
    font-weight: 800;
}
/* a bar that starts before / ends after this week keeps a flat edge and an
   arrow, so "continues" is visible instead of being silently truncated */
.cw-span.clip-l { border-top-left-radius: 0; border-bottom-left-radius: 0; border-left-style: dashed; margin-left: 0; }
.cw-span.clip-r { border-top-right-radius: 0; border-bottom-right-radius: 0; border-right-style: dashed; margin-right: 0; }
.cw-span-cont { flex: 0 0 auto; opacity: 0.75; font-size: 0.5rem; }
.cw-span-label { overflow: hidden; text-overflow: ellipsis; }
.cw-span[data-event] { cursor: pointer; }
.cw-span[data-event]:hover { filter: brightness(1.15); }
.cw-span[data-event]:focus-visible { outline: 2px solid var(--cat-events); outline-offset: 1px; }

/* ── TIMED EVENTS ON THE GRID (2026-08-24) ──────────────────────────
   Same absolute layer as .cw-block, deliberately a different object: a
   task block is a plan you can move, an event is a commitment that
   already exists. Events purple, a heavy left rule, time above name.
   ⛔ No unschedule ✕ — removing an event is a deletion, and its confirm
   lives in the panel list, not one mis-tap away on the grid. */
.cw-event {
    position: absolute; left: 2px; right: 2px; z-index: 3;
    box-sizing: border-box;
    border: 1px solid var(--cat-events); border-left-width: 3px;
    border-radius: var(--radius-sm);
    background: linear-gradient(180deg,
        color-mix(in srgb, var(--cat-events) 34%, var(--bg-card)),
        color-mix(in srgb, var(--cat-events) 18%, var(--bg-card)));
    color: var(--text-primary);
    padding: 1px 4px; overflow: hidden;
    font-size: 0.58rem; font-weight: 700; line-height: 1.2;
    box-shadow: 0 2px 8px rgba(0,0,0,0.35);
}
.cw-event-time { display: block; font-size: 0.5rem; font-weight: 800; opacity: 0.8; line-height: 1; }
.cw-event-label { display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
/* a short block gets ONE line, not two clipped ones */
.cw-event.is-short {
    display: flex; align-items: center; gap: 4px;
    padding-top: 0; padding-bottom: 0; white-space: nowrap;
}
.cw-event.is-short .cw-event-time { display: inline; flex: 0 0 auto; }
.cw-event.is-short .cw-event-label { display: inline; min-width: 0; }
.cw-event.is-short .cw-event-resize { height: 6px; }
.cw-event.is-interactive { cursor: grab; }
.cw-event.is-interactive:active { cursor: grabbing; }
.cw-event.is-dragging { opacity: 0.45; }
.cw-event.is-resizing { opacity: 0.85; }
.cw-event:focus-visible { outline: 2px solid var(--cat-events); outline-offset: 1px; }
.cw-event-resize {
    position: absolute; left: 0; right: 0; bottom: 0; height: 8px;
    cursor: ns-resize; touch-action: none;
}
/* ⚠ HIDDEN UNTIL HOVER/FOCUS, matching .cw-block-resize. Painted always-on
   it reads as a white artifact stuck to the bottom of every event —
   visible in the first shot on all four themes — rather than as a grip. */
.cw-event-resize::after {
    content: ''; position: absolute; left: 50%; bottom: 2px;
    width: 16px; height: 2px; transform: translateX(-50%);
    border-radius: 2px; background: rgba(255,255,255,0.45);
    opacity: 0; transition: opacity var(--t-fast) ease;
}
.cw-event:hover .cw-event-resize::after,
.cw-event:focus-within .cw-event-resize::after { opacity: 1; }
/* touch has no hover — the grip is the only resize affordance there */
@media (hover: none) { .cw-event-resize::after { opacity: 1; } }

.cw-block.impact-needle   { background: var(--impact-needle,   #e9c46a); }
.cw-block.impact-standard { background: var(--impact-standard, #2a9d8f); }
.cw-block.impact-minor    { background: var(--impact-minor,    #8a9ba8); }
.cw-block.impact-work     { background: var(--cw-work, #6b7682); color: rgba(255,255,255,0.78); }
/* CP-W3 — Routine ("work") blocks paint slightly translucent to read as soft/movable. */
.cw-block.impact-work { background: rgba(107, 118, 130, 0.78); }

/* ============================================================
   PLANNER  (reports page) — interactive dual view (CP-W2)
   Task rail on the left, full 1h week grid on the right. Drag a
   chip onto an hour to schedule it; drag a block to move it; drag
   its bottom edge to resize in 30-min ticks; ✕ to unschedule.
------------------------------------------------------------ */
/* the view the tab bar is NOT showing must truly hide — .mission-control sets
   display:flex, which otherwise beats the UA [hidden]{display:none} rule */
#viewReports[hidden], #viewPlanning[hidden] { display: none !important; }

/* Reports ⇆ Planning tabs — live in a row below the header, next to the
   page title (moved out of .hub-header 2026-07-03: crammed next to the
   brand name/clock/bell read as cluttered). */
.page-tabs { display: flex; gap: 8px; }
.page-tab {
    font-size: 0.76rem; font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase;
    color: var(--text-muted);
    background: var(--bg-inset);
    border: 2px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: 8px 20px; cursor: pointer;
    transition: all var(--t-fast) ease;
}
.page-tab:hover { color: var(--text-secondary); border-color: var(--accent-muted); transform: translateY(-1px); }
.page-tab.active {
    color: var(--text-primary);
    border-color: var(--accent);
    background: var(--accent-glow);
    box-shadow: 0 0 12px var(--accent-glow);
}

/* PLANNING STAGE — the planner expanded to fill the page
   ────────────────────────────────────────────────────────────────────
   DIRECTION A, GUTTER DOCK (Sami, 2026-08-24). The stage's width and
   centring moved UP to .planning-stage-wrap so the Events dock can sit
   BESIDE the card instead of inside it. That placement is the whole
   point: the card keeps exactly the width it has when events are shut,
   so the week grid never resizes and never loses Saturday.
   ⚠ Measured before the change, at 1440x950 on all four hubs: opening
   Events cost the grid 256px — 31.4% — and left 6 of 7 day columns
   inside the scroller while the header still read "Aug 23 – 29".
   ⛔ Do not put max-width back on .planning-stage. It is what makes the
   card a 1fr column of the wrapper, and a fixed width here re-creates
   the squeeze from the other side. */
.planning-stage-wrap {
    width: 100%; max-width: 1120px; margin: var(--space-xl) auto var(--space-2xl);
    position: relative; z-index: 1;
}
.planning-stage {
    width: 100%; min-width: 0;
    position: relative;
    background: linear-gradient(180deg, rgba(255,255,255,0.03), var(--bg-card));
    border: var(--card-outline-w, 3px) solid var(--card-outline, var(--border-muted));
    border-radius: var(--radius-xl);
    padding: var(--space-lg) var(--space-xl) var(--space-xl);
    box-shadow: 0 6px 0 var(--card-shade, rgba(0,0,0,0.4)), 0 18px 50px rgba(0,0,0,0.5);
}
.plan-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-md); margin-bottom: var(--space-lg); flex-wrap: wrap; }
.plan-head .cw-head { margin-bottom: 0; }
.plan-vt-row { display: flex; gap: 4px; }              /* Month / Week pills (reuse .cal-vt) */
.plan-rail-toggle {
    font-size: 0.66rem; font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase;
    color: var(--text-secondary); background: var(--bg-inset);
    border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
    padding: 6px 12px; cursor: pointer; transition: all var(--t-fast) ease;
}
.plan-rail-toggle:hover { color: var(--text-primary); border-color: var(--accent-muted); }
.plan-rail-toggle.is-off { color: var(--text-muted); opacity: 0.7; }

/* per-member planning-grid start hour (Sami, 2026-08-09) — a <select> wearing the
   .plan-rail-toggle skin, so it reads as a sibling of ⏱ Shifts rather than a stray
   form control. appearance:none removes the OS chevron the toggles do not have;
   the padding keeps the same 6/12 rhythm. */
.plan-grid-start { display: inline-flex; align-items: center; }
.plan-grid-start-sel {
    appearance: none; -webkit-appearance: none; -moz-appearance: none;
    font-family: inherit; line-height: 1.2;
}
.plan-grid-start-sel:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* ⚠ WRAP, not a width (hub-repair 5c-CLOSE, 2026-08-11). This was a nowrap flex
   sitting ONE pixel inside a 375 viewport — head right=374 vs vw=375 on
   jerry/jp/austin — so 5c-vii's #pwGridStart <select> (95px, mounted by the
   engine into this same row for all four members) pushed every member's Planning
   view to a document scrollWidth of 478. Measured against a pre-Batch-6 worktree:
   0 overflow before, 103 after, on all four. Same mechanism as the .overlay-head
   find one surface over — a no-wrap flex does not fail when it grows, it fails
   the day something is ADDED to it, which is why the fix is the wrap rule and not
   a narrower control. min-width:0 on the status text so a long save message
   ("Couldn't save your plan.") shrinks instead of re-inflating the row. */
.plan-head-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; row-gap: 6px; }
.plan-head-actions .plan-save { min-width: 0; }
.plan-save {
    font-size: 0.66rem; font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase;
    border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
    background: var(--bg-inset); color: var(--text-muted);
    padding: 6px 14px; cursor: pointer; transition: all var(--t-fast) ease;
}
.plan-save:disabled { cursor: default; opacity: 0.7; }
.plan-save.is-dirty {
    color: var(--accent-contrast, #07121c); background: var(--accent); border-color: var(--accent); opacity: 1; cursor: pointer;
    animation: planSavePulse 1.5s ease-in-out infinite;
}
@keyframes planSavePulse {
    0%, 100% { box-shadow: 0 0 6px var(--accent-glow); }
    50%      { box-shadow: 0 0 16px var(--accent); }
}
/* unsaved-changes warning banner */
.plan-dirty {
    margin: 0 0 var(--space-md);
    padding: 8px 13px; border-radius: var(--radius-sm);
    font-size: 0.72rem; font-weight: 600; color: var(--accent-text);
    background: var(--accent-glow); border: 1px dashed var(--accent-muted);
}
.plan-dirty b { font-weight: 800; }

/* collapse the rail → the calendar gets the whole stage */
.plan-split.rail-collapsed .plan-rail { display: none; }

/* ---- planner MONTH grid (interactive: drop a task onto a day) ---- */
.pw-month {
    display: grid; grid-template-columns: repeat(7, minmax(96px, 1fr));
    border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
    overflow: hidden; min-width: 680px;
}
.pw-mdow {
    text-align: center; font-size: 0.6rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.06em;
    color: var(--text-muted); background: var(--bg-inset);
    padding: 7px 2px; border-bottom: 1px solid var(--border-subtle); border-left: 1px solid var(--border-subtle);
}
.pw-mcell {
    min-height: 96px; border-top: 1px solid var(--border-subtle); border-left: 1px solid var(--border-subtle);
    padding: 4px; display: flex; flex-direction: column; gap: 3px;
    transition: background var(--t-fast) ease, box-shadow var(--t-fast) ease;
}
.pw-mcell.empty { background: var(--bg-inset); opacity: 0.35; }
.pw-mnum { font-size: 0.66rem; font-weight: 700; color: var(--text-muted); }
.pw-mcell.today { background: var(--accent-glow); }
.pw-mcell.today .pw-mnum { color: var(--accent-text); }
.pw-mcell.is-dragover { background: var(--accent-glow); box-shadow: inset 0 0 0 2px var(--accent-muted); }
.pw-mchips { display: flex; flex-direction: column; gap: 2px; overflow: hidden; }
.pw-chip {
    position: relative; display: flex; align-items: center;
    font-size: 0.58rem; font-weight: 700; color: #07121c;
    border-radius: 4px; padding: 2px 16px 2px 5px; cursor: grab;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pw-chip:active { cursor: grabbing; }
.pw-chip.is-dragging { opacity: 0.45; }
.pw-chip-label { overflow: hidden; text-overflow: ellipsis; }
.pw-chip.impact-needle   { background: var(--impact-needle,   #e9c46a); }
.pw-chip.impact-standard { background: var(--impact-standard, #2a9d8f); }
.pw-chip.impact-minor    { background: var(--impact-minor,    #8a9ba8); }
.pw-chip.impact-work     { background: var(--cw-work, #6b7682); color: rgba(255,255,255,0.82); }
.pw-chip .cw-block-x { top: 50%; transform: translateY(-50%); }

/* all-day block in the WEEK grid (parked at the top, dashed) */
.cw-block.is-allday {
    height: 15px; left: 2px; right: 2px;
    border: 1px dashed rgba(7,18,28,0.4); font-size: 0.55rem; padding: 1px 14px 1px 4px;
    box-shadow: none; opacity: 0.95;
}

.plan-split { display: flex; gap: var(--space-md); align-items: flex-start; }
.plan-rail {
    flex: 0 0 188px; max-width: 188px;
    display: flex; flex-direction: column; gap: 6px;
    max-height: 620px; overflow-y: auto; padding-right: 2px;
}
/* full-page variant: wider rail, no inner scroll cap (the page scrolls) */
.plan-split-full .plan-rail { flex-basis: 224px; max-width: 224px; max-height: none; }
.plan-rail-empty { font-size: 0.72rem; color: var(--text-muted); padding: 10px 4px; line-height: 1.5; }
.plan-grid-wrap { flex: 1 1 auto; min-width: 0; }

/* a draggable task chip in the rail */
.plan-chip {
    display: flex; align-items: flex-start; gap: 6px;
    background: var(--bg-inset);
    border: 1px solid var(--border-subtle);
    border-left: 3px solid var(--chip-color, var(--accent-muted));
    border-radius: var(--radius-sm);
    padding: 7px 8px; cursor: grab;
    transition: border-color var(--t-fast) ease, transform var(--t-fast) ease, opacity var(--t-fast) ease;
}
.plan-chip:hover { border-color: var(--accent-muted); transform: translateX(2px); }
.plan-chip:active { cursor: grabbing; }
.plan-chip.is-dragging { opacity: 0.4; }
.plan-chip-dot { flex: 0 0 auto; margin-top: 3px; width: 8px; height: 8px; border-radius: 50%; background: var(--chip-color, var(--accent-fill, var(--accent-muted))); }
.plan-chip-title { font-size: 0.72rem; font-weight: 600; color: var(--text-secondary); line-height: 1.3; overflow: hidden; }
.plan-chip.is-scheduled { opacity: 0.6; }
.plan-chip.is-scheduled .plan-chip-title { text-decoration: line-through; color: var(--text-muted); }
.plan-chip-n { margin-left: auto; font-size: 0.58rem; font-weight: 800; color: var(--accent-muted); flex: 0 0 auto; }
.plan-rail-hint { font-size: 0.62rem; color: var(--text-muted); padding: 4px 2px 2px; letter-spacing: 0.02em; }

/* interactive blocks */
.cw-block.is-interactive { cursor: grab; padding-right: 14px; }
.cw-block.is-interactive:active { cursor: grabbing; }
.cw-block.is-dragging { opacity: 0.45; }
.cw-block-time  { display: block; font-size: 0.52rem; font-weight: 800; opacity: 0.8; line-height: 1; }
.cw-block-label { display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.cw-block-x {
    position: absolute; top: 1px; right: 1px;
    width: 14px; height: 14px; line-height: 12px; text-align: center;
    font-size: 0.6rem; border: none; border-radius: 3px;
    background: rgba(7,18,28,0.28); color: inherit; cursor: pointer;
    opacity: 0; transition: opacity var(--t-fast) ease;
}
.cw-block:hover .cw-block-x { opacity: 1; }
.cw-block-x:hover { background: rgba(7,18,28,0.5); }
.cw-block-resize {
    position: absolute; left: 0; right: 0; bottom: 0; height: 8px;
    cursor: ns-resize; border-bottom-left-radius: var(--radius-sm); border-bottom-right-radius: var(--radius-sm);
}
.cw-block-resize::after {
    content: ''; position: absolute; left: 50%; bottom: 2px; transform: translateX(-50%);
    width: 16px; height: 2px; border-radius: 2px; background: rgba(7,18,28,0.35);
    opacity: 0; transition: opacity var(--t-fast) ease;
}
.cw-block:hover .cw-block-resize::after { opacity: 1; }
.cw-block.is-resizing { box-shadow: 0 0 0 2px var(--accent), 0 1px 4px rgba(0,0,0,0.4); z-index: 5; }

/* a column lights up as you drag over it */
.cw-col.is-dragover { background: var(--accent-glow); box-shadow: inset 0 0 0 2px var(--accent-muted); }

/* flash when the user clicks a scheduled task in the list */
@keyframes cwFlash {
    0%, 100% { box-shadow: 0 1px 4px rgba(0,0,0,0.4); }
    25%, 75% { box-shadow: 0 0 0 3px var(--accent), 0 0 14px var(--accent-glow); }
}
.cw-block.flash { animation: cwFlash 1.1s ease; z-index: 6; }

/* ── Recurring work shifts (pre-ship ruling 2026-07-23) ─────────────
   A muted, non-draggable context layer painted UNDER the task blocks:
   time that's spoken for, not a task. pointer-events:none keeps drag
   drops landing on the hour cells beneath. Tokens carry baked
   fallbacks so member themes can retint via --shift-* without any
   page owing an override. */
.cw-shift {
    position: absolute; left: 1px; right: 1px; box-sizing: border-box;
    pointer-events: none; overflow: hidden;
    border-radius: 4px;
    background: repeating-linear-gradient(135deg,
        var(--shift-tint, rgba(138, 155, 168, 0.16)) 0 5px, transparent 5px 10px);
    border: 1px dashed var(--shift-line, rgba(138, 155, 168, 0.55));
    color: var(--shift-ink, var(--text-secondary, #aebccb));
    font-size: 0.5rem; line-height: 1.2; padding: 2px 4px;
}
.cw-shift-label { display: block; font-weight: 800; letter-spacing: 0.03em; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.cw-shift-time  { display: block; opacity: 0.85; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.cw-shift-spill { border-top-style: solid; border-top-width: 0; border-radius: 0 0 4px 4px; }

/* the Shifts editor dialog (house .overlay shell; plan-brief focus contract) */
.cw-shift-dialog { max-width: 430px; width: min(430px, 92vw); }
.cw-shift-sub { margin: 0 0 var(--space-md); font-size: 0.72rem; color: var(--text-muted); line-height: 1.5; }
.cw-shift-empty { margin: 0; font-size: 0.72rem; color: var(--text-muted); font-style: italic; }
.cw-shift-row {
    border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
    padding: var(--space-sm); margin: 0 0 var(--space-sm); min-inline-size: 0;
}
.cw-shift-row-top { display: flex; gap: 8px; align-items: center; margin-bottom: 6px; }
.cw-shift-name { flex: 1; min-width: 0; }
.cw-shift-del {
    background: none; border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
    color: var(--text-muted); cursor: pointer; min-width: 32px; min-height: 32px;
    font-size: 0.8rem; line-height: 1; flex: 0 0 auto;
}
.cw-shift-del:hover { color: var(--danger, #fa5c5c); border-color: var(--danger, #fa5c5c); }
.cw-shift-del:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.cw-shift-days { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.cw-shift-day {
    display: inline-flex; flex-direction: column; align-items: center; gap: 2px;
    min-width: 32px; min-height: 32px; justify-content: center;
    font-size: 0.62rem; font-weight: 700; color: var(--text-secondary); cursor: pointer;
}
.cw-shift-day input { margin: 0; cursor: pointer; }
.cw-shift-times { display: flex; gap: var(--space-md); align-items: center; flex-wrap: wrap; }
.cw-shift-timelab { display: inline-flex; gap: 6px; align-items: center; font-size: 0.68rem; color: var(--text-secondary); }
.cw-shift-timelab input {
    /* --bg-input: the shared field-tint contract, defined in shared/tokens.css.
       Same fix as shared/professional-ui.css — the old rgba(0,0,0,.25) fallback
       was a theme-blind black wash on light members. */
    background: var(--bg-input, var(--bg-inset, transparent)); color: var(--text-primary);
    border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
    padding: 4px 6px; font: inherit; font-size: 0.7rem; min-height: 32px; box-sizing: border-box;
}
.cw-shift-timelab input:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
.cw-shift-hint { font-size: 0.62rem; color: var(--text-muted); font-style: italic; }
.cw-shift-foot { display: flex; justify-content: space-between; gap: var(--space-md); margin-top: var(--space-md); }

/* a planner task that already has block(s): grayed + struck in the list */
.task-item.is-scheduled .task-titletext { color: var(--text-muted); text-decoration: line-through; }
.task-planned {
    background: none; border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
    color: var(--accent-muted); font-size: 0.58rem; font-weight: 800; letter-spacing: 0.04em;
    text-transform: uppercase; padding: 2px 7px; cursor: pointer;
    transition: all var(--t-fast) ease;
}
.task-planned:hover { color: var(--text-primary); border-color: var(--accent); background: var(--accent-glow); }

/* ============================================================
   CP-W3 / W3.1 — Routine tasks (gym, work, school run)
   ----------------------------------------------------------------
   Routine = a recurring thing with no impact/horizon. Lives only in
   the Planning rail's "Routine" group, paints translucent gray on the
   grid. Focus ring on blocks (keyboard delete) + hover 🗑 / ✎ on rail.
============================================================ */
.plan-chip-x {
    position: absolute; top: 3px; right: 3px;
    width: 16px; height: 16px; line-height: 14px; text-align: center;
    font-size: 0.62rem; border: none; border-radius: 3px;
    background: rgba(7,18,28,0.45); color: #fff; cursor: pointer;
    opacity: 0; transition: opacity var(--t-fast) ease, background var(--t-fast) ease;
    padding: 0; display: flex; align-items: center; justify-content: center;
}
/* keep the 16px glyph but grow the tap area to 32px (dense chip corner) */
.plan-chip-x::before {
    content: ''; position: absolute; top: 50%; left: 50%;
    width: 32px; height: 32px; transform: translate(-50%, -50%);
}
/* reveal on hover, keyboard focus, and always on touch — never hover-only */
.plan-chip:hover .plan-chip-x,
.plan-chip:focus-within .plan-chip-x,
.plan-chip-x:focus-visible { opacity: 1; }
.plan-chip-x:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
.plan-chip-x:hover { background: rgba(180, 60, 60, 0.85); }

/* ✎ edit on a routine rail chip (the un-routine path) */
.plan-chip-edit {
    margin-left: auto; flex: 0 0 auto; position: relative;
    width: 18px; height: 18px; padding: 0; border: none; border-radius: 4px;
    background: transparent; color: var(--text-muted); cursor: pointer;
    font-size: 0.7rem; line-height: 1;
    display: inline-flex; align-items: center; justify-content: center;
    opacity: 0; transition: opacity var(--t-fast) ease, color var(--t-fast) ease;
}
/* 32px tap area, glyph stays 18px */
.plan-chip-edit::before {
    content: ''; position: absolute; top: 50%; left: 50%;
    width: 32px; height: 32px; transform: translate(-50%, -50%);
}
.plan-chip:hover .plan-chip-edit,
.plan-chip:focus-within .plan-chip-edit,
.plan-chip-edit:focus-visible { opacity: 1; }
.plan-chip-edit:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 1px; }
.plan-chip-edit:hover { color: var(--text-primary); }

/* touch devices can't hover — always show both rail-chip controls */
@media (hover: none) { .plan-chip-x, .plan-chip-edit { opacity: 1; } }

/* "Routine" group heading in the rail */
.plan-rail-group {
    font-size: 0.6rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--text-muted); margin: 12px 2px 4px;
    padding-top: 8px; border-top: 1px dashed var(--border-subtle);
}

/* keyboard focus on planner blocks (for Delete-key remove) */
.cw-block.is-interactive:focus { outline: none; box-shadow: 0 0 0 2px var(--accent), 0 1px 4px rgba(0,0,0,0.4); }
.cw-block.is-interactive:focus-visible { box-shadow: 0 0 0 2px var(--accent), 0 0 12px var(--accent-glow); }

/* "make routine" action on a Reports-list task row */
.task-perm-badge {
    display: inline-flex; align-items: center; gap: 3px;
    font-size: 0.58rem; font-weight: 800; letter-spacing: 0.04em;
    text-transform: uppercase; color: var(--text-muted);
    border: 1px solid var(--border-muted); border-radius: 999px;
    padding: 2px 8px; line-height: 1.3;
    background: rgba(107, 118, 130, 0.10);
    cursor: pointer; user-select: none;
    transition: color var(--t-fast) ease, border-color var(--t-fast) ease, background var(--t-fast) ease;
}
.task-perm-badge::before { content: '↻'; font-size: 0.62rem; opacity: 0.85; }
.task-perm-badge:hover { color: var(--text-primary); border-color: var(--cw-work, #6b7682); }

/* "Routine" toggle in the add-form (its own picker-group, next to Impact) */
#permPicker .picker-label { color: var(--text-secondary); }
#permPicker { align-items: center; }
.opt-chip[data-perm="on"].active {
    color: #fff; border-color: var(--cw-work, #6b7682); background: var(--cw-work, #6b7682);
}
.opt-chip[data-perm="on"].active:hover { filter: brightness(1.1); }

/* a picker-group locked because Routine is on (impact/horizon don't apply) */
.picker-group.is-locked { opacity: 0.4; pointer-events: none; filter: grayscale(0.6); }

/* routine color swatch — the little color circle in the add-form */
.routine-color-wrap { display: inline-flex; align-items: center; gap: 5px; margin-left: 4px; }
.routine-color-wrap[hidden] { display: none; }
.routine-color {
    width: 22px; height: 22px; padding: 0; cursor: pointer;
    border: 1px solid var(--border-muted); border-radius: 50%;
    background: none; -webkit-appearance: none; appearance: none;
    transition: border-color var(--t-fast) ease, transform var(--t-fast) ease;
}
.routine-color:hover { border-color: var(--accent); transform: scale(1.08); }
.routine-color::-webkit-color-swatch-wrapper { padding: 0; }
.routine-color::-webkit-color-swatch { border: none; border-radius: 50%; }
.routine-color::-moz-color-swatch { border: none; border-radius: 50%; }
.perm-tip {
    display: inline-flex; align-items: center; justify-content: center;
    width: 16px; height: 16px; border-radius: 50%;
    background: var(--bg-inset); color: var(--text-muted);
    font-size: 0.7rem; font-weight: 800; cursor: help; user-select: none;
    border: 1px solid var(--border-subtle);
    position: relative;
}
.perm-tip:hover { color: var(--text-primary); border-color: var(--cw-work, #6b7682); }
.perm-tip-pop {
    position: absolute; top: calc(100% + 6px); left: 50%; transform: translateX(-50%);
    width: 230px; padding: 8px 10px; border-radius: var(--radius-sm);
    background: var(--bg-card, #0d1b2a); color: var(--text-primary, #e8e8e8);
    font-size: 0.72rem; line-height: 1.4; font-weight: 500; text-align: left;
    border: 1px solid var(--border-subtle);
    box-shadow: 0 6px 20px rgba(0,0,0,0.45);
    opacity: 0; pointer-events: none; transition: opacity var(--t-fast) ease;
    z-index: 50;
}
.perm-tip:hover .perm-tip-pop { opacity: 1; }
.perm-tip:focus .perm-tip-pop,
.perm-tip:focus-within .perm-tip-pop { opacity: 1; }
/* ============================================================
   FLOATING GLASSMORPHIC DOCK NAV
============================================================ */
.dock-nav {
    position: fixed;
    bottom: var(--space-xl); left: 50%; transform: translateX(-50%);
    z-index: 9999;
    background: var(--bg-glass);
    backdrop-filter: blur(20px) saturate(180%);
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    border: 1px solid rgba(255,255,255,0.08);
    border-radius: var(--radius-xl);
    padding: var(--space-sm) var(--space-lg);
    display: flex; align-items: center; gap: var(--space-md);
    box-shadow: 0 8px 40px rgba(0,0,0,0.6), 0 0 0 1px var(--accent-glow) inset;
}
.dock-link {
    color: var(--text-muted); text-decoration: none;
    font-size: 0.72rem; font-weight: 800;
    text-transform: uppercase; letter-spacing: 0.1em;
    padding: var(--space-xs) var(--space-sm);
    border-radius: var(--radius-sm);
    transition: all 0.2s ease; position: relative;
}
.dock-link:hover { color: var(--accent-text); }
.dock-link.active { color: var(--accent-text); }
.dock-link::after {
    content: ''; position: absolute; bottom: -2px; left: 50%; transform: translateX(-50%);
    width: 0; height: 2px; background: var(--accent); border-radius: 2px;
    transition: width 0.2s ease;
}
.dock-link:hover::after, .dock-link.active::after { width: 80%; }
.dock-divider { width: 1px; height: 18px; background: var(--border-subtle); }

/* ============================================================
   TOAST
============================================================ */
/* ⛔ pointer-events:none is a CORRECTNESS rule, not polish (hub-repair 5c-CLOSE,
   2026-08-11). The toast fires exactly WHEN a member is acting on a list — "Added
   to Flight Plan ✓" — so the next row's button spends the toast's whole lifetime
   underneath it. Measured on jerry/reports.html at 375: .toast overlapped
   .sugg-actions button, and a tap at the button's centre hit the toast instead.
   Moving the toast only relocates the problem onto whatever is under it next; a
   toast that cannot be clicked cannot steal a click anywhere on the hub. Safe by
   audit, not by assumption: all 24 .toast elements in the tree are
   icon-span + text-span with ZERO interactive children (gated below), so there is
   nothing inside one that ever needed to receive a click. ⚠ If a toast ever gains
   an Undo button, this line must come off IN THE SAME COMMIT — the gate is what
   will tell you. */
.toast {
    position: fixed; right: var(--space-xl);
    bottom: calc(var(--space-xl) + env(safe-area-inset-bottom, 0px));
    background: var(--bg-card);
    border: 1px solid var(--accent-muted);
    padding: var(--space-md) var(--space-lg);
    border-radius: var(--radius-md);
    box-shadow: 0 6px 28px rgba(0,0,0,0.6);
    display: flex; align-items: center; gap: var(--space-sm);
    transform: translateY(80px); opacity: 0;
    transition: all var(--t-slow) var(--ease-out);
    z-index: 11000;
    pointer-events: none;
}
.toast.show { transform: translateY(0); opacity: 1; }
.toast-icon { color: var(--accent-text); font-size: 1.1rem; }
.toast-text { font-size: 0.88rem; font-weight: 600; color: var(--text-primary); }

/* ============================================================
   EMPTY STATE
============================================================ */
.empty-state {
    text-align: center; color: var(--text-muted);
    font-size: 0.83rem; padding: var(--space-lg) 0;
    grid-column: 1 / -1;
}

/* ============================================================
   TAB BAR  (.wk-tabs)  —  promoted to shared 2026-08-14, HUB/7B
   ------------------------------------------------------------
   The "workshop tabs" grammar. Named .wk-* for its origin page;
   it is now the hub's general one-panel-at-a-time tab bar and
   Batch 7B's Goals page (direction C) is built on it.

   WHY THIS BLOCK EXISTS. The grammar was forked EIGHT ways and
   lived in no shared sheet, so a NEW page inherited nothing:
     jp/theme.css, austin/theme.css        (2 theme copies)
     sami/workshop.html                     (inline <style>)
     jerry/{finance,fitness,ventures,workshop}.html  (inline)
   Two members — sami and JERRY — had it ONLY inside page <style>
   blocks, so a new page on their hub rendered the bar unstyled.
   (The Batch 8 note saying jerry/theme.css declares it was wrong;
   jerry/theme.css only mentions .wk-tabs in a comment.)

   CASCADE CONTRACT, AND ITS LIMIT — load order is tokens →
   components → theme, and every existing copy loads AFTER this
   file (member theme.css, or a page <style> block further down
   the document). ⚠ That makes this block inert only for the
   properties a fork actually DECLARES — the cascade resolves
   PROPERTIES, not blocks, so anything only SOME forks declare is
   not overridden at all: it wins, on their live pages. That is
   not a hypothetical, it is what the gate caught (see `motion`
   below). The block below is therefore trimmed to what every
   variant declares, which IS inert on all 9 live pages —
   measured, not argued, by tools/gate_7b_wk_tabs.mjs.

   WHAT IS DELIBERATELY *NOT* HERE. The four variants had drifted
   on three axes, so the values below are the ones all four agree
   on plus a conservative default for each disagreement:
     - box:    sami+austin sit inside .grid-page (no centering);
               jerry+jp self-centre (max-width 1200 + padding).
               Shared takes the UNCONSTRAINED form; a member that
               needs centering declares it in their own theme.
     - active: sami+austin use --accent-bright, jerry+jp --accent.
               Shared takes --accent (the semantic identity token);
               --accent-bright is an intensifier a member opts into.
     - glow:   sami's Grid text-shadow is his identity lever and
               lives in sami/theme.css, never here.
     - motion: NO `transition` here, and the gate is why. sami and
               austin declare one; jerry and jp declare NONE, so a
               shared transition is not overridden — it WINS, and
               `transition-property` moved `all` -> `color,
               border-color` on 5 live pages. The cascade only makes
               this block inert for properties a fork actually
               DECLARES; anything only some of them declare lands
               for real. Members who want the ease own it.
   Re-unifying the eight forks is a VISUAL change (it forces a
   winner per axis) and is its own step with its own render pass —
   this promotion deliberately does not touch them.

   ✅ TAKEN 2026-08-24 (the ring below), no longer carried.
   ⚠ FIRST, A CORRECTION TO THE CLAIM ABOVE: it said a keyboard user "cannot see
   which tab they are on". Measured 2026-08-24 — untrue. With no rule at all
   .wk-tab fell back to the UA default ring (`outline: auto 1px`), which is
   visible. What was missing was the HOUSE ring, not a ring. The item was real
   but its severity was not. It was
   held back so it could not contaminate the consolidation's
   zero-change proof; that proof has since landed, so this is the
   separate step it was filed as. It reaches all eight forks without
   touching one of them: none of them declares `outline` at all, and
   `.wk-tab:focus-visible` is (0,2,0) against their (0,1,0) — so it
   wins on specificity regardless of the member theme.css that loads
   after this file.
============================================================ */
.wk-tabs {
    display: flex; gap: 4px; flex-wrap: wrap;
    margin-bottom: var(--space-lg);
    border-bottom: 1px solid var(--border-subtle);
}
.wk-tab {
    background: transparent; border: none; border-bottom: 2px solid transparent;
    color: var(--text-secondary); font: inherit; font-size: 0.78rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.08em; padding: 10px 16px; margin-bottom: -1px;
    cursor: pointer; display: flex; align-items: center; gap: 8px;
}
.wk-tab:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: -2px; }
.wk-tab:hover { color: var(--text-primary); }
.wk-tab .wk-tab-ic { font-size: 0.98rem; }
.wk-tab .wk-tab-count { font-size: 0.62rem; font-family: var(--font-mono); font-weight: 700; color: var(--text-muted); }
.wk-tab.active { color: var(--accent-text); border-bottom-color: var(--accent); }
.wk-tab.active .wk-tab-count { color: var(--accent-muted); }
.wk-panel[hidden] { display: none; }
.wk-panel-sub { font-size: 0.8rem; color: var(--text-secondary); margin: 0 0 18px; line-height: 1.5; }

/* ============================================================
   HEADER TOOLS + ICON BUTTON
============================================================ */
.header-tools { display: flex; align-items: center; gap: var(--space-lg); }
.icon-btn {
    background: var(--bg-inset);
    border: 1px solid var(--border-muted);
    color: var(--text-secondary);
    width: var(--control-primary); height: var(--control-primary);
    border-radius: var(--radius-md);
    font-size: 1.15rem; cursor: pointer;
    display: flex; align-items: center; justify-content: center;
    transition: all var(--t-fast) var(--ease-out);
    position: relative; flex: 0 0 var(--control-primary);
}
.icon-btn:hover { border-color: var(--accent-muted); color: var(--accent-text); background: var(--accent-glow); transform: translateY(-2px); }
.icon-btn:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; border-color: var(--accent-muted); color: var(--accent-text); }
.icon-btn .icon-badge {
    position: absolute; top: -5px; right: -5px;
    background: var(--accent); color: var(--accent-contrast);
    font-size: 0.6rem; font-weight: 800;
    min-width: 16px; height: 16px; padding: 0 4px;
    border-radius: 999px; display: none;
    align-items: center; justify-content: center;
}
.icon-btn.has-badge .icon-badge { display: flex; }

/* ============================================================
   OVERLAY / MODAL  —  generic, reusable across pages
============================================================ */
.overlay {
    position: fixed; inset: 0;
    min-height: 100vh;
    min-height: var(--viewport-block);
    background: rgba(2, 6, 14, 0.80);
    backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
    display: none; align-items: flex-start; justify-content: center;
    padding: 8vh var(--space-lg) var(--space-lg);
    /* 100003, not 10000 — a modal must out-stack page chrome (Batch 6, 2026-08-09).
       shared/signin.js:43 gives the auth pill z-index 100001, ten times this
       layer, so the pill floated ON TOP of every overlay in the hub. Measured at
       375 on jerry and jp: elementsFromPoint at the centre of the calendar's ✕
       returned button.hub-signin-btn first — the close button was rendered,
       visible and completely un-tappable. Same mechanism as the recorded
       "signin.js auth pill eats a.c-back" defect, one surface over.
       Safe against the sign-in dialog (100002): openModal is reachable only from
       a click on the pill or from ?signin=1 at page load, and no overlay can be
       open in either case — both callers checked in signin.js:243 and :300. */
    overflow-y: auto; z-index: 100003;
}
.overlay.open { display: flex; }
.overlay-panel {
    width: 100%; max-width: 760px;
    background: var(--bg-card);
    border: 1px solid var(--accent-muted);
    border-radius: var(--radius-lg);
    box-shadow: 0 24px 70px rgba(0,0,0,0.7);
    animation: overlay-rise var(--t-slow) var(--ease-out);
}
/* the calendar is the only thing using .overlay right now and wants room to
   grow (bigger grid + grouped legend) without becoming its own page */
#calOverlay .overlay-panel { max-width: 1180px; }
@keyframes overlay-rise { from { opacity: 0; transform: translateY(26px); } to { opacity: 1; transform: none; } }
/* WRAPS ON PURPOSE (hub-repair Batch 6, 2026-08-09). This row was a no-wrap
   flex, so once its contents outgrew the panel they spilled PAST it instead of
   reflowing — and the ✕ close button is the last child, so it is the first thing
   to leave the screen. Measured at 375 before the fix: jerry's head was 522px in
   a 375px panel and jp's 492px, with the close button at inView:false and a tap
   at its centre hitting nothing. On a phone that leaves the backdrop as the only
   way out of the calendar, because there is no Escape key. Wrapping costs
   nothing at desktop width (nothing wraps while there is room) and converts the
   overflow into a second line everywhere else. */
.overlay-head {
    display: flex; justify-content: space-between; align-items: center;
    flex-wrap: wrap; row-gap: var(--space-sm);
    padding: var(--space-lg);
    border-bottom: 1px solid var(--border-subtle);
}
.overlay-title { font-size: 0.95rem; font-weight: 800; color: var(--accent-text); text-transform: uppercase; letter-spacing: 0.08em; }
.overlay-close {
    background: none; border: none; color: var(--text-muted);
    min-width: var(--control-dense); min-height: var(--control-dense);
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 1.4rem; cursor: pointer; line-height: 1;
    transition: color var(--t-fast);
}
.overlay-close:hover { color: var(--danger); }
.overlay-close:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; color: var(--danger); }
.overlay-head-right { display: inline-flex; align-items: center; gap: var(--space-md); flex-wrap: wrap; justify-content: flex-end; row-gap: var(--space-sm); }
.overlay-link {
    font-size: 0.7rem; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--accent-muted); text-decoration: none;
    border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
    padding: 4px 10px; transition: all var(--t-fast) ease; white-space: nowrap;
}
.overlay-link:hover { color: var(--text-primary); border-color: var(--accent); background: var(--accent-glow); }
.overlay-body { padding: var(--space-lg); }

/* ============================================================
   MISSION CONTROL  —  Reports page: digest · Flight Plan · suggestion queue
   ------------------------------------------------------------
   Structure only. All color comes from the theme's --wr-accent and
   --impact-* vars (painted in <member>/theme.css). Cel-shade
   signature (3px ink outline + hard 0 6px 0 drop) matches panels.
============================================================ */
.mission-control { width: 100%; max-width: 900px; margin: 0 auto; display: flex; flex-direction: column; gap: var(--space-xl); }

/* ---- top row: This Week, on its own ----
   ⛔ THE QUEUE LEFT THIS ROW 2026-08-11 (Sami: the suggestion items should be
   "a bigger card like the full size like the rest of them"). It sat here at 40%
   — 25% under Jerry's override — and a sentence-length rationale cannot live in
   that measure: MEASURED at 1280, Sami's rationale wrapped to 4 lines in 205px,
   and Jerry's .sugg-main was starved to 8px, breaking the TITLE one character
   per line down a 1161px-tall card. It is now its own full-width panel below,
   so a proposal is the same size as the Flight Plan task it becomes.
   align-items: flex-start (not stretch) is kept: it is what lets a panel size to
   ITS OWN content rather than to a neighbour's, if a second one ever joins.
   ⚠ min-height went with it. 280px was the floor that kept two side-by-side
   panels the same visual weight; with This Week alone and full width, the same
   floor renders an empty report as a 1052x280 blank slab — a cold visual review
   read it as "unfinished or failed to load" rather than as an empty state. A
   real report is taller than 280 on its own, so the floor only ever shaped the
   empty case, and it shaped it wrong. */
.mc-top-row { display: flex; align-items: flex-start; gap: var(--space-xl); }
.mc-top-row .wr-panel-square { display: flex; flex-direction: column; }
.mc-top-row #digestPanel { flex: 1 1 auto; min-width: 0; }
.mc-top-row .wr-panel-square #digestBody { flex: 1; }

.wr-panel {
    position: relative;
    background: linear-gradient(180deg, rgba(255,255,255,0.03), var(--bg-card));
    border: var(--card-outline-w, 3px) solid var(--card-outline, var(--border-muted));
    border-radius: var(--radius-xl);
    padding: var(--space-lg) var(--space-xl) var(--space-xl);
    box-shadow: 0 6px 0 var(--card-shade, rgba(0,0,0,0.4)), 0 18px 50px rgba(0,0,0,0.5);
}
/* accent strip on the top edge — the panel's depth color */
.wr-panel::before {
    content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 4px;
    background: var(--wr-accent, var(--accent));
    border-radius: var(--radius-xl) var(--radius-xl) 0 0; opacity: 0.9;
}
.wr-panel-head {
    display: flex; justify-content: space-between; align-items: center;
    gap: var(--space-md); margin-bottom: var(--space-lg);
}
.wr-title {
    font-size: 0.74rem; font-weight: 800; letter-spacing: 0.18em; text-transform: uppercase;
    color: var(--wr-accent-text, var(--wr-accent, var(--accent-text)));
    display: flex; align-items: center; gap: var(--space-sm);
}
.wr-sub { font-size: 0.68rem; color: var(--text-muted); letter-spacing: 0.04em; font-weight: 700; }

/* ---- ⟳ Generate (digest header) — ghost button, only Opus-billing trigger ---- */
.digest-head-right { display: flex; align-items: center; gap: var(--space-md); }
.gen-btn {
    display: inline-flex; align-items: center; gap: 0.45rem;
    padding: 0.28rem 0.7rem; border-radius: 999px;
    background: transparent;
    border: 1px solid color-mix(in srgb, var(--wr-accent, var(--accent)) 45%, transparent);
    color: var(--wr-accent-text, var(--wr-accent, var(--accent-text)));
    font-size: 0.66rem; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase;
    cursor: pointer; transition: background 0.18s ease, border-color 0.18s ease, opacity 0.18s ease;
}
.gen-btn:hover:not(:disabled) {
    background: color-mix(in srgb, var(--wr-accent, var(--accent)) 14%, transparent);
    border-color: var(--wr-accent, var(--accent));
}
.gen-btn:disabled { opacity: 0.5; cursor: default; }
.gen-btn.is-busy .gen-btn-label { animation: gen-spin-pulse 1.1s ease-in-out infinite; }
.gen-btn-count { font-size: 0.6rem; font-weight: 700; letter-spacing: 0.02em;
    text-transform: none; color: var(--text-muted); }
.gen-btn-count.is-spent { color: color-mix(in srgb, var(--text-muted) 70%, transparent); }
@keyframes gen-spin-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.45; } }

/* shared empty state inside a mission-control panel */
.wr-empty {
    text-align: center; color: var(--text-muted);
    font-size: 0.85rem; line-height: 1.6; padding: var(--space-lg) var(--space-md); font-style: italic;
}
.wr-empty .wr-empty-emoji { display: block; font-size: 1.7rem; margin-bottom: var(--space-sm); font-style: normal; opacity: 0.85; }

/* ---- weekly digest body (populated by AI in CP-D) ---- */
.digest-summary { font-size: 0.92rem; line-height: 1.6; color: var(--text-primary); margin-bottom: var(--space-md); }
.digest-cols { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-md); }
.digest-col-title { font-size: 0.64rem; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted); margin-bottom: var(--space-xs); }
.digest-list { list-style: none; display: flex; flex-direction: column; gap: 4px; }
.digest-list li { font-size: 0.8rem; color: var(--text-secondary); line-height: 1.4; }

/* ============================================================
   FLIP CARD  —  This Week ⇄ Past Weeks (Report Room v2, §10d)
   A crossfade swap, not a literal 3D rotateY — content height
   varies a lot (glance vs. a list of past weeks) so a fixed-height
   3D flip would fight real content. Purely visual polish to layer
   in later if wanted.
============================================================ */
.flip-card { display: flex; flex-direction: column; }
.flip-face { display: none; flex: 1; flex-direction: column; animation: flip-in 0.22s ease; }
.flip-front { display: flex; }
.flip-card.is-flipped .flip-front { display: none; }
.flip-card.is-flipped .flip-back { display: flex; }
@keyframes flip-in { from { opacity: 0; transform: scale(0.97); } to { opacity: 1; transform: none; } }

.flip-toggle {
    background: var(--bg-inset); border: 1px solid var(--border-muted); color: var(--text-secondary);
    width: 26px; height: 26px; border-radius: var(--radius-sm); font-size: 0.85rem; cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center; transition: all var(--t-fast) ease;
}
.flip-toggle:hover { border-color: var(--wr-accent, var(--accent)); color: var(--wr-accent-text, var(--wr-accent, var(--accent-text))); background: var(--accent-glow); }

/* ---- This Week glance (front face) — grade badge + verdict + top highlights ---- */
.glance { display: flex; gap: var(--space-md); align-items: flex-start; cursor: pointer; border-radius: var(--radius-md); padding: var(--space-xs); margin: calc(var(--space-xs) * -1); transition: background var(--t-fast) ease; }
.glance:hover { background: var(--accent-glow); }
.glance-stamp {
    flex: none; width: 58px; height: 58px; border-radius: 50%;
    border: 2px solid var(--grade-color, var(--accent)); display: flex; flex-direction: column;
    align-items: center; justify-content: center; line-height: 1;
}
.glance-score { font-size: 1.1rem; font-weight: 800; color: var(--grade-color, var(--accent-text)); }
.glance-letter { font-size: 0.58rem; font-weight: 800; letter-spacing: 0.04em; color: var(--text-muted); margin-top: 2px; }
.glance-body { flex: 1; min-width: 0; }
.glance-headline { font-size: 1.05rem; font-weight: 800; color: var(--text-primary); margin-bottom: 4px; }
.glance-line { font-size: 0.82rem; line-height: 1.5; color: var(--text-secondary); }
.glance-open-btn {
    margin-top: var(--space-md); width: 100%; background: transparent;
    border: 1px dashed var(--border-active); border-radius: var(--radius-md);
    color: var(--wr-accent-text, var(--wr-accent, var(--accent-text))); font-size: 0.72rem; font-weight: 800;
    letter-spacing: 0.04em; padding: 0.5rem; cursor: pointer; transition: all var(--t-fast) ease;
}
.glance-open-btn:hover { background: var(--accent-glow); border-color: var(--wr-accent, var(--accent)); }

.glance-overview { font-size: 0.82rem; line-height: 1.55; color: var(--text-secondary); margin: var(--space-md) 0 0; }
.glance-highlights { display: flex; flex-direction: column; gap: 4px; margin-top: var(--space-sm); }
.glance-detail { font-size: 0.74rem; color: var(--text-muted); }
.glance-detail.is-warn { color: var(--warning); }
.glance-nudge {
    font-size: 0.76rem; color: var(--wr-accent-text, var(--wr-accent, var(--accent-text))); margin-top: var(--space-md);
    padding-top: var(--space-md); border-top: 1px dashed var(--border-subtle); line-height: 1.5;
}

/* ---- Past Weeks (back face) — one row per week, keyword chips + grade ---- */
.history-row {
    display: flex; align-items: center; justify-content: space-between; gap: var(--space-md);
    padding: var(--space-sm) var(--space-xs); border-bottom: 1px solid var(--border-subtle);
    cursor: pointer; transition: background var(--t-fast) ease;
}
.history-row:last-child { border-bottom: 0; }
.history-row:hover { background: var(--accent-glow); }
.history-main { min-width: 0; }
.history-week { font-size: 0.78rem; font-weight: 700; color: var(--text-primary); margin-bottom: 3px; }
.history-keywords { display: flex; flex-wrap: wrap; gap: 4px; }
.history-kw {
    font-size: 0.62rem; font-weight: 700; color: var(--text-muted); background: var(--bg-inset);
    border: 1px solid var(--border-subtle); border-radius: 999px; padding: 1px 7px;
}
.history-grade { flex: none; font-size: 0.72rem; font-weight: 800; color: var(--grade-color, var(--accent-text)); white-space: nowrap; }

/* ============================================================
   FLIGHT PLAN  —  the add row + the tagged task list
============================================================ */
.planner-add {
    display: flex; flex-direction: column; gap: var(--space-md);
    padding-bottom: var(--space-lg); margin-bottom: var(--space-lg);
    border-bottom: 1px solid var(--border-subtle);
}
.planner-add-row { display: flex; gap: var(--space-sm); }
.planner-add-row .text-input { flex: 1; }

.picker-group { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-sm); }
.picker-label {
    font-size: 0.62rem; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase;
    color: var(--text-muted); min-width: 54px;
}
.opt-chip {
    background: var(--bg-inset); border: 1.5px solid var(--border-muted); color: var(--text-secondary);
    font-size: 0.68rem; font-weight: 800; letter-spacing: 0.04em;
    padding: 4px 11px; border-radius: 999px; cursor: pointer; text-transform: uppercase;
    transition: all var(--t-fast) ease; user-select: none;
    min-width: var(--control-dense); min-height: var(--control-dense); box-sizing: border-box;
    display: inline-flex; align-items: center; justify-content: center;
}
.opt-chip:hover { border-color: var(--accent-muted); color: var(--text-primary); }
.opt-chip.active { color: var(--text-primary); border-color: var(--accent); background: var(--accent-glow); }
.opt-chip[data-impact="needle"].active   { color: var(--impact-needle, var(--accent-text));   border-color: var(--impact-needle, var(--accent));   background: var(--impact-needle-soft, var(--accent-glow)); }
.opt-chip[data-impact="standard"].active  { color: var(--impact-standard, var(--accent-text)); border-color: var(--impact-standard, var(--accent)); background: var(--impact-standard-soft, var(--accent-glow)); }
.opt-chip[data-impact="minor"].active     { color: var(--impact-minor, var(--text-secondary)); border-color: var(--impact-minor, var(--border-active)); background: var(--impact-minor-soft, var(--bg-inset)); }
.planner-deadline { display: none; }
.planner-deadline.show { display: flex; }
.planner-deadline input[type="date"] {
    background: #0c1c2a; border: 1.5px solid var(--border-muted); color: var(--text-primary);
    border-radius: var(--radius-sm); padding: 5px 9px; font-size: 0.78rem; font-family: var(--font-sans);
    color-scheme: dark;
}

/* optional "+ details" textarea — collapsed until the toggle is opened */
.planner-notes-row { display: flex; flex-direction: column; gap: var(--space-sm); }
.notes-toggle {
    align-self: flex-start; background: none; border: none; cursor: pointer; padding: 0;
    font-size: 0.66rem; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--accent-muted); transition: color var(--t-fast) ease;
    min-height: var(--control-dense);
}
.notes-toggle:hover { color: var(--accent-text); }
.notes-toggle:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.notes-input {
    display: none; width: 100%; resize: vertical; min-height: 2.4rem;
    background: var(--bg-inset); border: 1.5px solid var(--border-muted); color: var(--text-primary);
    border-radius: var(--radius-sm); padding: 8px 10px; font-size: 0.82rem;
    font-family: var(--font-sans); line-height: 1.4;
}
.notes-input:focus { outline: none; border-color: var(--accent); }
.planner-notes-row.show .notes-input { display: block; }

.task-list { display: flex; flex-direction: column; gap: var(--space-md); }
.task-item {
    position: relative; display: flex; align-items: flex-start; gap: var(--space-md);
    background: var(--bg-inset);
    border: 2px solid var(--border-subtle); border-left-width: 4px;
    border-radius: var(--radius-md); padding: var(--space-md);
    transition: border-color var(--t-fast) ease, transform var(--t-fast) ease;
}
.task-item:hover { border-color: var(--border-active); transform: translateX(2px); }
.task-item[data-impact="needle"]   { border-left-color: var(--impact-needle, var(--accent)); }
.task-item[data-impact="standard"] { border-left-color: var(--impact-standard, var(--accent-muted)); }
.task-item[data-impact="minor"]    { border-left-color: var(--impact-minor, var(--border-active)); }
.task-main { flex: 1; display: flex; flex-direction: column; gap: var(--space-sm); min-width: 0; }
.task-titletext { font-size: 0.92rem; font-weight: 700; color: var(--text-primary); line-height: 1.35; word-break: break-word; }
.task-titletext.has-notes { cursor: pointer; }
.task-titletext.has-notes:hover { color: var(--accent-bright, var(--accent-text)); }
.task-caret {
    display: inline-block; margin-right: 5px; font-size: 0.7rem; color: var(--accent-muted);
    transition: transform var(--t-fast) ease;
}
.task-chips { display: flex; flex-wrap: wrap; gap: var(--space-sm); align-items: center; }
.task-sub { font-size: 0.7rem; color: var(--text-muted); }
/* detail notes — hidden until the row's caret is clicked */
.task-notes {
    display: none; font-size: 0.8rem; line-height: 1.5; color: var(--text-secondary);
    border-left: 2px solid var(--border-muted); padding: 2px 0 2px var(--space-md);
    margin-top: 2px; white-space: normal;
}
.task-notes.open { display: block; }
.task-caret.down { transform: rotate(90deg); }
.task-actions { display: flex; gap: 4px; flex-shrink: 0; }
.task-btn {
    background: none; border: 1.5px solid var(--border-muted); color: var(--text-secondary);
    width: 30px; height: 30px; border-radius: var(--radius-sm); cursor: pointer;
    font-size: 0.85rem; display: flex; align-items: center; justify-content: center;
    transition: all var(--t-fast) ease;
}
.task-btn:hover { border-color: var(--accent-muted); color: var(--accent-text); }
.task-btn.done-btn:hover { border-color: var(--success); color: var(--success); }
.task-btn.del-btn:hover  { border-color: var(--danger);  color: var(--danger); }

/* tag chips — impact (◆ pearl) + horizon (⏳ countdown) */
.chip {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 0.62rem; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase;
    padding: 3px 9px; border-radius: 999px; border: 1.5px solid transparent; line-height: 1;
}
.chip-impact[data-impact="needle"]   { color: var(--impact-needle, var(--accent-text));   border-color: var(--impact-needle, var(--accent));   background: var(--impact-needle-soft, var(--accent-glow)); box-shadow: 0 0 10px var(--impact-needle-soft, transparent); }
.chip-impact[data-impact="standard"] { color: var(--impact-standard, var(--accent-muted)); border-color: var(--impact-standard, var(--accent-muted)); background: var(--impact-standard-soft, var(--bg-inset)); }
.chip-impact[data-impact="minor"]    { color: var(--impact-minor, var(--text-muted)); border-color: var(--border-muted); background: transparent; }
.chip-horizon { color: var(--text-secondary); border-color: var(--border-muted); background: var(--bg-inset); }
.chip-horizon.soon { color: var(--warning); border-color: var(--warning); }
.chip-horizon.due  { color: var(--danger);  border-color: var(--danger); }

/* neglect pulse: a NEEDLE task untouched past the threshold nags */
.task-item.is-neglected { border-color: rgba(245,158,11,0.45); animation: neglect-pulse 2.6s ease-in-out infinite; }
.task-item.is-neglected .task-sub { color: var(--warning); font-weight: 700; }

/* ============================================================
   SUGGESTION QUEUE  —  AI proposes; accept drops into planner
   ------------------------------------------------------------
   FULL-SIZE CARDS (Sami, 2026-08-09 → built 2026-08-11). A
   suggestion is a task the member has not accepted yet, so it
   wears .task-item's grammar: the same impact rail on the left
   edge, the same .task-titletext, and a rationale set as PROSE
   at .task-notes' size rather than 0.72rem muted italic — the
   rationale is a whole sentence and was being treated as a
   caption. The DASHED border is the one thing kept different:
   it is what says "proposed", not "yours".
   ⚠ flex-wrap + a flex-basis on .sugg-main is the INVARIANT, not
   a width fix: .sugg-actions is flex-shrink:0, so in any column
   narrower than main+actions the old rule handed the actions
   their full width and starved the text to whatever was left
   (8px on Jerry's 25% panel). Wrapping drops the buttons to
   their own row instead, on every width, including ones nobody
   rendered.
============================================================ */
.sugg-item {
    display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--space-md);
    background: var(--bg-inset);
    border: 1.5px dashed var(--border-active);
    border-left: 4px solid var(--border-active);
    border-radius: var(--radius-md);
    padding: var(--space-md); margin-bottom: var(--space-md);
}
.sugg-item[data-impact="needle"]   { border-left-color: var(--impact-needle, var(--accent)); }
.sugg-item[data-impact="standard"] { border-left-color: var(--impact-standard, var(--accent-muted)); }
.sugg-item[data-impact="minor"]    { border-left-color: var(--impact-minor, var(--border-active)); }
.sugg-main { flex: 1 1 240px; display: flex; flex-direction: column; gap: var(--space-sm); min-width: 0; }
/* overflow-wrap because the rationale is MODEL-WRITTEN and its schema sets no
   maxLength and forbids no character: one unbroken token (a URL, a ticker, a
   long identifier) would otherwise run past the card and take the viewport with
   it at 375. break-word, deliberately NOT anywhere — anywhere breaks ordinary
   prose mid-word to satisfy a width, which is the fix that measured better and
   read worse (JP workshop, 2026-08-09). */
/* 0.88rem, not 0.8 — a cold visual pass called this text faint on all three
   themes it saw. ⚠ It is NOT a contrast problem, and the palette was left alone
   because of it: measured off rendered pixels (gradient panels make
   getComputedStyle's backgroundColor useless here), --text-secondary on the card
   scores 7.95:1 sami · 5.88 jerry · 7.12 jp · 6.92 austin, all past AA's 4.5.
   Faintness at a passing ratio is a SIZE problem, which is why WCAG relaxes to
   3:1 for large text — so the lever is the type scale, and it also costs nothing
   anywhere else. Still below the 0.92rem/700 title, so the hierarchy holds. */
.sugg-rationale { font-size: 0.88rem; color: var(--text-secondary); line-height: 1.55; overflow-wrap: break-word; }
.sugg-actions { display: flex; gap: var(--space-sm); flex-shrink: 0; align-items: center; }
.sugg-actions .btn-add, .sugg-actions .btn-ghost { white-space: nowrap; }

/* ============================================================
   REPORT ROOM  —  the stamped weekly report (REPORTS_SPEC §10f)
   ------------------------------------------------------------
   Full-page overlay, content only — reuses .overlay/.overlay-panel
   for the takeover mechanics (click-outside/✕ closes, same as the
   calendar). The DOCUMENT itself is deliberately light "paper" —
   fixed ink/teal/gold tones, NOT theme tokens — regardless of
   dark/grid/ocean mode, so it reads as an official artifact and
   prints clean (ported from _mockups/weekly_report_sample.html,
   which Sami signed off on). Theme-neutral: clone-ready as-is.
============================================================ */
:root {
    --rr-paper: #faf7ef; --rr-paper-2: #f3ede0;
    --rr-ink: #15323f; --rr-ink-soft: #3c5663; --rr-muted: #6f8791; --rr-hair: rgba(21,50,63,.14);
    --rr-teal: #0f7d8c; --rr-teal-deep: #0b5d68; --rr-gold: #b9862b; --rr-gold-soft: #d9b873;
    --rr-good: #1f8a6d; --rr-mixed: #c08a1e; --rr-rough: #b4543a;
}
#reportRoomOverlay .overlay-panel.rr-panel { max-width: 900px; background: transparent; border: 0; box-shadow: none; }
.rr-toolbar { padding: var(--space-md) var(--space-lg); }
.rr-body { padding: 0 var(--space-lg) var(--space-lg); }

.rr-doc {
    width: 100%; background: linear-gradient(180deg, var(--rr-paper), var(--rr-paper-2));
    border-radius: 8px; box-shadow: 0 18px 50px rgba(10,30,40,.35); position: relative; overflow: hidden;
    border: 1px solid rgba(0,0,0,.06); color: var(--rr-ink); font-family: "Segoe UI", system-ui, -apple-system, sans-serif;
}
.rr-doc::before {
    content: ""; position: absolute; inset: 0 0 auto 0; height: 5px;
    background: linear-gradient(90deg, var(--rr-teal-deep), var(--rr-teal), var(--rr-gold-soft), var(--rr-gold));
}
.rr-pad { padding: 40px 48px; }

.rr-mast { display: flex; justify-content: space-between; align-items: flex-start; gap: 18px; border-bottom: 1px solid var(--rr-hair); padding-bottom: 18px; }
.rr-brand { display: flex; align-items: center; gap: 12px; }
.rr-seal { font-size: 1.7rem; filter: drop-shadow(0 1px 0 rgba(0,0,0,.15)); }
.rr-brand h1 { margin: 0; font-family: Georgia, "Times New Roman", serif; font-size: 1.18rem; letter-spacing: .02em; color: var(--rr-ink); }
.rr-kicker { font-size: .62rem; letter-spacing: .34em; text-transform: uppercase; color: var(--rr-teal-deep); font-weight: 800; margin-top: 2px; }
.rr-meta { text-align: right; font-size: .72rem; color: var(--rr-muted); line-height: 1.5; }
.rr-range { font-family: Georgia, serif; font-size: .98rem; color: var(--rr-ink); letter-spacing: .01em; }
/* The period qualifier ("across a skipped meeting" / "no next meeting set") sits
   UNDER the range and stays subordinate: rendered at the same weight it competed
   with the date and pushed the meta column 133px -> 322px. Lives here, beside
   .rr-range, because all four members render the same masthead - three page-scoped
   copies is the fork this house keeps paying for. */
.rr-range-note { font-size: 11px; opacity: .62; letter-spacing: .02em; margin-top: 2px; }

.rr-verdict-row { display: flex; align-items: center; gap: 18px; margin: 26px 0 10px; position: relative; }
.rr-verdict { flex: 1; }
.rr-v-tag { display: inline-block; font-size: .6rem; letter-spacing: .3em; text-transform: uppercase; color: var(--rr-muted); font-weight: 800; margin-bottom: 8px; }
.rr-verdict h2 { margin: 0; font-family: Georgia, serif; font-size: 2.3rem; line-height: 1.05; letter-spacing: -.01em; }
.rr-v-line { margin: 12px 0 0; font-size: .96rem; color: var(--rr-ink-soft); line-height: 1.55; max-width: 46ch; }

.rr-stamp { width: 132px; height: 132px; flex: none; transform: rotate(-8deg); filter: drop-shadow(0 2px 4px rgba(15,125,140,.18)); }
.rr-stamp text { fill: var(--rr-teal-deep); }
.rr-ring { fill: none; stroke: var(--rr-teal-deep); }
.rr-ring-o { stroke-width: 3; opacity: .85; }
.rr-ring-i { stroke-width: 1.4; opacity: .5; }
.rr-arc { font-size: 11px; font-weight: 800; letter-spacing: 3px; text-transform: uppercase; }
.rr-score { font-family: Georgia, serif; font-size: 46px; font-weight: 700; }
.rr-of { font-size: 12px; fill: var(--rr-muted); font-weight: 700; }
/* .rr-stamp prefix on grade/star is LOAD-BEARING: the blanket `.rr-stamp text`
   ink rule above is (0,1,1) and silently beats a bare class (0,1,0) — the gold
   here was dead on every room until 2026-07-09. Keep these ≥ (0,2,0). */
.rr-grade { font-size: 12px; font-weight: 800; letter-spacing: 2px; }
.rr-stamp .rr-grade { fill: var(--rr-gold); }
.rr-stamp .rr-star { fill: var(--rr-gold); }

.rr-board { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin: 24px 0 6px; }
.rr-stat { background: #fff; border: 1px solid var(--rr-hair); border-radius: 8px; padding: 14px 12px; text-align: center; }
.rr-stat .rr-n { font-family: Georgia, serif; font-size: 1.6rem; color: var(--rr-teal-deep); line-height: 1; }
.rr-stat .rr-n small { font-size: .85rem; color: var(--rr-muted); }
.rr-stat .rr-l { font-size: .58rem; letter-spacing: .12em; text-transform: uppercase; color: var(--rr-muted); font-weight: 800; margin-top: 7px; }

.rr-sec {
    font-family: Georgia, serif; font-size: .8rem; letter-spacing: .16em; text-transform: uppercase;
    color: var(--rr-teal-deep); margin: 28px 0 12px; padding-bottom: 7px; border-bottom: 1px solid var(--rr-hair);
}
.rr-overview { font-size: 1rem; line-height: 1.7; color: var(--rr-ink); margin: 0; }

.rr-area { display: flex; gap: 14px; padding: 13px 0; border-bottom: 1px dashed var(--rr-hair); }
.rr-area:last-child { border-bottom: 0; }
.rr-pip { width: 9px; height: 9px; border-radius: 50%; margin-top: 5px; flex: none; }
.rr-pip.up { background: var(--rr-good); } .rr-pip.flat { background: var(--rr-mixed); } .rr-pip.down { background: var(--rr-rough); }
.rr-area-body { flex: 1; }
.rr-area-head { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; }
.rr-area-name { font-weight: 800; color: var(--rr-ink); font-size: .95rem; }
.rr-area-status { font-size: .6rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; padding: 2px 8px; border-radius: 999px; }
.rr-area-status.up { background: rgba(31,138,109,.12); color: var(--rr-good); }
.rr-area-status.flat { background: rgba(192,138,30,.14); color: var(--rr-mixed); }
.rr-area-status.down { background: rgba(180,84,58,.13); color: var(--rr-rough); }
.rr-area-next { margin: 6px 0 0; font-size: .8rem; color: var(--rr-teal-deep); }
/* CP-RR4 — real per-area metric chips (e.g. Body: sessions · top lift · progression) */
.rr-area-metrics { display: flex; flex-wrap: wrap; gap: 8px; margin: 9px 0 0; }
.rr-metric { display: inline-flex; align-items: baseline; gap: 5px; background: #fff;
    border: 1px solid var(--rr-hair); border-radius: 6px; padding: 4px 9px; }
.rr-metric-v { font-family: Georgia, serif; font-size: .9rem; color: var(--rr-teal-deep); font-weight: 700; }
.rr-metric-l { font-size: .56rem; letter-spacing: .1em; text-transform: uppercase; color: var(--rr-muted); font-weight: 800; }

.rr-ledger { display: flex; flex-direction: column; }
.rr-task { display: flex; align-items: center; gap: 11px; padding: 9px 2px; border-bottom: 1px solid var(--rr-hair); font-size: .9rem; }
.rr-task:last-child { border-bottom: 0; }
.rr-mark { width: 18px; height: 18px; border-radius: 5px; flex: none; display: grid; place-items: center; font-size: .7rem; font-weight: 900; color: #fff; }
.rr-mark.done { background: var(--rr-good); } .rr-mark.miss { background: #cdd6da; color: #6b8089; } .rr-mark.part { background: var(--rr-mixed); }
.rr-t { flex: 1; color: var(--rr-ink); }
.rr-t.struck { color: var(--rr-muted); text-decoration: line-through; text-decoration-color: rgba(0,0,0,.25); }
.rr-tag { font-size: .58rem; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; padding: 2px 7px; border-radius: 999px; border: 1px solid var(--rr-hair); color: var(--rr-muted); }
.rr-tag.needle { color: var(--rr-gold); border-color: var(--rr-gold-soft); background: rgba(185,134,43,.08); }

.rr-commit { display: flex; align-items: center; gap: 16px; background: #fff; border: 1px solid var(--rr-hair); border-radius: 8px; padding: 14px 16px; margin-top: 4px; }
.rr-big { font-family: Georgia, serif; font-size: 1.4rem; color: var(--rr-teal-deep); flex: none; }
.rr-of-muted { color: var(--rr-muted); }
.rr-txt { font-size: .86rem; color: var(--rr-ink-soft); line-height: 1.5; }

.rr-advice { counter-reset: rr-a; margin: 0; padding: 0; list-style: none; }
.rr-advice li {
    counter-increment: rr-a; position: relative; padding: 11px 0 11px 38px; border-bottom: 1px dashed var(--rr-hair);
    font-size: .92rem; line-height: 1.55; color: var(--rr-ink);
}
.rr-advice li:last-child { border-bottom: 0; }
.rr-advice li::before {
    content: counter(rr-a); position: absolute; left: 0; top: 9px; width: 24px; height: 24px;
    background: linear-gradient(180deg, var(--rr-teal), var(--rr-teal-deep)); color: #fff; border-radius: 50%;
    display: grid; place-items: center; font-size: .8rem; font-weight: 800; font-family: Georgia, serif;
}

.rr-foot { margin-top: 30px; padding-top: 16px; border-top: 1px solid var(--rr-hair); display: flex; justify-content: space-between; align-items: center; font-size: .68rem; color: var(--rr-muted); }
.rr-ai { display: flex; align-items: center; gap: 6px; }
.rr-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--rr-good); }

/* ---- print → clean single-page PDF (§10i). The OS printer/copies dialog
   itself can't be skipped, but only this document is visible to it. ---- */
@media print {
    body * { visibility: hidden; }
    #reportRoomOverlay, #reportRoomOverlay * { visibility: visible; }
    #reportRoomOverlay {
        position: static !important; background: none !important; backdrop-filter: none !important;
        padding: 0 !important; display: block !important; inset: auto;
    }
    #reportRoomOverlay .rr-toolbar, #reportRoomOverlay .overlay-close { display: none !important; }
    #reportRoomOverlay .overlay-panel.rr-panel { max-width: 100%; }
    .rr-doc { box-shadow: none; border: 0; border-radius: 0; }
    * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
    @page { margin: 14mm; }
}

/* ============================================================
   CP-G — EVENT PLANNER (Mission Control, Planning tab)
   ----------------------------------------------------------------
   Purple glance markers everywhere (dots on the 📅 overlay, small
   badges on the Planning grids) + a right-side drawer (mirrors
   .plan-rail but on the other side) where events are actually
   created/edited. Only the Planning tab's own grids are click-to-open
   (scoped selectors below) — the read-only 📅 overlay stays glance-only.
============================================================ */
/* ⛔ .cw-day-ev IS GONE (2026-08-24) — the 📌 count that used to ride the day
   header. Deleted with its emitter, not left behind: a live-looking selector
   for markup nothing produces is the thing a later session greps for, finds,
   and reasons from. Events are drawn on the grid now; the header's job is
   just the day, and it stays clickable. */
#pwGrid .cw-colhead[data-date] { cursor: pointer; transition: background var(--t-fast) ease; }
#pwGrid .cw-colhead[data-date]:hover { background: var(--accent-glow); }
#pwMonthGrid .pw-mcell:not(.empty) { cursor: pointer; }

.pw-chip.pw-event {
    background: var(--cat-events); color: #1a0d29; cursor: pointer;
}
.pw-chip.pw-event:focus-visible { outline: 2px solid var(--cat-events); outline-offset: 2px; }
.pw-chip.pw-event .cw-block-x { display: none; }
/* A month grid genuinely does show a trip on every day it covers — but
   the run has to read as ONE thing, so only the real ends are rounded
   and only the first day carries the 📌 and the title. */
.pw-chip.pw-event.span-start { border-top-right-radius: 0; border-bottom-right-radius: 0; margin-right: -2px; }
.pw-chip.pw-event.span-mid   { border-radius: 0; margin: 0 -2px; }
/* a run that resumes at the start of a week row re-earns a left edge */
.pw-chip.pw-event.span-reintro { border-top-left-radius: var(--radius-sm); border-bottom-left-radius: var(--radius-sm); margin-left: 0; }
.pw-chip.pw-event.span-end   { border-top-left-radius: 0; border-bottom-left-radius: 0; margin-left: -2px; }

/* ────────────────────────────────────────────────────────────────────
   THE EVENTS DOCK — direction A (Sami picked it, 2026-08-24)
   Markup is built by shared/events-panel.js; the four reports.html
   carry one empty <aside> each. Two regimes, one implementation:

     ≥ 1200px  GUTTER DOCK — the dock is a second column of
               .planning-stage-wrap, beside the card. The card is 1fr
               and keeps its shut width, so the week never resizes.
     < 1200px  SLIDE-OVER (direction B) — there is no gutter to dock
               into, so the same element goes fixed on the right over a
               scrim. Sami was told B would be the narrow fallback
               whichever direction he picked.
   ──────────────────────────────────────────────────────────────────── */
.planning-stage-wrap.events-open {
    max-width: 1416px;                          /* 1120 card + gap + dock */
    display: grid;
    grid-template-columns: minmax(0, 1fr) 280px;
    gap: var(--space-lg);
    align-items: start;
}
.event-dock[hidden] { display: none; }
.event-dock {
    background: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-top: 2px solid var(--cat-events);
    border-radius: var(--radius-lg);
    box-shadow: 0 6px 24px rgba(0,0,0,0.45);
    overflow: hidden;
    min-width: 0;
}
.event-dock .time-picker { --tp-accent: var(--cat-events); }

.ev-top {
    display: flex; align-items: center; gap: 8px;
    padding: 10px 12px;
    border-bottom: 1px solid var(--border-subtle);
    background: color-mix(in srgb, var(--cat-events) 8%, transparent);
}
.ev-top-title { font-size: 0.76rem; font-weight: 800; color: var(--cat-events); letter-spacing: 0.03em; }
.ev-top-date {
    font-size: 0.62rem; font-weight: 700; color: var(--text-muted);
    margin-left: auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* THE PRIMARY ACTION, top right, above the card — where Sami asked for
   it. The full-bleed cyan ADD EVENT slab it replaces was the loudest
   thing on the screen, louder than the calendar it was about. */
.ev-new {
    flex: 0 0 auto;
    font-size: 0.6rem; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase;
    background: var(--cat-events); color: #14061f;
    border: 0; border-radius: var(--radius-sm);
    padding: 0 10px; min-height: 32px; cursor: pointer;
    transition: filter var(--t-fast) ease;
}
.ev-new:hover { filter: brightness(1.12); }
.ev-close {
    flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 32px; min-height: 32px;
    background: none; border: 1px solid transparent; border-radius: var(--radius-sm);
    color: var(--text-muted); font-size: 0.8rem; cursor: pointer;
}
.ev-close:hover { color: var(--text-primary); border-color: var(--border-subtle); }

.ev-body { padding: 10px 12px 12px; display: flex; flex-direction: column; gap: var(--space-sm); }
.ev-sect {
    font-size: 0.54rem; font-weight: 800; letter-spacing: 0.14em; text-transform: uppercase;
    color: var(--text-muted); margin: 0;
}
/* THE LIST COMES FIRST. What is already on this day is the question the
   panel is usually open to answer; the form is the less common half and
   used to be the whole top of the drawer. */
.ev-form { display: flex; flex-direction: column; gap: var(--space-sm); }
.ev-form .ev-sect { margin-top: 2px; }
.event-dock.is-editing .ev-form {
    border-left: 2px solid var(--cat-events);
    padding-left: 8px; margin-left: -2px;
}
/* ⚠ A native date input needs ~118px for "mm/dd/yyyy" PLUS its picker
   glyph; two of them in a 280px dock is 122px each with the dock's own
   padding, so the type scale has to come down or the second field renders
   "mm/dd/yyy" with the last character cut. Measured in the shot, not
   guessed — the default .text-input size does not fit here. */
.ev-dates { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
.ev-date-f { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.ev-date-lbl { font-size: 0.54rem; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted); }
.ev-date-f input[type="date"] {
    min-width: 0; width: 100%; box-sizing: border-box;
    font-size: 0.68rem; padding-left: 6px; padding-right: 2px;
}

.ev-timerow { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ev-timectl { flex: 1 1 110px; min-width: 0; }
.ev-timerow.is-spanning .ev-timectl { display: none; }
.ev-time-hint { font-size: 0.6rem; color: var(--text-muted); font-style: italic; }
/* The label keeps the 32px dense-control target; .hub-check owns the visible
   20px box and its focus ring, so Events does not fork either paint rule. */
.event-allday {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 0.66rem; color: var(--text-secondary); white-space: nowrap; cursor: pointer;
    min-height: 32px; padding: 0 6px; margin-left: -6px;
    border: 1px solid transparent; border-radius: var(--radius-sm);
    --hub-check-accent: var(--cat-events);
}
.event-allday:has(input:disabled) { opacity: 0.55; cursor: default; }
/* the shared .notes-toggle DOES ship a ring now (2026-08-24) — this override
   survives only to tint it with the events category colour inside the dock, and
   wins on specificity (0,3,0). ⚠ Its old comment claimed the shared control had
   no ring; that was true when written and false the moment the shared one landed
   — a limitation claim in a code comment outlives one in an ask. */
.event-dock .notes-toggle:focus-visible { outline: 2px solid var(--cat-events); outline-offset: 2px; }

/* ONE SEGMENTED ROW, not three ragged lines of pills. The label is a
   section heading ABOVE the row — inside it, it ate ~60px of a 280px dock
   and truncated "Day before" to "Day be…". */
.ev-seg-head { margin-bottom: -2px; }
.ev-seg {
    display: flex; align-items: stretch; gap: 0;
    border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
    background: var(--bg-inset); overflow: hidden;
}
.ev-seg-b {
    flex: 1 1 0; min-width: 0; min-height: 32px;
    background: none; border: 0; border-left: 1px solid var(--border-subtle);
    color: var(--text-secondary); font-size: 0.6rem; font-weight: 700;
    padding: 0 3px; cursor: pointer; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis;
    transition: background var(--t-fast) ease, color var(--t-fast) ease;
}
.ev-seg-b:first-of-type { border-left: 0; }
.ev-seg-b:hover { color: var(--text-primary); background: rgba(255,255,255,0.04); }
.ev-seg-b.active { background: var(--cat-events); color: #14061f; font-weight: 800; }
.ev-seg-b:focus-visible { outline: 2px solid var(--cat-events); outline-offset: -2px; }

/* ⛔ SECONDARY, RIGHT-ALIGNED, SENTENCE CASE. The primary add control is
   "+ New" in the panel header — Sami's ruling. A full-bleed uppercase
   SAVE EVENT here is the cyan ADD EVENT slab back under another name. */
.ev-form-actions { display: flex; align-items: center; gap: 8px; justify-content: flex-end; }
.ev-save {
    flex: 0 0 auto; min-height: 32px; padding: 0 14px;
    font-size: 0.66rem; font-weight: 700;
    background: var(--bg-inset); color: var(--cat-events);
    border: 1px solid var(--cat-events); border-radius: var(--radius-sm);
    cursor: pointer; transition: background var(--t-fast) ease, color var(--t-fast) ease;
}
.ev-save:hover { background: var(--cat-events); color: #14061f; }
.ev-cancel {
    flex: 0 0 auto; min-height: 32px; padding: 0 10px;
    font-size: 0.64rem; font-weight: 600;
    background: none; color: var(--text-muted);
    border: 1px solid var(--border-subtle); border-radius: var(--radius-sm); cursor: pointer;
}
.ev-cancel:hover { color: var(--text-primary); border-color: var(--accent-muted); }
.ev-save:focus-visible, .ev-cancel:focus-visible, .ev-new:focus-visible, .ev-close:focus-visible {
    outline: 2px solid var(--cat-events); outline-offset: 1px;
}
/* the four-line dashed box became one muted line */
.ev-fine { font-size: 0.58rem; color: var(--text-muted); line-height: 1.45; margin: 2px 0 0; }

/* ── SLIDE-OVER (direction B) — no gutter exists below this width ── */
.ev-scrim[hidden] { display: none; }
.ev-scrim { display: none; }
@media (max-width: 1199px) {
    /* ⛔ THE Z-INDEX HAS TO GO ON THE WRAPPER, NOT THE DOCK. .planning-stage-wrap
       is position:relative + z-index:1, which makes it a STACKING CONTEXT — so
       the dock's own z-index, whatever it is, is resolved INSIDE it and cannot
       beat a sibling of the wrapper. Measured 2026-08-24: .hub-header is
       position:relative z-index:2, one rank above the wrapper, and
       elementFromPoint over the open slide-over returned #clockTime and
       #notifBell, not the dock. Raising .event-dock to 99999 would have changed
       nothing and looked like a fix.
       The value clears everything measured that could sit over it: .hub-header
       (2), .sl-dock and .chat-toggle (9000). Deliberately BELOW #toast (11000)
       and #hubSigninControl (100001) — feedback and sign-in should still reach
       the member over a panel. */
    .planning-stage-wrap.events-open { display: block; max-width: 1120px; z-index: 9500; }
    .event-dock {
        position: fixed; top: 0; right: 0; bottom: 0;
        width: min(340px, 92vw); z-index: 1210;
        border-radius: 0; border-top: 0; border-left: 2px solid var(--cat-events);
        display: flex; flex-direction: column;
        overflow-y: auto;
    }
    .ev-scrim {
        display: block; position: fixed; inset: 0; z-index: 1209;
        background: rgba(2,6,14,0.62);
    }
}
@media (prefers-reduced-motion: no-preference) and (max-width: 1199px) {
    .event-dock { animation: evSlideIn 180ms ease-out; }
    @keyframes evSlideIn { from { transform: translateX(16px); opacity: 0.4; } to { transform: none; opacity: 1; } }
}

.event-list { display: flex; flex-direction: column; gap: 6px; }
.event-list-item {
    display: flex; align-items: flex-start; gap: 6px;
    background: var(--bg-inset); border: 1px solid var(--border-subtle);
    border-left: 3px solid var(--cat-events); border-radius: var(--radius-sm);
    padding: 7px 8px;
}
.event-list-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.event-list-title { font-size: 0.72rem; font-weight: 700; color: var(--text-secondary); line-height: 1.3; }
.event-list-meta { font-size: 0.6rem; color: var(--text-muted); }
.event-list-item.is-editing { border-color: var(--cat-events); background: color-mix(in srgb, var(--cat-events) 10%, var(--bg-inset)); }
/* ⛔ ALWAYS VISIBLE, never a hover-reveal. Edit and Remove are the only
   way to reach an event's record, and the rail-chip pattern these borrow
   from hides at opacity 0 until hover — which is unreachable by touch
   and invisible to the keyboard until it is already focused. */
.event-list-item .plan-chip-edit,
.event-list-item .plan-chip-x { position: relative; opacity: 0.8; flex: 0 0 auto; }
.event-list-item .plan-chip-edit:hover,
.event-list-item .plan-chip-x:hover { opacity: 1; }
.event-list-item .plan-chip-edit:focus-visible,
.event-list-item .plan-chip-x:focus-visible { opacity: 1; outline: 2px solid var(--cat-events); outline-offset: 1px; }

/* ============================================================
   TIME PICKER (shared/time-picker.js) — Hour/Minute + AM-PM popover.
   --tp-accent lets a host section re-tint the selected state (the
   Events drawer sets it to --cat-events above); defaults to the
   page's normal accent otherwise.
============================================================ */
.time-picker { position: relative; display: inline-block; width: 100%; }
.time-picker-trigger {
    display: flex; align-items: center; justify-content: space-between; gap: 8px;
    width: 100%; height: 34px; padding: 0 10px; box-sizing: border-box;
    background: var(--bg-inset); border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
    color: var(--text-primary); font-size: 0.78rem; font-weight: 600;
    cursor: pointer; transition: border-color var(--t-fast) ease;
}
.time-picker-trigger:hover { border-color: var(--tp-accent, var(--accent-muted)); }
.time-picker-trigger.is-empty { color: var(--text-muted); font-weight: 500; }
.time-picker-trigger.is-disabled, .time-picker-trigger:disabled { opacity: 0.45; cursor: not-allowed; }
.time-picker-clock { opacity: 0.6; font-size: 0.82rem; }

.time-picker-pop {
    position: absolute; z-index: 40; top: calc(100% + 6px); left: 0;
    display: flex; gap: 2px;
    background: var(--bg-card, #0d1b2a); border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md); box-shadow: 0 10px 30px rgba(0,0,0,0.5);
    padding: 6px; max-height: 190px;
}
.time-picker-pop[hidden] { display: none; }
.time-picker-col {
    display: flex; flex-direction: column; gap: 1px;
    overflow-y: auto; width: 56px; scrollbar-width: thin;
}
.time-picker-col-ampm { width: 44px; overflow: visible; }
.time-picker-item {
    display: flex; align-items: center; gap: 4px;
    padding: 5px 6px; border: none; background: none; border-radius: var(--radius-sm);
    color: var(--text-secondary); font-size: 0.74rem; font-weight: 600; cursor: pointer;
    text-align: left; white-space: nowrap; width: 100%;
    min-height: var(--control-dense); box-sizing: border-box;
}
.time-picker-item:hover { background: var(--bg-inset); color: var(--text-primary); }
.time-picker-item.active {
    color: var(--tp-accent, var(--accent-text));
    background: color-mix(in srgb, var(--tp-accent, var(--accent)) 14%, transparent);
}
.tp-check { width: 12px; flex: none; font-size: 0.68rem; }

/* ============================================================
   NOTIFICATION BELL (shared/notify.js, CP-H Stage 1) — local-only:
   Flight Plan deadlines due soon, neglected needle-movers, and
   Events matching their notify window. Bell button reuses the
   existing .icon-btn/.icon-badge pattern (same as #calToggle).
============================================================ */
.notif-wrap { position: relative; }
/* ⛔ z-index 45 LOSES TO THE PAGE IT OPENS OVER. Measured 2026-08-19 by
   hit-testing the panel's own pixels: on desktop `.jr-banner` is drawn across
   it, on mobile the `.home-subnav` pill (which carries z-index 1000 precisely
   so ITS dropdown survives) cuts through it. Every cheaper check passed the
   whole time — the panel was un-hidden, correctly positioned, on screen and
   populated — because "is it there" and "can you see it" are different
   questions and only the second one is the feature.
   1200 outranks the subnav; the `:has()` rule stops an ancestor header from
   trapping the panel in a lower stacking context (sami's header carries
   z-index 2), and applies ONLY while the panel is open, so nothing about the
   page's normal layering changes. */
.notif-panel {
    position: absolute; z-index: 1200; top: calc(100% + 8px); right: 0;
    width: 300px; max-height: 360px; overflow-y: auto;
    background: var(--bg-card, #0d1b2a); border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md); box-shadow: 0 14px 36px rgba(0,0,0,0.55);
    padding: 10px;
}
.notif-panel[hidden] { display: none; }
header.hub-header:has(.notif-panel:not([hidden])) { position: relative; z-index: 1201; }
.notif-panel-head {
    font-size: 0.68rem; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--text-muted); padding: 2px 4px 8px;
}
.notif-list { display: flex; flex-direction: column; gap: 6px; }
.notif-item {
    display: flex; align-items: flex-start; gap: 8px;
    background: var(--bg-inset); border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm); padding: 8px;
}
.notif-icon { flex: 0 0 auto; font-size: 0.9rem; line-height: 1.3; }
.notif-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
a.notif-link { text-decoration: none; border-radius: var(--radius-sm); }
a.notif-link:hover .notif-title { color: var(--accent-text); }
a.notif-link:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.notif-title { font-size: 0.74rem; font-weight: 700; color: var(--text-secondary); line-height: 1.3; }
.notif-sub { font-size: 0.62rem; color: var(--text-muted); }
.notif-ack {
    flex: 0 0 auto; width: var(--control-dense); height: var(--control-dense); border-radius: 50%;
    border: 1px solid var(--border-subtle); background: none; color: var(--text-muted);
    cursor: pointer; font-size: 0.7rem; transition: all var(--t-fast) ease;
}
.notif-ack:hover { color: #07121c; background: var(--accent); border-color: var(--accent); }
.notif-choices { display: flex; gap: 6px; margin-top: 4px; flex-wrap: wrap; }
.notif-choice-btn {
    flex: 0 0 auto; padding: 3px 9px; border-radius: var(--radius-sm);
    border: 1px solid var(--border-subtle); background: none; color: var(--text-secondary);
    cursor: pointer; font-size: 0.62rem; font-weight: 700; transition: all var(--t-fast) ease;
    min-height: var(--control-dense);
}
.notif-choice-btn:hover { color: #07121c; background: var(--accent); border-color: var(--accent); }
.notif-choice-danger:hover { color: #07121c; background: var(--danger, #fa5c5c); border-color: var(--danger, #fa5c5c); }

/* ============================================================
   RETIRED 2026-07-29 — the 🗺 roadmap overlay and its .rm-* list.
   Two dev surfaces (this overlay + sami/projects.html) is how the
   roadmap went stale; the tracker's TREE view is now the only one,
   and the 🗺 tile links straight to it. shared/roadmap-overlay.js
   and sami/roadmap.data.js are deleted — git history holds them.
============================================================ */

/* ============================================================
   💬 FEEDBACK DASHBOARD (2026-07-03) — Sami's-eyes-only review overlay
   for what Jerry/JP/Austin submit via shared/feedback-widget.js on their
   own pages. Reuses .overlay/.overlay-panel; this is just the body list.
   Own namespace, a genuinely different concept (submitted member
   feedback, not a dev roadmap item).
============================================================ */
#feedbackDashOverlay .overlay-panel { max-width: 640px; }
.fbd-group { margin-bottom: var(--space-lg); }
.fbd-group:last-child { margin-bottom: 0; }
.fbd-group-title {
    font-size: 0.68rem; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase;
    color: var(--text-muted); margin-bottom: var(--space-sm);
}
.fbd-list { display: flex; flex-direction: column; gap: 8px; }
.fbd-item {
    background: var(--bg-inset); border: 1px solid var(--border-subtle);
    border-radius: var(--radius-sm); padding: 10px 12px;
}
.fbd-item-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; }
.fbd-member {
    font-size: 0.68rem; font-weight: 800; letter-spacing: 0.04em; text-transform: uppercase;
    color: var(--accent-muted);
}
.fbd-page { font-size: 0.76rem; font-weight: 700; color: var(--text-primary); }
.fbd-time { font-size: 0.62rem; color: var(--text-muted); margin-left: auto; }
.fbd-text { font-size: 0.78rem; color: var(--text-secondary); line-height: 1.5; white-space: pre-wrap; }
.fbd-ack {
    font: inherit; font-size: 0.62rem; font-weight: 700; letter-spacing: 0.02em;
    color: var(--text-muted); background: none; border: 1px solid var(--border-subtle);
    border-radius: 4px; padding: 3px 8px; margin-top: 8px; cursor: pointer;
    transition: all var(--t-fast) ease;
}
.fbd-ack:hover { color: var(--success); border-color: var(--success); }

/* THE FLIP — open face vs reviewed history (Sami, 2026-08-20: "a button to
   reverse the card to show feedback history that I've marked as done").
   The toggle sits above the list on BOTH faces so the archive is discoverable
   rather than remembered; reviewed items previously vanished behind no
   affordance at all. */
.fbd-flip {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    margin-bottom: var(--space-md); padding-bottom: var(--space-sm);
    border-bottom: 1px solid var(--border-subtle);
}
.fbd-flip-btn {
    font: inherit; font-size: 0.68rem; font-weight: 800; letter-spacing: 0.04em;
    color: var(--accent-muted); background: none;
    border: 1px solid var(--border-subtle); border-radius: var(--radius-sm);
    padding: 5px 10px; cursor: pointer; transition: all var(--t-fast) ease;
}
.fbd-flip-btn:hover { color: var(--accent-text); border-color: var(--accent); }
.fbd-flip-face {
    font-size: 0.62rem; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--text-muted); margin-left: auto;
}
/* A reviewed item is RECESSED, not hidden and not deleted — it keeps its full
   text, because the point of the history is reading what was said. */
.fbd-item.is-done { opacity: 0.78; border-style: dashed; }
.fbd-doneline {
    font-size: 0.62rem; font-weight: 700; letter-spacing: 0.02em;
    color: var(--success); margin-top: 6px;
}
.fbd-nodate { color: var(--text-muted); font-weight: 400; font-style: italic; }
.fbd-ack.fbd-undo:hover { color: var(--accent-text); border-color: var(--accent); }

/* ---- LOT 31: the server owns resolution, so the panel has THREE states ----
   `open` and `legacy` are both unresolved and are NOT the same thing: legacy
   means nobody has ever ruled (it predates the status field), and counting it
   as an open complaint would put 16 fake grievances on the badge. Two bands,
   two counts, one list. */
.fbd-band { margin-bottom: var(--space-lg); }
.fbd-band:last-child { margin-bottom: 0; }
.fbd-band-title {
    font-size: 0.72rem; font-weight: 800; letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--text-primary); margin-bottom: 2px;
}
.fbd-band-sub {
    font-size: 0.66rem; line-height: 1.45; color: var(--text-muted);
    margin-bottom: var(--space-sm);
}
/* Not-yet-ruled reads as WAITING, not as wrong — a muted left edge, no alarm
   colour. It is history nobody has answered, not a defect report. */
.fbd-item.is-legacy { border-left: 3px solid var(--accent-muted); }
/* A close that only exists in this browser says so, right on the done line. */
.fbd-unsaved {
    font-weight: 700; color: var(--warning); text-transform: uppercase;
    letter-spacing: 0.06em; font-size: 0.92em;
}
.fbd-note {
    font-size: 0.68rem; line-height: 1.45; color: var(--text-muted);
    font-style: italic; margin-top: 4px;
}
/* A write that failed lands ON the item. A button that silently does nothing
   is the same lie as an empty list that was really a 401. */
.fbd-err {
    font-size: 0.66rem; font-weight: 700; color: var(--danger);
    margin-top: 6px; line-height: 1.4;
}
.fbd-migrate {
    background: var(--bg-inset); border: 1px dashed var(--border-subtle);
    border-radius: var(--radius-sm); padding: 10px 12px;
    margin-bottom: var(--space-md);
    font-size: 0.72rem; line-height: 1.5; color: var(--text-secondary);
}
.fbd-migrate.is-bad { border-color: var(--danger); color: var(--text-primary); }
.fbd-migrate strong { color: var(--text-primary); }
.fbd-migrate .fbd-flip-btn { margin-top: 8px; }

/* ============================================================
   RESPONSIVE
============================================================ */
@media (max-width: 720px) {
    body {
        padding-top: max(var(--space-md), var(--safe-area-top));
        padding-right: max(var(--space-md), var(--safe-area-right));
        padding-bottom: max(100px, calc(100px + var(--safe-area-bottom)));
        padding-left: max(var(--space-md), var(--safe-area-left));
    }
    .overlay {
        align-items: flex-start;
        padding: max(var(--space-sm), var(--safe-area-top)) var(--safe-area-right) var(--safe-area-bottom) var(--safe-area-left);
    }
    .overlay-panel {
        max-width: none;
        max-height: calc(var(--viewport-block) - max(var(--space-sm), var(--safe-area-top)) - var(--safe-area-bottom));
        overflow-y: auto;
        border-right-width: 0;
        border-left-width: 0;
        border-radius: 0 0 var(--radius-lg) var(--radius-lg);
    }
    .dashboard-grid { grid-template-columns: 1fr; gap: var(--space-md); }
    .dock-nav { gap: var(--space-sm); padding: var(--space-sm) var(--space-md); flex-wrap: wrap; max-width: 92vw; justify-content: center; }
    .header-title h1 { font-size: 1.15rem; letter-spacing: 0.1em; }
    .wr-panel { padding: var(--space-md) var(--space-md) var(--space-lg); }
    .digest-cols { grid-template-columns: 1fr; }
    .mc-top-row { flex-direction: column; }
    .mc-top-row .wr-panel-square {
        flex: 1 1 auto; min-height: 0;
        width: 100%; max-width: 100%; box-sizing: border-box;
    }
    .mc-top-row #digestPanel { flex-basis: auto; }
    .wr-panel-head { flex-wrap: wrap; min-width: 0; }
    .planner-add-row { flex-wrap: wrap; min-width: 0; }
    .picker-group { min-width: 0; }
    .perm-tip-pop {
        position: fixed;
        top: auto;
        right: max(var(--space-md), var(--safe-area-right));
        bottom: max(var(--space-md), var(--safe-area-bottom));
        left: max(var(--space-md), var(--safe-area-left));
        width: auto;
        transform: none;
    }
    .rr-pad { padding: 24px; }
    .rr-mast { flex-direction: column; align-items: flex-start; gap: 10px; }
    .rr-meta { text-align: left; }
    .rr-verdict-row { flex-direction: column; align-items: flex-start; }
    .rr-board { grid-template-columns: repeat(2, 1fr); }
}

@media (prefers-reduced-motion: reduce) {
    .overlay-panel { animation: none; }
}

/* ── Health Score dimension receipts (J3, jerry 2026-08-05: "fitness score and
   health score is not populating any past or any info at all").
   Lives here rather than in four member themes because it is the same card on
   all four homes; each theme's own .hc-score-* rules are untouched and still
   win where they overlap. Tokens only, so it lands in every palette. ── */
.hc-score-dims { display: flex; flex-direction: column; gap: 4px; width: 100%; max-width: 320px; margin-top: -6px; }
.hc-score-dim {
    display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
    font-size: 0.7rem; line-height: 1.35; color: var(--text-muted);
    padding: 3px 0; border-bottom: 1px solid var(--border-subtle);
}
.hc-score-dim:last-child { border-bottom: none; }
.hc-score-dim-name { flex: none; font-weight: 800; letter-spacing: 0.04em; }
.hc-score-dim-why { text-align: right; min-width: 0; }
.hc-score-dim.is-real { color: var(--text-secondary); }
.hc-score-dim.is-real .hc-score-dim-name { color: var(--accent-muted, var(--text-secondary)); }

/* ============================================================
   QUIET TOGGLE — the "+ add details" idiom, one spelling
   hub-repair 5c-CLOSE, lot L3 + L5 (2026-08-11)

   The same affordance — a small, deliberately understated button that
   reveals a secondary form — had grown FOUR spellings across the hub:
   .add-details-toggle (austin/workshop, jerry/fitness), .tt-more-toggle
   (jerry/trading), .f-macro-toggle (jerry/fitness), plus a second,
   divergent copy of .add-details-toggle inlined in austin/workshop.html.
   Three font sizes (0.64 / 0.68 / 0.72rem), two border styles, two hover
   colours, and two of the four with no :focus-visible at all.

   ⚠ CONSOLIDATING IT FIXED A REAL DEFECT, not just the spelling.
   .add-details-toggle was `padding: 5px 11px` at 0.72rem — about 27px tall,
   UNDER the 32px tap floor this lane enforced on the suggestion cards in
   5c-ix, and it had no focus ring. Converging on the tallest, most
   accessible of the four is what makes this worth a commit; the tidiness
   is the by-product.

   ⛔ The three class names are KEPT rather than replaced with one. Renaming
   them would touch five HTML files and their JS querySelectors to change
   nothing a member can see — the fork was in the RULES, and that is where
   it is fixed. Positional properties stay local: .tt-more-toggle still
   spans its grid row, .add-details-toggle still carries the Parking Lot
   margin. Only the shared grammar lives here.
============================================================ */
/* ============================================================
   INLINE NOTE ACTIONS — the same 32px floor, promoted 2026-08-17
   (hub-repair, the L5 batch's lot).
   A sibling of the "+ add details" idiom below, and it had the same
   fork with the same consequence. MEASURED at 375 with a seeded
   tracker item and its card expanded — the control lives inside
   .trk-notes, which only renders at display:flex once .trk-card
   gains .expanded, so a probe that skips that step reads 0x0 and
   proves nothing:
     jerry  32px, 2px solid focus ring   (the only honest one)
     austin 12px, browser-default 1px outline
     sami   12px, browser-default 1px outline
   12px is well under half the 32px tap floor this lane set in 5c-ix.
   Only the GEOMETRY and the focus ring are promoted; colour, hover
   and text-transform stay local, because those are theme, not
   grammar — same split 0350b68 drew for the disclosure toggle.
   ⚠ Page <style> blocks come after this file, so a local
   `padding: 0` would still win — austin's and sami's were removed
   in the same commit. Adding a rule here is only half a de-fork.
============================================================ */
.trk-notes-add, .trk-notes-edit,
.hb-goal-notes-add, .hb-goal-notes-edit {
    min-width: 32px; min-height: 32px;
    padding: 4px 0;
    display: inline-flex; align-items: center;
}
.trk-notes-add:focus-visible, .trk-notes-edit:focus-visible,
.hb-goal-notes-add:focus-visible, .hb-goal-notes-edit:focus-visible {
    outline: 2px solid var(--focus-ring); outline-offset: 2px;
}

.add-details-toggle, .tt-more-toggle, .f-macro-toggle {
    display: inline-flex; align-items: center;
    min-height: 32px; padding: 0 11px;
    background: transparent;
    border: 1px dashed var(--border-muted); border-radius: var(--radius-sm);
    color: var(--text-muted);
    font: inherit; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.02em;
    text-align: left; cursor: pointer;
    transition: color var(--t-fast) ease, border-color var(--t-fast) ease;
}
.add-details-toggle:hover, .tt-more-toggle:hover, .f-macro-toggle:hover {
    border-color: var(--accent); color: var(--accent-text);
}
.add-details-toggle:focus-visible, .tt-more-toggle:focus-visible, .f-macro-toggle:focus-visible {
    outline: 2px solid var(--focus-ring); outline-offset: 2px;
}

/* ============================================================
   MONTH ROLLUP + DAY PANEL
   hub-repair 5c-CLOSE-b — Sami's pick 2026-08-11, mockup B

   JP: "Weekly events got thrown into my monthly planner." A task
   scheduled every weekday puts a chip on five days while an event
   happens once, so a normal week buried his two real events under
   nineteen task chips. Two or more blocks in a day now collapse to
   one muted count chip; the events beside them are untouched.

   ⚠ The rollup is deliberately the QUIETEST thing in the cell —
   dashed, transparent, muted — because it represents work the
   member already knows about. The events it stops burying are the
   things they need to SEE. Inverting that weight would move the
   problem rather than fix it.
============================================================ */
/* ⚠ NO `font: inherit` HERE, and that is the whole reason this rule looks the
   way it does. The first version reset the font to the page default, which is
   nearly twice .pw-chip's 0.58rem — so inside a ~110px month cell the label
   ellipsed to "▤ 4 sche…". The DRILL PASSED: it counts rollups and events, and
   a truncated label is still a rollup. Only the screenshot showed it. Keep
   .pw-chip's own type scale and override nothing but the paint. */
.pw-rollup {
    display: flex; align-items: center; width: 100%;
    background: transparent;
    border: 1px dashed var(--border-muted);
    color: var(--text-muted);
    font-family: inherit; font-weight: 700;
    cursor: pointer; text-align: left;
    transition: color var(--t-fast) ease, border-color var(--t-fast) ease;
}
.pw-rollup:hover { border-color: var(--accent); color: var(--accent-text); }
.pw-rollup:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }

/* Mounted on <body>: .cw-scroll clips horizontally and a month cell is ~110px
   tall, so an anchored popover would be cut off on exactly the busy days that
   can open one. 11500 clears the toast (11000) and stays under the sign-in
   dialog (100002) and .overlay (100003), neither of which can be open here. */
.pw-daypanel-wrap {
    position: fixed; inset: 0; z-index: 11500;
    background: rgba(0,0,0,0.55);
    backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
    display: flex; align-items: center; justify-content: center;
    padding: var(--space-lg);
}
.pw-daypanel {
    width: min(520px, 100%); max-height: 80vh; overflow-y: auto;
    background: var(--bg-card); border: 1px solid var(--border-active);
    border-radius: var(--radius-md); box-shadow: 0 10px 40px rgba(0,0,0,0.6);
    padding: var(--space-lg);
}
.pw-dp-head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-md); margin-bottom: var(--space-md); }
.pw-dp-heading { font-weight: 800; letter-spacing: 0.03em; color: var(--text-primary); }
.pw-dp-close {
    min-width: 32px; min-height: 32px; padding: 0 8px;
    background: transparent; border: 1px solid var(--border-muted); border-radius: var(--radius-sm);
    color: var(--text-muted); font: inherit; cursor: pointer;
}
.pw-dp-close:hover { border-color: var(--accent); color: var(--accent-text); }
.pw-dp-close:focus-visible, .pw-dp-x:focus-visible,
.pw-dp-time:focus-visible, .pw-dp-date:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; }
.pw-dp-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-sm); }
.pw-dp-row {
    background: var(--bg-inset); border-radius: var(--radius-sm);
    border-left: 4px solid var(--border-active);
    padding: var(--space-sm) var(--space-md);
}
.pw-dp-row.impact-needle   { border-left-color: var(--impact-needle, var(--accent)); }
.pw-dp-row.impact-standard { border-left-color: var(--impact-standard, var(--accent-muted)); }
.pw-dp-row.impact-minor    { border-left-color: var(--impact-minor, var(--border-active)); }
.pw-dp-title { font-weight: 700; color: var(--text-primary); margin-bottom: 6px; word-break: break-word; }
/* wrap, because three controls do not fit a 375px row — same rule the planner
   header had to learn in this batch: a control row that cannot wrap fails the
   day something is added to it */
.pw-dp-controls { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-sm); }
.pw-dp-field { display: inline-flex; min-width: 0; }
.pw-dp-time, .pw-dp-date {
    min-height: 32px; padding: 0 8px; min-width: 0;
    background: var(--field-bg); color: var(--text-primary);
    border: 1px solid var(--border-muted); border-radius: var(--radius-sm);
    font: inherit; font-size: 0.78rem;
}
.pw-dp-x {
    min-height: 32px; padding: 0 11px;
    background: transparent; border: 1px dashed var(--border-muted); border-radius: var(--radius-sm);
    color: var(--text-muted); font: inherit; font-size: 0.72rem; font-weight: 700; cursor: pointer;
}
.pw-dp-x:hover { border-color: var(--accent); color: var(--accent-text); }
.pw-dp-foot { margin: var(--space-md) 0 0; font-size: 0.72rem; color: var(--text-muted); }

/* ══════════════════════════════════════════════════════════════════════════
   THE PHONE GRAMMAR LAYER  —  Arc 2 / N0   ·   hub-mobile   ·   2026-08-24
   --------------------------------------------------------------------------
   ⛔ WHY THIS IS ONE BLOCK IN A SHARED FILE AND NOT 36 PAGE EDITS.
   Measured (`_specs/hub-mobile/diagnose_n0.py`, 28 pages at 390x844): 525
   sub-44px tap targets, 167 inputs under 16px, 2369 sub-12px text nodes — and
   every page's worst offenders are SHARED components, not page-local ones.
   One pass here moves all of them without opening a member page file, which is
   also N0's acceptance bar. Page-local offenders (sami/projects' .lane-chev
   and .ptr-btn, 54 of the 525) are deliberately NOT touched here — they are
   filed on the lane, per Sami's 2026-08-24 scope rule.

   ⚠ ≤720px ONLY, AND THAT IS THE WHOLE SAFETY ARGUMENT. Sami, 2026-08-24:
   "all these changes should be focused on his mobile app only." Nothing below
   can reach a desktop pixel, because nothing below exists above 720px. 720 is
   the house phone breakpoint already (10 other blocks in shared/ use it).

   ⚠ THE THREE FLOORS THIS DECLARES, AND WHY EACH NUMBER:
     · 16px on any text-entry control — below it, iOS zooms the whole viewport
       on focus. Not a taste call; a functional defect on the one device this
       arc exists for.
     · --control-primary (44px, already a house token since Wave 1a) on touch
       targets. --control-dense (32px) is the desktop escape hatch for dense
       rows; on a phone there is no such thing as a dense row, so the two
       tokens COLLAPSE INTO ONE. That single line moves eight components at
       once and is the root fix the rest of this block only supplements.
     · 13px floor on micro-labels. The survey's defect threshold is 12px; the
       floor is set one notch ABOVE it on purpose, so the fix is a real
       improvement rather than a number tuned to clear a gate.

   ⛔ WHY SOME RULES CARRY !important AND MOST DO NOT — THE SPLIT IS THE POINT.
   Load order is tokens.css -> components.css -> <member>/theme.css -> page
   <style>. So a plain rule here wins any property that no later file declares,
   and loses every property that a later file DOES declare. Measured, the
   second set is small and specific: `.f-input`'s font-size is forked into all
   four member theme.css files; `.cz-arrow` into three of them AND
   `html[data-mode="grid"] .cz-arrow` in tron.css at (0,2,1); `.flip-toggle`'s
   height into a page's own `.flip-shell .flip-toggle` at (0,2,0). Those get
   !important — because a specificity ladder in a shared file is a band-aid the
   next member theme edit walks straight through, and because what is being
   declared is a FLOOR, which is the one thing !important is honestly for. Only
   min-height / min-width / font-size ever carry it. Nothing about colour,
   background, texture or layout is touched, so N1's identity layer — glass,
   Grid, each member's real look — is unaffected by every line below.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 720px) {

    /* ── 1 · THE TOKEN RETUNE — the root fix ─────────────────────────────
       Eight components read --control-dense (.btn-ghost, .overlay-close,
       .opt-chip, .notes-toggle, .time-picker-item, .notif-ack,
       .notif-choice-btn and the toolbar button at :1445). All eight grow to
       the touch minimum from this one line, and none of them can grow on
       desktop because this block does not exist there. */
    :root { --control-dense: var(--control-primary, 44px); }

    /* ── 2 · TEXT ENTRY — the iOS zoom floor ──────────────────────────────
       Every text-entry control, not just the ones measured today, because the
       next one added would otherwise arrive broken. Checkbox/radio/range are
       excluded: they render no text, and forcing 16px on them only inflates
       their box. */
    input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]),
    select,
    textarea { font-size: 16px !important; }

    /* .f-input is the house field. Its font-size is forked into four member
       theme.css files (all at 0.82rem) and is handled by the floor above; its
       HEIGHT is declared nowhere later, so a plain rule is enough. */
    .f-input,
    select.f-input,
    textarea.f-input { min-height: var(--control-primary, 44px); }

    /* ── 3 · TOUCH TARGETS declared right here, where the cascade suffices ── */
    .flip-toggle,
    .task-btn { width: var(--control-primary, 44px); height: var(--control-primary, 44px); }

    .wk-tab,
    .page-tab,
    .gen-btn,
    .btn-deep,
    .btn-ghost-deep,
    .task-perm-badge,
    .add-details-toggle,
    .tt-more-toggle,
    .f-macro-toggle { min-height: var(--control-primary, 44px); }

    /* .cons-star lives in plan-funnel.css, which loads after this file on the
       pages that use it — so this one is stated there, not here. */

    /* ── 4 · TOUCH TARGETS forked into files this batch may not edit ───────
       Each line below names the fork it is beating. Removing the !important
       silently reverts the control to its desktop size on a phone. */
    /* .flip-toggle height:auto from a page's own `.flip-shell .flip-toggle` */
    .flip-shell .flip-toggle { height: var(--control-primary, 44px) !important; }
    /* .cz-arrow — austin/jerry/jp theme.css at 32px, tron.css at (0,2,1) */
    .cz-arrow { min-height: var(--control-primary, 44px) !important;
                min-width: var(--control-primary, 44px) !important; }
    /* .due-item-done — jerry/theme.css 26x26 */
    .due-item-done { min-height: var(--control-primary, 44px) !important;
                     min-width: var(--control-primary, 44px) !important; }

    /* ── 5 · MICRO-TYPE FLOOR ─────────────────────────────────────────────
       The measured sub-12px shared labels, named rather than swept, so a
       future label does not silently inherit a size decision nobody made.
       13px, not 12 — see the header. */
    .f-label,
    .stat-label,
    .picker-label,
    .wr-title,
    .wr-sub,
    .icon-badge,
    .opt-chip,
    .notif-sub,
    .task-perm-badge,
    .gen-btn-count,
    .wk-tab .wk-tab-count,
    .pw-dp-foot,
    .pw-dp-x { font-size: 0.8125rem; }

    /* .hc-link is forked into tron.css at `html[data-mode="grid"] .hc-link`
       (0,2,1) and into three member theme.css files. */
    .hc-link { font-size: 0.8125rem !important; }
}

/* ══════════════════════════════════════════════════════════════════════════
   ARC 2 / N2 — CHROME ECONOMY, PHONE ONLY (hub-mobile, 2026-08-24)
   --------------------------------------------------------------------------
   N0 gave the phone its grammar (44px targets, 16px type floors). This block
   gives it back its FIRST SCREEN. Measured on the four fitness pages at
   390x844 before this block: the masthead, the tool row and the clock cost
   150-190px, the nav pill another ~45px and the page head ~90px — so roughly
   40% of the only screen a member sees on arrival was chrome, and the thing
   they came to do started below it.

   ⛔ SAME CONTRACT AS THE N0 BLOCK ABOVE: every declaration is inside
   `@media (max-width:720px)`, nothing here touches colour, texture or
   identity, and `desk_guard_n0.py` re-proves the desktop is byte-identical.
   Sami's standing Arc 2 rule is phone-only; a desktop pixel moving here is a
   defect, not a bonus.

   ⚖ WHY THE CLOCK GOES AND THE MASTHEAD STAYS. The clock block is the single
   biggest item in the header and it is the one piece of it a phone already
   shows: iOS paints the time in the status bar, in standalone PWA mode too.
   The masthead is the opposite — it is the member's own hub name (JMG War
   Room / Light Speed / The Board / Seal Studios), the ownable asset a
   stranger would remember, and design-director's standing rule is that a
   cleanup which removes one is a regression wearing the clothes of rigour.
   ⚠ `display:none` and not markup removal, deliberately: `#clockTime` and
   `#clockDate` keep being written by every page's clock loop, so nothing has
   to know this happened, and turning it back on is one line.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 720px) {

    /* ── 1 · THE HEADER COLLAPSES ─────────────────────────────────────────
       The desktop header is a two-cluster row with a --space-xl bottom margin
       and a --space-lg bottom padding, sized for a 1200px page. On a phone
       both are paid twice over: once as the gap, once as the border rule. */
    header.hub-header {
        margin-bottom: var(--space-md);
        padding-bottom: var(--space-sm);
        gap: var(--space-sm);
    }
    .header-tools { gap: 4px; }
    /* ⚠ NOT below 30px. The logo mark is a 44px-class object on desktop and
       is NOT interactive — it is the member's emblem, so it is exempt from
       the touch floor but not from being legible. 30px keeps the glyph
       readable while returning 6px of header height.
       ⚖ REACHES THREE MEMBERS, NOT FOUR, AND THAT IS LEFT ALONE ON PURPOSE.
       `austin/theme.css:236` forks `.logo-mark` to 46px and loads after this
       file, so his emblem keeps its size and his header measures 74px against
       the others' 53px. It could be overridden and it is not: his rhino at
       46px is a deliberate choice in his own theme, the 16px it costs is not
       needed (his log verb already lands inside screen 1 with room), and
       quietly shrinking a member's emblem from a shared file to buy pixels
       nobody is short of is how identity leaks out of a system one override
       at a time. */
    .logo-mark { width: 30px; height: 30px; font-size: 0.95rem; border-radius: 7px; }

    /* The one real cut. See the header note above for why this and not the
       masthead. */
    .clock-container { display: none; }

    /* ── 2 · THE PAGE HEAD STOPS BEING A COVER PAGE ───────────────────────
       .page-title is a desktop display size and .page-sub is a full sentence
       of orientation copy. Both keep their words — the sub is real content, a
       member's own framing of the page, and cutting it is a content decision
       nobody made. They just stop being sized for a 1200px canvas. */
    /* ⛔ NO font-size HERE, AND THAT IS A DECISION, NOT AN OMISSION. `.page-title`
       is forked into every member theme that loads after this file — austin
       and jerry at 1.5rem, jp at 1.9rem in his display face — so a size stated
       here would be inert for three of the four while reading as if it applied
       to all of them. It was written that way first and measured: only sami's
       title moved. What is NOT forked is the leading and the gap, so those are
       what this takes. ⚖ jp's 1.9rem Permanent Marker stays: his page-head
       costs 105px because his SUBTITLE runs to three lines, not because of the
       title, and shrinking a member's display face to buy back a few pixels
       trades an identity for arithmetic. */
    .page-title { line-height: 1.15; margin-bottom: 0.25rem; }
    /* ⚠ AND NO `.page-sub` RULE AT ALL, for the same reason plus a better one:
       all four themes fork its size (0.82rem x3, jp 0.86rem — 13.1px and
       13.8px), every one of them already clears the 12px floor, and the value
       written here first was 0.875rem, which would have been an INCREASE if it
       had applied to anything. A rule that is both inert and pointed the wrong
       way is worse than no rule: it documents an intention the file does not
       have. */
    .page-head { margin-bottom: var(--space-md); }

    /* ── 3 · TAB BARS WRAP ONLY WHEN CONTENT NEEDS IT ──────────────────
       N4 measured jerry/fitness at 390: the 44px touch floor made its three
       short tabs (Training / Sleep / Food) take two rows and 102px because
       `.wk-tabs` carries `flex-wrap: wrap`. Its `flex: 1 1 0` lets those tabs
       share one row; the second row N4 removed does not come back.
       ⚠ N4's `min-width: 0` went too far: it lets a flex item shrink below
       its content and therefore denies wrapping the width pressure it needs,
       giving four long labels one crushed row instead of clean wrapped rows.
       `min-width: auto` restores the content floor. Measured on four pages:
       jerry/finance, 1 crushed row → 2 clean rows; jp/finance → 4 clean
       rows; austin/finance stays at 1 row; jerry/fitness stays at 1 row and
       54px, and stops the 6px spill it had carried since N4 shipped. `flex`
       and `min-width` are declared in no member or page file (verified across
       all four fitness pages and both theme forks of `.wk-tab`), so a plain
       rule wins and no !important is needed here. */
    .wk-tab { flex: 1 1 0; min-width: auto; justify-content: center; }

    /* ── 4 · THE PERFORMANCE-DETAIL CONTROLS ──────────────────────────────
       `.pd-toggle button` (54x23, 64x23, 44x23) and `.pd-cal input` (144x29)
       are the LAST sub-44px controls left on all four fitness pages after N0,
       and they are the same block copied into all four page files — so they
       are declared in page <style>, which loads after this file, and only
       !important reaches them. Same narrow contract N0 set — min-height and
       min-width only, nothing about colour, layout or type. Fixing it here
       rather than four times is the root-fix rule; the fork itself is filed
       for the hub lane, not un-forked inside a phone batch.
       ⚠ min-WIDTH as well as min-height, and the reason is a quarter of a
       pixel: with the height floor alone the "YTD" button measured 43.75 wide
       — three characters plus 0.6rem of padding — and 43.75 fails a `< 44`
       test exactly as loudly as 20 would. A floor that only holds one axis
       leaves the shortest label in every segmented control under the line. */
    .pd-toggle button { min-height: var(--control-primary, 44px) !important;
                        min-width: var(--control-primary, 44px) !important; }
    .pd-cal input { min-height: var(--control-primary, 44px) !important; }
}

/* ══════════════════════════════════════════════════════════════════════════
   ARC 2 / N3 — HOME: TODAY FIRST, PHONE ONLY (hub-mobile, 2026-08-25)
   --------------------------------------------------------------------------
   N0 gave the phone its grammar, N2 gave it back its first screen. N3 answers
   the question those two cannot: is the thing today the first thing under the
   thumb. Measured at 390x844 before this block (`n3_home.py`, pinned to
   9664b69) — and the answer was no, in two different ways:

     jerry   today banner y=173, then MUSINGS — a reflective card — first
     jp      today banner y=183, then Open Loops at y=398
     sami    NO today element at all; his first full screen is a QUOTES card
     austin  NO today element at all; quotes first, after 84px of nothing

   Sami and Austin do not get a today banner here — Arc 2 invents no features
   (the map is explicit). What they get is their existing today card moved to
   the top, which is a reordering, not a capability.

   ⛔ SAME CONTRACT AS THE N0 AND N2 BLOCKS: every declaration is inside
   `@media (max-width: 720px)`, nothing here touches colour, texture or
   identity, and the desktop is untouched because none of it exists there.
   ══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 720px) {

    /* ── 1 · THE COLUMNS STOP EXISTING, SO ORDER CAN CROSS THEM ───────────
       Every home splits its cards into `.hc-col-left` / `.hc-col-right`. CSS
       `order` only sorts SIBLINGS, so while those wrappers exist a card in the
       right column can never be lifted above one in the left — which is
       exactly austin's case (his fitness and finance live in the right
       column). `display: contents` removes the wrapper boxes from the layout
       while leaving the DOM untouched, so all cards become direct flex items
       of `.home-cards` and one order table can sort all of them.

       ⚖ THE DOM IS WHY THIS IS SAFE, AND IT IS ALSO WHY `order` IS THE RIGHT
       TOOL AT ALL. Sami's standing Arc 2 rule is phone-only; reordering the
       markup would move the DESKTOP too. `order` cannot — it does not exist
       above 720px. And `display: contents` changes rendering only, so
       jerry/sami/jp's `layoutHomeCards()` masonry (which appends every card
       into `.hc-col-left` and returns early below 1040px) still finds the
       wrappers it queries and still works untouched.
       ⚠ austin runs NO masonry at all — his cards stay where the markup put
       them — which is the whole reason this has to flatten rather than rely on
       the packer having already done it. */
    .home-cards > .hc-col { display: contents; }
    /* ⛔ AND THE LINE ABOVE LOSES ON SAMI WITHOUT THIS ONE. `shared/tron.css`
       forks `html[data-mode="grid"] .hc-col { display: flex }` at (0,2,1) and
       loads AFTER this file, so the rule above parsed, showed in the diff,
       showed in a census of the rules the browser built — and computed to
       `flex` on his page. Caught by n3_home.py's cascade arm, not by reading.
       ⚠ Matching that selector's specificity is not enough (equal specificity
       loses on source order), and a longer selector is the specificity ladder
       N0 explicitly refused — "a band-aid the next member theme edit walks
       straight through". So `!important`, which is the honest tool for a
       structural declaration a later file contradicts.
       🔑 It happens to change nothing visible on his page today: sami, jerry
       and jp all run a `layoutHomeCards()` masonry that appends every card
       into `.hc-col-left` below 1040px, so their cards are already siblings
       and `order` sorts them with or without the flatten. Austin runs no
       masonry and genuinely needs it. That is exactly why this is fixed rather
       than shrugged at — a rule that is inert on one member for an accidental
       reason is a landmine for the day that reason stops holding. */
    html[data-mode="grid"] .home-cards > .hc-col { display: contents !important; }
    /* The gap moves with the flattening: it used to come from `.hc-col`
       (--space-lg) and `.home-cards`'s own gap is --space-xl. Without this
       line every card on every home gains 16px of separation — a spacing
       change nobody asked for, arriving as a side effect of a reorder. */
    .home-cards { gap: var(--space-lg, 1.5rem); }
    /* ⛔ THE FLATTENING PROMOTED THE CARDS TO FLEX ITEMS AND LEFT THEIR WIDTH
       UNGOVERNED, and austin/home.html has scrolled sideways ever since.
       Measured 2026-09-04 at 375px: `.hc-finance` sat at 346px inside a 279px
       content box and pushed the document to 394.
       ⛔ AND `min-width: 0` — the obvious repair, and the one `.hc-col` itself
       carries — DOES NOTHING HERE. Below 1040px austin/theme.css turns
       `.home-cards` into `flex-direction: column`, so the main axis is VERTICAL:
       flex-shrink and min-width govern HEIGHT, and the width is decided on the
       cross axis by `align-items: flex-start`, which sizes every item to its own
       content. Applying min-width:0 was measured and left the card at 345.594px.
       🔑 IN A COLUMN FLEX CONTAINER THE WIDTH IS AN ALIGNMENT QUESTION, NOT A
       SHRINK QUESTION — the fix is to stretch the item across the container.
       ⚠ `align-items: flex-start` stays as it is on `.home-cards`: it is correct
       on the desktop ROW, where it governs height and stops columns stretching to
       equal length. Only the promoted cards, only below 720px. */
    .home-cards > .hc-col > .hc-card { align-self: stretch; }

    /* ── 2 · THE ORDER TABLE ──────────────────────────────────────────────
       Read off each member's OWN pocket surface, which is the one phone-first
       artifact of theirs that is already proven and installed — not invented
       here, and not a single house opinion applied to four different people:

         jerry   Morning Ritual · This Week · quick(trade/meal/note) · Today
         sami    Now · quick(workout/weight/note) · Today · Group Pulse
         austin  Daily Check-In · This Month · quick(expense/...) · Today · Streak
         jp      Why · Open Loops · Gym/Weight · The Board This Week

       The shape they share: today's obligations, then that member's daily
       verbs, then weekly surfaces, then reflective ones.

       ⛔ `.hc-card { order: 50 }` IS THE LOAD-BEARING LINE, not boilerplate.
       `order` defaults to 0, so any card this table does not name would sort
       ABOVE every card it does — a new card added next month would silently
       land at the top of the page. The base rank makes an unnamed card land
       mid-pack, which is wrong quietly instead of wrong loudly. */
    .home-cards .hc-card { order: 50; }

    /* ⛔ ABOVE TODAY, AND ONLY BECAUSE IT IS NOT A FEATURE — hub row 107,
       2026-09-09. `shared/stranded-card.js` renders at most one card, only on
       a browser that is actually holding legacy data this hub could not carry,
       and it is the one card on the page whose subject is data the member may
       believe he LOST. Everything ranked below it is a thing he does daily and
       will still be there tomorrow.
       ⚠ THE COST IS REAL AND IS THE COST DIRECTION C WAS PICKED WITH: it sits
       above Due Soon / The Workshop / Open Loops until he taps once, and it
       stays (in a one-line "Sent to Sami" form) while the data is still
       stranded, because hiding it would tell him it was resolved when nothing
       has been restored. If Sami wants it demoted, this number is the whole
       change — 46 puts it directly under the base rank. */
    .home-cards [data-card-id="stranded"] { order: 5; }

    /* today — the same rung under three different names, because the members
       built the same card and called it what their own hub calls it: jerry's
       "Due Soon" (+ habits), sami's and austin's "The Workshop" (slide 1 is
       Today's Habits), jp's "Open Loops". */
    .home-cards [data-card-id="due-soon"],
    .home-cards [data-card-id="systems"],
    .home-cards [data-card-id="loops"]       { order: 10; }
    /* live insight, when there is one — it is about today by construction.
       ⛔ MATCHED BY CLASS, NOT BY data-card-id, AND THE FIRST VERSION OF THIS
       LINE WAS INERT. The insight card is the ONE card on all four homes that
       carries no `data-card-id` — jerry:218, austin:177, jp:105 are all
       `<section class="hc-card hc-insight" id="hcInsightCard" hidden>`. So
       `[data-card-id="insight"]` matched nothing, and the card fell through to
       the base rank: computed `order` read **50** on all three, measured.
       ⚠ A cold reviewer found this; none of our own arms could. The rule was
       BUILT (the css-rule census counts it), it simply had no element to
       apply to — and the cascade arm can only check selectors that resolve. A
       selector matching zero elements is invisible to every instrument here.
       ⚖ Fixed in CSS rather than by adding the attribute to three member
       files: `data-card-id` is also what `shared/engagement.js` logs against,
       so adding it would silently start recording a new card id in everyone's
       engagement data — a data change smuggled inside a phone restyle. */
    .home-cards .hc-insight,
    .home-cards [data-card-id="insight"]     { order: 12; }
    /* the daily verbs */
    .home-cards [data-card-id="fitness"]     { order: 20; }
    .home-cards [data-card-id="trading"]     { order: 30; }
    .home-cards [data-card-id="finance"]     { order: 40; }
    .home-cards [data-card-id="journal"]     { order: 45; }
    /* weekly / read surfaces */
    .home-cards [data-card-id="reports"]     { order: 60; }
    .home-cards [data-card-id="consistency"] { order: 70; }
    /* reflective — real content, and not what anyone opens their hub at 7am to
       do. Last is a position, not a demotion. */
    .home-cards [data-card-id="quotes"],
    .home-cards [data-card-id="musings"]     { order: 90; }

    /* ── 3 · THE TAP TARGETS HOME STILL OWES ──────────────────────────────
       Measured, per page, in the landing state. Every one of these is a shared
       component forked into member themes that load after this file, so each
       needs !important for the same reason N0's section 4 did — and, as there,
       only min-height / min-width / font-size ever carry it. Nothing here
       touches colour, layout or type.

       `.hc-head` is the card's header row and it is an <a> on most cards
       ("REPORTS   OPEN ▸"). 284x25 on jerry and sami, 269x25 on austin,
       274x25 on jp — 1 to 5 per page, the single most repeated sub-44px
       control on all four homes.
       ⚠ THE margin CUT IS PART OF THE FIX, NOT TIDYING. Growing the row to 44
       costs 19px on EVERY card of EVERY page; taking 6px back off the 14px
       bottom margin pays a third of it, and the border rule under the header
       keeps its breathing room because that comes from padding-bottom, which
       is untouched. */
    .hc-head { min-height: var(--control-primary, 44px) !important;
               margin-bottom: 8px !important; }
    .hc-link { min-height: var(--control-primary, 44px) !important;
               display: inline-flex !important; align-items: center !important; }

    /* jp's per-task log button (26x26) and sami's list action (32x32). Both
       are round; both simply grow. */
    .hc-log,
    .hc-list-action { min-width: var(--control-primary, 44px) !important;
                      min-height: var(--control-primary, 44px) !important; }

    /* ── jp's open-loop row: 44px TARGETS, not 44px BOXES ─────────────────
       Three controls per loop, nine on his landing screen, all 32x32.
       ⛔ GROWING ALL THREE TO 44 WAS THE FIRST FIX AND IT WAS A REGRESSION —
       measured, then seen. His row is a fixed 274px, so +12px x 3 came
       straight out of the only flexible child: `.wb-loop-body` went 138px ->
       102px, a 26% cut, and "Get another job?" started wrapping onto two
       lines. The gate went GREEN on that build. A tap-target count cannot see
       a text column collapse, and the screenshot is what caught it.

       ⚖ So the checkbox — the primary act, ticking a loop done — keeps its
       real 44px, and the two icon buttons keep their 32px BOX while getting a
       44px HIT AREA from a transparent pseudo-element. That costs zero layout
       width, and it is the technique this codebase already uses (sami's
       `.injury-toggle` is built exactly this way, which is why n2_frame.py
       tests tap areas with `elementFromPoint` rather than with the rect).
       ⚠ THE CONSEQUENCE FOR THE GATE, STATED SO NOBODY "FIXES" IT BACK: these
       two now measure 32px wide and are SUPPOSED to. A rect-based count will
       flag them forever; the hit test is the arm that answers for them. */
    .wb-loop-check { min-width: var(--control-primary, 44px) !important;
                     min-height: var(--control-primary, 44px) !important; }
    .wb-loop-edit,
    .wb-loop-del { min-height: var(--control-primary, 44px) !important;
                   position: relative; }
    .wb-loop-edit::after,
    .wb-loop-del::after {
        content: ''; position: absolute; left: 50%; top: 50%;
        width: var(--control-primary, 44px); height: var(--control-primary, 44px);
        transform: translate(-50%, -50%);
    }

    /* ⛔ THE CAROUSEL DOTS ARE THE ONE CONTROL THAT MUST NOT GROW, and the
       naive fix is visibly wrong. `.cz-dot` is a 6x6 pagination dot (8x8 when
       active); a 44px dot is not a bigger target, it is a different design.
       Padding moves the TARGET without moving the DOT — `background-clip:
       content-box` keeps the paint inside the 6px content box while the
       element a thumb can land on becomes the full 44px. The `transform:
       scale(1.25)` the active state uses still scales only what is painted.
       ⚠ `box-sizing: content-box` is required: under the house's global
       border-box, `min-width: 44px` would count the 19px of padding on each
       side and leave the 6px dot with a 0px content area. */
    .cz-dot { box-sizing: content-box !important;
              padding: 19px !important;
              background-clip: content-box !important; }
    /* ⚠ AND THE ROW HAS TO BE ABLE TO BREAK, WHICH THE PADDING ABOVE MAKES
       NEWLY NECESSARY. Found by a cold reviewer, then measured: `.cz-dots` is
       `display:flex` with `gap:5px` and NO wrap and NO scroll in any of the
       four member themes. Each dot now occupies 44px of layout width instead
       of 6, so a card ~300px wide fits six dots and overflows at seven.
       ⛔ NOT LIVE TODAY — the widest carousel on any home renders 3 dots at
       142px in a 298px card, and page overflow measures 0 on all four. This is
       a latent bound the padding introduced, and one line removes the whole
       class rather than leaving it for whoever adds a seventh slide. */
    .cz-dots { flex-wrap: wrap; }

    /* the three page-local "edit / add" affordances on the today surfaces */
    .jr-banner-edit,
    .wb-banner-edit,
    .mus-add-toggle { min-width: var(--control-primary, 44px) !important;
                      min-height: var(--control-primary, 44px) !important; }

    /* ⛔ jerry's "What's the one thing today?" IS THE PRIMARY ACTION OF HIS
       HOME PAGE and it measured 229x24 — the smallest primary control on any
       of the four, on the highest-traffic phone page in the hub. It is a
       <div role="button">, so nothing about it looks like a control to a
       stylesheet; only the measurement found it. */
    .jr-banner-task { min-height: var(--control-primary, 44px) !important;
                      display: flex !important; align-items: center !important; }

    /* austin's inline "win" field, 117x21 */
    .hc-win-text { min-height: var(--control-primary, 44px) !important; }
}
