/* InvictusCAD - shared components (buttons, badges, panels, tables, tabs, fields, top navigation).
   Loaded by BOTH apps after tokens.css; each app's own sheet only adds layout. */

/* buttons */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: var(--h-ctl); padding: 0 var(--s3); border: 1px solid var(--rule-strong); border-radius: var(--r-sm);
  background: var(--hull-2); color: var(--ink); font: 550 var(--fs-ui)/1 var(--sans);
  white-space: nowrap; cursor: pointer; text-decoration: none; transition: background .12s, border-color .12s, color .12s; }
.btn:hover { background: var(--hull-3); border-color: var(--ink-4); text-decoration: none; }
.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; box-shadow: none; }
.btn:disabled { opacity: .5; cursor: default; }
.btn.primary { background: var(--accent-deep); border-color: var(--accent-deep); color: #fff; }
.btn.primary:hover { background: color-mix(in srgb, var(--accent-deep) 85%, #000); border-color: transparent; }
.btn.ghost { background: transparent; border-color: transparent; color: var(--ink-2); }
.btn.ghost:hover { background: var(--hull-3); color: var(--ink); border-color: transparent; }
.btn.danger { color: var(--err); }
.btn.danger:hover { border-color: var(--err); background: color-mix(in srgb, var(--err) 10%, transparent); }
.btn.sm { height: var(--h-ctl-sm); padding: 0 var(--s2); font-size: var(--fs-meta); }
.btn.lg { height: 38px; padding: 0 var(--s4); font-size: var(--fs-body); }
.btn.icon { width: var(--h-ctl); padding: 0; }
.btn.icon.sm { width: var(--h-ctl-sm); }
.btn.busy { cursor: progress; opacity: .7; }
.btn-row { display: flex; gap: var(--s2); flex-wrap: wrap; align-items: center; }

/* badges: one shape (tinted fill, 20 px pill), state by colour.
   UX review 17 Oct (D7): the tint is mixed with --hull-1, so it is opaque and the chip reads the same on a hover or a
   selected row (mixed with transparent it took the row's colour: down to 3.3:1 on a selected row, and warn was 4.3:1 even
   on white); the word is the tone with 15 % --ink (darker in light, lighter in dark). Every tone is >= 5.2:1 on its own
   chip in both themes, on any row (tests/test_ux1017_d.py recomputes it from tokens.css and these rules). */
.badge { display: inline-flex; align-items: center; gap: 4px; height: 20px; padding: 0 8px;
  border-radius: 999px; font: 600 var(--fs-meta)/1 var(--sans); letter-spacing: .01em; white-space: nowrap;
  background: var(--hull-3); color: var(--ink-2); }
.badge.ok { background: color-mix(in srgb, var(--ok) 16%, var(--hull-1)); color: color-mix(in srgb, var(--ok) 85%, var(--ink)); }
.badge.warn { background: color-mix(in srgb, var(--warn) 18%, var(--hull-1)); color: color-mix(in srgb, var(--warn) 85%, var(--ink)); }
.badge.stale { background: color-mix(in srgb, var(--stale) 18%, var(--hull-1)); color: color-mix(in srgb, var(--stale) 85%, var(--ink)); }
.badge.err { background: color-mix(in srgb, var(--err) 15%, var(--hull-1)); color: color-mix(in srgb, var(--err) 85%, var(--ink)); }
.badge.info, .badge.example { background: color-mix(in srgb, var(--accent) 10%, var(--hull-1)); color: color-mix(in srgb, var(--accent) 85%, var(--ink)); }
.badge.dim { background: var(--hull-3); color: var(--ink-2); }   /* the plain badge, named (shared/status.js tones) */
.badge.outline { background: transparent; box-shadow: inset 0 0 0 1px var(--rule-strong); }

/* panels and cards */
.panel, .card { background: var(--hull-1); border: 1px solid var(--rule); border-radius: var(--r-xl); }
.card { box-shadow: var(--elev-1); overflow: hidden; }

/* tables */
.table, .jobs { width: 100%; border-collapse: collapse; font-size: var(--fs-meta); }
.table th, .jobs th { text-align: left; font-size: var(--fs-meta); font-weight: 600; color: var(--ink-3); padding: 10px 14px;
  border-bottom: 1px solid var(--rule); background: var(--hull-2); }
.table td, .jobs td { padding: 10px 14px; border-bottom: 1px solid var(--rule); vertical-align: middle; }
.table tr:last-child td, .jobs tr:last-child td { border-bottom: 0; }
.table .num { text-align: right; font-family: var(--mono); }

/* wide tables (UX review P2-3): a box with class scroll-cue fades the edge that has more to scroll to;
   shared/scrollcue.js sets cue-r / cue-l from the scroll position, so the fade goes at the end.
   pin1 keeps the first column (the option, the item) in view while the rest scrolls under it. */
.scroll-cue { overflow-x: auto; --cue: 56px; }
.scroll-cue.cue-r { -webkit-mask-image: linear-gradient(to left, transparent, #000 var(--cue));
  mask-image: linear-gradient(to left, transparent, #000 var(--cue)); }
.scroll-cue.cue-l:not(.pin1) { -webkit-mask-image: linear-gradient(to right, transparent, #000 var(--cue));
  mask-image: linear-gradient(to right, transparent, #000 var(--cue)); }
.scroll-cue.cue-l.cue-r:not(.pin1) {
  -webkit-mask-image: linear-gradient(to right, transparent, #000 var(--cue), #000 calc(100% - var(--cue)), transparent);
  mask-image: linear-gradient(to right, transparent, #000 var(--cue), #000 calc(100% - var(--cue)), transparent); }
:where(.scroll-cue.pin1) tr > :first-child { position: sticky; left: 0; z-index: 1; background: var(--panel); }
:where(.scroll-cue.pin1.cue-l) tr > :first-child { box-shadow: 6px 0 6px -5px rgba(0, 0, 0, .28); }
/* on a phone the pinned column never takes more than ~40% of the width (review LOW-13) */
@media (max-width: 760px) { :where(.scroll-cue.pin1) tr > :first-child { max-width: 38vw; overflow-wrap: anywhere; } }

/* segmented tabs */
.tabs { display: inline-flex; gap: 2px; padding: 2px; border: 1px solid var(--rule); border-radius: var(--r-md); background: var(--hull-1); }
.tabs button { height: 28px; padding: 0 12px; border: 0; border-radius: var(--r-sm); background: transparent; color: var(--ink-3); font: 550 var(--fs-ui) var(--sans); }
.tabs button:hover { color: var(--ink); background: var(--hull-3); }
.tabs button.on { background: var(--hull-4); color: var(--ink); }

/* fields */
.field, .select { height: var(--h-ctl); padding: 0 10px; border: 1px solid var(--rule-strong); border-radius: var(--r-sm);
  background: var(--hull-1); color: var(--ink); font: var(--fs-ui) var(--sans); min-width: 0; }
.field:focus, .select:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }

/* the Ctrl K hint has no meaning on a touch device */
@media (pointer: coarse) { #btnSearch kbd, .topbar-search kbd { display: none !important; } }

/* skeleton */
.skeleton { background: linear-gradient(90deg, var(--hull-2), var(--hull-3), var(--hull-2)); background-size: 200% 100%;
  animation: shimmer 1.2s linear infinite; border-radius: var(--r-md); }
@keyframes shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) { .skeleton { animation: none; } }

/* the shared top bar (brand, sections, Apps, account) lives in shared/header.css; its search box is shared here */
.topbar-search { display: flex; align-items: center; gap: 8px; height: var(--h-ctl); padding: 0 10px; min-width: 0;
  background: var(--hull-0); border: 1px solid var(--rule); border-radius: var(--r-md); color: var(--ink-3); cursor: pointer; font: var(--fs-ui) var(--sans); }
.topbar-search:hover { border-color: var(--rule-strong); color: var(--ink-2); }
.topbar-search kbd { margin-left: auto; font: var(--fs-meta) var(--mono); padding: 1px 6px; border: 1px solid var(--rule-strong); border-radius: 4px; color: var(--ink-3); background: none; }

.brand { display: flex; align-items: center; gap: 8px; font: 700 14px var(--sans); letter-spacing: -.01em; color: var(--ink); text-decoration: none; white-space: nowrap; flex: none; }
.brand svg, .sh-brand svg { color: var(--accent); }
/* InvictusCAD mark (concept B, "Dimensioned"). The shells still carry the old hexagon as an inline <svg>; hide it and
   draw the brand mark from /static/brand/invictuscad-mark.svg. When a shell swaps in <img class="mark"> (or marks its own
   svg with data-mark) this rule steps aside. */
.brand > svg:not([data-mark]), .sh-brand > svg:not([data-mark]) { display: none; }
.brand::before, .sh-brand::before { content: ""; flex: none; width: 24px; height: 24px; background: url("/static/brand/invictuscad-mark.svg") center / contain no-repeat; }
.brand:has(> .mark, > svg[data-mark])::before, .sh-brand:has(> .mark, > svg[data-mark])::before { display: none; }
.brand .mark, .sh-brand .mark { width: 24px; height: 24px; flex: none; }
.brand:hover, .sh-brand:hover { text-decoration: none; }

/* one in-page dialog (shared/ask.js, 2026-10-10 W17): every question the product asks, in both shells.
   Title, a body that scrolls when long, then the safe button and the action. Light/dark from the tokens.
   Documents' form dialogs add .ddlg (docs.css) for their labelled fields. */
.ask-dlg { box-sizing: border-box; width: min(440px, calc(100vw - 32px)); max-height: calc(100dvh - 16vh - 16px); margin: 16vh auto auto; padding: 20px;
  border: 1px solid var(--rule-strong); border-radius: var(--r-xl); background: var(--hull-1); color: var(--ink);
  box-shadow: var(--elev-2); font: var(--fs-body)/1.5 var(--sans); }
.ask-dlg[open] { display: flex; flex-direction: column; }
.ask-dlg::backdrop { background: var(--scrim); }
.ask-dlg > form { display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto; }
.ask-dlg h2 { margin: 0 0 4px; font: 600 var(--fs-title)/1.35 var(--sans); letter-spacing: -.01em; overflow-wrap: anywhere; }
.ask-dlg .ask-b { overflow: auto; min-height: 0; padding: 3px; margin: -3px; color: var(--ink-2); overflow-wrap: anywhere; }   /* the padding keeps a field's focus ring inside the scroll box */
.ask-dlg .ask-b > p { margin: 0 0 6px; }
.ask-dlg .ask-s { color: var(--ink); font-weight: 650; }
.ask-dlg ul { margin: 8px 0 0; padding-left: 18px; display: grid; gap: 6px; }
.ask-dlg .ask-n { margin: 10px 0 0; font-size: var(--fs-meta); color: var(--ink-3); }
.ask-dlg .ask-f { display: grid; gap: 5px; margin-top: 10px; font-size: var(--fs-meta); color: var(--ink-2); }
.ask-dlg .ask-f input { height: var(--h-ctl); padding: 0 10px; border: 1px solid var(--rule-strong); border-radius: var(--r-sm);
  background: var(--hull-0); color: var(--ink); font: var(--fs-body) var(--sans); min-width: 0; }
.ask-dlg .ask-f input:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
.ask-dlg .ask-a { display: flex; justify-content: flex-end; gap: 8px; margin-top: 18px; flex: none; }
.btn.primary.danger { background: var(--err); border-color: var(--err); color: var(--accent-ink); }
.btn.primary.danger:hover { background: color-mix(in srgb, var(--err) 86%, #000); border-color: transparent; }
@media (max-width: 560px), (pointer: coarse) {
  .ask-dlg { margin-top: 10vh; max-height: calc(100dvh - 10vh - 12px); padding: 18px 16px 16px; }
  .ask-dlg .ask-a .btn, .ask-dlg .ask-f input { min-height: 44px; }
  .ask-dlg .ask-a .btn { flex: 1 1 0; }
}
@media (prefers-reduced-motion: no-preference) {
  .ask-dlg[open] { animation: ask-in .14s var(--ease); }
  @keyframes ask-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
}
/* WP34: a link to a part in Recently deleted: "Deleted" + Restore (shared/delchip.js) */
.del-chip { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; vertical-align: middle; margin-left: 6px; }
.del-chip .btn.sm { padding: 0 8px; min-height: 22px; font-size: var(--fs-meta); }
@media (pointer: coarse) { .del-chip .btn.sm { min-height: 36px; } }

/* the assistant is offline (no language model configured): ONE quiet notice where the Copilot's inputs were, and those
   inputs greyed (shared/model-state.js, UX review P1-2, 2026-10-13) */
.model-off { margin: 0; padding: 8px 12px; border: 1px solid var(--rule-strong); border-radius: var(--r-sm);
  background: var(--hull-3); color: var(--ink-2); font-size: var(--fs-meta); line-height: 1.45; }
.is-model-off, .is-model-off:hover { opacity: .6; cursor: not-allowed; }
/* a disabled button reads as off in both themes, not as a dimmed live one (review LOW-11) */
button.is-model-off, button.is-model-off:hover, .composer .send.is-model-off, .composer .send.is-model-off:hover { background: var(--hull-3); color: var(--ink-3); border-color: var(--rule); box-shadow: none; }
textarea[aria-disabled="true"], input[aria-disabled="true"] { opacity: .55; cursor: not-allowed; }

/* "What you can do here" (RC22.7 D, SM UX 1; shared/hint.js): one quiet hint under a page head, a native <details>. Open on the
   first visit with up to three lines that name real controls; "Got it" or the summary folds it to one quiet line, remembered
   per account. Used on the Client brief and the Flotation tab only. A tint and a 1 px rule, never a coloured edge. */
.hint { margin: calc(var(--s1) * -1) 0 var(--s4); border: 1px solid var(--rule); border-radius: var(--r-md); background: var(--hull-2); }
.hint-s { display: flex; align-items: center; gap: var(--s2); width: fit-content; max-width: 100%; padding: var(--s2) var(--s3); list-style: none;
  cursor: pointer; border-radius: var(--r-md); font: 650 var(--fs-ui)/var(--lh-ui) var(--sans); color: var(--ink); }
.hint-s::-webkit-details-marker { display: none; }
.hint-s::marker { content: ""; }
.hint-s:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.hint-s .hint-i { flex: none; color: var(--accent); }
.hint-s .hint-c { flex: none; color: var(--ink-3); transition: transform .15s var(--ease); }
.hint[open] .hint-s .hint-c { transform: rotate(180deg); }
.hint-b { display: flex; align-items: flex-end; gap: var(--s4); padding: 0 var(--s3) var(--s3) calc(var(--s3) + 24px); }
.hint-l { margin: 0; padding: 0; list-style: none; display: grid; gap: var(--s1); max-width: 78ch; font: 400 var(--fs-ui)/var(--lh-body) var(--sans); color: var(--ink-2); }
.hint-l li { position: relative; padding-left: var(--s3); }
.hint-l li::before { content: ""; position: absolute; left: 0; top: .62em; width: 4px; height: 4px; border-radius: 50%; background: var(--ink-4); }
.hint-l b { font-weight: 650; color: var(--ink); }
.hint-b .btn { margin-left: auto; flex: none; }
.hint:not([open]) { margin: calc(var(--s3) * -1) 0 var(--s3) calc(var(--s3) * -1); border-color: transparent; background: none; }
.hint:not([open]) .hint-s { padding: var(--s1) var(--s3); font-weight: 500; color: var(--ink-3); }
@media (prefers-reduced-motion: reduce) { .hint-s .hint-c { transition: none; } }

/* ── RC22.6 K15 kit patterns (ux-audit/rc226-big/design-system/SPEC.md section 3), flag STUDIO_KIT_PATTERNS: the server stamps
   <html data-kit="v2"> for a listed account (app/kit_flags.py). Every rule here is under that attribute, so with the flag off
   none of it applies. Toasts and menus share one overlay: --hull-2, a 1 px --rule-strong edge, --r-md, --elev-2. ── */

/* toast (shared/toast.js): bottom centre of the work column, clear of the docked rail and the phone tab bar; 3 at most.
   Tone is the 16 px icon, never a coloured edge. The host is a region, not a live region: each toast says itself once. */
:root[data-kit="v2"] .toasts { position: fixed; z-index: 96; left: 50%; bottom: calc(16px + env(safe-area-inset-bottom)); transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 8px; width: max-content; max-width: calc(100vw - 32px); pointer-events: none; }
:root[data-kit="v2"][data-crail="open"] .toasts { left: calc((100% - var(--crail-w, 0px)) / 2); }
@media (max-width: 760px) { :root[data-kit="v2"] .toasts { bottom: calc(72px + env(safe-area-inset-bottom)); } }
@media (max-width: 760px) { :root[data-kit="v2"][data-crail] .toasts { left: 50%; bottom: calc(var(--crail-tab, 56px) + 64px + env(safe-area-inset-bottom)); } }
:root[data-kit="v2"] .toast { display: flex; align-items: center; gap: 10px; width: fit-content; max-width: min(480px, 100%); min-height: 44px;
  padding: 6px 6px 6px 12px; margin: 0; background: var(--hull-2); border: 1px solid var(--rule-strong); border-radius: var(--r-md);
  box-shadow: var(--elev-2); color: var(--ink); font: 400 var(--fs-ui)/var(--lh-ui) var(--sans); pointer-events: auto; text-align: left; }
:root[data-kit="v2"] .toast.bare { padding-right: 14px; }
:root[data-kit="v2"] .toast-i { flex: none; display: block; }
:root[data-kit="v2"] .toast[data-tone="info"] .toast-i { color: var(--accent); }
:root[data-kit="v2"] .toast[data-tone="ok"] .toast-i { color: var(--ok); }
:root[data-kit="v2"] .toast[data-tone="warn"] .toast-i { color: var(--warn); }
:root[data-kit="v2"] .toast[data-tone="err"] .toast-i { color: var(--err); }
:root[data-kit="v2"] .toast-t { flex: 1 1 auto; min-width: 0; padding: 5px 0; overflow-wrap: anywhere; }
:root[data-kit="v2"] .toast-t b { font-weight: 650; }
:root[data-kit="v2"] .toast-a { flex: none; display: flex; align-items: center; gap: 4px; }
:root[data-kit="v2"] .toast-x { width: var(--h-ctl-sm); padding: 0; }
@media (pointer: coarse) { :root[data-kit="v2"] .toast-a .btn { min-height: 40px; } :root[data-kit="v2"] .toast-x { width: 40px; } }
@media (prefers-reduced-motion: no-preference) { :root[data-kit="v2"] .toast { animation: kit-toast-in .16s var(--ease); } }
@media (prefers-reduced-motion: reduce) { :root[data-kit="v2"] .toast { animation: kit-toast-fade .16s linear; } }
@keyframes kit-toast-in { from { opacity: 0; transform: translateY(8px); } }
@keyframes kit-toast-fade { from { opacity: 0; } }

/* menu (shared/menu.js): fixed to its button (a table's overflow never clips it), flips up near the bottom of the window.
   32 px items, 44 px on touch; the keyboard's item has --hull-3 and a 2 px inset ring (K4). */
:root[data-kit="v2"] .menu { min-width: 220px; max-width: min(340px, calc(100vw - 16px)); max-height: min(70vh, 520px); overflow-y: auto; padding: 4px;
  display: grid; align-content: start; background: var(--hull-2); border: 1px solid var(--rule-strong); border-radius: var(--r-md);
  box-shadow: var(--elev-2); color: var(--ink); z-index: 120; }
:root[data-kit="v2"] .menu[hidden] { display: none; }
:root[data-kit="v2"] .menu.is-fixed { position: fixed; }
:root[data-kit="v2"] .menu-h { padding: 8px 10px 4px; font: 600 var(--fs-meta)/var(--lh-meta) var(--sans); color: var(--ink-3); }
:root[data-kit="v2"] .menu-i { display: grid; grid-template-columns: 16px minmax(0, 1fr) auto; column-gap: 10px; align-items: start; width: 100%;
  min-height: 32px; padding: 7px 10px; margin: 0; border: 0; border-radius: var(--r-sm); background: none; color: var(--ink); text-align: left;
  cursor: pointer; font: 400 var(--fs-ui)/var(--lh-ui) var(--sans); text-decoration: none; }
:root[data-kit="v2"] .menu-i:hover { background: var(--hull-3); }
:root[data-kit="v2"] .menu-i:focus-visible, :root[data-kit="v2"] .menu-i.kb { background: var(--hull-3); outline: 2px solid var(--accent); outline-offset: -2px; }
:root[data-kit="v2"] .menu-ck { display: grid; place-items: center; height: 18px; color: var(--accent); }
:root[data-kit="v2"] .menu-l { min-width: 0; overflow-wrap: anywhere; }
:root[data-kit="v2"] .menu-i b { font-weight: 600; }
:root[data-kit="v2"] .menu-sub { display: block; margin-top: 1px; font: var(--fs-meta)/var(--lh-meta) var(--sans); color: var(--ink-3); }
:root[data-kit="v2"] .menu-k { font: 500 var(--fs-meta)/var(--lh-ui) var(--sans); color: var(--ink-4); }
:root[data-kit="v2"] .menu-i.danger { color: var(--err); }
:root[data-kit="v2"] .menu-i.danger:hover { background: color-mix(in srgb, var(--err) 10%, var(--hull-2)); }
:root[data-kit="v2"] .menu-i[aria-disabled="true"] { color: var(--ink-4); cursor: default; }
:root[data-kit="v2"] .menu-i[aria-disabled="true"]:hover { background: none; }
:root[data-kit="v2"] .menu-sep { height: 1px; margin: 4px 6px; background: var(--rule); }
:root[data-kit="v2"] .menu-i[hidden] { display: none; }
:root[data-kit="v2"] .menu-list { display: grid; align-content: start; min-width: 0; }
:root[data-kit="v2"] .menu-find { display: block; width: auto; height: 32px; margin: 2px 2px 4px; padding: 0 8px; border: 1px solid var(--rule-strong);
  border-radius: var(--r-sm); background: var(--hull-1); color: var(--ink); font: var(--fs-ui)/1 var(--sans); }
:root[data-kit="v2"] .menu-find:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }
@media (pointer: coarse) { :root[data-kit="v2"] .menu-i { min-height: 44px; padding-block: 12px; } }

/* empty state: first use teaches and offers the first action; no results names the filter and offers Clear filters; error gives
   the server's sentence and Retry. Left-aligned (centred only on a canvas), dashed only on a drop target. */
:root[data-kit="v2"] .empty-state { display: grid; justify-items: start; align-content: start; gap: 6px; padding: 28px 24px; text-align: left;
  grid-column: 1 / -1; }
:root[data-kit="v2"] .empty-state[hidden] { display: none; }
:root[data-kit="v2"] .empty-state > .es-i { color: var(--ink-3); margin-bottom: 6px; }
:root[data-kit="v2"] .empty-state .es-t { margin: 0; font: 650 var(--fs-title)/var(--lh-title) var(--sans); color: var(--ink); letter-spacing: -.005em; }
:root[data-kit="v2"] .empty-state .es-x { margin: 0; max-width: 52ch; font: 400 var(--fs-body)/var(--lh-body) var(--sans); color: var(--ink-3); }
:root[data-kit="v2"] .empty-state .btn-row { margin-top: 10px; }
:root[data-kit="v2"] .empty-state.canvas { justify-items: center; text-align: center; margin: auto; }
:root[data-kit="v2"] .empty-state.drop { border: 1px dashed var(--rule-strong); border-radius: var(--r-xl); }
:root[data-kit="v2"] .empty-state.compact { padding: 12px 14px; gap: 2px; }
:root[data-kit="v2"] .empty-state.compact .es-t { font: 600 var(--fs-ui)/var(--lh-ui) var(--sans); }
:root[data-kit="v2"] .empty-state.compact .es-x { font: var(--fs-ui)/var(--lh-ui) var(--sans); }
:root[data-kit="v2"] .empty-state.compact .btn-row { margin-top: 6px; }
