/* ==========================================================================
   Anaïse chassis — layout & components. Consumes tokens.css only; no raw
   colors here. Flat, calm, DevOps/PO-Manager lineage: panels separated by
   1px borders on an off-white page.
   Scaffold scope: chassis + W-1 (idle). Later states extend, not rewrite.
   ========================================================================== */

* { box-sizing: border-box; margin: 0; padding: 0; }

/* Panel display rules would otherwise defeat the hidden attribute */
[hidden] { display: none !important; }

/* Scrolling everywhere, visible scrollbars nowhere (Franck 2026-07-19). */
* { scrollbar-width: none; }
*::-webkit-scrollbar { display: none; }

html, body { height: 100%; }

body {
    font-family: var(--font);
    background: var(--bg-page);
    color: var(--text-primary);
    display: flex;
    height: 100svh;
    overflow: hidden;
    -webkit-font-smoothing: antialiased;
}

svg { fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

button { font-family: inherit; background: none; border: none; cursor: pointer; color: inherit; }

/* ==================== Primary nav rail ====================
   Quiet grey chrome (spec C.2: "light grey or pastel background"),
   left-justified rows on a shared text axis, DevOps-style active band
   with a 3px accent bar. Sub-items indent ~¼ inch (--subitem-indent). */

.rail {
    width: var(--rail-width);
    flex: 0 0 var(--rail-width);
    background: var(--bg-nav);
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    align-items: stretch;
    padding: 16px 10px 12px;
}

/* Customer org logo — sized to the 56px top-bar band (PO Manager
   --topbar-height), left on the content axis (Franck 2026-07-19). */
.org-logo {
    height: 38px;
    width: auto;
    display: block;
    margin: 0 0 18px 10px;
}

.cap-word {
    font-family: var(--font);
    font-size: 47px;
    font-weight: 700;
    letter-spacing: 0.12em;
    fill: var(--cap-navy);
    stroke: none;
}

.cap-swoosh { fill: none; stroke: var(--cap-steel); stroke-width: 5; stroke-linecap: round; }

.cap-tri { fill: var(--cap-sage); stroke: none; }

.cap-tag {
    font-family: var(--font);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.16em;
    fill: var(--cap-tag);
    stroke: none;
}

.rail-items {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 1px;
    width: 100%;
    overflow-y: auto;
    scrollbar-width: none;
}
.rail-items::-webkit-scrollbar { display: none; }

/* Left-justified row: icon left, label beside it, one shared text axis.
   Bands round symmetrically — both corners, with the rail's 10px inset
   keeping the right edge visibly clear of the border. */
.rail-item {
    position: relative;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: flex-start;
    gap: 10px;
    width: 100%;
    padding: 8px 10px;
    border-radius: var(--radius-lg);
    font-size: 13px;
    font-weight: 500;
    color: var(--nav-ink);     /* white in dark (PO Manager); grey in light */
    text-align: left;
    transition: background var(--transition), color var(--transition);
}

.rail-item span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.rail-item:hover { background: var(--nav-hover); color: var(--text-primary); }

.rail-item.active {
    background: var(--bg-active);
    color: var(--nav-ink-active);  /* pale blue in dark (PO Manager) */
    font-weight: 600;
}

.rail-icon { width: 18px; height: 18px; flex: 0 0 18px; stroke-width: 1.7; }

/* Solid (filled) icons — e.g., the Vault lock, so it jumps a bit more. */
.rail-icon--solid { fill: currentColor; stroke: none; }

/* The selection band is the focus indicator on rail rows; the default
   focus ring reads as a stray stroke here and gets clipped by the rail. */
.rail-item:focus-visible { outline: none; }

/* Root categories (Focus Areas · Vault · Sessions): same rank, same
   format — uppercase, simple colored icon (quieter than DevOps). */
.rail-root {
    font-size: 11px;
    font-weight: 700;               /* bold by default (Franck 2026-07-19) */
    letter-spacing: 0.03em;
    text-transform: uppercase;
    margin-top: 12px;
    gap: 8px;
}

/* Engagement icons: one deep electric blue across the rail. */
.rail-root .rail-icon,
#newSessionBtn .rail-icon { color: var(--icon-rail); }

/* Collapsible categories: chevron at the row's right end. */
.chevron {
    width: 13px;
    height: 13px;
    flex: 0 0 13px;
    margin-left: auto;
    color: var(--text-muted);
    stroke-width: 2;
    transition: transform var(--transition);
}

.chevron.open { transform: rotate(90deg); }

.rail-subgroup {
    display: flex;
    flex-direction: column;
    gap: 1px;
}

/* Selection grammar (Franck 2026-07-18): the blue band belongs ONLY to
   the element actually selected. A directly-selected category wears the
   band; the parent of a selected sub-item goes bold — nothing more. */
.rail-root.active {
    background: var(--bg-active-cat);
    color: var(--nav-ink-active);  /* pale blue in dark (PO Manager) */
}

/* Ancestor of the selection: a thick grey underscore under the label —
   not a frame (Franck 2026-07-19: one band + one underscore, no more).
   The underscore's space is RESERVED on every root label (transparent
   border) so selection never reflows the rail — rows below would
   otherwise shift after alignment. */
.rail-root span {
    border-bottom: 3px solid transparent;
    padding-bottom: 1px;
}

.rail-root.has-active-sub { color: var(--text-primary); }

.rail-root.has-active-sub span {
    border-bottom-color: var(--parent-underscore);
}

/* Sub-items: a clear ~¼ inch indent past the parent text axis
   (10px pad + 18px icon + 10px gap = 38px axis, + the indent). */
.rail-item--sub {
    /* Band starts indented (margin); width accounts for the margin so
       the box ends at the container edge like the roots — both corners
       round, nothing clipped (hover, select, all states). */
    margin-left: calc(24px + var(--subitem-indent));
    width: calc(100% - 24px - var(--subitem-indent));
    padding-left: 14px;
    font-size: 12.5px;
}

.rail-item--sub.active {
    background: var(--bg-active-sub);
    color: var(--nav-ink-active-sub);  /* pale blue in dark; grey on light-blue in light */
    font-weight: 600;
}

.rail-footer {
    margin-top: auto;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    width: 100%;
    padding-top: 8px;
}

.rail-user {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 9px;
    padding: 7px 10px 3px;
}

/* Matches the Send button: Nerval gradient orb, initial in white.
   A button (Franck 2026-08-11): it opens the logout popup. */
.avatar {
    width: 26px;
    height: 26px;
    flex: 0 0 26px;
    border: 0;
    padding: 0;
    border-radius: 50%;
    background: var(--accent-gradient);
    color: var(--on-gradient);
    font-family: inherit;
    font-size: 11px;
    font-weight: 600;
    display: grid;
    place-items: center;
    cursor: pointer;
    transition: filter var(--transition), box-shadow var(--transition);
}

.avatar:hover { filter: brightness(1.06); }
.avatar:focus-visible {
    outline: none;
    box-shadow: 0 0 0 2px var(--bg-nav), 0 0 0 4px var(--accent-border);
}

/* The vendor's due (memo §2): black bar, "Powered by" + the gold
   Anaïse mark (the logo lives on black, so the bar is the composition).
   Spans the rail edge-to-edge beneath everything. */
.powered-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;   /* text left on grey; the black logo
                                         block forms the bar's right end */
    gap: 8px;
    height: 42px;               /* uniform strip height across rail, panel,
                                   and engagement — one continuous run */
    margin: 10px -10px -12px;   /* cancel rail padding: edge-to-edge */
    padding: 0 0 0 12px;        /* no right/vertical padding: logo bleeds */
    background: var(--powered-bg);
    border-top: 1px solid var(--border);   /* one continuous bottom rule
                                              across rail, panel, engagement */
    overflow: hidden;
}

/* Same voice as the rail rows — prominent, not a whisper. */
.powered-bar span {
    font-size: 13px;
    font-weight: 500;
    color: var(--powered-ink);
}

.powered-logo { height: 42px; width: auto; display: block; }

/* When the bar expands, the powered strip expands with it: the rail
   half keeps the lettering, the panel half continues the grey and the
   Anaïse block hugs the expanded bar's right edge. */
.powered-bar--panel {
    margin: auto -8px -10px -8px;
    padding: 0;
    display: flex;
    align-items: center;
    background: var(--powered-bg);
    /* Match the rail's 42px strip so the two read as one continuous bar — and, crucially, so the file
       list above CLIPS at the strip's top instead of scrolling down over it (Franck 2026-08-30). */
    min-height: 42px;
    border-top: 1px solid var(--border);
}

/* The rail keeps its "Powered by" + Anaïse mark even while the Vault is open — the mark stays in the
   SAME frame as the label (Franck 2026-08-30), rather than jumping to the panel. */

/* ==================== Secondary nav: the Vault panel (spec C.3) ============
   Navigation never occupies the Engagement Frame. The grey panel expands
   beside the rail at double its width; content wider than the panel
   scrolls horizontally. */

.secondary {
    position: relative;
    width: var(--secondary-width);
    flex: 0 0 var(--secondary-width);
    background: var(--bg-panel-alt);    /* a step LIGHTER than the rail
                                           (Franck 2026-07-20 — natural
                                           progression toward the page;
                                           resolves the C.3 tone ping):
                                           the tone break IS the separator */
    border-right: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    padding: 8px 8px 10px;
    min-width: 0;
}

/* The rail's divider disappears entirely while the bar is expanded —
   no color, no 1px footprint — the two columns are one surface. */
body.vault-open .rail { border-right: none; }
/* In dark, only the faintest hint of a divider — the connector does the real work of tying the two
   columns; this is just a whisper of separation (Franck 2026-08-30: 20% opacity). In light the panel
   is a step lighter than the rail and that tone break already separates them, so no line. */
[data-theme="dark"] body.vault-open .rail {
    border-right: 1px solid color-mix(in srgb, var(--border) 20%, transparent);
}

.panel-actions {
    position: absolute;
    top: 8px;
    right: 6px;
    display: flex;
    align-items: center;
    gap: 2px;
    z-index: 2;
}

.panel-actions .icon-btn { width: 30px; height: 30px; flex: 0 0 30px; }

.panel-actions .icon-btn svg { width: 15px; height: 15px; }

/* Full Finder model (Franck 2026-07-19): one surface, incremental
   indentation, single-zone highlights — every band is its own rounded
   object; nothing must align across the rail/panel boundary. */
.vault-tree {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
    display: flex;
    flex-direction: column;
    gap: 1px;
    /* Start below the 56px top band + hairline, with a clear gap
       (panel pad 8 + 62 = first row at ~70, hairline at 56). */
    padding: 62px 0 8px;
}

/* The selected folder's name, titling the Vault column. It sits on the SAME line as the rail's
   section headers (FOCUS AREAS, at y≈104) — a shared cross-column line, so the panel's content no
   longer floats above the primary nav with nothing naming it. The margin-top clears the panel's
   action band and drops the title onto that line. */
/* The head row: the selected-folder title band + a search icon to its right. It HUGS its content
   (align-self) rather than stretching to the panel edge (FD Rule 3.5); max-width lets a long name
   ellipsize. margin-TOP is set in JS to line up with the first Focus Area row. */
.panel-head {
    flex: 0 0 auto;
    align-self: flex-start;
    max-width: calc(100% - 8px);
    margin: 8px 8px 4px 0;
    display: flex;
    align-items: center;
    gap: 6px;
}
.panel-head[hidden] { display: none; }

.panel-title {
    display: flex;
    align-items: center;
    gap: 5px;
    min-width: 0;
    padding: 4px 10px;
    border-radius: var(--radius-lg);
    /* The selected folder, wearing the SAME selection band as its row in the rail — one selection,
       shown in two places. Same size and weight as the rail's selected sub-item (the bold was the
       illusion); the highlight is what marks it. */
    background: var(--bg-active-sub);
    /* Light theme: grey ink on a light-blue band, not jarring bold-black (Franck; POManager cue).
       Dark theme: the token resolves to the same pale blue as before — unchanged. */
    color: var(--nav-ink-active-sub);
    font-size: 12.5px;
    font-weight: 600;
    letter-spacing: 0.01em;
}
/* A small folder icon, sized to match the file icons on the rows below. */
.panel-folder-icon {
    width: 13px;
    height: 13px;
    flex: 0 0 13px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.7;
}
.panel-title-text {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The search affordances (magnifier icon by a title; the bar it opens). Not wired yet. */
.panel-search-btn,
.rail-search-btn {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    padding: 0;
    border: none;
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
}
.panel-search-btn:hover,
.rail-search-btn:hover { color: var(--connector); background: var(--bg-hover); }
/* The magnifier shares the VAULT row but is NOT part of the ancestor underscore — only the label
   wears it. (Without this, `.rail-root span` paints the underscore under the icon too.) */
.rail-item.rail-root .rail-search-btn { border-bottom: 0; padding-bottom: 0; }
.panel-search-btn svg,
.rail-search-btn svg {
    width: 14px;
    height: 14px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* A miniature of the message composer (Franck's reference): a rounded PILL on a solid card
   background, a hairline border, a small drop shadow, and a gradient send button. */
.panel-search,
.rail-search {
    display: flex;
    align-items: center;
    gap: 4px;
    /* overflow:hidden (below) makes a flex item's automatic min-height 0, so the rail's column flex
       would shrink this bar when the rail overflows. Pin it: it never shrinks or grows. */
    flex: 0 0 auto;
    /* Horizontal padding is constant; the VERTICAL size, opacity and top/bottom margins animate, so
       the bar SLIDES open and shut and the rows below glide with it rather than snapping. JS reveals
       the container (removes [hidden]) then adds .search--in a frame later to run the transition; on
       close it removes .search--in, then sets [hidden] once the slide finishes. The connector is
       redrawn each frame of the slide so it stays glued to the folder the whole way. */
    padding: 0 4px 0 12px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--bg-card);
    box-shadow: var(--shadow-pill);
    overflow: hidden;
    max-height: 0;
    opacity: 0;
    margin: 0 8px 0 0;
    transition: max-height 200ms ease,
                padding-top 200ms ease,
                padding-bottom 200ms ease,
                margin-top 200ms ease,
                margin-bottom 200ms ease,
                opacity 150ms ease,
                border-color var(--transition), box-shadow var(--transition);
}
.rail-search { margin: 0 6px 0 6px; }
/* Open state — the small breathing gap above and below returns here (FD 3.2). */
.panel-search.search--in,
.rail-search.search--in {
    max-height: 40px;
    opacity: 1;
    padding-top: 3px;
    padding-bottom: 3px;
    margin-top: 6px;
    margin-bottom: 7px;
}
@media (prefers-reduced-motion: reduce) {
    .panel-search, .rail-search { transition: none; }
}
/* Focus lives on the pill (border + soft ring), like the composer — so the input itself needs no
   rectangular focus outline (which the global :focus-visible rule would otherwise draw, breaking
   the pill silhouette). */
.panel-search:focus-within,
.rail-search:focus-within {
    border-color: var(--accent-border);
    box-shadow: 0 0 0 3px var(--accent-soft), var(--shadow-pill);
}
.panel-search-input {
    flex: 1 1 auto;
    min-width: 0;
    height: 22px;
    border: none;
    background: transparent;
    outline: none;
    color: var(--text-primary);
    font: inherit;
    font-size: 12px;
}
.panel-search-input:focus-visible { outline: none; }
.panel-search-input::placeholder { color: var(--text-placeholder); }
.panel-search-send {
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: none;
    border-radius: 50%;
    /* Subdued grey gradient, no glow — a quiet secondary affordance, not the loud accent send. */
    background: var(--search-send-bg);
    color: var(--search-send-ink);
    cursor: pointer;
    transition: filter var(--transition);
}
.panel-search-send:hover { filter: brightness(0.97); }
.panel-search-send svg {
    width: 11px;
    height: 11px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2.2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* With the title occupying the top, the Vault's own tree no longer clears the band itself — it
   follows the title. (The set-aside and focus lists keep the .vault-tree clearance; they have no
   title.) */
#vaultTree { padding-top: 4px; }

/* The leader line tying the selected folder (in the rail) to its title (in the panel). A fixed
   full-viewport overlay so its path can be drawn in raw viewport coordinates; it never intercepts
   clicks. Thin, in the selection's own blue. */
.vault-connector {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 4;
    /* 50% transparent as a whole — opacity on the group composites the three bars as one layer, so the
       overlapping corners do not darken (Franck 2026-08-30). */
    opacity: 0.5;
}
.vault-connector[hidden] { display: none; }
/* The three bars (two horizontal, one vertical). Positioned in JS from live geometry. */
.vault-connector i {
    position: absolute;
    background: var(--connector);
    border-radius: 1px;
}

.tree-node {
    position: relative;   /* anchors the absolutely-placed download button */
    display: flex;
    align-items: center;
    gap: 5px;                  /* icon and text tuck closer (Franck 2026-07-20) */
    width: 100%;
    min-width: 0;
    /* Half the main nav's vertical rhythm (rail-item is 8px): the secondary panel packs more lines
       into a first glance than the primary rail does, without feeling cramped. Left padding is small
       so a subfolder header sits against the edge. */
    padding: 4px 8px 4px 6px;
    border-radius: var(--radius-lg);
    font-size: 12.5px;
    font-weight: 500;
    color: var(--text-primary);
    text-align: left;
    transition: background var(--transition);
}

/* Finder ellipsis: text yields before the band's rounded corner. */
.tree-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tree-node:hover { background: var(--bg-hover); }

.tree-node.active {
    background: var(--bg-active-sub);
    font-weight: 600;
}

/* The selection surfaces are the indicator — no focus rings in the tree. */
.tree-node:focus-visible { outline: none; }

.tree-node .chevron { margin-left: 0; flex: 0 0 11px; width: 11px; height: 11px; }

/* A subfolder header ("Documents") reads left-to-right: its folder icon and name sit against the
   left edge, and the disclosure chevron is a trailing affordance on the right, not a leading indent
   that eats the width the names need. `order` moves it without reordering the DOM. */
.tree-node--sub { font-weight: 600; }
.tree-node--sub .chevron { order: 9; }

/* Files step in only a little under their header — a small indent, a smaller icon — so the name gets
   the width. */
.tree-node--file { padding-left: 16px; color: var(--text-secondary); }
.tree-node--file .tree-icon--file { width: 13px; height: 13px; flex: 0 0 13px; }

/* A "loose" file sits DIRECTLY in the Focus-Area folder (a flat upload, no real sub-folder), so it
   is not nested under anything — it hugs the base like a top-level Finder row rather than indenting
   as if a "Documents" folder were above it. */
.tree-node--file-loose { padding-left: 12px; }   /* indented a small step under the folder title */

/* Tree icons inherit the row's font color — electric blue is reserved
   for the rail's root-level icons (Franck 2026-07-19). */
.tree-icon {
    width: 16px;
    height: 16px;
    flex: 0 0 16px;
    stroke-width: 1.7;
    color: currentColor;
}

/* The origin dot marks where a file came from (uploaded vs Anaïse-generated). It is the row's least
   important fact, so it is a small, quiet marker — not a badge competing with the name. */
.origin-dot {
    width: 5px;
    height: 5px;
    flex: 0 0 5px;
    border-radius: 50%;
}


.origin-dot--generated { background: var(--badge-generated-ink); }

/* --- Role: what a document is TO the organization -----------------------------------------
   Core material shaped the Focus Areas; reference and supporting material did not. Rather than a
   second coloured dot on every row (two badges per line reads as clutter, and the Vault is mostly
   core), the row itself carries the meaning: a document with no voice sits quieter on the page.
   The tooltip says why, in words. */
/* Reference material voted, so it reads like the organization's own work; only material with no
   voice at all is quietened. See --ink-supporting for why this is one step and not two. */
.tree-node--file[data-role="supporting"] .tree-label { color: var(--ink-supporting); }

/* The origin dot must quieten WITH the row. At full strength beside a faded name it became the
   brightest thing on the line — the row's least important fact shouting over its most important. */
.tree-node--file[data-role="reference"] .origin-dot { opacity: 0.7; }

.tree-node--file[data-role="supporting"] .origin-dot { opacity: 0.5; }

.tree-node--file[data-role="reference"] .tree-icon--file { opacity: 0.55; }

.tree-node--file[data-role="supporting"] .tree-icon--file { opacity: 0.4; }

/* Selection must never be quieter than the unselected rows around it. */
.tree-node--file.active[data-role] .tree-label { color: var(--text-primary); }

.tree-node--file.active[data-role] .tree-icon--file,
.tree-node--file.active[data-role] .origin-dot { opacity: 1; }

/* --- Set aside: what Anaïse did NOT read --------------------------------------------------- */
.setaside-heading {
    margin: 0.9rem 0.6rem 0.1rem;
    color: var(--text-primary);
    font-size: 12.5px;
    font-weight: 600;
}

.setaside-note {
    margin: 0 0.6rem 0.5rem;
    color: var(--text-muted);
    font-size: 0.76rem;
    line-height: 1.45;
}

/* The panel is narrow by design (it is the rail, widened). A path is long and a horizontal row
   would squeeze it to nothing — so the row stacks: the file's NAME, then WHY it was left alone,
   which is the whole reason someone opens this surface. The full path lives in the tooltip. */
.setaside-row {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    padding: 0.34rem 0.6rem;
    /* Same corner as a Vault row: these sit in one column and had visibly different radii. */
    border-radius: var(--radius-lg);
}

.setaside-row:hover { background: var(--bg-hover); }

.setaside-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text-primary);
    /* The same 12.5px the Vault tree uses — these rows share a column, so they share a size. */
    font-size: 12.5px;
}

.setaside-why {
    color: var(--text-muted);
    font-size: 0.71rem;
    line-height: 1.35;
    overflow-wrap: anywhere;
}

/* The way back, for when the user changes their mind. An offer, not a nag — so it is quiet until
   the row is hovered. It must NOT be `display: none`: a display-none control cannot take focus, so
   `:focus-within` can never fire and the affordance becomes mouse-only. Collapsed to nothing here,
   restored on hover OR on its own keyboard focus, so it stays in the tab order throughout. */
.setaside-release {
    align-self: flex-start;
    margin-top: 0.18rem;
    padding: 0.14rem 0.4rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text-secondary);
    font: inherit;
    font-size: 0.7rem;
    line-height: 1.3;
    /* Quiet, but it OCCUPIES its space. Collapsing the row and expanding it on hover moved every
       row below by 37px — so approaching row 2's control slid row 2 out from under the cursor. And
       it must stay in the layout: a control removed from flow cannot take focus, which made this
       affordance mouse-only on the one surface built to give people power over their private
       files. Transparent, never absent. */
    opacity: 0;
    white-space: nowrap;
    cursor: pointer;
}

.setaside-row:hover .setaside-release,
.setaside-release:focus-visible,
.setaside-release:focus { opacity: 1; }

/* The app suppresses focus rings on its own rows and paints its own; this control must not be the
   one place wearing a raw browser outline. */
.setaside-release:focus-visible {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 2px var(--bg-active);
}

.setaside-release:hover {
    border-color: var(--accent);
    color: var(--accent);
}

.setaside-release[disabled] { cursor: default; opacity: 0.5; }

.setaside-release.failed { border-color: var(--health-down); color: var(--health-down); }

.setaside-row--released .setaside-name { color: var(--text-primary); }

.setaside-said { color: var(--badge-generated-ink); }

.setaside-name:focus-visible { outline: none; }

/* Download the Vault's authoritative original — an unobtrusive icon that lifts on hover. */
.vault-download {
    /* Absolute so it does NOT reserve space in the row — the name runs to the edge and the download
       fades in over it on hover (Finder-style), instead of every row truncating 22px early for an
       invisible button (Franck 2026-08-30). */
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: none;
    border-radius: 6px;
    background: var(--bg-hover);   /* an opaque chip so it covers, not overlaps, the name's tail */
    color: var(--text-muted);
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.12s ease, background 0.12s ease, color 0.12s ease;
}
.tree-node--file:hover .vault-download,
.vault-download:focus-visible { opacity: 1; }
.vault-download:hover { background: var(--nav-hover); color: var(--accent); }
.vault-download svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.vault-download.busy { opacity: 1; color: var(--accent); animation: vault-download-pulse 0.9s ease-in-out infinite; }
.vault-download.failed { opacity: 1; color: var(--health-down); }
@keyframes vault-download-pulse { 50% { opacity: 0.4; } }

.tree-empty {
    font-size: 12px;
    color: var(--text-muted);
    padding: 6px 10px 6px 30px;
    /* A sentence must wrap inside a 165px panel. File and folder NAMES still scroll horizontally
       (that is the Finder model, and a name must never be silently reflowed) — but prose is prose. */
    white-space: normal;
    line-height: 1.4;
}

/* ==================== Engagement Window ==================== */

.engagement {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

/* --- Status strips (Franck 2026-07-19) ------------------------------------
   Top: location/navigation clues — height matches the rail's logo block
   (16px pad + 32px logo = 48px), closed by the deep-electric-blue line
   across the engagement's full width.
   Bottom: continuation of the rail's powered-bar grey, same 42px height,
   carrying the conversation/task title (document state later). */

.status-top {
    flex: 0 0 auto;
    height: 56px;    /* PO Manager --topbar-height */
    display: flex;
    align-items: center;
    gap: 10px;       /* the little gap between org and path */
    padding: 0 18px;
}

/* The top rule: PO-Manager grey hairline crossing the ENTIRE screen
   (nav bar included) at the foot of the top-bar band. */
.top-rule {
    position: fixed;
    top: 56px;
    left: 0;
    width: 100%;
    height: 1px;
    background: var(--border);
    z-index: 10;
    pointer-events: none;
}

/* The organization: larger, in the rail's deep electric blue. One org
   may hold several sovereign workspaces (own Vault + Focus Areas). */
#statusOrg {
    font-size: 15px;
    font-weight: 700;
    letter-spacing: -0.1px;
    color: var(--gradient-mean);   /* the gradient's average hue, flat
                                      (Franck 2026-07-20) */
    white-space: nowrap;
}

/* The workspace path: "/ International Development" (+ Vault trail). */
#statusPath {
    font-size: 12.5px;
    font-weight: 500;
    color: var(--status-ink);  /* white in dark (PO Manager); grey in light */
    letter-spacing: 0.01em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.status-bottom {
    flex: 0 0 auto;
    height: 42px;
    display: flex;
    align-items: center;
    padding: 0 18px;
    background: var(--powered-bg);
    border-top: 1px solid var(--border);   /* the bottom strip's rule —
                                              mirrors the top rule */
}

.status-bottom span {
    font-size: 13px;
    font-weight: 500;
    color: var(--status-ink);  /* white in dark (PO Manager); grey in light */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.engagement-frame {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
}

/* --- Idle vs conversational geometry -------------------------------------
   Idle (W-1): the invitation (in the Engagement Frame) and the Prompt
   Frame center together as one unit — auto top margin above, auto bottom
   margin below. Conversational (W-2/W-3): the frame scrolls, the Prompt
   Frame docks to the bottom. One prompt node, two geometries. */

body[data-state="idle"] .engagement-frame {
    flex: 0 0 auto;
    margin-top: auto;
    overflow: visible;
}

body[data-state="idle"] .prompt-frame {
    margin-bottom: auto;
    padding-bottom: 6vh;        /* optical center: slightly above true center */
}

/* Onboarding and assembly (Franck 2026-08-27): the prompt DOCKS TO THE BOTTOM.
   It sat in the middle of the screen with the account of the work below it, so
   the largest, brightest element on the page was an input inviting typing that
   is not wanted yet, and the thing the person actually came to watch was pushed
   underneath it. Order by usefulness: the title, then the work, then the
   instrument — which is also where every conversational tool puts it.

   Done with `order` rather than by moving the node: there is ONE prompt in this
   document and it keeps its identity, its focus and its listeners across every
   state. The markup order still reads correctly for anything that ignores flex. */
body[data-state="assembling"] .assembly { order: 1; }

body[data-state="assembling"] .prompt-frame {
    order: 2;
    flex: 0 0 auto;
    margin-top: 0;              /* no auto-centring: it is docked, not floated */
    padding-bottom: 14px;       /* a small gap to the bottom bar, not a chasm */
}

/* The skip line lives under the prompt and must not be crowded against the edge. */
body[data-state="onboard"] .onb-skip:not([hidden]),
body[data-state="assembling"] .onb-skip:not([hidden]) {
    margin-top: 8px;
    margin-bottom: 2px;
}

body[data-state="assembling"] .engagement-frame { order: 0; }

/* Ordering ONE child of a flex row reorders it against everything that has not
   been ordered: the bottom status bar defaults to 0 and duly sorted itself in
   front of the assembly, landing under the title with the whole account below
   it. Every child of this row is placed explicitly now. */
body[data-state="assembling"] .status-top { order: -1; }
body[data-state="assembling"] .status-bottom { order: 3; }

/* Focus (W-4c): two anchored zones — Anaïse's speech in the upper
   third, the prompt alone at the optical center (the position it holds
   in idle, so the prompt never jumps when an area is chosen). A grid
   air track between prompt and bottom bar carries the breathing room. */
body[data-state="focus"] .engagement {
    display: grid;
    grid-template-rows: auto minmax(0, 1fr) auto minmax(0, 1fr) auto;
}

body[data-state="focus"] .status-bottom { grid-row: 5; }

body[data-state="focus"] .engagement-frame { overflow: visible; }

body:not([data-state="idle"]) .idle-stage { display: none; }

/* --- What arrived after the workspace settled -------------------------------
   Shown only when late material holds a function the workspace has no area for — which is most of
   the time NOT the case, and then nothing appears at all. Quiet by construction: it is news, not an
   alarm, and the person did not open the app to be told about filing. */
.late-notice {
    width: min(560px, calc(100% - 64px));
    margin: 26px auto 0;
    padding: 14px 18px;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: var(--bg-card);
    text-align: left;
}

.late-notice-lead {
    margin: 0;
    color: var(--text-primary);
    font-size: 13.5px;
    line-height: 1.5;
}

.late-notice-group { margin-top: 10px; }

.late-notice-what {
    margin: 0;
    color: var(--text-primary);
    font-size: 13px;
    font-weight: 600;
    line-height: 1.45;
}

/* The titles, so a person can judge the suggestion instead of taking its word. */
.late-notice-examples {
    margin: 2px 0 0;
    color: var(--text-muted);
    font-size: 12px;
    line-height: 1.45;
    overflow-wrap: anywhere;
}

.late-notice-actions {
    display: flex;
    gap: 14px;
    margin-top: 14px;
}

/* --- The assembly screen (Franck 2026-08-24) ------------------------------
   "Slide the input box and the main title from the middle to a third up, and leave the middle
   third and below for unfolding info about what is going on."

   So the heading and the prompt rise TOGETHER — the prompt keeps the same relationship to the
   title it holds at rest, it just moves up as a unit instead of flying to the foot of a
   transcript. Everything below the prompt is the live account. The prompt sitting ABOVE the
   information is deliberate: during assembly the user is watching, and the prompt is there to
   interrupt, not to take turns. */

body[data-state="assembling"] .engagement-frame {
    flex: 0 0 auto;
    margin-top: 11vh;          /* heading lands about a third down, under the status strip */
    overflow: visible;
}

body:not([data-state="assembling"]) .assembly-head { display: none; }

.assembly-head {
    width: min(680px, calc(100% - 64px));
    margin: 0 auto 26px;
}

.assembly-head h1 {
    font-size: 26px;
    font-weight: 600;
    letter-spacing: -0.3px;
    color: var(--text-primary);
    text-align: center;
}

.assembly {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

.assembly-inner {
    width: min(680px, calc(100% - 64px));
    margin: 0 auto;
    /* A clear gap under the prompt: the instrument and the account are different things and should
       not read as one block. */
    padding: 18px 0 28px;
}

/* The count line: one sentence, rewritten as the truth changes. */
.assembly-counts {
    margin: 0;
    color: var(--text-primary);
    font-size: 14px;
    line-height: 1.55;
    text-align: center;
}

.assembly-counts:empty { display: none; }

.assembly-bar {
    height: 4px;
    margin: 14px 0 8px;
    border-radius: 2px;
    background: var(--bg-hover);
    overflow: hidden;
}

.assembly-bar-fill {
    height: 100%;
    width: 0;
    border-radius: 2px;
    background: var(--accent);
    /* Eased, because a bar that jumps reads as a glitch rather than progress. */
    transition: width 600ms ease-out;
}

.assembly-note {
    margin: 0;
    color: var(--text-secondary);
    font-size: 13px;
    line-height: 1.5;
    text-align: center;
}

.assembly-note:empty { display: none; }

.assembly-lanes {
    margin: 12px 0 0;
    padding: 0;
    list-style: none;
    text-align: center;
}

.assembly-lanes li {
    color: var(--text-muted);
    font-size: 12.5px;
    line-height: 1.6;
}

/* The organization guess, narrowing in place. */
.assembly-guess {
    /* No frame. It used to be a bordered card because it held the emerging Focus
       Areas and they needed somewhere to sit; those now go to the rail, and what
       is left is two sentences about what kind of organization this looks like.
       A border around a sentence is chrome asking to be noticed. */
    margin-top: 18px;
    text-align: center;
}

.assembly-guess-head {
    margin: 0;
    color: var(--text-primary);
    font-size: 14px;
    font-weight: 600;
}

.assembly-guess-why {
    margin: 4px 0 0;
    color: var(--text-secondary);
    font-size: 12.5px;
    line-height: 1.5;
}

.assembly-guess-areas {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 10px;
}

.assembly-guess-areas span {
    padding: 3px 9px;
    border-radius: 999px;
    background: var(--bg-hover);
    color: var(--text-secondary);
    font-size: 12px;
}

/* ---- The three tracks ------------------------------------------------------
   Organization type, reading, Focus Areas. Three questions with three answers
   that resolve on different clocks, so each gets its own heading and its own
   state rather than taking turns on one shared line.

   The hierarchy is carried by SIZE and WEIGHT, not by boxes: a small
   letter-spaced heading, the answer beneath it at reading size, and the
   supporting detail smaller and quieter. Nothing is framed. */
.tracks {
    width: min(560px, 100%);
    margin: 26px auto 0;
    display: flex;
    flex-direction: column;
    gap: 26px;
    text-align: left;
}

.track-title {
    margin: 0 0 7px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-tertiary, var(--text-secondary));
}

/* A track that has not started says so by receding, not by disappearing — the
   shape of what is coming is part of what makes the wait legible. */
.track[data-track-state="pending"] { opacity: 0.4; }

.track-row {
    display: flex;
    align-items: baseline;
    /* a small tab between the mark and its line — 10px read as touching */
    gap: 14px;
}

.track-line {
    margin: 0;
    font-size: 15px;
    line-height: 1.45;
    color: var(--text-primary);
}

.track[data-track-state="pending"] .track-line,
.track[data-track-state="working"] .track-line { color: var(--text-secondary); }

.track[data-track-state="done"] .track-line {
    color: var(--text-primary);
    font-weight: 600;
}

/* The reading breakdown: a small three-column table — what kind, how far, and
   what is being done to it. Aligned so the counts form a column the eye can run
   down, which a comma-separated sentence never allows. */
.read-kinds {
    display: grid;
    grid-template-columns: auto auto 1fr;
    column-gap: 14px;
    row-gap: 2px;
    margin-top: 2px;
}

.read-kinds-count {
    text-align: right;
    font-variant-numeric: tabular-nums;   /* digits line up */
    color: var(--text-secondary);
}

.read-kinds-doing { color: var(--text-tertiary, var(--text-secondary)); }

/* What is NOT being opened, on its own line and set apart — it is a different
   kind of statement from progress. */
.read-notopened {
    margin-top: 7px;
    opacity: 0.85;
}

/* The confirmation screen — its own state, replacing the progress tracks once
   the workspace is proposed. Calm, centred, one clear action. Reading is over;
   this is a different question, so it does not wear the progress screen's face. */
#assemblyStage[data-phase="confirm"] .tracks,
#assemblyStage[data-phase="confirm"] #assemblyGuess { display: none; }
#assemblyStage[data-phase="working"] .confirm { display: none; }

.confirm {
    width: min(480px, 100%);
    margin: 0 auto;
    text-align: center;
}

.confirm-title {
    font-size: 26px;
    font-weight: 650;
    color: var(--text-primary);
    margin: 0 0 8px;
}

.confirm-sub {
    font-size: 14px;
    line-height: 1.5;
    color: var(--text-secondary);
    margin: 0 0 32px;
}

.confirm-sect {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-tertiary, var(--text-secondary));
    margin: 0 0 8px;
    text-align: left;
}

.confirm-org {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    margin: 0 0 30px;
    text-align: left;
}

.confirm-org-line { margin: 0; font-size: 16px; color: var(--text-primary); }
.confirm-org-line b { font-weight: 650; }

/* The invitation to reword the organization type — a settled, confident statement followed by an
   open hand, not a terse link. Quiet, and it carries the correction affordance (typing adjusts). */
.confirm-org-invite {
    display: block;
    margin-top: 10px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-tertiary, var(--text-secondary));
    max-width: 54ch;
}

/* A quiet inline affordance — correct the organization, add an area — that opens
   the prompt rather than a form (pre-spec: no configuration; the prompt is the
   sole instruction surface). */
.confirm-inline {
    flex: 0 0 auto;
    background: none;
    border: 0;
    padding: 0;
    font-size: 12.5px;
    color: var(--gradient-mean);
    cursor: pointer;
}
.confirm-inline:hover { text-decoration: underline; }

/* Focus Areas: a single centred column. Settled solid, contested hollow — the
   distinction the rail draws, said here where it is confirmed. */
.confirm-areas {
    display: flex;
    flex-direction: column;
    gap: 3px;
    margin: 0 0 6px;
}

.confirm-area {
    display: flex;
    align-items: center;
    gap: 11px;
    padding: 5px 2px;
    text-align: left;
}

.confirm-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex: 0 0 auto;
}
.confirm-dot--s { background: var(--gradient-mean); }
.confirm-dot--c { border: 1.5px solid var(--text-tertiary, var(--text-secondary)); }

.confirm-name { flex: 1 1 auto; font-size: 15px; color: var(--text-primary); }
.confirm-area--contested .confirm-name { color: var(--text-secondary); }
.confirm-area--removed .confirm-name {
    text-decoration: line-through;
    color: var(--text-tertiary, var(--text-secondary));
}

.confirm-remove {
    flex: 0 0 auto;
    background: none;
    border: 0;
    padding: 2px 6px;
    font-size: 12px;
    color: var(--text-secondary);
    cursor: pointer;
    border-radius: 5px;
}
.confirm-remove:hover { color: var(--text-primary); background: var(--bg-hover); }

.confirm-add {
    align-self: flex-start;
    margin-top: 4px;
    background: none;
    border: 0;
    padding: 4px 2px;
    font-size: 13.5px;
    color: var(--gradient-mean);
    cursor: pointer;
}
.confirm-add:hover { text-decoration: underline; }

.confirm-unsure {
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--text-tertiary, var(--text-secondary));
    text-align: left;
    margin: 8px 0 30px;
}
/* The folder CHECK note (Phase 2): a quiet line, or a clickable orphan suggestion (.confirm-inline). */
.confirm-folder {
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--text-tertiary, var(--text-secondary));
    text-align: left;
    margin: 4px 0 30px;
}
.confirm-unsure:empty { display: none; }
.confirm-folder:empty { display: none; margin: 0; }
/* The deep-extraction disclosure: a quiet, positive status line — enrichment underway, not doubt.
   Same treatment as the folder note so it reads as one more calm line, not an alarm. */
.confirm-enrich {
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--text-tertiary, var(--text-secondary));
    text-align: left;
    margin: 4px 0 30px;
}
.confirm-enrich:empty { display: none; margin: 0; }

.confirm-cta {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    /* Clearance from the docked composer below: the primary button sat almost flush against it when
       the confirmation content was tall enough to fill the scroll region. */
    margin-bottom: 22px;
}

.confirm-primary {
    background: var(--gradient-mean);
    color: var(--accent-contrast);
    border: 0;
    border-radius: 999px;
    padding: 12px 30px;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
}
.confirm-primary:disabled { opacity: 0.45; cursor: default; }

.confirm-edit {
    background: none;
    border: 0;
    font-size: 13px;
    color: var(--text-secondary);
    cursor: pointer;
}
.confirm-edit:hover { color: var(--text-primary); }
.confirm-edit[hidden] { display: none; }

.track-note b {
    color: var(--text-secondary);
    font-weight: 650;
}

.track-note {
    margin: 5px 0 0 19px;      /* aligned under the line, clear of the mark */
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--text-tertiary, var(--text-secondary));
}

.track-note:empty { display: none; }

/* The mark: a dot that pings while the track is working, and a tick once it has
   an answer. Same footprint in both states so nothing shifts when it settles. */
.track-mark {
    position: relative;
    flex: 0 0 auto;
    width: 9px;
    height: 9px;
    align-self: center;
}

.track-mark::after {
    content: "";
    position: absolute;
    inset: 2.5px;
    border-radius: 50%;
    background: var(--text-secondary);
    opacity: 0.85;
}

.track-mark i {
    position: absolute;
    inset: 0;
    border-radius: 50%;
    border: 1px solid var(--text-secondary);
    opacity: 0;
}

.track[data-track-state="working"] .track-mark i {
    animation: assembly-ping 2.8s cubic-bezier(0, 0.3, 0.3, 1) infinite;
}

.track[data-track-state="working"] .track-mark i:nth-child(2) { animation-delay: 0.95s; }
.track[data-track-state="working"] .track-mark i:nth-child(3) { animation-delay: 1.9s; }

@keyframes assembly-ping {
    0%   { transform: scale(1);   opacity: 0.45; }
    70%  { opacity: 0.05; }
    100% { transform: scale(3.4); opacity: 0; }
}

/* Pending: no work is happening on this track yet (Focus Areas, before the reading
   is done), so it shows NO mark — a lone static dot reads as "busy" when nothing is.
   The dot appears the moment the track starts working. Same 9px footprint, so nothing
   shifts when it does. */
.track[data-track-state="pending"] .track-mark::after { display: none; }

/* Settled: the dot becomes a tick. Drawn rather than typed so it sits on the
   same baseline at any font size. */
.track[data-track-state="done"] .track-mark::after {
    /* A tick, not a chevron. A SQUARE rotated -45deg gives a V; a check needs a
       tall, narrow box with two adjacent borders, rotated the other way. */
    inset: auto;
    top: -1px;
    left: 2px;
    width: 4px;
    height: 8px;
    border-radius: 0;
    background: none;
    border-right: 1.7px solid var(--gradient-mean, var(--text-primary));
    border-bottom: 1.7px solid var(--gradient-mean, var(--text-primary));
    transform: rotate(45deg);
    opacity: 1;
}

.track[data-track-state="done"] .track-mark i { display: none; }

/* The line shimmers only while its own track is working — the direction reading
   runs. DECREASING background-position is what sends the band left to right:
   the gradient is wider than the text, so the offset range is negative and a
   falling percentage slides the image right. Measured: -75% puts the band at
   x=66, -120% at x=790. Do not "correct" the sign. The tiling is load-bearing
   too — with background-repeat:no-repeat the glyphs are unpainted at the ends. */
@supports ((background-clip: text) or (-webkit-background-clip: text)) {
    .track[data-track-state="working"] .track-line {
        background-image: linear-gradient(
            100deg,
            var(--text-secondary) 42%,
            var(--text-primary) 50%,
            var(--text-secondary) 58%);
        background-size: 300% 100%;
        -webkit-background-clip: text;
        background-clip: text;
        color: transparent;
        animation: assembly-shimmer 2.4s linear infinite;
    }
}

@keyframes assembly-shimmer {
    from { background-position: 150% 0; }
    to   { background-position: -150% 0; }
}

@media (prefers-reduced-motion: reduce) {
    .track-mark i { animation: none; }
    .track[data-track-state="working"] .track-line {
        animation: none;
        background-image: none;
        color: var(--text-secondary);
    }
}

/* ── The organization HERO ──────────────────────────────────────────────────
   Org type is what the person came to find out, so it leads — as a headline. Its
   mark is EXACTLY the sibling mark: same size, same pinging rings (inherited from
   .track-mark). It must not be a size of its own — three marks of three sizes reads
   as a mistake, not a hierarchy. The hero leads by its TYPE, not a bigger dot. The
   only difference is the settled state: the hero locks to a solid brand-tone dot
   where the siblings tick. Alive from the first second (the line states PURPOSE
   before any label is earned). */
.track--hero .track-line {
    font-size: 20px;
    line-height: 1.35;
    font-weight: 560;
    color: var(--text-primary);
}
.track--hero[data-track-state="pending"] { opacity: 0.6; }
/* settled: a solid dot in the brand tone — "locked in" (overrides the sibling tick,
   which is drawn by re-shaping ::after; here it stays the round dot, just brand-blue) */
.track--hero[data-track-state="done"] .track-mark::after {
    /* inset (all four sides) defines the dot; width/height must go back to auto so the
       base tick's explicit 4x8 box does not over-constrain it into a sliver. Do NOT add
       top/left:auto — that cancels two insets and collapses the dot to zero. */
    inset: 2.5px;
    width: auto; height: auto;
    border: none;
    border-radius: 50%;
    background: var(--gradient-mean, var(--accent));
    transform: none;
    opacity: 1;
}
.track--hero[data-track-state="done"] .track-mark i { display: none; }

/* The areas arrive rather than appear. They ARE decided together — the screen says so in words —
   but eight names landing in a single frame read as a data dump, and the same eight over half a
   second read as something being assembled. Presentation of a true event, not a staged one.
   `backwards` holds each pill invisible through its own delay, so none flashes before its turn. */
/* Rail items arrive rather than appear — the same treatment the pill row used to
   carry, now on the place these areas actually live. */
.rail-item--sub.area-arriving {
    animation: area-arriving 0.42s ease backwards;
}

@keyframes area-arriving {
    from { opacity: 0; transform: translateY(4px) scale(0.96); }
    to   { opacity: 1; transform: none; }
}

/* Someone who has asked not to be moved is not moved. */
@media (prefers-reduced-motion: reduce) {
    .rail-item--sub.area-arriving { animation: none; }
}

/* What she is not opening. Quiet, but present at the moment it matters. */
.assembly-aside {
    /* Unframed, like the tracks it sits among. It was the last bordered thing on
       this screen and read as a leftover from a different design. */
    margin: 10px 0 0 19px;
    max-width: 560px;
    text-align: left;
    font-size: 12.5px;
    color: var(--text-tertiary, var(--text-secondary));
}

.assembly-aside-head {
    display: flex;
    align-items: baseline;
    gap: 8px;
}

.assembly-aside-title {
    flex: 1 1 auto;
    color: var(--text-secondary);
    font-size: 13px;
}

.assembly-aside-toggle {
    flex: 0 0 auto;
    padding: 0;
    border: none;
    background: none;
    color: var(--accent);
    font: inherit;
    font-size: 12.5px;
    cursor: pointer;
}

.assembly-aside-toggle:hover { text-decoration: underline; }

.assembly-aside-list {
    margin: 10px 0 0;
    padding: 0;
    list-style: none;
}

.assembly-aside-list li {
    display: flex;
    gap: 10px;
    padding: 3px 0;
    font-size: 12.5px;
}

.assembly-aside-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--text-primary);
}

.assembly-aside-why {
    flex: 0 0 auto;
    color: var(--text-muted);
}

.assembly-talk { margin-top: 20px; }

.assembly-talk:empty { display: none; }

/* --- W-6 Document Mode (spec D.3/D.4; memo §16, §20, §22) ----------------
   FRAME INTEGRITY (memo §22, Franck 2026-07-20): the left column IS the
   Engagement Window — conversation + toggle + PROMPT stacked, all sized
   to the column. The Prompt Frame is the Engagement column's instrument;
   it NEVER spans or rides over the Document Frame or its desk while the
   conversation is on screen. The document owns the right, full height.
   PARITY split (Franck 2026-07-20): equal column shares; the document's
   readable minimum (650px) and the conversation's floor (320px) are the
   only constraints. Independent scrolls (D.4.1). */

/* Document Mode keeps BOTH navs — you never lose your place while reading — and hands the preview more
   room by yielding the SECONDARY panel's width 20% (Franck 2026-08-30). The rail stays full: at −20%
   its own section headers ("FOCUS AREAS", "SETTINGS") truncate, which reads as broken, and the ~35px
   it would save is not worth that. The secondary is only navigation context here, so tighter is fine. */
body[data-state="doc"] .secondary {
    width: calc(var(--secondary-width) * var(--doc-nav-scale));
    flex-basis: calc(var(--secondary-width) * var(--doc-nav-scale));
}

body[data-state="doc"] .engagement {
    display: grid;
    grid-template-columns: minmax(320px, 1fr) minmax(650px, 1fr);
    grid-template-rows: auto minmax(0, 1fr) auto auto auto;
    /* Toggle sits UNDER the prompt (Franck 2026-07-20 — spec override,
       see docs/SPEC-OVERRIDES.md: D.4.2 says above; the prompt keeps
       only dialogue above it). */
    grid-template-areas:
        "top    top"
        "conv   doc"
        "prompt doc"
        "toggle doc"
        "bottom bottom";
}

/* The frame dissolves into the grid: its children become the columns. */
body[data-state="doc"] .engagement-frame { display: contents; }

body[data-state="doc"] .status-top { grid-area: top; }
body[data-state="doc"] .status-bottom { grid-area: bottom; }
body[data-state="doc"] .view-toggle { grid-area: toggle; }
body[data-state="doc"] .prompt-frame {
    grid-area: prompt;
    padding-bottom: 20px;      /* toggle sits centered below (20 / 20 —
                                  doubled, Franck 2026-07-20: breathe) */
}

body[data-state="doc"] .conversation {
    grid-area: conv;
    min-width: 0;
    overflow-y: auto;
    margin: 0;
    padding: 26px 24px 14px;
    width: auto;
}

.doc-frame {
    position: relative;
    grid-area: doc;              /* the right column, full height (§22) */
    display: flex;
    min-height: 0;
    min-width: 0;
}

.doc-desk {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;            /* the desk scrolls, not the page */
    background: var(--bg-desk);
    padding: 28px 32px 40px;
}

/* The paper: a white sheet in BOTH themes (memo §20) — US Letter
   proportions like a real .docx/PDF (Franck 2026-07-20, PO Manager
   treatment): 8.5 × 11 aspect, growing taller only when the content
   runs past the page. Rounded corners, soft shadow — the deliverable,
   not chrome. */
.doc-paper {
    /* FIXED canonical page — 8.5in at 96dpi. The width never changes
       with the viewport (Franck 2026-07-20: a real .docx/PDF preview);
       when the desk is narrower, app.js fits the whole sheet down
       (PDF-viewer fit-width zoom), never reflowing the page. */
    width: 816px;
    aspect-ratio: 8.5 / 11;      /* letter sheet; height follows width */
    margin: 0 auto;
    background: var(--bg-paper);
    color: var(--paper-ink);
    border-radius: 8px;
    box-shadow: var(--shadow-paper);
    padding: 56px 64px 64px;
}

.doc-paper h1 {
    font-size: 22px;
    font-weight: 700;
    letter-spacing: -0.2px;
    margin-bottom: 22px;
}

.doc-paper h2 {
    font-size: 14px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin: 22px 0 8px;
}

.doc-paper p {
    font-size: 13.5px;
    line-height: 1.7;
    margin-bottom: 10px;
}

/* Flyer dressing (landing stage): a DESIGNED poster, not a report —
   the Haiti–Brazil flyer lineage on the letter sheet: near-black
   canvas, gold letterspaced small caps, serif roman+italic headline
   over a full-bleed photo hero, gold-ruled sections, dark bordered
   cards, a photo strip, big serif pricing numerals. Demo-only class;
   the flyer is document CONTENT — the sheet itself stays canon. */
.doc-paper--flyer {
    padding: 0;
    background: var(--flyer-canvas);
    color: var(--flyer-ink);
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.flyer-masthead {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 20px 36px 16px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.28em;
}

.flyer-masthead-left { color: var(--flyer-white); }
.flyer-masthead-right { color: var(--flyer-gold); }

.flyer-hero { position: relative; }

.flyer-hero-img {
    display: block;
    width: 100%;
    height: 278px;
    object-fit: cover;
}

.flyer-hero::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(
        180deg,
        rgba(14, 13, 11, 0.32) 0%,
        rgba(14, 13, 11, 0.08) 38%,
        rgba(14, 13, 11, 0.82) 100%
    );
}

.flyer-hero-content {
    position: absolute;
    inset: 0;
    z-index: 1;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    padding: 18px 36px 20px;
}

.flyer-chip-row { display: flex; gap: 8px; }

.flyer-chip {
    padding: 6px 12px;
    border: 1px solid rgba(255, 255, 255, 0.4);
    border-radius: 3px;
    background: var(--flyer-scrim);
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.24em;
    color: var(--flyer-white);
}

.flyer-title {
    margin: auto 0 10px;
    font-family: "Playfair Display", Georgia, serif;
    font-size: 52px;
    font-weight: 600;
    line-height: 1.04;
    color: var(--flyer-cream);
    text-shadow: 0 2px 22px rgba(14, 13, 11, 0.75);
}

.flyer-title-italic {
    font-style: italic;
    font-weight: 500;
    color: var(--flyer-gold-bright);
}

.flyer-dateline {
    padding: 7px 12px;
    border-left: 2px solid var(--flyer-gold);
    background: var(--flyer-scrim);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.28em;
    color: var(--flyer-white);
}

/* The petal thread: rose → gold → green, the Bloomfield palette
   (the Haiti–Brazil flag-line analogue). */
.flyer-petal-rule {
    height: 3px;
    background: linear-gradient(
        90deg,
        var(--flyer-rose) 0%,
        var(--flyer-gold) 50%,
        var(--flyer-green) 100%
    );
}

.flyer-body { padding: 22px 36px 0; }

.flyer-intro-serif {
    font-family: "Playfair Display", Georgia, serif;
    font-style: italic;
    font-size: 14.5px;
    line-height: 1.6;
    color: var(--flyer-ink);
    margin: 0 0 6px;
}

.flyer-intro-bold {
    font-size: 12.5px;
    font-weight: 700;
    line-height: 1.6;
    color: var(--flyer-white);
    margin: 0;
}

.flyer-rule-label {
    display: flex;
    align-items: center;
    gap: 14px;
    margin: 20px 0 12px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.3em;
    color: var(--flyer-gold);
}

.flyer-rule-label::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--flyer-line);
}

.flyer-cards {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 10px;
}

.flyer-card {
    border: 1px solid var(--flyer-card-border);
    border-radius: 2px;
    background: var(--flyer-card);
    padding: 14px 12px 12px;
}

.flyer-card-icon {
    width: 20px;
    height: 20px;
    color: var(--flyer-gold);
}

.flyer-card h4 {
    margin: 9px 0 6px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.16em;
    color: var(--flyer-white);
}

.flyer-card p {
    margin: 0;
    font-size: 10.5px;
    line-height: 1.55;
    color: var(--flyer-ink-dim);
}

.flyer-strip {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
}

.flyer-strip img {
    display: block;
    width: 100%;
    height: 142px;
    object-fit: cover;
}

.flyer-credit {
    margin: 8px 0 0;
    font-family: "Playfair Display", Georgia, serif;
    font-style: italic;
    font-size: 10px;
    color: var(--flyer-ink-dim);
}

.flyer-visit {
    display: grid;
    grid-template-columns: 1fr 1fr 1.6fr;
    gap: 10px;
    margin-bottom: 20px;
}

.flyer-price {
    border: 1px solid var(--flyer-card-border);
    border-radius: 2px;
    background: var(--flyer-card);
    padding: 13px 14px 12px;
}

.flyer-price-label,
.flyer-preview-panel .flyer-price-label {
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.22em;
    color: var(--flyer-gold);
}

.flyer-price-amount {
    margin: 5px 0 4px;
    font-family: "Playfair Display", Georgia, serif;
    font-size: 32px;
    font-weight: 600;
    line-height: 1;
    color: var(--flyer-cream);
}

.flyer-price-note {
    font-size: 8.5px;
    font-weight: 600;
    letter-spacing: 0.16em;
    color: var(--flyer-ink-dim);
}

.flyer-preview-panel {
    border: 1px solid var(--flyer-gold);
    border-radius: 2px;
    padding: 13px 15px 12px;
}

.flyer-preview-panel p {
    margin: 7px 0 0;
    font-family: "Playfair Display", Georgia, serif;
    font-style: italic;
    font-size: 11.5px;
    line-height: 1.55;
    color: var(--flyer-ink);
}

.flyer-foot {
    margin-top: auto;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
    border-top: 1px solid var(--flyer-line);
    padding: 14px 36px 18px;
}

.flyer-foot-label {
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.22em;
    color: var(--flyer-gold);
}

.flyer-foot-value {
    margin-top: 3px;
    font-size: 12px;
    font-weight: 600;
    color: var(--flyer-white);
}

.flyer-foot-center { text-align: center; }

.flyer-foot-top {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.22em;
    color: var(--flyer-white);
}

.flyer-foot-sub {
    margin-top: 2px;
    font-family: "Playfair Display", Georgia, serif;
    font-style: italic;
    font-size: 13px;
    color: var(--flyer-gold-bright);
}

.flyer-foot-right {
    max-width: 200px;
    text-align: right;
    font-size: 8.5px;
    font-weight: 600;
    letter-spacing: 0.16em;
    line-height: 1.6;
    color: var(--flyer-ink-dim);
}

/* D.10 updates landing on the dark flyer stay legible. */
.doc-paper--flyer .doc-update { padding: 0 36px 16px; }
.doc-paper--flyer .doc-update h2 { color: var(--flyer-gold); }
.doc-paper--flyer .doc-update p { color: var(--flyer-ink); }

/* D.10: the newest applied update announces itself briefly. */
.doc-paper .doc-update { animation: doc-update-in 0.6s ease; }

@keyframes doc-update-in {
    from { background: var(--accent-soft); }
    to { background: transparent; }
}

.doc-close {
    position: absolute;
    top: 10px;
    right: 12px;
    z-index: 1;
    color: var(--nav-ink);
}

/* D.4.2 toggle, positioned UNDER the prompt (override, 2026-07-20).
   SEGMENTED CAPSULE (Franck 2026-07-20): one control, two halves —
   shared border, joined halves, the chosen half filled. Reads as a
   choice, not two random pills. */
.view-toggle {
    display: flex;
    justify-content: center;
    gap: 0;
    padding: 0 0 20px;         /* equal air above (prompt pad) and below */
}

.view-toggle-btn {
    padding: 4px 14px;
    border: 1px solid var(--border);
    background: var(--bg-card);
    font-size: 11.5px;
    font-weight: 600;
    color: var(--text-secondary);
    transition: background var(--transition), color var(--transition);
}

.view-toggle-btn:first-of-type { border-radius: 999px 0 0 999px; }

.view-toggle-btn:last-of-type {
    border-radius: 0 999px 999px 0;
    margin-left: -1px;             /* halves share one border */
}

.view-toggle-btn:hover { background: var(--bg-hover); }

.view-toggle-btn.active {
    background: var(--bg-active-sub);
    color: var(--nav-ink-active);
}

/* Document-only view (D.4.2): the conversation pane yields entirely.
   With no conversation on screen the prompt spans under the document —
   the document is then the sole engagement surface (§22). */
body[data-state="doc"].doc-only .conversation { display: none; }

body[data-state="doc"].doc-only .engagement {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
        "top"
        "doc"
        "prompt"
        "toggle"
        "bottom";
}

/* The bottom bar carries the AWF state at its right edge (doc mode). */
#statusDocState {
    margin-left: auto;
    font-style: italic;
}

/* --- W-8 first slice: Render Document (PO Manager top-right action) --- */

/* Doc actions, flat in the gradient's average hue — the same color the
   org name wears (Franck 2026-07-20). Save to Vault is the filled
   primary; Render Document is its ghost sibling. */
.render-btn {
    padding: 7px 14px;
    border-radius: var(--radius-md);
    background: var(--gradient-mean);
    color: var(--on-gradient);
    box-shadow: var(--accent-glow);
    font-size: 12.5px;
    font-weight: 600;
    white-space: nowrap;
    transition: filter var(--transition), box-shadow var(--transition);
}

.render-btn:hover { filter: brightness(1.08); box-shadow: var(--accent-glow-strong); }

.render-btn:disabled { opacity: 0.6; cursor: default; }

.render-btn--ghost {
    margin-right: 12px;        /* a clear step off the right edge (Franck) */
    background: transparent;
    color: var(--gradient-mean);
    border: 1px solid var(--gradient-mean);
    box-shadow: none;
}

.render-btn--ghost:hover {
    filter: none;
    box-shadow: none;
    background: var(--accent-soft);
}

/* Share/download tray (D.12): small grey utility left of the actions;
   an anchored menu with the spec's own three options. */
.share-wrap {
    position: relative;
    margin-left: auto;         /* pushes the doc-action group right */
    display: flex;
    align-items: center;
}

.share-btn { color: var(--nav-ink); }

.share-btn svg { width: 18px; height: 18px; }

.share-menu {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    z-index: 30;
    min-width: 176px;
    display: flex;
    flex-direction: column;
    padding: 4px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
}

.share-menu button {
    text-align: left;
    padding: 8px 12px;
    border-radius: var(--radius-md);
    font-size: 12.5px;
    font-weight: 500;
    color: var(--text-primary);
    transition: background var(--transition);
}

.share-menu button:hover { background: var(--bg-hover); }

/* Logout confirm (Franck 2026-08-11): the PO26 pattern — a compact
   centered dialog over a scrim. The scrim carries its own tint (not
   blur-dependent) so it reads even where backdrop-filter is unsupported;
   the blur is a progressive enhancement. Theme-aware via tokens. */
.logout-backdrop {
    position: fixed;
    inset: 0;
    z-index: 80;
    background: var(--overlay-scrim);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
}

.logout-card {
    width: min(320px, 100%);
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
    padding: 22px 22px 18px;
    text-align: center;
}

.logout-title {
    margin: 0 0 6px;
    font-size: 17px;
    font-weight: 700;
    color: var(--text-primary);
}

.logout-body {
    margin: 0 0 20px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-secondary);
}

.logout-actions {
    display: flex;
    gap: 10px;
}

.logout-btn {
    flex: 1;
    padding: 10px 14px;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    font-family: inherit;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    transition: background var(--transition), filter var(--transition);
}

.logout-btn--ghost {
    background: transparent;
    border-color: var(--border);
    color: var(--text-primary);
}

.logout-btn--ghost:hover { background: var(--bg-hover); }

.logout-btn--go {
    background: var(--accent-gradient);
    color: var(--on-gradient);
}

.logout-btn--go:hover { filter: brightness(1.05); }

/* Full Preview (D.12): the rendered page on a scrim, clean of chrome. */
.full-preview {
    position: fixed;
    inset: 0;
    z-index: 40;
    background: var(--overlay-scrim);
}

.full-preview-scroll {
    height: 100%;
    overflow-y: auto;
    padding: 40px 0 60px;
}

.full-preview-scroll .doc-paper { margin: 0 auto; }

/* .icon-btn (later in source) would win the cascade — compound
   selector keeps the X white on the scrim (AA finding 2026-07-20). */
.icon-btn.full-preview-close {
    position: fixed;
    top: 14px;
    right: 18px;
    z-index: 41;
    color: var(--on-gradient);
}

.idle-stage {
    width: min(680px, calc(100% - 64px));
    margin: 0 auto 26px;
}

.idle-stage h1 {
    font-size: 26px;
    font-weight: 600;
    letter-spacing: -0.3px;
    color: var(--text-primary);
    text-align: center;
}

/* --- W-3 conversation: results above, input below (C.5.4) --- */

.conversation {
    width: min(760px, calc(100% - 48px));
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    gap: 16px;
    padding: 26px 0 14px;
}

.msg { display: flex; gap: 10px; }

.msg--user { justify-content: flex-end; }

.msg--user .msg-body {
    background: var(--bg-bubble);
    border-radius: 14px 14px 4px 14px;
    padding: 10px 14px;
    max-width: 78%;
    font-size: 14px;
    line-height: 1.55;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* Anaïse speaks left-aligned as bare text — no glyph (Franck
   2026-07-20, memo §2 amended): alignment + bubble asymmetry carry
   speaker identity, Claude/ChatGPT style. */
.msg--anaise { align-items: flex-start; }

.msg--anaise .msg-body {
    font-size: 14px;
    line-height: 1.65;
    padding-top: 3px;
    max-width: 82%;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* Thinking indicator — the reserved streaming touchpoint (memo §17) */
.thinking-dots {
    display: flex;
    gap: 5px;
    padding: 9px 0;
}

.thinking-dots span {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--text-muted);
    animation: thinking-pulse 1.2s infinite;
}

.thinking-dots span:nth-child(2) { animation-delay: 0.2s; }
.thinking-dots span:nth-child(3) { animation-delay: 0.4s; }

@keyframes thinking-pulse {
    0%, 60%, 100% { opacity: 0.25; }
    30% { opacity: 1; }
}

/* --- W-4: Anaïse hosts the Focus Area (C.5.1 via D4) --- */

.fa-stage {
    width: min(680px, calc(100% - 48px));   /* matches the prompt width */
    margin: 9vh auto 0;                     /* headroom: upper third */
}

/* The welcome reads as a greeting, not a bubble — one step up. */
.fa-intro {
    font-size: 17px;
    line-height: 1.6;
    color: var(--text-primary);
}

.pill-row {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 14px;
}

/* Pills are ACTIONS — they speak blue (selection stays grey). */
.pill {
    padding: 7px 14px;
    border-radius: 999px;
    border: 1.5px solid var(--accent-border);
    color: var(--accent);
    font-size: 12.5px;
    font-weight: 600;
    transition: background var(--transition);
}

.pill:hover { background: var(--accent-soft); }

/* --- Settings on the rail (memo §21a): third-level choices inline,
   one more indent step; the chosen option carries a blue check
   (icons speak blue — never a second selection band). --- */

.rail-item--opt {
    margin-left: calc(24px + 2 * var(--subitem-indent));
    width: calc(100% - 24px - 2 * var(--subitem-indent));
    font-size: 12px;
}

.opt-check {
    width: 14px;
    height: 14px;
    flex: 0 0 auto;
    margin-left: auto;
    fill: none;
    stroke: var(--icon-rail);
    stroke-width: 2.4;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* --- Prompt Frame & pill (spec C.5.4 contents: +, input, mic, send) --- */

.prompt-frame { padding: 10px 24px 18px; }

.prompt-pill {
    display: flex;
    align-items: flex-end;
    gap: 4px;
    width: min(680px, 100%);
    margin: 0 auto;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    box-shadow: var(--shadow-pill);
    padding: 7px 8px;
    transition: border-color var(--transition), box-shadow var(--transition);
}

.prompt-pill:focus-within {
    border-color: var(--accent-border);
    box-shadow: 0 0 0 3px var(--accent-soft), var(--shadow-pill);
}

.prompt-pill textarea {
    flex: 1 1 auto;
    min-width: 0;
    border: none;
    outline: none;
    background: transparent;
    font-family: inherit;
    font-size: 14.5px;
    line-height: 1.5;
    color: var(--text-primary);
    padding: 7px 6px;
    resize: none;
    max-height: 160px;          /* W-2: expands naturally, then scrolls */
    overflow-y: auto;
}

.prompt-pill textarea::placeholder { color: var(--text-placeholder); }

.icon-btn {
    width: 34px;
    height: 34px;
    flex: 0 0 34px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    color: var(--text-secondary);
    transition: background var(--transition), color var(--transition);
}

.icon-btn:hover { background: var(--bg-hover); color: var(--text-primary); }

.icon-btn svg { width: 17px; height: 17px; stroke-width: 1.7; }

.send-btn {
    width: 36px;
    height: 36px;
    flex: 0 0 36px;
    border-radius: 50%;
    background: var(--accent-gradient);
    color: var(--on-gradient);
    box-shadow: var(--accent-glow);
    display: grid;
    place-items: center;
    transition: filter var(--transition), box-shadow var(--transition);
}

.send-btn:hover { filter: brightness(1.08); box-shadow: var(--accent-glow-strong); }

.send-btn svg { width: 17px; height: 17px; }

/* ==================== Screen 0 — auth (spec A) ====================
   A pure boundary: calm, familiar, no product concepts. The Anaïse
   mark fronts here (memo §2 — the doorway is Nerval's). */

body.auth {
    display: block;
    overflow-y: auto;
    background: var(--bg-nav);      /* quiet grey door; the card is the light */
}

.auth-mark {
    position: absolute;
    top: 18px;
    left: 22px;
    background: var(--brand-black); /* the mark lives on black (memo §2) */
    border-radius: var(--radius-md);
    padding: 8px 12px;
}

.auth-mark img { height: 30px; display: block; }

.auth-main {
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    padding: 72px 20px 40px;
}

.auth-card {
    width: min(420px, 100%);
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: var(--shadow-md);
    padding: 34px 36px 26px;
}

.auth-card h1 {
    font-size: 20px;
    font-weight: 700;
    letter-spacing: -0.2px;
    margin-bottom: 20px;
    text-align: center;
}

.auth-form { display: flex; flex-direction: column; gap: 7px; }   /* compacted (Franck 2026-07-23) */

.auth-row { display: flex; gap: 10px; }

.auth-row label { flex: 1 1 0; }

.auth-form label {
    display: flex;
    flex-direction: column;
    gap: 3px;
    font-size: 12px;
    font-weight: 600;
    color: rgb(0, 0, 0);              /* jet black */
}

.auth-form input {
    width: 100%;               /* row inputs must shrink with their half */
    min-width: 0;
    font: inherit;
    font-size: 13.5px;
    color: var(--text-primary);
    background: var(--bg-page);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 7px 11px;
    outline: none;
    transition: border-color var(--transition), box-shadow var(--transition);
}

.auth-form input:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

/* Primary auth action: the Nerval gradient — the door is the one place
   the product's own light leads (memo §2/§3). */
.auth-primary {
    margin-top: 4px;
    padding: 10px 14px;
    border-radius: var(--radius-md);
    background: var(--accent-gradient);
    color: var(--on-gradient);
    box-shadow: var(--accent-glow);
    font-size: 13.5px;
    font-weight: 600;
    width: 100%;
    transition: filter var(--transition), box-shadow var(--transition);
}

.auth-primary:hover { filter: brightness(1.08); box-shadow: var(--accent-glow-strong); }

.auth-divider {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 16px 0 12px;
    color: var(--text-muted);
    font-size: 11.5px;
}

.auth-divider::before,
.auth-divider::after {
    content: "";
    flex: 1 1 0;
    height: 1px;
    background: var(--border);
}

.auth-sso { display: flex; flex-direction: column; gap: 8px; }

.auth-sso-btn {
    padding: 9px 14px;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: var(--bg-page);
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
    transition: background var(--transition);
}

.auth-sso-btn:hover { background: var(--bg-hover); }

.auth-links {
    display: flex;
    justify-content: space-between;
    gap: 10px;
    margin-top: 16px;
}

.auth-link {
    font-size: 12px;
    font-weight: 600;
    color: var(--accent);
    text-decoration: none;
}

.auth-link:hover { text-decoration: underline; }

.auth-trial {
    margin-top: 10px;
    font-size: 11.5px;
    color: rgb(0, 0, 0);              /* jet black */
    text-align: center;
}

.auth-copy {
    font-size: 13px;
    line-height: 1.6;
    color: rgb(0, 0, 0);              /* jet black */
    margin-bottom: 12px;
}

.auth-state[data-state="verify"] .auth-primary { margin-top: 10px; }

.auth-footer {
    display: flex;
    gap: 8px;
    align-items: center;
    color: var(--text-muted);
    font-size: 11.5px;
}

.auth-footer .auth-link { font-weight: 500; color: var(--text-muted); }

/* Language picker (palefo-lang-picker chip + upward popover; sizes in
   px per the skill — do not convert to rem) */
.signin-lang-picker {
    position: relative;
    display: flex;
    justify-content: center;
    margin-top: 18px;
}

.signin-lang-chip {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 6px 12px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--bg-page);
    font-size: 12px;
    font-weight: 600;
    color: var(--text-secondary);
    transition: background var(--transition);
}

.signin-lang-chip:hover { background: var(--bg-hover); }

.signin-lang-options {
    position: absolute;
    bottom: calc(100% + 6px);
    z-index: 10;
    min-width: 150px;
    display: flex;
    flex-direction: column;
    padding: 4px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
}

.signin-lang-option {
    display: flex;
    align-items: center;
    gap: 8px;
    text-align: left;
    padding: 7px 11px;
    border-radius: var(--radius-md);
    font-size: 12.5px;
    font-weight: 500;
    color: var(--text-primary);
    transition: background var(--transition);
}

.signin-lang-option:hover { background: var(--bg-hover); }

.signin-lang-option.selected { background: var(--bg-active-sub); }

/* ================= Screen 1 — Onboarding (spec B.1–B.5) =================
   One screen, evolving states. The stage borrows the focus-state
   composition (content upper third, prompt at its idle seat); frames
   are cards on the work surface, optional by design. */

/* Same two-anchored-zones grid as focus (W-4c): stage upper, prompt
   at its idle seat — the prompt never jumps when onboarding begins. */
body[data-state="onboard"] .engagement {
    display: grid;
    /* Franck 2026-08-27 — the prompt DOCKS. It used to hold the second flexible
       row, which floated it at the optical centre with the conversation above
       it; the composition was shared with Focus so the prompt never jumped
       between the two. Onboarding is the one state where that costs more than
       it buys: the person is watching their workspace being built, and the
       biggest, brightest thing on screen was an input inviting typing that is
       not wanted yet. The air moves to the content row, the prompt sits on the
       bottom bar. Logged in docs/SPEC-OVERRIDES.md. */
    grid-template-rows: auto minmax(0, 1fr) auto auto auto;
}

body[data-state="onboard"] .status-bottom { grid-row: 5; }

body[data-state="onboard"] .engagement-frame { overflow: visible; }

.onb-stage { padding: 34px 48px 0; }

.onb-heading {
    font-size: 21px;
    font-weight: 700;
    letter-spacing: -0.2px;
    text-align: center;
    margin-bottom: 22px;
}

.onb-frames {
    display: grid;
    grid-template-columns: repeat(2, minmax(240px, 340px));
    justify-content: center;
    gap: 18px;
}

.onb-frame {
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 20px 22px 18px;
    text-align: center;
    cursor: pointer;
    transition: border-color var(--transition), box-shadow var(--transition), background var(--transition);
}

.onb-frame:hover, .onb-frame:focus-visible {
    border-color: var(--accent-border);
    box-shadow: var(--shadow-md);
    outline: none;
}

/* --- The consent moment (B.1/B.2): what will be read, and what is being held back ---------------
   A user may point Anaise at an entire drive. These surfaces exist so that ingestion is something
   they agreed to, having seen it, rather than something that happened to them. */

.onb-consent {
    list-style: none;
    margin: 6px 0 10px;
    padding: 10px 14px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: 10px;
    max-width: 640px;
}

.onb-consent li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 10px;
    padding: 4px 0;
}

.onb-consent li + li { border-top: 1px solid var(--border); }

.onb-consent-name {
    font-weight: 600;
    font-size: 13.5px;
    color: var(--text-primary);
    overflow-wrap: anywhere;
}

.onb-consent-why {
    font-size: 12.5px;
    color: var(--text-secondary);
}

/* The live account of what Anaise is doing. One line, updated in place: the conversation should not
   fill with near-identical progress messages. */
.onb-progress {
    margin: 8px 0;
    font-size: 13.5px;
    color: var(--text-secondary);
    line-height: 1.5;
    max-width: 640px;
}

/* The guess at the organization, shown as it narrows. */
.onb-hypothesis {
    margin: 10px 0 14px;
    padding: 12px 16px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 12px;
    max-width: 640px;
}

.onb-hypothesis-head {
    font-size: 14.5px;
    font-weight: 700;
    color: var(--text-primary);
    margin-bottom: 4px;
}

.onb-hypothesis-why,
.onb-hypothesis-areas {
    font-size: 12.5px;
    color: var(--text-secondary);
    line-height: 1.5;
}

.onb-hypothesis-areas { margin-top: 4px; }

@media (max-width: 640px) {
    .onb-consent, .onb-progress, .onb-hypothesis { max-width: 100%; }
}

/* Live drop target (B.1): the document frame answers the drag. */
.onb-frame--docs.dragover {
    border-color: var(--accent);
    background: var(--accent-soft);
}

.onb-frame-icon {
    width: 26px;
    height: 26px;
    stroke-width: 1.6;
    color: var(--icon-rail);
    margin-bottom: 8px;
}

.onb-frame h3 { font-size: 14.5px; font-weight: 700; margin-bottom: 6px; }

.onb-frame p {
    font-size: 12.3px;
    line-height: 1.6;
    color: var(--text-secondary);
    margin-bottom: 12px;
}

.onb-browse {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--accent);
    border: 1px solid var(--accent-border);
    border-radius: 999px;
    padding: 6px 16px;
    transition: background var(--transition);
}

.onb-browse:hover { background: var(--accent-soft); }

/* Two options side by side: "Browse files" (primary pill) and the quieter "or a whole folder". */
.onb-browse-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 4px;
}
.onb-browse--folder {
    border-color: transparent;
    color: var(--text-secondary);
    padding: 6px 10px;
}
.onb-browse--folder:hover { background: var(--bg-hover); color: var(--text-primary); }

/* B.2 — the receded tray: compact, above the conversation, honest
   per-file status; never a blocking overlay. */
.onb-tray {
    margin: 12px 48px 0;
    padding: 10px 16px 12px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: 12px;
}

.onb-tray-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-secondary);
    margin-bottom: 6px;
}

.onb-tray-list { list-style: none; }

.onb-tray-list li {
    display: flex;
    justify-content: space-between;
    gap: 14px;
    font-size: 12.5px;
    padding: 3.5px 0;
}

.onb-tray-list .onb-doc-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.onb-tray-list .onb-doc-status {
    flex: 0 0 auto;
    color: var(--text-secondary);
    font-style: italic;
}

.onb-tray-list li.done .onb-doc-status {
    color: var(--badge-generated-ink);
    font-style: normal;
}

/* Suggestion pills reuse the Focus-Area pill grammar (C.5.1 lineage);
   proposal chips carry a quiet check. */
.onb-chip-row { display: flex; flex-wrap: wrap; gap: 8px; margin: 6px 0 2px; }

.onb-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--text-primary);
    background: var(--bg-bubble);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 5px 14px;
}

.onb-chip svg { width: 13px; height: 13px; color: var(--accent); stroke-width: 2.4; }

/* A Focus Area the Assembler designed repeatedly and did not settle on. Dashed rather than solid,
   and quieter ink: provisional, and visibly the one you may argue with. It is still proposed —
   withholding our best guess would make the client do the work we were hired for. */
.onb-chip--contested {
    /* Dashed, but no LOUDER than the settled chip's solid border. Measured, the first version
       inverted the hierarchy: the contested outline scored 2.27:1 against the settled 1.22:1, so the
       chip meant to recede had the most prominent edge on the row. The dash pattern alone carries
       "provisional"; it does not need extra weight to say it. */
    border-style: dashed;
    border-color: var(--border);
    background: transparent;
    color: var(--text-secondary);
}

.onb-chip--contested svg { color: var(--text-muted); }

/* The skip line: quiet, centered under the prompt (B.5). */
.onb-skip {
    display: block;
    margin: 8px auto 0;
    font-size: 11.5px;
    color: var(--text-muted);
}

.onb-skip:hover { color: var(--text-secondary); }

/* Onboarding rail (canon: minimal nav = Sessions + avatar; no Home per
   memo §10; a new org has NO logo — its registered name anchors). */
.onb-orgname {
    margin: 4px 0 18px 10px;
    font-size: 15px;
    font-weight: 700;
    letter-spacing: 0.02em;
    color: var(--gradient-mean);
}

body.onboarding #newSessionBtn,
body.onboarding [data-root="focus"],
body.onboarding [data-root="focus"] ~ .rail-subgroup,
body.onboarding [data-root="vault"],
body.onboarding [data-root="settings"],
body.onboarding [data-root="settings"] ~ .rail-subgroup,
body.onboarding .org-logo { display: none; }

/* ...except once the Focus Areas start arriving. The rail is emptied during
   onboarding because there is nothing to put in it; the moment there IS, it
   fills — which is the workspace assembling itself in the place it will live,
   rather than a panel in the middle of the screen describing it. */
body.onboarding.assembling-rail [data-root="focus"],
body.onboarding.assembling-rail [data-root="focus"] ~ .rail-subgroup,
body.onboarding.assembling-rail [data-root="vault"],
body.onboarding.assembling-rail [data-root="vault"] ~ .rail-subgroup { display: block; }

/* An area the readings did not agree on. Quieter, not hidden: it is a real
   question about the organization and the person who owns it decides — so it
   has to be visible enough to argue with, and distinct enough to spot. */
.rail-item--provisional {
    opacity: 0.55;
    font-style: italic;
}

/* SELF-ASSEMBLY (B.4/B.5): the rail's roots arrive staggered — the one
   visible moment of becoming; no ceremony beyond it. */
body.assembling .rail-item.assemble-in {
    animation: onb-assemble 0.5s ease backwards;
}

@keyframes onb-assemble {
    from { opacity: 0; transform: translateX(-10px); }
    to { opacity: 1; transform: none; }
}

/* ============ The landing (memo §25 — Khroma model, theme-FIXED) ============
   Left 1/3 black: centered mark, serif hero (roman/italic), subtitle,
   canonical-blue CTA, language chip. Right 2/3: staged triad snapshot
   over three capability cards. Its own palette — the app theme never
   touches it (palefo.com / O365 model). */

body.landing {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100svh;
    overflow: hidden;
    background: var(--landing-right);
}

/* The landing rides inside a canvas wrapper: full-bleed below the
   ultra-wide caps (identical to before), a floating Palefo-style
   glass frame above them (CTO 2026-07-23). */
.landing-canvas {
    display: flex;
    width: 100%;
    height: 100%;
    min-width: 0;
}

/* The expanded-mosaic stage exists only in the framed regime. */
.landing-stage { display: none; }

/* THE CAPS (derived from Franck's gap rules): width 1620 — where the
   inter-card gap reaches double the reference 20px; height 1080 —
   where the band's bottom gap outgrows the top. Beyond either, the
   canvas floats (Palefo.com main-container grammar: corner-lit
   borders, triple shadow, radius 26). */
/* Height cap 800: framed, the cards' top/bottom margins match their
   side margins (~28-36px, Franck 2026-07-23). Once framed the canvas
   NEVER grows vertically; only the stage grows. Width keeps its own
   1620 regime. */
@media (min-width: 1485px), (min-width: 661px) and (min-height: 841px) {
    /* DEFAULT (Franck 2026-07-23): GB — the float sits on the expanded
       mosaic stage, grounded by a dark shadow. The review toggle flips
       to BB (black + champagne bloom). */
    body.landing { padding: 20px 40px; }

    .landing-stage {
        display: block;
        position: fixed;
        inset: 0;
        width: 100%;
        height: 100%;
        z-index: 0;
        pointer-events: none;
    }

    /* BB state (review toggle): black stage; the float declared by a
       champagne GLOW — the pill's black-on-black idea, scaled dramatic. */
    body.stage-bb { background: var(--landing-black); }

    body.stage-bb .landing-stage { display: none; }

    body.stage-bb .landing-canvas {
        box-shadow:
            0 0 24px rgba(206, 190, 148, 0.45),
            0 0 90px rgba(206, 190, 148, 0.28),
            0 0 220px rgba(206, 190, 148, 0.12);
    }

    .landing-canvas {
        position: relative;
        z-index: 1;
        width: min(100%, 1484px);
        height: min(100%, 780px);
        border-radius: 26px;
        overflow: hidden;
        /* CLEAN edges; dark grounding shadow (GB default). */
        box-shadow:
            0 30px 80px rgba(0, 0, 0, 0.32),
            0 12px 24px rgba(0, 0, 0, 0.18);
    }
}

.landing-left {
    flex: 0 0 33.333%;
    /* WIDTH LOCK (Franck 2026-07-22): the column never narrows past the
       point where the hero's first line would break — governed by the
       wider catalog: FR "Votre souveraine," 316.7px (EN 284.4px) at the
       fixed 40px face, plus the 44px side paddings. */
    min-width: 408px;
    /* WIDTH CAP (Franck 2026-07-23): a third only until 440px — beyond,
       the column stops growing and the right side absorbs the rest. */
    max-width: 440px;
    background: var(--landing-black);
    color: var(--landing-ink);
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: 46px 44px 20px;
    overflow-y: auto;
}

/* The twin auto margins (mark top, legal top) split the free space, so
   the composition sits centered between the top edge and the legal
   line (Franck 2026-07-22: lowered, more centered along the page). */
.landing-mark {
    height: 58px;
    display: block;
    margin-top: auto;
    margin-bottom: 3vh;           /* layout gap (Franck's middle distance) */
    /* Visual nudge only — the mark rides 1.5vh higher, restoring the
       ORIGINAL mark-to-title air while every other element keeps its
       exact seat (transform does not reflow; Franck 2026-07-22). */
    transform: translateY(-1.5vh);
}

.landing-hero {
    font-family: "Playfair Display", Georgia, serif;
    font-weight: 500;
    font-size: 40px;             /* FIXED — no viewport coupling (Franck: no zoom effect) */
    line-height: 1.18;
    letter-spacing: 0.002em;
}

.landing-hero span { display: block; }

.landing-hero em {
    display: block;
    font-style: italic;
    font-weight: 500;
}

.landing-sub {
    margin-top: 26px;
    font-size: 13.5px;
    line-height: 1.7;
    color: var(--landing-ink-dim);
    max-width: 310px;
}

/* The CTA: a black pill on the black column — visible by its halo
   (the Tchala frame treatment, Franck 2026-07-22), ringed by a thin
   bronze line traced just inside the pill's edge. */
.landing-cta {
    position: relative;
    margin-top: calc(40px + 1.5vh); /* absorbs the gap freed above — the
                                       pill does not move */
    padding: 13px 34px;
    border-radius: 999px;
    background: var(--landing-pill);
    color: var(--landing-ink);
    font-size: 14px;
    font-weight: 600;
    box-shadow: 0 0 18px var(--landing-pill-glow), 0 0 52px var(--landing-pill-glow-soft);
    transition: box-shadow var(--transition);
}

.landing-cta::after {
    content: "";
    position: absolute;
    inset: 2px;
    border-radius: 999px;
    border: 0.5px solid var(--landing-pill-ring);   /* true hairline on retina */
    pointer-events: none;
}

.landing-cta:hover {
    box-shadow: 0 0 26px var(--landing-pill-glow-hover), 0 0 70px var(--landing-pill-glow);
}

/* Language chip on black — the Mesaj bare-chip form (flag over name).
   In the flow, close under the CTA (Franck 2026-07-22) — no longer
   pinned to the column's foot. */
body.landing .signin-lang-picker {
    margin-top: 34px;
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
}

/* TEMPORARY stage toggle (review): a quiet circular-arrow, framed
   regime only. Absolutely positioned below the picker so it adds ZERO
   flow height — the whole composition stays exactly where it was. */
.stage-toggle {
    display: none;
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    margin-top: 12px;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    border: 1px solid rgba(245, 244, 241, 0.25);
    border-radius: 50%;
    color: var(--landing-ink-dim);
    transition: color var(--transition), border-color var(--transition);
}

.stage-toggle svg { width: 9px; height: 9px; stroke-width: 1.8; }

.stage-toggle:hover { color: var(--landing-ink); border-color: rgba(245, 244, 241, 0.5); }

@media (min-width: 1485px), (min-width: 661px) and (min-height: 841px) {
    .stage-toggle { display: flex; }
}

/* Bottom-anchored legal line — moves with the column's foot. */
.landing-legal {
    margin-top: auto;
    padding-top: 26px;
    font-size: 10.5px;
    line-height: 1.5;
    color: var(--landing-ink-dim);
    max-width: 100%;    /* one line whenever the column allows
                           (Franck 2026-07-23) — was capped at 300px */
}

body.landing .signin-lang-chip {
    background: none;
    border: none;
    padding: 4.6px 8px;
    color: var(--landing-ink-dim);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.7px;
    font-size: 12.6px;
    font-weight: 500;
    transition: color var(--transition);
}

body.landing .signin-lang-chip:hover { color: var(--landing-ink); }

body.landing .signin-lang-flag { font-size: 18.4px; line-height: 1; }

body.landing .signin-lang-options {
    position: absolute;
    bottom: calc(100% + 7px);
    left: 50%;
    transform: translateX(-50%);
    min-width: 150px;
    display: flex;
    flex-direction: column;
    padding: 4px;
    background: var(--bg-card);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
    z-index: 10;
}

body.landing .signin-lang-option {
    display: flex;
    align-items: center;
    gap: 8px;
    text-align: left;
    padding: 7.5px 10.3px;
    border-radius: 7px;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-primary);
    transition: background var(--transition);
}

body.landing .signin-lang-option:hover { background: var(--bg-hover); }

body.landing .signin-lang-option.selected { background: var(--bg-active-sub); font-weight: 600; }

.landing-right {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    overflow-x: hidden;
    overflow-y: auto;
    /* Fallback UNDER the mosaic svg — matches the mosaic's own sweep
       (Franck 2026-07-23) so wrapped card rows past the svg's box read
       harmonious, not the old teal tech gradient. */
    background: linear-gradient(72deg,
        rgb(218, 74, 197) 0%, rgb(171, 64, 181) 25%, rgb(115, 100, 203) 50%,
        rgb(87, 129, 224) 75%, rgb(80, 110, 209) 100%);
}

/* Franck's mosaic, procedural (2026-07-23): stretches the full sweep
   across the band at any aspect. Waves parked while it reigns. */
.landing-mosaic {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
}

.landing-waves { display: none; }

/* The line-waves layer sits under everything in the column. */
.landing-waves {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
}

/* The hero scene: the dusk composite FILLS the top sub-screen — full
   bleed, no background showing (Franck 2026-07-22). Past the image's
   ratio it CROPS from the edges, anchored on the computer screen's
   center (measured ~50%/45% in the file). THE DIVIDE IS PINNED: the
   scene owns exactly the top HALF of the viewport at every width —
   when the cards wrap to more rows they continue below the fold
   (clipped at rest, scrollable), the dividing line never jumps
   (Franck 2026-07-22, superseding "cards always fully on screen"). */
.landing-scene {
    position: relative;
    z-index: 1;
    flex: 0 0 50%;
    min-height: 0;
    padding: 0;
    margin: 0;
    container-type: size;
    container-name: scene;
}

/* The widescreen composite fills the half's HEIGHT at natural ratio,
   centered: narrower regions crop its flanks; wider regions summon the
   INFINITE SKY BORDER (Franck 2026-07-22) — per-side panels in the
   image's own sampled edge hues, feathered OVER the picture edge so
   the photo melts into matching sky and runs to the viewport edges. */
.scene-frame {
    position: relative;
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    overflow: hidden;
}

.scene-frame img {
    display: block;
    height: 100%;
    width: auto;
    flex: 0 0 auto;
}

.scene-ext {
    display: none;
    flex: 1 1 0;
    min-width: 0;
    position: relative;
    z-index: 2;
}

.scene-ext--l { background: var(--scene-sky-l); }
.scene-ext--r { background: var(--scene-sky-r); }

.scene-ext::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    width: 110px;
    pointer-events: none;
}

.scene-ext--l::after {
    right: -110px;
    background: linear-gradient(90deg, var(--scene-sky-l) 0%, transparent 100%);
}

.scene-ext--r::after {
    left: -110px;
    background: linear-gradient(270deg, var(--scene-sky-r) 0%, transparent 100%);
}

/* The extensions exist only when the half is wider than the image. */
@container scene (min-aspect-ratio: 1916/706) {
    .scene-ext { display: block; }
}

/* The card band floats directly on the tech atmosphere. It fills the
   bottom half at minimum and grows past the fold when rows wrap.
   Surplus height distributes EQUALLY above and below the cards
   (Franck 2026-07-23: the bottom gap must never outgrow the top) —
   auto margins, so overflow still clips bottom-first, never the top. */
.landing-band {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    flex: 0 0 auto;
    min-height: 50%;
    padding: 28px;
}

/* ONE card width, every step (Franck 2026-07-22: "the constraints
   carry from viewport to viewport — whether 1, 2, 3, or 4 per row").
   Cards live in [188px, 188px + 20%] (Franck 2026-07-22, re-affirmed
   2026-07-23 after a 160-floor trial read as towers): they may breathe
   up to 226px as the viewport widens, then gaps take over. */
/* FLEX cards (Franck 2026-07-23): all four gaps — side, inter-card,
   top, bottom — land on ONE value (28) at EVERY card count, because
   the cards flex to fill (capped at 226, floored at 188) against a
   single fixed 28 gap and the band's 28 padding. Wrapping is native:
   4 → 3+1 → 2×2 → 1, each row centered. No container queries, no
   grid-column tricks — the ladder is a structural property. */
.landing-cards {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 28px;
    margin-top: auto;
    margin-bottom: auto;
}

/* Cards in the reference aesthetic: picture on top, text under. */
.landing-card {
    flex: 1 1 188px;
    min-width: 188px;
    max-width: 226px;
    background: var(--bg-card);
    border-radius: 16px;
    overflow: hidden;
    /* BOTTOM-directional drop (Franck 2026-07-23): negative spreads pull
       the sides in, so the shadow reads as a grounded drop beneath the
       card — subtle but unmistakable — not an all-around halo. */
    box-shadow:
        0 20px 30px -10px rgba(16, 30, 80, 0.55),
        0 5px 10px -4px rgba(16, 30, 80, 0.3);
}

.landing-card .card-photo {
    width: 100%;
    height: 128px;
    object-fit: cover;          /* photos can never overflow or clip oddly */
    display: block;
}

.landing-card h3 {
    font-size: 15px;
    font-weight: 700;
    letter-spacing: -0.15px;
    color: var(--card-ink);
    margin: 15px 18px 7px;
}

.landing-card p {
    font-size: 11.8px;
    line-height: 1.6;
    color: var(--text-secondary);
    margin: 0 18px 17px;
}

/* Mobile: the whole page stacks into one column — the black panel on
   top, then the imagery, then the cards one per row (Franck
   2026-07-22). The width lock yields; the page scrolls naturally. */
@media (max-width: 660px) {
    body.landing {
        height: auto;
        min-height: 100svh;
        overflow: visible;
    }

    .landing-canvas {
        flex-direction: column;
        height: auto;
    }

    .landing-left {
        flex: none;
        width: 100%;
        min-width: 0;
        max-width: none;   /* the float's 440 cap must not clip the stack */
        overflow-y: visible;
        padding: 38px 28px 18px;
    }

    .landing-mark { margin-top: 0; margin-bottom: 34px; }

    .landing-right { flex: none; overflow: visible; }

    .landing-scene { flex: none; height: 300px; min-height: 0; }

    .landing-band { min-height: 0; padding: 22px 22px 34px; }
}

/* ============ The GATE — NervalAI standard card (Mesaj Pa m model) =========
   Frosted glass over the blurred landing; banner buttons, alt pills,
   and the state machine inside. Proportions ported from Mesaj Pa m. */

.gate-backdrop {
    position: fixed;
    inset: 0;
    z-index: 60;
    background: var(--overlay-scrim);
    /* Deepened 5 -> 14px (2026-07-23): the Weather frost floats over a
       calm sky; our landing is busy — the backdrop melts it into soft
       fields so the card's muted text stays legible. */
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
}

/* The gate wears the WEATHER frost standard (Franck 2026-07-23,
   superseding the Mesaj values): a truly translucent fill + blur on
   ::before, and the angled-light edge — a masked 1.5px gradient ring,
   ice-bright at the top, nearly vanishing mid-height, lifting again at
   the foot — so the glass reads lit from above. Card slimmed to fit
   its content. */
.gate-card {
    position: relative;
    width: min(356px, 100%);
    max-height: min(880px, 100%);   /* the enterprise form breathes without interior scroll */
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 24px 26px 16px;
    border-radius: 25px;                 /* Mesaj gate radius */
    box-shadow: 0 16px 30px rgba(30, 40, 90, 0.28);
    isolation: isolate;
}

.gate-card::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: rgba(255, 255, 255, 0.66);   /* lightened (Franck 2026-07-23) */
    backdrop-filter: blur(14px) saturate(1.05);
    -webkit-backdrop-filter: blur(14px) saturate(1.05);
    z-index: -1;
    pointer-events: none;
}

.gate-card::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    padding: 1.5px;
    background: linear-gradient(
        180deg,
        rgba(230, 238, 255, 1) 0%,
        rgba(200, 215, 255, 0.78) 8%,
        rgba(150, 170, 240, 0.32) 25%,
        rgba(110, 135, 210, 0.12) 45%,
        rgba(110, 135, 210, 0.12) 55%,
        rgba(140, 160, 230, 0.25) 75%,
        rgba(170, 190, 250, 0.5) 100%
    );
    -webkit-mask:
        linear-gradient(white 0 0) content-box,
        linear-gradient(white 0 0);
    -webkit-mask-composite: xor;
    mask-composite: exclude;
    pointer-events: none;
    z-index: 1;
}

.icon-btn.gate-close {
    position: absolute;
    top: 12px;
    right: 14px;
    color: var(--text-secondary);
}

.gate-hero {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    margin-bottom: 10px;
}

.gate-logo {
    height: 54px;
    display: block;
    background: var(--brand-black);      /* the mark lives on black */
    border-radius: 10px;
    padding: 7px 10px;
}

/* The enterprise register runs headless — no Welcome block (Franck
   2026-07-23); the mark alone anchors the card and height drops. */
.gate-card[data-gate="register"] .gate-title,
.gate-card[data-gate="register"] .gate-subtitle { display: none; }

.gate-title {
    font-family: "Playfair Display", Georgia, serif;
    font-style: italic;
    font-weight: 500;
    font-size: 26px;                     /* Mesaj gate-title */
    color: rgb(0, 0, 0);                 /* jet black (Franck 2026-07-23) */
    letter-spacing: 0.01em;
    text-align: center;
}

.gate-subtitle {
    font-size: 12.5px;
    line-height: 1.5;
    color: rgb(0, 0, 0);              /* jet black */
    text-align: center;
    max-width: 300px;
}

.gate-state {
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
}

.gate-state .auth-form { width: 100%; max-width: 320px; }

.gate-state .auth-links { width: 100%; max-width: 320px; }

/* Banner buttons — Mesaj/Tchala values: 289×38, radius 10, stack gap 16 */
.signin-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8.6px;
    width: 197px;                 /* 246 − 20% (Franck 2026-07-23): content
                                     with a little gap, all three equal */
    max-width: 100%;
    min-height: 38px;
    font-size: 13.8px;
    font-weight: 500;
    border-radius: 10px;
    padding: 0 14px;
    margin-bottom: 14px;
    transition: background var(--transition), border-color var(--transition);
}

.signin-btn-google {
    background: var(--sso-google-bg);
    color: var(--sso-google-ink);
    border: 1px solid var(--sso-google-border);
}

.signin-btn-google:hover { background: var(--bg-hover); }

.signin-btn-facebook {
    background: var(--sso-facebook);
    color: var(--on-gradient);
    border: 1px solid var(--sso-facebook);
}

.signin-btn-facebook:hover { background: var(--sso-facebook-hover); border-color: var(--sso-facebook-hover); }

.signin-btn-apple {
    background: var(--sso-apple);
    color: var(--on-gradient);
    border: 1px solid var(--sso-apple);
}

.signin-btn-apple:hover { background: var(--sso-apple-hover); }

/* Alt methods — GLASS pills in the Weather language (designer pass,
   Franck 2026-07-23): a top-rim sheen where the light catches, an airy
   ice-blue body, an inset highlight, and a quiet brand-blue line —
   icons lead, labels follow. */
.signin-alt-methods {
    display: flex;
    gap: 12px;
    margin: 4px 0 6px;
}

.signin-alt-pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    min-height: 34px;
    padding: 6px 19px;
    border-radius: 999px;
    background:
        linear-gradient(180deg,
            rgba(255, 255, 255, 0.6) 0%,
            rgba(255, 255, 255, 0.22) 18%,
            rgba(255, 255, 255, 0) 45%),
        linear-gradient(135deg,
            rgba(196, 212, 246, 0.55) 0%,
            rgba(224, 232, 250, 0.42) 50%,
            rgba(246, 249, 255, 0.28) 100%);
    backdrop-filter: blur(15px) saturate(1.1);
    -webkit-backdrop-filter: blur(15px) saturate(1.1);
    border: 1px solid rgba(33, 127, 218, 0.3);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.55),
        0 2px 6px rgba(33, 127, 218, 0.12);
    font-size: 13px;
    font-weight: 600;
    color: rgba(20, 32, 58, 0.92);
    transition: border-color var(--transition), box-shadow var(--transition);
}

.signin-alt-pill .pill-icon {
    width: 15px;
    height: 15px;
    flex: 0 0 15px;
    stroke-width: 1.8;
    color: var(--gradient-mean);
}

.signin-alt-pill:hover {
    border-color: rgba(33, 127, 218, 0.5);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.7),
        0 3px 9px rgba(33, 127, 218, 0.2);
}

/* The account-URL field (enterprise register): live prefix input with
   the domain suffix attached — visibly editable, the suffix fixed. */
.url-field {
    display: flex;
    align-items: center;
    width: 100%;
}

.url-field input {
    flex: 1 1 auto;
    min-width: 0;
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

.url-suffix {
    flex: 0 0 auto;
    padding: 8px 12px;
    font-size: 13px;
    font-weight: 600;
    color: rgb(0, 0, 0);
    background: rgba(255, 255, 255, 0.5);
    border: 1px solid var(--border);
    border-left: none;
    border-radius: 0 var(--radius-md) var(--radius-md) 0;
}

.url-hint {
    margin-top: 2px;
    font-size: 10.5px;
    font-style: italic;
    color: rgb(0, 0, 0);
}

.auth-form select {
    width: 100%;
    font: inherit;
    font-size: 13px;
    padding: 7px 10px;
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    background: rgba(255, 255, 255, 0.75);
    color: var(--text-primary);
}

.gate-hint {
    font-size: 11.5px;
    font-style: italic;
    line-height: 1.45;
    color: rgb(0, 0, 0);              /* jet black */
    text-align: center;
    max-width: 290px;
    margin: 10px 0 4px;
}

.gate-channel-label {
    font-size: 12px;
    font-weight: 600;
    color: var(--text-secondary);
    margin-top: 2px;
}

.gate-channels { display: flex; gap: 8px; }

.gate-channel {
    padding: 7px 14px;
    border-radius: 999px;
    border: 1px solid var(--border);
    font-size: 12px;
    font-weight: 600;
    color: var(--text-secondary);
    transition: background var(--transition), color var(--transition), border-color var(--transition);
}

.gate-channel.active {
    background: var(--accent-soft);
    color: var(--gradient-mean);
    border-color: var(--gradient-mean);
}

.gate-code {
    font: inherit;
    font-size: 22px;
    letter-spacing: 0.55em;
    text-align: center;
    color: var(--text-primary);
    background: var(--bg-page);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 10px 0 10px 12px;
    width: 100%;
    outline: none;
}

.gate-code:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }

.gate-back { margin-top: 12px; }

.gate-fineprint {
    font-size: 10.5px;
    line-height: 1.5;
    color: rgb(0, 0, 0);              /* jet black */
    text-align: center;
    max-width: 300px;
    margin-top: 14px;
}

/* ==================== Focus outlines ==================== */

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.prompt-pill textarea:focus-visible { outline: none; }

/* A failed turn on the working screen. The model's own answers and the app's
   report of a failure must not read alike — one is Anaïse talking, the other
   is the system explaining why she can't. */
.msg--error .msg-body { color: var(--health-down); }

/* One question at a time: the send button is the only affordance that says so. */
.prompt-pill[data-busy="true"] { opacity: 0.7; }

/* ==================== Ask Anaïse (live backend) ====================
   The first surface wired to the real API. Chrome stays quiet so the
   answer is the only thing with weight; the model stack's health is
   reported in its own strip because it fails independently of anaise.
   Opts out of the app chassis the way body.auth and body.landing do. */

body.ask {
    display: flex;
    flex-direction: column;
    background: var(--bg-page);
}

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.ask-head {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 20px 24px 16px;
    border-bottom: 1px solid var(--border);
    background: var(--bg-nav);
}

.ask-head-line {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 8px 16px;
}

.ask-title {
    font-size: 19px;
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--text-primary);
}

.ask-sub, .ask-note { font-size: 12.5px; color: var(--text-muted); }

/* Health strip. State drives the dot's color, so it reads before the text. */
.ask-status {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 12.5px;
    color: var(--text-secondary);
}

.ask-status-dot {
    width: 8px;
    height: 8px;
    flex: 0 0 8px;
    border-radius: 50%;
    background: var(--text-muted);
}

.ask-status[data-state="ok"] .ask-status-dot { background: var(--health-ok); }
.ask-status[data-state="degraded"] .ask-status-dot { background: var(--health-warn); }
.ask-status[data-state="down"] .ask-status-dot { background: var(--health-down); }

.ask-status-detail {
    font-size: 12.5px;
    color: var(--accent);
    text-decoration: underline;
    padding: 0;
}

.ask-services {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 2px 12px;
    margin-top: 4px;
    font-size: 12px;
    color: var(--text-secondary);
}

.ask-services dt { font-weight: 600; }
.ask-services dt.is-ok { color: var(--health-ok); }
.ask-services dt.is-down { color: var(--health-down); }

.ask-main {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    justify-content: center;
    overflow: hidden;
}

.ask-thread {
    width: min(680px, 100%);
    overflow-y: auto;
    padding: 24px;
    display: flex;
    flex-direction: column;
    gap: 22px;
}

.ask-empty {
    margin: auto;
    color: var(--text-placeholder);
    font-size: 14.5px;
}

.ask-turn { display: flex; flex-direction: column; gap: 5px; }

.ask-who {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted);
}

.ask-body {
    font-size: 14.5px;
    line-height: 1.6;
    color: var(--text-primary);
    white-space: pre-wrap;      /* the model's own line breaks are meaningful */
    overflow-wrap: anywhere;    /* a long token must not widen the column */
}

.ask-turn--user .ask-body {
    background: var(--bg-bubble);
    border-radius: var(--radius-lg);
    padding: 10px 14px;
}

.ask-thinking { color: var(--text-muted); }

/* The count includes reasoning tokens nobody sees, so it is stated plainly
   rather than hidden — it is the difference between a cheap and costly call. */
.ask-cost {
    font-size: 11.5px;
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

.ask-turn--error .ask-body { color: var(--health-down); }

.ask-foot {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 12px 24px 18px;
}

.ask-pill { align-items: flex-end; }

.ask-pill textarea {
    flex: 1 1 auto;
    min-width: 0;
    border: none;
    outline: none;
    background: transparent;
    font-family: inherit;
    font-size: 14.5px;
    line-height: 1.5;
    color: var(--text-primary);
    padding: 7px 12px;
    resize: none;
    max-height: 160px;
    overflow-y: auto;
}

.ask-pill textarea::placeholder { color: var(--text-placeholder); }
.ask-pill textarea:focus-visible { outline: none; }

.ask-form-disabled { opacity: 0.6; }
.ask-pill[data-busy="true"] { opacity: 0.65; }
.send-btn:disabled { filter: grayscale(0.6); cursor: default; }

@media (max-width: 600px) {
    .ask-head { padding: 16px 16px 12px; }
    .ask-thread { padding: 18px 16px; }
    .ask-foot { padding: 10px 16px 14px; }
}

/* Sign-in error line (invalid credentials). */
.auth-error {
    margin: 8px 0 0;
    color: var(--health-down);
    font-size: 12.5px;
    text-align: center;
}

/* Sources behind a grounded answer — compact, secondary, beneath the reply. */
.msg-citations {
    margin-top: 10px;
    padding-top: 8px;
    border-top: 1px solid var(--border-light);
}
.msg-citations-title {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-muted);
    margin-bottom: 4px;
}
.msg-citation {
    font-size: 12px;
    line-height: 1.45;
    color: var(--text-secondary);
    margin-top: 3px;
}

/* Rendered markdown inside an answer bubble. */
.msg--anaise .msg-body p { margin: 0 0 8px; }
.msg--anaise .msg-body p:last-child { margin-bottom: 0; }
.msg--anaise .msg-body ul,
.msg--anaise .msg-body ol { margin: 4px 0 8px; padding-left: 20px; }
.msg--anaise .msg-body li { margin: 2px 0; }
.msg--anaise .msg-body strong { font-weight: 650; }
.msg--anaise .msg-body code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.92em;
    background: var(--bg-hover);
    padding: 1px 4px;
    border-radius: 4px;
}
