/* --------------------------------------------------------------------------- query/query.css — the Query module's own stylesheet. The Query module IS the database module — `shell-db-frame` + `CustomerGrid` — so the frame, the grid and every view kind are styled by rules that already exist. What is left here is what this surface adds: the VIEW RAIL (owner item 3: the AI's views, grouped by the database they read), a one-line provenance disclosure, and the empty state. ⚠ THE UNIVERSAL DATABASE HEADER IS GONE FROM THIS FRAME (owner item 7, W36-T67), so the rail's group heading is now the only place the source database is named. That is not a gap: it was always the richer of the two, carrying the icon as well, and the header was the copy. The chat rules live in `assistant/assistant.css` with the markup they style. EVERY FONT SIZE IS A `--lp-fs-*` TOKEN AND EVERY COLOUR A `--lp-*` TOKEN. Wave-21 R5 is gated app-wide over every CSS file under src, and a literal here reds `web_ui` for the whole tree. No emojis, no ALL-CAPS chrome (DESIGN.md R6). AND DO NOT WRITE A GLOB IN THIS COMMENT. A star-star followed by a slash spells the CSS COMMENT TERMINATOR — the comment would end in the middle of its own explanation and `lightningcss` would die far from the cause. Found by `web_build` in wave 32's QA; it blocked the deploy outright. --------------------------------------------------------------------------- */ /* ── the workspace: rail + surface, in the database module's own geometry ─── */ .qy-workspace { display: flex; width: 100%; height: 100%; min-width: 0; overflow: hidden; background: var(--lp-surface); } /* The same width and hairline `CustomerGrid`'s own views rail uses (`.cg-views`, index.css) — this rail stands in that slot, so it has to measure the same or Query stops looking like a database. */ .qy-rail { flex: 0 0 var(--lp-rail-w); width: var(--lp-rail-w); min-width: var(--lp-rail-w); height: 100%; box-sizing: border-box; display: flex; flex-direction: column; padding: 10px 8px; border-right: 1px solid var(--lp-line); background: var(--lp-surface); overflow: hidden; } .qy-rail-head { margin: 0 0 6px; padding: 0 6px; color: var(--lp-muted); font-size: var(--lp-fs-xs); font-weight: 600; } .qy-rail-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; } .qy-rail-group { margin-bottom: 10px; } /* ── R20: the per-view menu, the same vocabulary a database view offers ───── */ .qy-rail-more { flex: 0 0 auto; width: 24px; height: 24px; margin-right: 4px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 6px; background: transparent; color: var(--lp-muted); opacity: 0; cursor: pointer; } .qy-rail-more svg { fill: currentColor; stroke: none; } .qy-rail-row:hover .qy-rail-more, .qy-rail-row.is-active .qy-rail-more, .qy-rail-more:focus-visible, .qy-rail-more[aria-expanded="true"] { opacity: 1; } /* Sits above the rows, under the menu, so a click anywhere else closes it without every row needing its own outside-click listener. */ .qy-rail-scrim { position: fixed; inset: 0; z-index: 20; border: 0; padding: 0; background: transparent; cursor: default; } /* FIXED, not absolute: its natural parent `.qy-rail-scroll` is `overflow-y: auto`, which clips both axes, so a menu opened on a row near the bottom of the list would render cut off or invisible. `top`/`right` are supplied inline from the button's own rect. */ .qy-rail-menu { position: fixed; z-index: 21; min-width: 168px; margin-top: 2px; padding: 4px; display: flex; flex-direction: column; border: 1px solid var(--lp-line); border-radius: var(--lp-r-md); background: var(--lp-surface); box-shadow: 0 6px 18px rgba(20, 24, 40, 0.12); } .qy-rail-item { display: block; padding: 6px 10px; border: 0; border-radius: 6px; background: transparent; color: var(--lp-ink); font: inherit; font-size: var(--lp-fs-2xs); text-align: left; text-decoration: none; cursor: pointer; } .qy-rail-item:hover { background: var(--lp-surface-2); } .qy-rail-item.is-danger { color: var(--lp-red-deep); } .qy-rail-edit { flex: 1 1 auto; min-width: 0; padding: 2px 4px; } .qy-rail-input { width: 100%; box-sizing: border-box; padding: 5px 8px; border: 1px solid var(--lp-blue-solid); border-radius: 6px; background: var(--lp-surface); color: var(--lp-ink); font: inherit; font-size: var(--lp-fs-2xs); } .qy-rail-note { flex: 1 0 100%; margin: 0 0 4px; padding: 0 10px; color: var(--lp-muted); font-size: var(--lp-fs-2xs); line-height: var(--lp-lh); } .qy-rail-problem { margin: 0 6px 6px; color: var(--lp-red-deep); font-size: var(--lp-fs-2xs); } /* A confirmation, not a refusal. Same slot, different voice. */ .qy-rail-notice { margin: 0 6px 6px; color: var(--lp-muted); font-size: var(--lp-fs-2xs); line-height: var(--lp-lh); } .qy-rail-db { display: flex; align-items: center; gap: 6px; margin: 0 0 2px; padding: 4px 6px; color: var(--lp-muted); font-size: var(--lp-fs-xs); font-weight: 600; } .qy-rail-db-mark { flex: 0 0 auto; display: inline-flex; width: 14px; height: 14px; color: var(--lp-muted); } .qy-rail-db-mark svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; } .qy-rail-db-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .qy-rail-row { position: relative; display: flex; /* R20's description sits on its own line under the name, so the row wraps. `position: relative` was already here and is what the menu anchors to. */ flex-wrap: wrap; align-items: center; min-width: 0; border-radius: var(--lp-r-md); } .qy-rail-row:hover { background: var(--lp-surface-2); } .qy-rail-row.is-active { background: var(--lp-blue-tint); } .qy-rail-view { flex: 1 1 auto; min-width: 0; min-height: 32px; display: flex; flex-direction: column; justify-content: center; gap: 1px; padding: 5px 8px; border: 0; background: transparent; font: inherit; text-align: left; color: var(--lp-ink); cursor: pointer; } .qy-rail-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--lp-fs-xs); font-weight: 500; } .qy-rail-row.is-active .qy-rail-name { font-weight: 600; } .qy-rail-kind { display: flex; align-items: center; gap: 6px; min-width: 0; color: var(--lp-muted); font-size: var(--lp-fs-2xs); } /* ⭐ W35-T25 (R3) — the Edited mark. Quiet on purpose: it reports a state the reader created themselves, so it belongs beside the kind rather than competing with the view's name. */ .qy-rail-edited { flex: 0 0 auto; padding: 0 5px; border-radius: var(--lp-r-sm); background: var(--lp-surface-2); color: var(--lp-muted); font-size: var(--lp-fs-2xs); font-weight: 600; line-height: 15px; } .qy-rail-row.is-active .qy-rail-edited { background: var(--lp-surface); } /* ── ⭐⭐ W35-T47 (R4, C2) — the SHARED star button, re-placed for a rail row ───────────────── * * ⛔ GEOMETRY ONLY. `StarButton` is B's component and `starred.css` owns everything about how it * looks; overriding its colour or its glyph here would be a second opinion about one mark. What a * rail row genuinely needs different is placement: the shared rule is `position: absolute` on a * TILE wrapper, and this row is a flex list whose right end already holds the actions button, so * an absolutely positioned star would sit on top of it. * * ⚠ AND THE REVEAL RULE HAS TO BE RESTATED, not inherited. The shared sheet reveals the star with * `.st-tilewrap:hover`, a wrapper that does not exist here — so without the row's own hover rule * the button would be `opacity: 0` forever except when starred or focused, i.e. a control you can * only find by tabbing to it. `.is-on` and `:focus-visible` keep working from the shared sheet. */ .qy-rail-row .st-star { position: static; transform: none; flex: 0 0 auto; width: 22px; height: 22px; margin-right: 2px; } .qy-rail-row:hover .st-star { opacity: 1; } /* Hover-revealed, like the grid rail's own row menu — visible on focus so it is reachable from the keyboard rather than only from a pointer. */ .qy-rail-del { flex: 0 0 auto; width: 24px; height: 24px; margin-right: 4px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 6px; background: transparent; color: var(--lp-muted); opacity: 0; cursor: pointer; } .qy-rail-del svg { fill: none; stroke: currentColor; stroke-width: 1.3; stroke-linecap: round; stroke-linejoin: round; } .qy-rail-row:hover .qy-rail-del, .qy-rail-row.is-active .qy-rail-del, .qy-rail-del:focus-visible { opacity: 1; } .qy-rail-confirm { flex: 0 0 auto; margin-right: 4px; padding: 3px 8px; border: 0; border-radius: 6px; background: var(--lp-red-deep); color: var(--lp-surface); font: inherit; font-size: var(--lp-fs-2xs); font-weight: 600; cursor: pointer; } .qy-rail-ask { flex: 0 0 auto; margin-top: 8px; padding: 7px 8px; border-radius: var(--lp-r-md); color: var(--lp-blue-solid); font-size: var(--lp-fs-xs); font-weight: 600; text-decoration: none; } .qy-rail-ask:hover { background: var(--lp-surface-2); } /* ── the surface: the grid, and nothing above it ──────────────────────────── */ /* W35-T21 (owner item 4 / R2): the `.qy-prov*` family is deleted with the component. */ .qy-surface { flex: 1 1 auto; min-width: 0; height: 100%; display: flex; flex-direction: column; overflow: hidden; } .qy-grid-host { flex: 1 1 auto; min-height: 0; overflow: hidden; } /* ── the empty state ──────────────────────────────────────────────────────── */ .qy-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; height: 100%; min-height: 260px; padding: 34px 44px; box-sizing: border-box; background: var(--lp-wash); text-align: center; } .qy-empty-h { margin: 0 0 8px; color: var(--lp-ink); font-size: var(--lp-fs-lg); font-weight: 650; } .qy-empty-p { max-width: 58ch; margin: 0 0 18px; color: var(--lp-muted); font-size: var(--lp-fs-sm); line-height: var(--lp-lh); } .qy-empty-go { padding: 8px 14px; border-radius: var(--lp-r-md); background: var(--lp-blue-solid); color: var(--lp-surface); font-size: var(--lp-fs-sm); font-weight: 650; text-decoration: none; } .qy-empty-go:hover { background: var(--lp-blue-solid); }