/* ── Layout ──────────────────────────────────────────────────────────────── */ html, body { margin: 0; padding: 0; height: 100%; font-family: var(--font); color: var(--text); background-color: var(--bg); } /* Body is a flex column so the header (which may wrap to a second row at narrow viewports), #appMain, and the status bar each get their natural height — no more fixed-pixel calc() that breaks when the header reflows. Horizontal overflow scrolls on the body as a final fallback when content can't shrink any further. */ body { display: flex; flex-direction: column; height: 100vh; overflow-x: auto; overflow-y: hidden; /* Hard floor for the body. Below this, the html-level scrollbar picks up and the user can pan horizontally rather than seeing the right edge clipped. */ min-width: 320px; } #appMain { position: relative; flex: 1 1 auto; min-height: 0; height: auto; /* override the old calc(100vh - 2.65rem) */ display: flex; flex-direction: column; overflow: hidden; } .browse-root { display: flex; flex-direction: column; flex: 1; height: 100%; overflow: hidden; background: var(--bg); } /* ── Toolbar ─────────────────────────────────────────────────────────────── */ .browse-toolbar { display: flex; align-items: center; gap: 0.75rem; padding: 0.4rem 1rem; background: var(--bg-secondary); border-bottom: 1px solid var(--border); flex-shrink: 0; } .view-mode-toggle { display: inline-flex; gap: 0; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; } .view-mode-toggle .btn { border-radius: 0; border: none; border-right: 1px solid var(--border); } .view-mode-toggle .btn:last-child { border-right: none; } .view-mode-toggle .btn[aria-selected="true"] { background: var(--primary); color: var(--text-light); } /* Breadcrumbs */ .breadcrumbs { flex: 1; display: flex; align-items: center; flex-wrap: wrap; gap: 0.15rem 0.4rem; font-size: 0.85rem; color: var(--text-muted); min-width: 0; } .breadcrumbs a, .breadcrumbs button { color: var(--text-muted); background: none; border: 0; padding: 0.1rem 0.3rem; border-radius: var(--radius); cursor: pointer; text-decoration: none; font: inherit; } .breadcrumbs a:hover, .breadcrumbs button:hover { color: var(--text); background: var(--bg-hover); } .breadcrumbs .bc-sep { color: var(--text-muted); user-select: none; } .breadcrumbs .bc-current { color: var(--text); font-weight: 600; padding: 0.1rem 0.3rem; } .bc-home-icon { width: 1em; height: 1em; vertical-align: -0.15em; } /* ── Two-pane browse view ────────────────────────────────────────────────── */ .browse-view { display: flex; flex: 1; overflow: hidden; min-height: 0; } .pane { overflow: hidden; background: var(--bg); display: flex; flex-direction: column; } .tree-pane { width: 360px; min-width: 200px; max-width: 60%; border-right: 1px solid var(--border); flex-shrink: 0; } .tree-pane__toolbar { padding: 0.4rem 0.5rem; border-bottom: 1px solid var(--border); background: var(--bg-secondary); flex-shrink: 0; } /* Single-input autofilter — same grammar as the archive app's column filters (terms, quotes, !negation, multi-word AND). type=search so the browser ships the native clear-X for free; the .filter-active class amber-highlights the input while a query is set, matching the archive `.column-filter.filter-active` cue. */ .tree-filter { width: 100%; box-sizing: border-box; padding: 0.3rem 0.5rem; font-family: var(--font); font-size: 0.85rem; color: var(--text); background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius); outline: none; transition: border-color 0.12s, background 0.12s; } .tree-filter:focus { border-color: var(--primary); box-shadow: 0 0 0 2px var(--primary-light); } .tree-filter.filter-active { background: rgba(234, 179, 8, 0.18); border-color: rgba(234, 179, 8, 0.7); } .tree-pane__body { flex: 1; overflow: auto; padding: 0.25rem 0; font-size: 0.875rem; } /* Pane resizer — 4px grab handle between tree and preview */ .pane-resizer { width: 4px; background: transparent; cursor: col-resize; flex-shrink: 0; position: relative; z-index: 1; } .pane-resizer:hover, .pane-resizer.is-dragging { background: var(--primary); } .preview-pane { flex: 1; min-width: 0; } .preview-pane__header { display: flex; align-items: center; gap: 0.5rem; padding: 0.4rem 0.75rem; background: var(--bg-secondary); border-bottom: 1px solid var(--border); flex-shrink: 0; min-height: 2.1rem; } .preview-pane__title { flex: 1; font-size: 0.9rem; font-weight: 500; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; } .preview-pane__meta { font-size: 0.8rem; color: var(--text-muted); white-space: nowrap; } .preview-pane__body { flex: 1; min-height: 0; /* critical: lets the flex child shrink to fit the viewport instead of growing to its content's natural size (which clips the YAML editor's bottom when there are many lines, even with the editor's own scroll) */ overflow: auto; display: flex; flex-direction: column; background: var(--bg); } /* The body's children fill the available space. Plugins inject different content here — img, iframe, pre, custom markdown editor. */ .preview-pane__body > * { flex: 1; min-height: 0; } .preview-empty { display: flex; align-items: center; justify-content: center; color: var(--text-muted); font-size: 0.95rem; padding: 2rem; text-align: center; } .preview-pane__body img.preview-image { max-width: 100%; max-height: 100%; object-fit: contain; margin: auto; display: block; flex: none; /* avoid flex sizing interfering with object-fit */ } .preview-pane__body iframe.preview-iframe { width: 100%; height: 100%; border: none; } .preview-pane__body pre.preview-text { padding: 1rem; font-family: var(--font-mono); font-size: 0.85rem; white-space: pre-wrap; word-wrap: break-word; margin: 0; overflow: auto; background: var(--bg); color: var(--text); } /* ── Tree (vertical, file-explorer style) ───────────────────────────────── */ .tree-row { display: flex; /* Top-aligned so the chevron + icon anchor to the title line on two-line ZDDC rows. Single-line rows are unaffected because the icon, chevron, and label all share a top edge. */ align-items: flex-start; gap: 0.25rem; padding: 0.2rem 0.5rem; cursor: pointer; user-select: none; border-radius: 0; color: var(--text); } .tree-row:hover { background: var(--bg-hover); } .tree-row.is-selected { background: var(--bg-selected); color: var(--text); } /* Per-row "⋯" actions button — the visible affordance that a row has a context menu. Hidden until the row is hovered/selected or the button itself is keyboard-focused, so it stays out of the way during reading but is discoverable without knowing to right-click. Pushed to the right edge; never part of the tab order (rows use roving tabindex). */ .tree-row__kebab { margin-left: auto; align-self: flex-start; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; width: 1.4rem; height: 1.4rem; padding: 0; border: none; background: transparent; color: var(--text-muted, #888); border-radius: var(--radius); cursor: pointer; opacity: 0; transition: opacity 0.1s, background 0.1s, color 0.1s; } .tree-row__kebab svg { width: 1em; height: 1em; } .tree-row:hover .tree-row__kebab, .tree-row.is-selected .tree-row__kebab, .tree-row__kebab:focus-visible { opacity: 1; } .tree-row__kebab:hover, .tree-row__kebab:focus-visible { background: var(--bg-hover); color: var(--text); } /* Tree-pane toolbar controls row (New folder/file, Sort, Show hidden), sitting under the filter input. */ .tree-pane__controls { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; margin-bottom: 0.4rem; } .tree-pane__controls .tp-control { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.8rem; color: var(--text-muted, #888); } .tree-pane__controls .tp-control--check { cursor: pointer; } .tree-pane__controls select { font-family: var(--font); font-size: 0.8rem; color: var(--text); background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius); padding: 0.15rem 0.3rem; } /* Per-row drop target highlight: applied while a file/folder drag is hovering this row. The dashed outline reads as "drop here" without shifting layout. */ .tree-row.is-droptarget { background: var(--primary-light); outline: 2px dashed var(--primary); outline-offset: -2px; } .tree-row.is-selected .tree-name__label { color: var(--text); } .tree-name__chevron { /* Fixed-width slot so leaf rows (empty chevron) still align with expandable rows. The SVG inside is sized via the rule below. Top-anchored to the title-line baseline by the row's flex-start alignment + this small top offset. */ display: inline-flex; align-items: center; justify-content: center; width: 1rem; height: 1.2em; flex-shrink: 0; color: var(--text-muted); } .tree-name__chevron svg { width: 0.85em; height: 0.85em; transition: transform 0.12s ease; } /* Expanded state — rotate the same chevron 90° rather than swapping to a second glyph. Smooth, single-sprite, and consistent with the way most modern file trees indicate expand state. */ .tree-row.expanded .tree-name__chevron svg { transform: rotate(90deg); } .tree-name__icon { flex-shrink: 0; /* Stacked column — glyph on top, extension chip below for files. Wider min-width than the 1em glyph itself so common extensions (pdf/docx/xlsx/json) don't push the label sideways. Height grows with content; flex-start anchors to the title-line. */ min-width: 2.2em; display: inline-flex; flex-direction: column; align-items: center; justify-content: flex-start; color: var(--text-muted); gap: 1px; } .tree-name__icon svg { width: 1em; height: 1em; display: block; } .tree-name__ext { font-size: 0.58rem; line-height: 1; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; font-weight: 600; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* Folder rows get the primary accent so directories stand out from files at a glance — same convention as macOS Finder / GNOME Files. */ .tree-row[data-isdir="true"] .tree-name__icon, .tree-row[data-iszip="true"] .tree-name__icon { color: var(--primary); } /* Selected rows tint icon to match the label color (the bg-selected token already differentiates the row background). */ .tree-row.is-selected .tree-name__icon { color: var(--text); } .tree-name__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); min-width: 0; } /* Two-line ZDDC variant. Top line is monospace + small + muted so the trackingNumber / revision / status fields line up vertically across adjacent rows (every field has a fixed width by convention). Bottom line is the human-readable title at normal weight. */ .tree-name__label--zddc { display: flex; flex-direction: column; line-height: 1.15; /* Tight gap between meta and title; tweak by 1-2 px if the rows feel crowded on dense lists. */ gap: 0.05rem; } .tree-name__meta { font-family: var(--font-mono); font-size: 0.7rem; /* Explicit weight: the folder-row rule below bolds .tree-name__label, which would otherwise inherit through to the meta span. We want the meta to stay light + muted on every row. */ font-weight: 400; color: var(--text-muted); /* Belt-and-braces: monospace already gives column-alignment, but tabular-nums hardens it on the rare proportional fallback. */ font-variant-numeric: tabular-nums; letter-spacing: 0.01em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .tree-name__title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); } .tree-row.is-selected .tree-name__title { color: var(--text); } .tree-row[data-isdir="true"] .tree-name__label, .tree-row[data-iszip="true"] .tree-name__label { font-weight: 500; } /* ── Drag-drop upload overlay ─────────────────────────────────────────────── */ /* Shown only while a drag is active over the page AND the current scope accepts uploads. Pointer-events:none below dragover so the underlying drop event still reaches the document handlers. */ .upload-overlay { position: fixed; inset: 0; z-index: 50; pointer-events: none; background: rgba(42, 90, 138, 0.18); backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px); display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.12s ease; } .upload-overlay.is-active { opacity: 1; } .upload-overlay__panel { background: var(--bg); border: 2px dashed var(--primary); border-radius: var(--radius); padding: 1.5rem 2.25rem; text-align: center; box-shadow: 0 6px 24px rgba(0, 0, 0, 0.18); pointer-events: none; color: var(--text); max-width: 80vw; } .upload-overlay__icon { font-size: 2.5rem; line-height: 1; color: var(--primary); } .upload-overlay__title { font-family: var(--font-display); font-size: 1.15rem; font-weight: 600; margin-top: 0.5rem; } .upload-overlay__path { margin-top: 0.35rem; font-family: var(--font-mono); font-size: 0.82rem; color: var(--text-muted); word-break: break-all; } /* Virtual rows: synthesized for folders/files declared by the cascade but absent from disk. The visual language reads as "expected, not yet materialized" — italic label, muted accent color, dashed left rail, and an outlined icon. Hover/select chrome still applies on top; the dashed rail sits inside the row so it doesn't fight padding-left indentation. */ .tree-row--virtual { box-shadow: inset 2px 0 0 0 transparent; position: relative; } .tree-row--virtual::before { content: ''; position: absolute; top: 4px; bottom: 4px; left: 2px; border-left: 2px dashed var(--accent-muted, #8aa4cc); pointer-events: none; } .tree-row--virtual .tree-name__label { font-style: italic; color: var(--text-muted, #6b7280); } .tree-row--virtual .tree-name__icon { /* Hollow out the filled Lucide glyph: reduce fill opacity so the icon reads as an outline-only sketch — the conventional "placeholder, not actual" cue across UI systems. */ opacity: 0.5; } .tree-row--virtual .tree-name__icon svg { fill: none; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; } .tree-row--virtual.is-selected::before { /* Selected virtual row: rail brightens to selection accent so the row reads as both selected and placeholder. */ border-left-color: var(--accent, #2868c8); } .tree-name__hint { margin-left: 0.5rem; font-size: 0.78rem; color: var(--accent-muted, #8aa4cc); font-style: italic; } /* ── Grid view (Phase C) ─────────────────────────────────────────────────── */ .grid-view { flex: 1; overflow: auto; background: var(--bg); padding: 0; } .grid-empty { padding: 3rem; text-align: center; color: var(--text-muted); } /* ── Markdown plugin (right-pane internals when a .md is selected) ──────── */ /* CSS-Grid shell mirroring mdedit's layout: sidebar on the LEFT (front matter top + TOC bottom), content on the RIGHT (informational header above the Toast UI editor). The grid gives every cell a definite size, which Toast UI needs to compute its scroll regions correctly. */ .md-shell { display: grid; grid-template-rows: 1fr; /* minmax(0, …) on BOTH tracks is load-bearing: a bare `1fr` is `minmax(auto, 1fr)`, whose `auto` floor is the editor's min-content width (Toast UI's toolbar). That floor stops the content track from shrinking, so the whole shell overflows #previewBody as the window narrows instead of the editor getting narrower. minmax(0, 1fr) drops the floor so the editor reflows down to nothing. JS overrides the column widths on drag — it preserves the minmax(0, …) form. */ grid-template-columns: minmax(0, 280px) minmax(0, 1fr); grid-template-areas: "sidebar content"; height: 100%; min-height: 0; background: var(--bg); overflow: hidden; } /* Sidebar (col 1): three stacked items — Front matter (fixed height, drag-resizable), the horizontal resizer (between FM and TOC), then the TOC section taking the remaining height. Flexbox keeps the resizer position unambiguous; the previous grid-overlay approach was hard to read and prone to misplacement. */ .md-shell__sidebar { grid-area: sidebar; display: flex; flex-direction: column; min-width: 0; min-height: 0; overflow: hidden; border-right: 1px solid var(--border); background: var(--bg); position: relative; } /* Vertical sidebar/content resizer. Sits absolutely on the column boundary so it doesn't occupy a grid track. */ .md-shell__resizer { grid-area: sidebar; align-self: stretch; justify-self: end; width: 6px; margin-right: -3px; cursor: col-resize; background: transparent; z-index: 2; transition: background 0.12s; } .md-shell__resizer:hover, .md-shell__resizer.is-dragging, .md-shell__resizer:focus-visible { background: var(--primary); outline: none; } /* Horizontal resizer — a real flex item between FM and TOC. Drag it up/down to change the front-matter pane's height; the JS handler updates fmSection.style.height directly. */ .md-shell__fmresizer { flex: 0 0 6px; height: 6px; cursor: row-resize; background: var(--border); transition: background 0.12s; /* Subtle "grab" affordance — a slightly darker bar appears on hover so users see this is the drag handle. */ } .md-shell__fmresizer:hover, .md-shell__fmresizer.is-dragging, .md-shell__fmresizer:focus-visible { background: var(--primary); outline: none; } /* Content (col 2): informational header above the Toast UI editor. */ .md-shell__content { grid-area: content; display: grid; grid-template-rows: auto 1fr; min-width: 0; min-height: 0; overflow: hidden; } /* Informational header above the editor: file name on the left, then dirty marker, status, source hint, save button. Reads as a header for the content panel — file metadata at a glance. */ .md-shell__infohdr { display: flex; align-items: center; gap: 0.5rem; padding: 0.4rem 0.75rem; background: var(--bg-secondary); border-bottom: 1px solid var(--border); font-size: 0.85rem; } .md-shell__title { flex: 1; font-family: var(--font-display); font-size: 1rem; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; } .md-shell__dirty { color: var(--text-muted); font-size: 0.85rem; min-width: 5.5rem; text-align: right; } .md-shell__status { color: var(--text-muted); font-size: 0.85rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 14rem; } .md-shell__source { color: var(--text-muted); font-size: 0.75rem; font-style: italic; padding: 0.15rem 0.4rem; border-radius: var(--radius); background: var(--bg); border: 1px solid var(--border); } .md-shell__download { /* Slightly tighter than the Save button so a row of three doesn't crowd the title. The base .btn styles still drive padding/color. */ font-variant-numeric: tabular-nums; letter-spacing: 0.02em; } .md-shell__download[disabled] { opacity: 0.55; cursor: progress; } /* Editor host: a single grid cell with overflow:hidden so Toast UI's internal scrollers handle the content. */ .md-shell__editor { min-width: 0; min-height: 0; overflow: hidden; } .md-side { display: flex; flex-direction: column; min-height: 0; overflow: hidden; } /* Front-matter section: fixed (resizable) height, set inline by the markdown plugin's mount + drag-handler. flex:0 0 auto so the explicit height wins over the parent flex layout. */ .md-side--fm { flex: 0 0 auto; } /* TOC section: takes everything that's left. min-height:0 so the inner body's overflow:auto kicks in instead of pushing the resizer off-screen. */ .md-side--toc { flex: 1 1 auto; min-height: 0; } .md-side__header { /* Header is its own flex item so the body can stretch to fill. */ flex: 0 0 auto; padding: 0.35rem 0.75rem; background: var(--bg-secondary); border-bottom: 1px solid var(--border); font-size: 0.72rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-muted); } .md-side__body { /* Both axes — the textarea uses white-space:pre so long YAML lines need horizontal scroll, and the TOC entries below now extend their full width so deep headings need it too. */ flex: 1 1 auto; overflow: auto; min-height: 0; padding: 0.3rem 0; font-size: 0.85rem; line-height: 1.45; } /* ── Outline list ───────────────────────────────────────────────────────── */ .md-toc__empty { color: var(--text-muted); font-style: italic; padding: 0.5rem 0.75rem; margin: 0; font-size: 0.82rem; } .md-toc__list { list-style: none; margin: 0; padding: 0; } .md-toc__item { margin: 0; padding: 0.22rem 0.75rem; color: var(--text); cursor: pointer; border-left: 2px solid transparent; transition: background 0.1s, border-color 0.1s, color 0.1s; /* Single-line items but no ellipsis — long headings extend the item's intrinsic width, and the parent .md-side__body has overflow:auto, so they create a horizontal scrollbar instead of getting clipped. The title attribute still carries the full text for SR users. */ white-space: nowrap; } .md-toc__item:hover { background: var(--bg-secondary); border-left-color: var(--primary); } .md-toc__item:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; } .md-toc__item--l1 { padding-left: 0.75rem; font-weight: 600; } .md-toc__item--l2 { padding-left: 1.4rem; } .md-toc__item--l3 { padding-left: 2.05rem; font-size: 0.82rem; } .md-toc__item--l4 { padding-left: 2.7rem; font-size: 0.8rem; color: var(--text-muted); } .md-toc__item--l5 { padding-left: 3.35rem; font-size: 0.78rem; color: var(--text-muted); } .md-toc__item--l6 { padding-left: 4rem; font-size: 0.78rem; color: var(--text-muted); } /* Flash on click — applied to the heading element in the editor pane. The class is scoped to .md-toc__flash so it doesn't paint outside this plugin. */ .md-toc__flash { background-color: rgba(95, 168, 224, 0.25) !important; transition: background-color 0.3s ease; } /* ── Front matter editor ────────────────────────────────────────────────── */ .md-fm__body { /* Body cell owns the CodeMirror editor; sized by the sidebar's grid row. */ padding: 0; display: block; overflow: hidden; } /* Recognised-keys caption under the header (tooltip carries the full list). */ .md-fm__hint { padding: 2px 0.6rem 4px; font-size: 0.72rem; color: var(--text-muted); cursor: help; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* CodeMirror YAML front-matter editor — fills the body cell + scrolls internally, matching the .zddc previewer's editor styling. */ .md-fm__editor, .md-fm__editor .CodeMirror { height: 100%; } .md-fm__editor .CodeMirror { font-family: var(--font-mono, ui-monospace, SFMono-Regular, Consolas, monospace); font-size: 0.8rem; line-height: 1.45; background: transparent; color: var(--text); } .md-fm__editor .CodeMirror-gutters { background: var(--bg-secondary); border-right: 1px solid var(--border); } /* Schema-completion dropdown (show-hint add-on) — theme it to the app palette so it reads in dark mode; show-hint.css ships light-only. */ .CodeMirror-hints { z-index: 9600; font-family: var(--font-mono, ui-monospace, SFMono-Regular, Consolas, monospace); font-size: 0.78rem; background: var(--bg-elevated, var(--bg, #fff)); border: 1px solid var(--border, #ccc); box-shadow: 0 4px 16px rgba(0, 0, 0, 0.28); } .CodeMirror-hint { color: var(--text, #222); padding: 2px 8px; } li.CodeMirror-hint-active { background: var(--primary, #2868c8); color: #fff; } /* Older .md-fm-section / .fm-list / .md-toc-resizer rules were replaced by the .md-shell BEM block above. */ /* ── Hover info card ────────────────────────────────────────────────────── */ /* Singleton element appended to by browse/js/hovercard.js. Replaces the native title="…" tooltip on tree rows with a rich metadata view (ZDDC parse fields + filesystem info). */ .tree-hovercard { position: fixed; z-index: 9000; max-width: 28rem; min-width: 17rem; background: var(--bg); color: var(--text); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18), 0 2px 6px rgba(0, 0, 0, 0.10); padding: 0.5rem 0.7rem 0.45rem; font-family: var(--font); font-size: 0.8rem; line-height: 1.35; opacity: 0; visibility: hidden; /* pointer-events:auto so the user can mouse into the card to select text. The hide is delayed (HIDE_DELAY_MS in hovercard.js) so the cursor has time to traverse the gap between row and card before the card dismisses. */ pointer-events: auto; /* The tree rows set user-select:none — explicitly allow it here so dragging across the card builds a real selection that can be Ctrl/Cmd-C'd or right-click-Copied via the browser's native menu. */ user-select: text; cursor: default; transition: opacity 0.1s ease; } .tree-hovercard.is-visible { opacity: 1; visibility: visible; } /* Highlight selected text inside the card with the primary accent so it reads as "yes, you can copy this" rather than the default browser selection color. */ .tree-hovercard ::selection { background: var(--primary-light); color: var(--text); } .tree-hovercard__header { margin-bottom: 0.35rem; } .tree-hovercard__title { font-weight: 600; font-size: 0.95rem; line-height: 1.2; color: var(--text); word-break: break-word; } .tree-hovercard__sub { margin-top: 0.15rem; font-family: var(--font-mono); font-size: 0.72rem; color: var(--text-muted); letter-spacing: 0.01em; } .tree-hovercard__list { display: grid; grid-template-columns: max-content 1fr; gap: 0.12rem 0.7rem; align-items: baseline; } .tree-hovercard__key { color: var(--text-muted); font-size: 0.74rem; text-transform: uppercase; letter-spacing: 0.03em; } .tree-hovercard__val { color: var(--text); font-size: 0.82rem; word-break: break-word; } .tree-hovercard__val--mono { font-family: var(--font-mono); font-size: 0.78rem; } /* Archive-reference links inside the hovercard pick up the primary accent so they read as clickable, and stay inline with the mono font when they sit inside a mono cell. */ .tree-hovercard__val a { color: var(--primary, #2868c8); text-decoration: none; } .tree-hovercard__val a:hover { text-decoration: underline; } /* Separator stretches across both grid columns. Bleed into the card's padding so it visually reads as a divider, not a hairline. */ .tree-hovercard__sep { grid-column: 1 / -1; border-top: 1px solid var(--border); margin: 0.25rem -0.7rem; }