/* === 20. Sources list: sidebar entries ================================ Charter target (F5): .source-item collapses onto .card --row. ========================================================================== */ .sources-list { padding: var(--gap-bound) 0; } .source-item { display: flex; justify-content: space-between; align-items: center; padding: var(--gap-peer) var(--gap-group); cursor: pointer; min-height: 2.5rem; /* 3px, wider than a frame on purpose: this edge is the state marker. */ border-left: 3px solid transparent; } .source-item:hover { background-color: var(--surface-sunken); } .source-item.active { background-color: var(--surface-sunken); border-left-color: var(--category-four); } .source-name { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 600; font-size: var(--font-size-base); } /* Carries `.raised`; same as `.tag`, the fill and the bevel are the design system's now. */ .source-count { padding: var(--step-hair) var(--step-snug); border-radius: var(--radius-xl); font-size: var(--font-size-xs); color: var(--content-muted); border: var(--border-width) solid var(--border); min-width: 1.8rem; text-align: center; } .source-count.all-read { color: var(--success); border-color: var(--success); } .source-delete { display: none; background: none; border: none; color: var(--content-muted); cursor: pointer; font-size: var(--font-size-base); padding: 0 var(--step-tight); line-height: 1; } .source-delete:hover { color: var(--danger); } .source-item:hover .source-delete { display: inline; } /* === 20. Sources: tag filter bar ====================================== */ .tag-filter-bar { display: flex; flex-wrap: wrap; gap: var(--gap-peer); padding: var(--gap-peer) var(--gap-group); border-bottom: var(--border-width) solid var(--border); background-color: var(--surface-overlay); } /* Carries `.chip`, which is what a filter chip is: raised at rest, hover, press, and a `.latched` state for the one that is on. The fill and both bevels come from layout.css; the pill shape, the type and the accent are this app's. */ .tag-chip { border: var(--border-width) solid var(--border); border-radius: var(--radius-xl); padding: var(--step-hair) var(--step-base); font-size: var(--font-size-xs); color: var(--content-secondary); cursor: pointer; white-space: nowrap; } .tag-chip:hover { border-color: var(--category-four); color: var(--content); } /* An active filter chip is latched down, same inversion as a pressed button. The inset bevel comes from `.latched`, which the markup sets alongside `.active`; the accent fill stays here, because which colour a latched chip takes is this app's call and the depth is not. */ .tag-chip.active { background-color: var(--category-four); border-color: var(--category-four); color: var(--content); font-weight: 600; } .source-tags { display: flex; flex-wrap: wrap; gap: var(--gap-peer); margin-top: var(--step-hair); } /* Source-row tags use .tag.tag--xs[data-color="muted"] (see ยง Tag). */ /* === 20. Sources: edit button ========================================= */ .source-edit { display: inline; background: none; border: none; color: var(--content-muted); cursor: pointer; font-size: var(--font-size-md); padding: 0 var(--step-tight); line-height: 1; opacity: 0.4; } .source-edit:hover { color: var(--category-four); opacity: 1; } /* === 22. Reader-expanded mode ========================================= Hides items panel and expands detail to full width. */ .main.reader-expanded .items-panel { display: none; } .main.reader-expanded .detail-panel { flex: 1; width: auto; min-width: 0; border-left: none; } .main.reader-expanded .item-detail { max-width: 800px; margin: 0 auto; } .reader-back-btn { margin-right: auto; } /* === 23. Query feeds: sidebar divider ================================ */ .source-divider { padding: var(--gap-peer) var(--gap-group) var(--gap-bound); font-size: var(--font-size-xxs); font-weight: 600; color: var(--content-muted); text-transform: uppercase; letter-spacing: 0.5px; border-top: var(--border-width) solid var(--border); margin-top: var(--gap-bound); cursor: default; } /* Query feed items: subtle visual distinction */ .source-item.query-feed .source-name { font-style: italic; } /* Add query feed button */ .source-item.add-query-feed-btn { padding: var(--gap-peer) var(--gap-group); border-top: var(--border-width) solid var(--border); } .source-item.add-query-feed-btn:hover { background-color: var(--surface-sunken); } /* === 23. Query feeds: builder ========================================= `.condition-row` is the canonical NAMED LAYOUT for mixed-intent rows (see compose-first rule, named-layout exception). ========================================================================== */ .qf-builder .form-group { margin-bottom: var(--gap-section); } .condition-row { display: flex; align-items: center; gap: var(--gap-bound); margin-bottom: var(--gap-peer); } .condition-row .field { flex: 1; min-width: 0; font-size: var(--font-size-md); padding: var(--gap-bound) var(--gap-peer); } .condition-row .cond-field { flex: 0 0 auto; width: 90px; } .condition-row .cond-op { flex: 0 0 auto; width: 120px; } .condition-row .cond-val { flex: 1; } .condition-row .cond-remove { flex-shrink: 0; padding: var(--step-tight) var(--step-snug); font-size: var(--font-size-lg); line-height: 1; } .add-condition-btn { margin-top: var(--gap-peer); } .match-preview { padding: var(--gap-peer) 0; font-size: var(--font-size-md); color: var(--content-muted); } .modal-actions { display: flex; justify-content: flex-end; gap: var(--gap-peer); margin-top: var(--gap-section); padding-top: var(--gap-section); border-top: var(--border-width) solid var(--border); } /* === 24. Sidebar saved articles ======================================== */ .sidebar-saved { display: flex; justify-content: space-between; align-items: center; padding: var(--gap-peer) var(--gap-group); cursor: pointer; border-top: var(--border-width) solid var(--border); /* 3px, wider than a frame on purpose: this edge is the state marker. */ border-left: 3px solid transparent; min-height: 2.5rem; font-size: var(--font-size-base); font-weight: 600; } .sidebar-saved:hover { background-color: var(--surface-sunken); } .sidebar-saved.active { background-color: var(--surface-sunken); border-left-color: var(--category-four); } /* Sidebar footer */ .sidebar-footer { margin-top: auto; padding: var(--gap-peer) var(--gap-group); border-top: var(--border-width) solid var(--border); } /* F2 additions (2026-06-02): utility primitives. .hidden: visibility toggle utility (replaces ad-hoc style.display = 'none'). .confirm-message: retires the inline-style violation in components.js confirmAction (was setting marginBottom, fontSize, color via style.cssText). */ .hidden { display: none !important; } .confirm-message { margin-bottom: var(--gap-section); font-size: var(--font-size-base); color: var(--content); } /* === 26. Help shortcuts ================================================ */ .help-shortcuts { display: grid; grid-template-columns: 1fr 1fr; gap: var(--gap-pane); } .help-shortcuts .help-section:last-child { grid-column: 1 / -1; } .help-section h3 { font-size: var(--font-size-sm); font-weight: 600; color: var(--content-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: var(--gap-peer); } .help-row { display: flex; align-items: center; gap: var(--gap-bound); margin-bottom: var(--gap-peer); font-size: var(--font-size-base); } .help-row kbd { background: var(--surface-raised); border: var(--border-width) solid var(--border); border-radius: var(--radius-sm); box-shadow: var(--bevel-raised); padding: var(--step-hair) var(--step-snug); font-family: inherit; font-size: var(--font-size-md); min-width: 1.4rem; text-align: center; color: var(--content); } .help-row span { color: var(--content-secondary); margin-left: auto; } /* === 27. Welcome modal ================================================= */ .welcome-content h3 { font-size: var(--font-size-base); font-weight: 600; color: var(--content); margin-top: var(--gap-section); margin-bottom: var(--gap-bound); } .welcome-content h3:first-child { margin-top: 0; } .welcome-content p { font-size: var(--font-size-base); color: var(--content-secondary); line-height: 1.5; } .welcome-cta { display: flex; justify-content: center; gap: var(--gap-peer); margin-top: var(--gap-section); padding-top: var(--gap-section); border-top: var(--border-width) solid var(--border); } /* === 28. Settings modal ================================================ */ .settings-content .form-group { margin-bottom: var(--gap-section); } .sync-manual-entry { margin-top: var(--gap-section); } .sync-manual-form { margin-top: var(--gap-peer); } .sync-action-spaced { margin-bottom: var(--gap-section); } .sync-disconnect { margin-top: var(--gap-section); } /* === 30. Reading list / Bookmarks ====================================== */ .bookmark-tag-bar { display: flex; gap: var(--gap-peer); padding: var(--gap-peer) var(--gap-group); border-bottom: var(--border-width) solid var(--border); overflow-x: auto; flex-shrink: 0; } /* Bookmark Open/more actions use renderRow's .row-actions (hover-revealed); pinned state uses .row--article.is-pinned. */ .bookmark-tags { display: flex; gap: var(--gap-peer); flex-wrap: wrap; margin-top: var(--gap-bound); } .bookmark-tag { cursor: pointer; font-size: var(--font-size-xs); } .bookmark-tag:hover { background: var(--action); color: white; } .context-menu-item.danger, .context-menu-item--danger { /* F2 (2026-06-02): added --danger parity name; .danger kept as legacy alias until F5 retires call sites. Inline #dc3545 fallback removed; --accent-red is themed and always defined. */ color: var(--danger); } /* === 31. Screen-reader only (.sr-only) ================================ */ .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } /* === 32. Focus indicators ============================================== The design system's ring for BB's own clickable elements; the same colour as a box-shadow ring for inputs, which avoids the layout shift an outline causes on a bevelled field. Rule: every new interactive primitive must appear in one of these selector lists. makeover's own primitives are absent on purpose. `.button` and `.tab` take an identical ring from layout.css, and restating it here unlayered would override the design system with a copy of itself. ========================================================================== */ .source-item:focus-visible, .star-btn:focus-visible, .source-delete:focus-visible, .row--article:focus-visible, .plugin-item:focus-visible, .toast-action:focus-visible { outline: 2px solid var(--focus-ring); outline-offset: 2px; } /* A field's focus ring comes from layout.css now. What stood here was this app's own third answer to the same question: `outline: none` plus a composed box-shadow that had to restate the bevel beside it. `outline: none` also killed the generated `.field:focus-visible`, so keeping this rule would have left fields with no ring rather than with two. Same 2px, and --focus-ring resolves to the same colour; the generated ring sits inside the well (`outline-offset: calc(-1 * 2px)`), which is where a well's ring belongs. `:focus-visible` rather than `:focus` is not a behaviour change for a text field: a focused text input always matches `:focus-visible`. */