/* 08-skills - part of the former static/style.css. * * The stylesheet is split by area, but the split is mechanical: rules were * moved verbatim and NOT regrouped. The numeric prefix is the load order, and * that order is what preserves the cascade. A rule sits in the file its * original position fell into, so an area's rules are not all in its file and * a file is not only that area's rules. * * Do not reorder the tags in static/index.html, and do not move a rule * between files: with 1,151 redeclarations and 1,886 !important in this * cascade, either can change which declaration wins. * * static/index.html and the PRECACHE list in static/sw.js must agree on this * order. tests/test_static_stylesheet_manifest.py and * tests/test_css_computed_style_snapshot.py fail if that drifts. */ /* Document cards can be expanded in the normal windowed library, where the grid has no definite height. A zero-basis
then collapses to nothing
while the footer remains visible. Keep the document text's intrinsic height
in that layout; bounded/fullscreen layouts can still shrink it and scroll. */
#doclib-modal .doclib-card.doclib-card-expanded .doclib-card-preview > pre {
flex: 1 1 auto;
min-height: 1.5em;
}
/* Collapsible skills section headers (Your skills / Built-in). */
.skills-section-header {
display: flex;
align-items: center;
gap: 6px;
cursor: pointer;
user-select: none;
padding: 6px 4px 4px;
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.06em;
opacity: 0.6;
font-weight: 600;
}
.skills-section-header:hover { opacity: 0.9; }
.skills-section-chevron { transition: transform 0.15s ease; flex-shrink: 0; }
.skills-section-header.collapsed .skills-section-chevron { transform: rotate(-90deg); }
.skills-section-count {
margin-left: auto;
opacity: 0.6;
font-variant-numeric: tabular-nums;
font-weight: normal;
}
.skill-card-section-hidden { display: none !important; }
/* Warning banner shown when previewing/editing a built-in capability. */
.skill-builtin-warn {
font-size: 9.5px;
line-height: 1.45;
color: var(--color-warning, #f0ad4e);
background: color-mix(in srgb, var(--color-warning, #f0ad4e) 12%, transparent);
border: 1px solid color-mix(in srgb, var(--color-warning, #f0ad4e) 35%, transparent);
border-radius: 6px;
padding: 6px 9px;
margin: 0 0 8px;
flex-shrink: 0;
}
/* #skills-list wears both .memory-list and .doclib-grid. doclib-grid's
max-height:400px would cap the list AND clamp an expanded card — keep
the memory-list fill-the-modal behaviour instead. */
#skills-list.doclib-grid {
max-height: none;
flex: 1;
min-height: 0;
}
#skills-list.doclib-grid:has(.doclib-card-expanded) {
overflow: hidden; /* expanded card owns its own scroll */
}
/* When a skill is expanded, hide the sibling Add-Skill form card so the
expanded SKILL.md uses the full panel. Otherwise the two admin-cards
split the height ~50/50 and the expanded skill is stuck in its half
while the Add form idles in the other. */
.memory-tab-panel[data-memory-panel="skills"]:has(.doclib-card-expanded) > .admin-card:nth-of-type(2) {
display: none !important;
}
/* Skills cards reuse the doclib expand/footer machinery. The SKILL.md
preview + the edit textarea need to fill the expanded card and own
their own scroll, same as a document preview. */
/* Collapsed skill rows used a smaller (0.9em code) title, so the click row
read slimmer than document/chat/library cards. Give the header a min-height
so the tap target matches the other library items. */
.skill-card > .skill-card-header {
min-height: 53px;
box-sizing: border-box;
display: flex;
align-items: center;
gap: 6px;
}
/* Skill rows share Tasks' card hover colors. Draft state belongs in the
status badge, not in a second opacity layer across the whole row. */
/* Name + description column. Name wraps to 2 lines (skill slugs are long —
use the space rather than truncating to "check-model-downl…"). */
.skill-card-textcol {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
}
.skill-card-title-row {
display: flex;
align-items: center;
gap: 6px;
min-width: 0;
}
.skill-card-name {
font-family: inherit;
font-weight: 500;
font-size: 12px;
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-break: normal;
line-height: normal;
}
.skill-card-title-row .skill-card-name { min-width: 0; }
.skill-status-published {
width: 20px;
height: 20px;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--color-success, #4ade80);
background: color-mix(in srgb, var(--color-success, #4ade80) 15%, transparent);
border-color: color-mix(in srgb, var(--color-success, #4ade80) 32%, transparent);
border-radius: 50%;
}
.skill-stats .skill-status-published {
width: 15px;
height: 15px;
margin-left: 3px;
vertical-align: middle;
}
.skill-stats .skill-status-published svg {
width: 9px;
height: 9px;
}
.skill-card-desc {
font-size: 10px;
opacity: 0.4;
margin-top: -1px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.skills-audit-model-field {
display: flex;
align-items: center;
gap: 8px;
margin-top: 10px;
font-size: 11px;
}
.skills-audit-model-field > span {
flex: 0 0 auto;
color: color-mix(in srgb, var(--fg) 68%, transparent);
}
.skills-audit-model-field select {
min-width: 0;
flex: 1;
height: 26px;
padding: 0 6px;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--bg);
color: var(--fg);
font: inherit;
}
.skill-card-tags {
display: flex;
gap: 4px;
flex-wrap: wrap;
margin-top: 4px;
}
.skill-tag-pill {
padding: 1px 5px;
font-size: 9px;
}
.skill-usage-pill {
margin-left: auto;
color: color-mix(in srgb, var(--fg) 62%, transparent);
}
.skill-tag-filter-chip { cursor: pointer; }
@media (max-width: 768px) {
.skill-card > .skill-card-header {
align-items: flex-start;
}
.skill-card-textcol {
min-width: 0;
}
.skill-card-tags {
max-width: 100%;
overflow: hidden;
}
.skill-card-tags .skill-tag-pill {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
/* Right cluster — pills (right-aligned), stats, then the menu/chevron. */
.skill-card-right {
display: flex;
align-items: center;
gap: 6px;
flex-shrink: 0;
margin-left: auto;
}
.skill-card-right .memory-cat-badge { flex-shrink: 0; }
.skill-status-bin {
background: color-mix(in srgb, var(--color-danger, #d9534f) 20%, transparent);
color: var(--color-danger, #d9534f);
}
.skill-model-pill {
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.skill-model-student {
background: color-mix(in srgb, var(--fg) 10%, transparent);
}
.skill-audit-pass {
color: var(--color-success, #4ade80);
background: color-mix(in srgb, var(--color-success, #4ade80) 16%, transparent);
}
.skill-audit-needs_work {
color: var(--color-warning, #f0ad4e);
background: color-mix(in srgb, var(--color-warning, #f0ad4e) 16%, transparent);
}
.skill-audit-fail {
color: var(--color-danger, #e06c75);
background: color-mix(in srgb, var(--color-danger, #e06c75) 16%, transparent);
}
.skill-audit-inconclusive,
.skill-audit-skipped,
.skill-audit-unknown {
color: color-mix(in srgb, var(--fg) 64%, transparent);
background: color-mix(in srgb, var(--fg) 8%, transparent);
}
.skill-model-teacher {
background: color-mix(in srgb, var(--color-warning, #f0ad4e) 24%, transparent);
color: var(--color-warning, #f0ad4e);
}
.skill-necessity-pill {
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.skill-necessity-duplicate {
background: color-mix(in srgb, var(--color-warning, #f0ad4e) 22%, transparent);
color: var(--color-warning, #f0ad4e);
}
.skill-necessity-trivial {
background: color-mix(in srgb, var(--fg) 8%, transparent);
color: color-mix(in srgb, var(--fg) 62%, transparent);
border-color: color-mix(in srgb, var(--fg) 16%, transparent);
}
.skill-necessity-irrelevant {
background: color-mix(in srgb, var(--color-danger, #e06c75) 18%, transparent);
color: var(--color-danger, #e06c75);
}
.skills-summary-strip {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: nowrap;
overflow-x: auto;
overflow-y: hidden;
overscroll-behavior-x: contain;
touch-action: pan-x;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
margin: -3px 0 8px;
}
.skills-summary-strip::-webkit-scrollbar { display: none; }
.skills-summary-strip:empty {
display: none;
}
.skills-summary-chip {
display: inline-flex;
align-items: center;
gap: 5px;
height: 23px;
padding: 0 8px;
border: 1px solid color-mix(in srgb, var(--fg) 12%, transparent);
border-radius: 6px;
background: color-mix(in srgb, var(--fg) 4%, transparent);
color: color-mix(in srgb, var(--fg) 70%, transparent);
font-size: 10px;
line-height: 1;
cursor: pointer;
flex: 0 0 auto;
}
.skills-summary-chip:hover {
background: color-mix(in srgb, var(--fg) 8%, transparent);
border-color: color-mix(in srgb, var(--fg) 18%, transparent);
}
.skills-summary-chip.active {
background: color-mix(in srgb, var(--accent, var(--red)) 13%, transparent);
border-color: color-mix(in srgb, var(--accent, var(--red)) 28%, transparent);
color: var(--fg);
}
.skills-summary-chip strong {
font-family: ui-monospace, 'SF Mono', 'Fira Code', monospace;
font-size: 10px;
color: var(--fg);
}
/* Shared filter/tag strip contract. These rows all sit immediately below
their screen's search or primary toolbar and stay one line at every width. */
:is(
.skills-summary-strip,
#memory-category-filters,
.memory-category-filters:has(> .memory-cat-chip),
.doclib-lang-chips,
.doclib-chips,
.notes-labels-bar,
.tasks-activity-filters,
.gallery-tag-chips,
.gallery-album-chips,
.gallery-ai-tags,
.cal-filters
) {
align-items: center;
flex-wrap: nowrap !important;
gap: 6px;
width: 100%;
max-width: 100%;
min-width: 0;
max-height: none;
overflow-x: auto !important;
overflow-y: hidden !important;
overscroll-behavior-x: contain;
touch-action: pan-x;
-webkit-overflow-scrolling: touch;
scrollbar-width: none;
box-sizing: border-box;
padding: 0;
margin: -3px 0 8px;
}
:is(
.skills-summary-strip,
#memory-category-filters,
.memory-category-filters:has(> .memory-cat-chip),
.doclib-lang-chips,
.doclib-chips,
.notes-labels-bar,
.tasks-activity-filters,
.gallery-tag-chips,
.gallery-album-chips,
.gallery-ai-tags,
.cal-filters
)::-webkit-scrollbar { display: none; }
:is(
.skills-summary-strip > .skills-summary-chip,
#memory-category-filters > .skills-summary-chip,
.memory-category-filters > .memory-cat-chip,
.doclib-lang-chips > :is(.memory-cat-chip, .skills-summary-chip),
.doclib-chips > .doclib-chip,
.notes-labels-bar > .notes-label-chip,
.tasks-activity-filters > .memory-cat-chip,
.gallery-tag-chips > .gallery-chip,
.gallery-album-chips > .gallery-chip,
.gallery-ai-tags > .gallery-ai-chip,
.cal-filters > .cal-filter-item
) {
flex: 0 0 auto;
box-sizing: border-box;
height: 23px !important;
min-height: 23px;
padding: 0 8px !important;
border-radius: 6px !important;
font-size: 10px !important;
line-height: 1;
white-space: nowrap;
}
.doclib-lang-chips {
flex: 0 0 auto;
min-height: 25px;
padding: 1px 0 !important;
scroll-padding-inline: 1px;
}
.doclib-chip-scroll-frame {
position: relative;
display: flex;
align-items: center;
flex: 1 1 100%;
min-width: 0;
max-width: 100%;
}
.doclib-chip-scroll-frame > .chip-scroll-strip {
flex: 1 1 auto;
min-width: 0;
margin-left: 0;
margin-right: 0;
}
.doclib-chip-scroll-arrow {
display: none;
position: absolute;
top: calc(50% - 1px) !important;
transform: translateY(-50%);
margin-top: 0;
z-index: 2;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
padding: 0;
border: 1px solid color-mix(in srgb, var(--fg) 14%, transparent);
border-radius: 50%;
box-sizing: border-box;
background: color-mix(in srgb, var(--bg) 86%, var(--fg) 14%);
color: var(--accent, var(--red));
box-shadow: 0 6px 18px rgba(0,0,0,0.16);
font-size: 18px;
line-height: 18px;
cursor: pointer;
opacity: 0.84;
transition: opacity 0.12s ease, background 0.12s ease, color 0.12s ease;
}
.doclib-chip-scroll-arrow-glyph {
display: inline-block;
position: relative;
top: -1px;
line-height: 1;
}
.doclib-chip-scroll-arrow:hover {
opacity: 1;
background: color-mix(in srgb, var(--bg) 76%, var(--fg) 24%);
}
@media (max-width: 768px) {
.doclib-chip-scroll-arrow { display: none !important; }
}
.doclib-chip-scroll-frame.has-overflow > .doclib-chip-scroll-arrow:not([hidden]) {
display: inline-flex;
}
.doclib-chip-scroll-arrow.left { left: 0; }
.doclib-chip-scroll-arrow.right { right: 0; }
.doclib-chip-scroll-frame.has-overflow > .chip-scroll-strip {
padding-left: 0 !important;
padding-right: 22px !important;
}
.doclib-chip-scroll-frame > .chip-scroll-strip.is-pointer-dragging {
cursor: grabbing;
user-select: none;
}
.doclib-chip-scroll-frame > .chip-scroll-strip:not(.is-pointer-dragging) {
cursor: grab;
}
/* Calendar tags are wrapped by chipScroll.js after render. Keep that wrapper
content-sized inside the calendar's column layout; the default flex growth
otherwise turns the one-line tag strip into a large empty panel. */
#cal-body > .doclib-chip-scroll-frame:has(> .cal-filters) {
flex: 0 0 auto;
min-height: 0;
}
/* The completed-task filters are a compact toolbar, not a flexible panel.
Without this override the shared chip frame grows into the available
height of the card, which is especially visible the first time the mobile
view switches to Completed. */
.tasks-completed-card > .doclib-chip-scroll-frame:has(> #tasks-completed-status-chips) {
flex: 0 0 auto;
min-height: 0;
}
/* The active Tasks filter row is also a compact toolbar. Keep its wrapper
content-sized so changing a filter cannot make the mobile panel grow a
large empty gap between the chips and the task cards. */
.admin-card > .doclib-chip-scroll-frame:has(> #tasks-filter-chips) {
flex: 0 0 auto;
min-height: 0;
}
#memory-category-filters {
flex: 0 0 auto;
min-height: 25px;
padding: 1px 0 !important;
scroll-padding-inline: 1px;
}
.cookbook-serve-tag-label {
display: inline-flex;
align-items: center;
min-width: 0;
}
#serve-search,
#hwfit-cache-select,
#serve-tags {
position: relative;
top: -2px;
}
#hwfit-cache-select {
top: -4px;
}
/* Calendar previously framed its tags as a separate card. It now follows the
same unframed placement as the other filter strips. */
.cal-filters {
border: 0;
background: none;
box-shadow: none;
}
.skill-duplicate-keep {
background: color-mix(in srgb, var(--color-success, #4ade80) 22%, transparent);
color: var(--color-success, #4ade80);
}
.skill-duplicate-group {
font-family: ui-monospace, 'SF Mono', 'Fira Code', monospace;
color: color-mix(in srgb, var(--fg) 62%, transparent);
background: color-mix(in srgb, var(--fg) 8%, transparent);
}
.skill-duplicate-lower {
background: color-mix(in srgb, var(--color-danger, #e06c75) 14%, transparent);
color: var(--color-danger, #e06c75);
}
.skill-stats {
font-size: 9px;
font-family: monospace;
white-space: nowrap;
color: color-mix(in srgb, var(--fg) 45%, transparent);
}
.skill-turns-saved {
font-size: 9px;
opacity: 0.82;
font-variant-numeric: tabular-nums;
white-space: nowrap;
flex: 0 0 auto;
}
.skill-expanded-savings {
display: flex;
align-items: center;
min-height: 18px;
margin: 0 0 5px;
}
.skill-turns-positive {
color: color-mix(in srgb, var(--color-success, #4ade80) 82%, var(--fg));
}
.skill-turns-zero,
.skill-turns-unknown {
color: color-mix(in srgb, var(--fg) 48%, transparent);
}
.skill-turns-negative {
color: color-mix(in srgb, var(--color-danger, #e06c75) 80%, var(--fg));
}
.skill-conf {
display: inline-block;
min-width: 4ch;
text-align: right;
font-weight: 700;
color: var(--accent, var(--red));
}
.skill-verified,
.skill-teachermark,
.skill-needsmark {
display: inline-flex;
align-items: center;
vertical-align: middle;
margin-right: 3px;
}
.skill-verified { color: var(--accent, #4ade80); }
.skill-teachermark { color: var(--color-warning, #f0ad4e); }
.skill-needsmark { color: var(--color-danger, #e06c75); cursor: help; }
/* The card currently being processed by an "Audit now" run glows + pulses so
it's obvious which one the audit is on. */
@keyframes skill-audit-pulse {
0%, 100% { box-shadow: 0 0 6px 0 color-mix(in srgb, var(--accent, #4ade80) 55%, transparent); }
50% { box-shadow: 0 0 16px 3px color-mix(in srgb, var(--accent, #4ade80) 85%, transparent); }
}
.skill-card.skill-audit-active {
border-color: var(--accent, #4ade80) !important;
animation: skill-audit-pulse 1.3s ease-in-out infinite;
}
/* A test is running for this skill — the app's whirlpool spinner is injected
next to the name from JS (see _setCardRunning) so the collapsed/folded card
still makes it obvious work is in progress. */
/* Collapsed bar shows the kebab menu; expanded shows an up-chevron to
collapse. (Toggled by the .doclib-card-expanded class.) */
.skill-kebab-btn {
display: inline-flex;
align-items: center;
justify-content: center;
background: none;
border: none;
padding: 4px;
margin: 0;
cursor: pointer;
color: var(--fg);
opacity: 0.5;
border-radius: 5px;
flex-shrink: 0;
position: relative;
top: -2px;
}
.skill-kebab-btn:hover { opacity: 1; background: color-mix(in srgb, var(--fg) 10%, transparent); }
.skill-chevron-up { display: none; align-items: center; opacity: 0.5; flex-shrink: 0; }
/* Kebab dropdown */
.skill-kebab-menu {
position: fixed;
/* z-index is set inline via topPortalZ() at open time (#4720); a static
value here loses once the modal bring-to-front counter climbs past it. */
min-width: auto;
width: max-content;
padding: 4px;
background: var(--panel, var(--bg));
border: 1px solid var(--border);
border-radius: 8px;
box-shadow: 0 6px 20px rgba(0,0,0,0.22);
display: flex;
flex-direction: column;
gap: 1px;
}
.skill-kebab-item {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
padding: 6px 8px;
background: none;
border: none;
border-radius: 8px;
cursor: pointer;
color: var(--fg);
font-size: 11px;
text-align: left;
}
.skill-kebab-item:hover { background: color-mix(in srgb, var(--fg) 10%, transparent); }
.skill-kebab-item.danger { color: var(--color-error, #e55); }
.skill-kebab-item.danger:hover { background: color-mix(in srgb, var(--color-error, #e55) 15%, transparent); }
/* Section labels separating "Your skills" from the read-only "Built-in
capabilities" list. */
.skills-section-label {
font-size: 10px;
opacity: 0.5;
text-transform: uppercase;
letter-spacing: 0.04em;
margin: 10px 2px 4px;
flex-shrink: 0;
}
.skills-section-label:first-child { margin-top: 0; }
/* When a learned skill is expanded the grid hides sibling cards; hide the
section labels too so they don't orphan above the collapsed list. */
#skills-list:has(.doclib-card-expanded) .skills-section-label { display: none; }
/* Built-in cards are read-only — no expand/hover-pointer affordance. */
.skill-builtin-card { cursor: default; }
.skill-builtin-card:hover { border-color: var(--border); }
.skill-card-preview .skill-md-pre {
font-family: ui-monospace, 'SF Mono', 'Fira Code', monospace;
font-size: 11.5px;
line-height: 1.5;
white-space: pre-wrap;
word-break: break-word;
}
.skill-preview-copy {
position: absolute;
top: 6px;
right: 8px;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
padding: 0;
opacity: 0;
color: var(--fg);
background: var(--bg);
border: 1px solid var(--border);
border-radius: 6px;
cursor: pointer;
transition: opacity .15s, color .15s, border-color .15s;
}
.skill-card-preview:hover .skill-preview-copy { opacity: 0.7; }
.skill-preview-copy:hover,
.skill-preview-copy:focus-visible { opacity: 1 !important; }
.skill-preview-copy.copied {
opacity: 1;
color: var(--color-save-green, #4caf50);
border-color: var(--color-save-green, #4caf50);
}
/* :has()-free expand layout. skills.js adds .skills-has-expanded to the
skills admin-card when a skill opens, so this works on engines that don't
support :has() (e.g. older Firefox mobile, where the expand stuck at
~50%). Hide everything but the list so the grid fills the card; the
absolutely-positioned expanded card (mobile) then fills the grid. */
.admin-card.skills-has-expanded > *:not(#skills-list) {
display: none !important;
}
.admin-card.skills-has-expanded > #skills-list.doclib-grid {
flex: 1 1 auto !important;
height: 100% !important;
min-height: 0 !important;
overflow: hidden !important;
}
/* Expand animation — a clean opacity fade. The mobile card snaps to a
full-screen overlay (position:absolute), so any translate/scale on it
reads as a jarring "explosion"; a pure fade just appears smoothly. (No
transform also keeps skills.js's height measurement accurate.) */
@keyframes skill-card-expand {
from { opacity: 0; }
to { opacity: 1; }
}
.skill-card.doclib-card-expanded {
animation: skill-card-expand 0.18s ease-out;
}
/* Switching directly from one expanded card to another: no fade (it would
show the previous card collapsing through the semi-transparent new one). */
.skill-card.doclib-card-expanded.skill-expand-instant {
animation: none !important;
}
@media (prefers-reduced-motion: reduce) {
.skill-card.doclib-card-expanded,
.skill-card.doclib-card-expanded .doclib-card-preview {
animation: none !important;
}
}
/* The preview WRAPPER inherits flex:1 (grow) from the generic doclib expand
rule — that's what stretched the card and left a void / pushed the footer
out. Size it to content (shrink + scroll only when long) so the footer
sits right under the preview and stays fully visible. */
.skill-card.doclib-card-expanded .doclib-card-preview {
flex: 0 1 auto !important;
min-height: 0;
/* The footer lives INSIDE this wrapper. Don't let the wrapper scroll, or
the footer scrolls off with the content. Keep it clipped; the
inside is the scroller, so the footer stays pinned at the bottom. */
overflow: hidden !important;
}
.skill-card.doclib-card-expanded .skill-md-pre {
flex: 1 1 auto;
min-height: 0;
overflow-y: auto;
}
/* Skill footer buttons sit 4px high (shared rule sets top:-4px) — drop them
back down 4px so they're vertically centered in the skill card footer. */
.skill-card .doclib-card-expanded-actions > .doclib-card-action-btn,
.skill-card .doclib-card-expanded-actions .doclib-action-btn-row > .doclib-card-action-btn {
top: 0;
}
/* Skills select-mode bulk bar — nudge Cancel / Approve / Delete up 2px. */
#skills-bulk-bar .memory-toolbar-btn {
position: relative;
top: -2px;
}
/* ── Audit-all progress panel ── */
.skills-audit-panel {
border: 1px solid var(--border);
border-radius: 8px;
padding: 8px 10px;
margin: 6px 0;
background: color-mix(in srgb, var(--fg) 3%, transparent);
}
.skills-audit-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.skills-audit-title { font-size: 12px; font-weight: 600; }
.skills-audit-bar { height: 4px; border-radius: 2px; background: color-mix(in srgb, var(--fg) 12%, transparent); margin: 6px 0; overflow: hidden; }
.skills-audit-fill { height: 100%; background: var(--accent, var(--red)); transition: width 0.3s ease; }
.skills-audit-summary { font-size: 11px; opacity: 0.7; margin-bottom: 4px; }
.skills-audit-log {
max-height: 22vh; overflow-y: auto;
font-family: ui-monospace, 'SF Mono', 'Fira Code', monospace;
font-size: 10.5px; line-height: 1.5; opacity: 0.8;
}
/* ── Skill test monitor + AI eval verdict ── */
.skill-test { display: flex; flex-direction: column; gap: 8px; min-height: 0; }
.skill-test-log {
flex: 1 1 auto;
min-height: 120px;
max-height: 48vh;
overflow-y: auto;
font-family: ui-monospace, 'SF Mono', 'Fira Code', monospace;
font-size: 9.5px;
line-height: 1.45;
padding: 8px 10px;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--bg);
white-space: pre-wrap;
word-break: break-word;
}
.skill-test-log > div { margin: 1px 0; }
.skill-test-meta { opacity: 0.5; }
.skill-test-task { color: var(--accent, var(--red)); font-weight: 500; font-size: 9px; opacity: 0.85; }
.skill-test-round { opacity: 0.6; margin-top: 6px !important; }
.skill-test-tool { color: var(--accent, var(--red)); opacity: 0.85; }
.skill-test-out { opacity: 0.7; padding-left: 10px; }
.skill-test-say { white-space: pre-wrap; }
.skill-test-err { color: var(--color-danger, #e06c75); }
.skill-eval-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.skill-eval-badge {
font-size: 10px; font-weight: 700; letter-spacing: 0.03em;
padding: 2px 8px; border-radius: 10px; flex-shrink: 0;
}
.skill-eval-ok { background: color-mix(in srgb, var(--color-success, #4ade80) 26%, transparent); color: var(--color-success, #4ade80); }
.skill-eval-warn { background: color-mix(in srgb, var(--color-warning, #f0ad4e) 26%, transparent); color: var(--color-warning, #f0ad4e); }
.skill-eval-bad { background: color-mix(in srgb, var(--color-danger, #e06c75) 26%, transparent); color: var(--color-danger, #e06c75); }
.skill-eval-unknown { background: color-mix(in srgb, var(--fg) 14%, transparent); }
.skill-eval-summary { font-size: 11px; opacity: 0.85; }
.skill-eval-issues { margin: 6px 0 0; padding-left: 18px; font-size: 11px; opacity: 0.8; }
.skill-eval-issues li { margin: 2px 0; }
.skill-eval-actions { display: flex; gap: 6px; justify-content: flex-end; margin-top: 8px; }
.skill-eval-approve.suggested {
border-color: var(--color-success, #4ade80) !important;
color: var(--color-success, #4ade80) !important;
}
/* Already published — the button confirms the state (click to unpublish). */
.skill-eval-approve.is-approved {
border-color: var(--color-success, #4ade80) !important;
color: var(--color-success, #4ade80) !important;
background: color-mix(in srgb, var(--color-success, #4ade80) 14%, transparent) !important;
}
/* Add-Skill form: a "rich placeholder" overlay so only the FIRST word (Title,
When to use, How, Tags) is accent-colored while the rest stays muted — real
placeholders can't be partially colored. The native placeholder is a single
space so :placeholder-shown still toggles the overlay. */
.skill-ph-wrap { position: relative; }
.skill-ph-wrap .skill-hint-input { width: 100%; box-sizing: border-box; }
.skill-rich-ph {
position: absolute;
left: 11px; right: 11px; top: 50%;
transform: translateY(-50%);
pointer-events: none;
font-size: 12px;
line-height: 1.2;
color: color-mix(in srgb, var(--fg) 40%, transparent);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Textarea: align the hint to the first line instead of vertical-centering. */
.skill-rich-ph-top { top: 7px; transform: none; }
.skill-rich-ph .k { color: var(--accent, var(--red)); }
/* Keep the memory-input enter hint pinned to the field's right edge instead
of letting it drift after the example text. */
#new-memory-input ~ .skill-rich-ph > svg {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
margin-left: 0 !important;
}
/* Hide the overlay once the field has real content. */
.skill-hint-input:not(:placeholder-shown) ~ .skill-rich-ph { display: none; }
.skill-md-editor {
flex: 1 1 auto;
min-height: 0;
width: 100%;
box-sizing: border-box;
resize: none;
font-family: ui-monospace, 'SF Mono', 'Fira Code', monospace;
font-size: 11.5px;
line-height: 1.5;
padding: 8px;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--bg);
color: var(--fg);
}
.doclib-empty {
text-align: center;
color: color-mix(in srgb, var(--fg) 35%, transparent);
padding: 32px 16px;
font-size: 12px;
font-style: italic;
}
/* Unified loading row across every Library tab (Chats / Documents / Research /
Archive): one opacity, one size, with the whirlpool spinner next to it. */
.lib-loading-row {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 28px 16px;
font-size: 12px;
color: color-mix(in srgb, var(--fg) 45%, transparent);
}
.pdf-loading-state {
min-height: 120px;
margin: 0;
box-sizing: border-box;
flex-direction: column;
gap: 10px;
border: 0;
border-radius: 0;
background: transparent;
color: color-mix(in srgb, var(--fg) 68%, transparent);
box-shadow: none;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.01em;
}
.pdf-loading-state .ai-spinner-whirlpool {
color: var(--accent, var(--red));
opacity: 0.95;
}
.pdf-loading-state .ai-spinner-whirlpool canvas {
display: block;
}
.doclib-load-more {
display: block;
margin: 10px auto 0;
padding: 6px 16px;
background: transparent;
border: 1px solid var(--border);
border-radius: 6px;
color: var(--fg-muted);
font-size: 11px;
cursor: pointer;
transition: border-color 0.15s, color 0.15s;
flex-shrink: 0;
position: relative;
top: 10px;
}
/* Soft dark gradient above the button — blends the list content into the
load-more strip instead of a hard cutoff. Spans the modal width and
sits behind/above the button. */
.doclib-load-more::before {
content: "";
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 100%;
margin-bottom: 4px;
width: min(600px, 92vw);
height: 34px;
pointer-events: none;
background: linear-gradient(to top, color-mix(in srgb, #000 24%, transparent), transparent);
}
.doclib-load-more:hover {
border-color: var(--red);
color: var(--red);
}
.doclib-inline-load-more {
border: 0;
border-radius: 0;
padding: 0;
background: none;
top: 0;
font-size: 9px !important;
line-height: 1.2 !important;
}
/* Document library toolbar buttons */
.doclib-toolbar-btn {
background: none;
border: 1px solid var(--border);
color: var(--fg-muted);
font-size: 11px;
padding: 5px 10px;
border-radius: 6px;
cursor: pointer;
transition: all 0.15s;
white-space: nowrap;
font-family: inherit;
display: inline-flex;
align-items: center;
gap: 3px;
}
.doclib-toolbar-btn:hover {
color: var(--fg);
border-color: var(--fg);
}
.doclib-toolbar-btn.active {
background: color-mix(in srgb, var(--red) 15%, transparent);
border-color: color-mix(in srgb, var(--red) 40%, transparent);
color: var(--red);
}
.doclib-toolbar-btn.danger {
color: var(--color-error, #e55);
}
.doclib-toolbar-btn.danger:hover:not(:disabled) {
border-color: var(--color-error, #e55);
}
.doclib-toolbar-btn:disabled {
opacity: 0.4;
cursor: default;
}
/* Bulk action bar */
.doclib-bulk-bar {
display: flex;
align-items: center;
gap: 8px;
padding: 4px 8px;
border: 1px solid color-mix(in srgb, var(--red) 30%, transparent);
border-radius: 6px;
background: color-mix(in srgb, var(--red) 5%, transparent);
font-size: 10px;
margin-bottom: 8px;
}
.doclib-bulk-bar.hidden {
display: none;
}
.doclib-bulk-check-all {
display: flex;
align-items: center;
gap: 3px;
cursor: pointer;
color: color-mix(in srgb, var(--fg) 60%, transparent);
font-size: 10px;
}
#doclib-bulk-bar .memory-toolbar-btn {
position: relative;
top: -3px;
}
/* Custom checkboxes */
.doclib-select-cb,
.doclib-bulk-check-all input {
-webkit-appearance: none;
appearance: none;
width: 13px;
height: 13px;
border: 1px solid var(--border);
border-radius: 4px;
background: var(--bg);
cursor: pointer;
flex-shrink: 0;
margin: 0;
position: relative;
transition: all 0.15s;
}
.doclib-select-cb:hover,
.doclib-bulk-check-all input:hover {
border-color: var(--red);
}
.doclib-select-cb:checked,
.doclib-bulk-check-all input:checked {
background: var(--red);
border-color: var(--red);
}
.doclib-select-cb:checked::after,
.doclib-bulk-check-all input:checked::after {
content: '';
position: absolute;
left: 3px;
top: 0px;
width: 4px;
height: 8px;
border: solid var(--bg);
border-width: 0 1.5px 1.5px 0;
transform: rotate(45deg);
}
/* + button outside scroll area (when doc is on right side) */
@media (max-width: 600px) {
.doclib-toolbar {
flex-direction: column;
}
.doclib-card-session,
.doclib-card-time {
display: none;
}
.doclib-card-preview {
max-height: 40vh;
}
}
/* Collapse card tags before they compete with attachment/favorite controls. */
.email-card-tags .email-tags-more {
color: var(--email-tags-more-color, var(--fg));
background: color-mix(in srgb, var(--email-tags-more-color, var(--fg)) 18%, transparent);
}
.email-tags-more-single,
[data-email-tags-count-mini] { display: none; }
.email-tags-more-single svg { display: none; }
.email-card-tags.email-tags-mini:not(.email-tags-expanded) > .email-tag,
.email-card-tags.email-tags-mini:not(.email-tags-expanded) > .email-tag-extra {
display: none;
}
.email-card-tags.email-tags-mini:not(.email-tags-expanded) .email-tags-more-single,
.email-card-tags.email-tags-expanded .email-tags-more-single {
display: inline-flex;
}
.email-card-tags.email-tags-mini:not(.email-tags-expanded) [data-email-tags-count-normal] { display: none; }
.email-card-tags.email-tags-mini:not(.email-tags-expanded) [data-email-tags-count-mini],
.email-card-tags.email-tags-expanded .email-tags-more-single [data-email-tags-count-mini] { display: inline; }
/* Keep the reader actions in drafting order. */
.email-reader-actions-inline > [data-act="ai-reply"] { order: 1; }
.email-reader-actions-inline > [data-act="reply"] { order: 2; }
.email-reader-actions-inline > [data-act="reply-all"] { order: 3; }
.email-reader-actions-inline > [data-act="forward"] { order: 4; }