cyb/optica/static/style.css

/* cyber-publish default stylesheet
 * All colors use CSS custom properties (injected by build).
 * Edit publish.toml [style] section to customize.
 */

/* ============================================================
   PAGE TRANSITIONS β€” entrance fade on the article only.
   Animates .main-content from opacity 0 to 1 on every page load.
   Sidebar and corner chrome stay still (no visual jolt on
   identity badge / contribute fab / clock). No JS click
   interception; the old page paints normally until the browser
   commits navigation, then the new page's article fades in.
   ============================================================ */
.main-content {
    animation: page-content-in 220ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

@keyframes page-content-in {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .main-content { animation: none; }
}

/* ============================================================
   FONTS (self-hosted)
   ============================================================ */
/* latin-ext */
@font-face {
    font-family: "Play";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url(/static/fonts/play-400-latin-ext.woff2)
        format("woff2");
    unicode-range:
        U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
        U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
        U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
        U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
    font-family: "Play";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url(/static/fonts/play-400-latin.woff2)
        format("woff2");
    unicode-range:
        U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC,
        U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
        U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
        U+2215, U+FEFF, U+FFFD;
}
/* latin-ext */
@font-face {
    font-family: "Play";
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url(/static/fonts/play-700-latin-ext.woff2)
        format("woff2");
    unicode-range:
        U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7,
        U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF,
        U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
        U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
    font-family: "Play";
    font-style: normal;
    font-weight: 700;
    font-display: swap;
    src: url(/static/fonts/play-700-latin.woff2)
        format("woff2");
    unicode-range:
        U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC,
        U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
        U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212,
        U+2215, U+FEFF, U+FFFD;
}

/* ============================================================
   PAGE TRANSITIONS
   ============================================================ */

/* Cross-document View Transitions were tried and reverted: the
   @view-transition { navigation: auto } path in current Chromium/Safari
   intermittently drops clicks that land during an in-progress transition
   β€” reproducible as a ~10% drop on back-to-back navigations. The
   smoothness isn't worth the cost of users having to click twice. Keep
   the hover-prefetch as the perceived-snappiness primitive instead. */

/* Keep scrollbar width stable so sidebar/header don't shift when page
   content is shorter or longer than the viewport. This is the most common
   source of perceived "jumping" during navigation. */
html {
    scrollbar-gutter: stable;
}

/* ============================================================
   RESET & BASE
   ============================================================ */

*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html {
    font-size: var(--font-size-base, 1rem);
    scroll-behavior: smooth;
    -webkit-text-size-adjust: 100%;
}

body {
    font-family: var(
        --font-body,
        system-ui,
        -apple-system,
        sans-serif
    );
    line-height: var(--line-height, 1.7);
    color: var(--color-text, #f0f0f0);
    background-color: var(--color-bg, #000000);
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

/* ============================================================
   TYPOGRAPHY
   ============================================================ */

h1,
h2,
h3,
h4,
h5,
h6 {
    line-height: 1.3;
    font-weight: 600;
    margin-top: 1.5em;
    margin-bottom: 0.5em;
    color: var(--color-text);
}

h1 {
    font-size: 2rem;
    margin-top: 0;
}
h2 {
    font-size: 1.5rem;
}
h3 {
    font-size: 1.25rem;
}
h4 {
    font-size: 1.1rem;
}
h5 {
    font-size: 1rem;
}
h6 {
    font-size: 0.9rem;
}

p {
    margin-bottom: 1em;
}

/* --- Links: NO underlines anywhere --- */
a {
    color: var(--color-primary);
    text-decoration: none;
    transition: color 0.15s ease;
}

a:hover {
    opacity: 0.85;
    text-decoration: none;
}

/* Internal links (wikilinks) β€” green, no underline, no border */
a.internal-link {
    color: var(--color-primary);
}

a.internal-link:hover {
    opacity: 0.8;
}

a.stub-link {
    color: #eab308;
    opacity: 0.7;
}

a.stub-link:hover {
    opacity: 1;
}

/* External links β€” blue, no underline */
a.external-link {
    color: var(--color-blue, #3b82f6);
}

a.external-link:hover {
    opacity: 0.8;
}

/* Lists */
ul,
ol {
    margin-bottom: 1em;
    padding-left: 1.5em;
}

li {
    margin-bottom: 0.25em;
}

li > ul,
li > ol {
    margin-top: 0.25em;
    margin-bottom: 0;
}

/* Blockquotes */
blockquote {
    border-left: 3px solid var(--color-primary);
    margin: 1em 0;
    padding: 0.5em 1em;
    color: var(--color-text);
    opacity: 0.85;
    background: var(--color-surface);
    border-radius: 0 4px 4px 0;
}

blockquote > *:last-child {
    margin-bottom: 0;
}

/* Code β€” inline */
code {
    font-family: var(--font-mono, monospace);
    font-size: 0.9em;
    padding: 0.15em 0.35em;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 3px;
}

/* Code blocks β€” frameless, same background as page */
pre,
pre.syntax-highlighting {
    margin: 1.5em 0;
    padding: 0;
    background: transparent !important;
    border: none !important;
    border-radius: 0;
    overflow-x: auto;
    line-height: 1.6;
}

pre code {
    padding: 0;
    background: none !important;
    border: none !important;
    border-radius: 0;
    font-size: 0.85em;
    color: var(--color-text);
}

/* Syntax highlighting β€” cyber palette */
pre span.source { color: #e0e0e0; }
pre span.comment,
pre span.comment span { color: #555; font-style: italic; }
pre span.keyword,
pre span.storage,
pre span.keyword span { color: #22c55e; }
pre span.entity,
pre span.entity span.name,
pre span.entity span { color: #06b6d4; }
pre span.string,
pre span.string span { color: #3b9eff; }
pre span.constant,
pre span.constant span { color: #c084fc; }
pre span.variable,
pre span.variable span { color: #f0f0f0; }
pre span.support,
pre span.support span { color: #06b6d4; }
pre span.meta,
pre span.meta span { color: #888; }
pre span.punctuation,
pre span.punctuation span { color: #777; }
pre span.invalid { color: #ef4444; }
pre span.storage.type { color: #06b6d4; }
pre span.storage.modifier { color: #22c55e; }

/* Tables */
.page-content table {
    display: block;
    width: 100%;
    overflow-x: auto;
    border-collapse: collapse;
    margin: 1em 0;
}

table {
    width: 100%;
    border-collapse: collapse;
    margin: 1em 0;
}

th,
td {
    padding: 0.4em 0.75em;
    border: none;
    border-bottom: 1px solid
        color-mix(
            in srgb,
            var(--color-text) 6%,
            transparent
        );
    text-align: left;
    white-space: nowrap;
}

th {
    font-weight: 400;
    font-size: 0.8em;
    opacity: 0.5;
    text-transform: lowercase;
    letter-spacing: 0.04em;
}

.page-content table tr:hover {
    background: color-mix(
        in srgb,
        var(--color-primary) 3%,
        transparent
    );
}

/* Images */
img {
    max-width: 100%;
    height: auto;
    border-radius: 4px;
}

hr {
    border: none;
    border-top: 1px solid var(--color-border);
    margin: 2em 0;
}

/* Task lists */
.contains-task-list {
    list-style: none;
    padding-left: 0;
}

.task-list-item input[type="checkbox"] {
    margin-right: 0.5em;
}

/* ============================================================
   LAYOUT
   ============================================================ */

.layout {
    padding-left: 0;
}

.main-content {
    max-width: var(--max-width, 48rem);
    width: 100%;
    margin: 0 auto;
    padding: 2rem 1rem 5rem;
    flex: 1;
}

/* ============================================================
   HEADER β€” COMMANDER
   ============================================================ */

/* Bottom-anchored, only as wide as its content, centered.
   Full-width was painting a backdrop strip across the bottom and
   covering anything sitting in the corners (e.g. the app orbs).
   The bar is now a self-contained widget that doesn't paint outside
   its own box. */
.site-header {
    position: fixed;
    left: 50%;
    bottom: 0;
    top: auto;
    transform: translateX(-50%);
    width: 100%;
    max-width: var(--max-width, 48rem);
    z-index: 30;
    background: color-mix(in srgb, var(--color-bg) 88%, transparent);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    padding: 0.75rem 1rem;
    pointer-events: auto;
    border-radius: 8px 8px 0 0;
}

.header-inner {
    width: 100%;
    margin: 0;
}

.header-search {
    position: relative;
    width: 100%;
}

/* Commander β€” 3D trapezoid bar.
   Two pseudo layers: ::before is the always-rendered base (gradient
   + dim border), ::after is the neon glow (heavy box-shadow) which
   fades in with opacity on focus. Animating opacity on the glow
   layer is GPU-accelerated, vs. animating the box-shadow value
   itself which forced full-region repaints β€” that's what felt laggy. */
.commander {
    position: relative;
    padding: 0.5em 0;
    background: transparent;
    border: none;
    text-align: center;
    perspective: 400px;
    cursor: text;
}

.commander::before,
.commander::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 2.2em;
    transform: rotateX(-1.5deg);
    transform-origin: top center;
    pointer-events: none;
    will-change: opacity;
}

.commander::before {
    background: linear-gradient(
        180deg,
        rgba(128, 0, 255, 0) 0%,
        rgba(128, 0, 255, 0.06) 40%,
        rgba(168, 85, 247, 0.12) 100%
    );
    border-bottom: 2px solid rgba(168, 85, 247, 0.25);
    transition: border-color 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}

.commander::after {
    background: linear-gradient(
        180deg,
        rgba(128, 0, 255, 0) 0%,
        rgba(128, 0, 255, 0.18) 30%,
        rgba(168, 85, 247, 0.42) 100%
    );
    border-bottom: 2px solid rgba(168, 85, 247, 0.95);
    /* Glow stays inside the bar: a tight outer hairline + an inset
       wash. The previous 80px outward halo bled across the rest of
       the screen, e.g. over the bottom-left app orbs β€” the search
       must not paint anything visible outside its own bar. */
    box-shadow:
        0 1px 0 rgba(168, 85, 247, 0.9),
        inset 0 -8px 18px rgba(168, 85, 247, 0.35),
        inset 0 -1px 0 rgba(168, 85, 247, 0.6);
    opacity: 0;
    transition: opacity 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}

.commander:focus-within::before {
    border-bottom-color: rgba(168, 85, 247, 0.9);
}

.commander:focus-within::after {
    opacity: 1;
}

/* Tilde prompt */
.commander-prompt {
    font-family: var(--font-mono, monospace);
    font-size: 0.9rem;
    color: var(--color-primary);
    opacity: 0.5;
    user-select: none;
    transition: opacity 0.3s ease;
    display: inline;
    vertical-align: middle;
}

.commander:focus-within .commander-prompt {
    opacity: 0.8;
}

/* Input field */
.header-search #search-input {
    display: inline-block;
    width: 80%;
    padding: 0.15em 0.3em;
    font-family: var(--font-mono, monospace);
    font-size: 0.9rem;
    border: none;
    border-radius: 0;
    background: transparent;
    color: var(--color-text);
    outline: none;
    caret-color: var(--color-primary);
    vertical-align: middle;
    position: relative;
    z-index: 2;
}

.header-search #search-input::placeholder {
    color: transparent;
}

.header-search .search-results {
    position: absolute;
    bottom: 100%;
    left: 0;
    right: 0;
    z-index: 50;
    background: var(--color-bg);
    border: 1px solid rgba(168, 85, 247, 0.25);
    border-bottom: none;
    border-radius: 6px 6px 0 0;
    max-height: 60vh;
    overflow-y: auto;
    box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.6);
}

.header-search .search-results:empty {
    display: none;
}

.header-search .search-results a {
    display: block;
    padding: 0.5em 0.75em;
    border-bottom: 1px solid rgba(255, 255, 255, 0.04);
    transition: background 0.1s;
}

.header-search .search-results a:last-child {
    border-bottom: none;
}

.header-search .search-results a:hover,
.header-search .search-results a.active {
    background: rgba(168, 85, 247, 0.1);
}

.header-search .search-results a.active {
    outline: none;
    border-left: 2px solid rgba(168, 85, 247, 0.7);
}

.header-search .search-results .search-result-title {
    display: flex;
    align-items: baseline;
    gap: 0.5em;
    flex-wrap: wrap;
    font-weight: 500;
    color: var(--color-text);
}

.header-search .search-results .search-result-excerpt {
    margin-top: 0.15em;
    font-size: 0.75rem;
    line-height: 1.4;
    opacity: 0.55;
    color: var(--color-text);
}

.header-search .search-results .search-result-tags {
    display: inline-flex;
    gap: 0.25em;
    flex-wrap: wrap;
}

.header-search .search-results .search-result-tag {
    font-family: var(--font-mono, monospace);
    font-size: 0.65rem;
    letter-spacing: 0.04em;
    padding: 0 0.4em;
    border-radius: 999px;
    background: rgba(168, 85, 247, 0.1);
    color: rgba(168, 85, 247, 0.8);
}

.header-search .search-results mark {
    background: rgba(168, 85, 247, 0.25);
    color: inherit;
    padding: 0 0.05em;
    border-radius: 2px;
}

.header-search .search-results .search-empty {
    margin: 0;
    padding: 0.6em 0.75em;
    font-size: 0.8rem;
    opacity: 0.5;
    color: var(--color-text);
}

/* ============================================================
   APP ORBS β€” primary functions as a small constellation in the
   bottom-left corner. Graph is the bold central orb; Files and
   Blog flank it as satellites at offset positions, so the cluster
   reads as a launcher, not a flat menu.
   ============================================================ */

/* Launcher lives inside the .sidebar flex column. The .sidebar-nav
   above it has flex: 1, so the launcher is pushed down to the
   bottom of the sidebar automatically β€” no fixed positioning, no
   width math, geometry is inherited verbatim from the sidebar. */
.app-launcher {
    margin-top: auto;
    padding-bottom: 1rem;
}

.app-launcher-list {
    margin: 0 0 0.75rem;
    padding: 0;
    list-style: none;
}

/* SVG icons inside .sidebar-icon mirror the size of the emoji
   glyphs sidebar items use (~1.1rem) so the line-art weight
   reads as a peer of 🌍 / 🧠 / etc., not larger. */
.app-launcher-list .sidebar-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.app-launcher-list .sidebar-icon svg {
    width: 1.1rem;
    height: 1.1rem;
    display: block;
}

/* Graph orb β€” circular hero. Stays a separate visual from the
   sidebar-style rows above. */
.app-orb {
    pointer-events: auto;
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-left: 1rem;
    border-radius: 50%;
    background: radial-gradient(
        circle at 50% 38%,
        color-mix(in srgb, var(--color-bg) 80%, transparent) 0%,
        color-mix(in srgb, var(--color-bg) 95%, transparent) 70%
    );
    color: var(--color-text);
    text-decoration: none;
    box-shadow:
        0 0 0 1px color-mix(in srgb, var(--color-primary) 28%, transparent),
        0 0 14px color-mix(in srgb, var(--color-primary) 20%, transparent);
    transition:
        transform 0.22s cubic-bezier(0.22, 1, 0.36, 1),
        box-shadow 0.22s cubic-bezier(0.22, 1, 0.36, 1),
        color 0.22s ease;
}

.app-orb:hover {
    transform: translateY(-1px) scale(1.04);
    color: var(--color-primary);
    box-shadow:
        0 0 0 1px color-mix(in srgb, var(--color-primary) 65%, transparent),
        0 0 22px color-mix(in srgb, var(--color-primary) 50%, transparent);
    z-index: 2;
}

.app-orb.active {
    color: var(--color-primary);
    box-shadow:
        0 0 0 1px color-mix(in srgb, var(--color-primary) 85%, transparent),
        0 0 28px color-mix(in srgb, var(--color-primary) 60%, transparent),
        0 0 60px color-mix(in srgb, var(--color-primary) 22%, transparent);
}

.app-orb--main {
    width: 10.8rem;
    height: 10.8rem;
}

/* Shrink the graph orb to a third on narrow viewports β€” at desktop
   size it dominates the column nicely, but on mobile it eats most
   of the screen and obstructs reading. */
@media (max-width: 768px) {
    .app-orb--main {
        width: 3.6rem;
        height: 3.6rem;
    }
}
.app-orb--main .app-orb-glyph {
    width: 70%;
    height: 70%;
    color: var(--color-primary);
    display: block;
}
.app-orb--main .app-orb-graph-svg {
    width: 100%;
    height: 100%;
    display: block;
    filter: drop-shadow(0 0 6px color-mix(in srgb, var(--color-primary) 45%, transparent));
}

.app-orb-glyph { line-height: 1; }

/* ============================================================
   SIDEBAR NAVIGATION
   ============================================================ */

.sidebar {
    position: fixed;
    top: 0;
    left: 0;
    width: var(--sidebar-width, 220px);
    height: 100vh;
    background: var(--color-bg);
    border-right: none;
    z-index: 20;
    display: flex;
    flex-direction: column;
    /* No top padding here β€” the sidebar-header carries the top
       offset so it lines up with the contribute fab's top: 0.6rem. */
    padding: 0 0 1rem;
    overflow-y: auto;
}

/* Bold identity badge β€” icon dominates, label sits inside.
   Mirrors the contribute fab on the top-right so the two outer
   corners frame the page. */
.sidebar-header {
    padding: 0.6rem 1rem 1rem;
    border-bottom: none;
    margin-bottom: 0.25rem;
}

.site-title {
    display: inline-flex;
    align-items: center;
    gap: 0.75rem;
    font-weight: 700;
    font-size: 1.35rem;
    line-height: 1;
    color: var(--color-text);
    white-space: nowrap;
}

.site-icon {
    width: 3.5rem;
    height: 3.5rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 3.2rem;
    line-height: 1;
    flex-shrink: 0;
}

/* Top-right contribute fab β€” same icon size as the identity
   badge. Layout is reversed (label first, icon last) so the icon
   ends up flush at the right edge of the viewport, mirroring the
   identity badge whose icon sits flush at the left edge. */
.contribute-fab {
    position: fixed;
    top: 0.6rem;
    right: 1rem;
    z-index: 25;
    display: inline-flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0;
    color: var(--color-text);
    text-decoration: none;
    font-weight: 700;
    font-size: 1.35rem;
    line-height: 1;
    white-space: nowrap;
    transition: color 0.18s ease, transform 0.22s cubic-bezier(0.22, 1, 0.36, 1);
}
.contribute-fab:hover {
    color: var(--color-primary);
    transform: translateY(-1px);
}
/* Mask the Guy Fawkes PNG with the primary colour. CSS mask lets
   us drop the alpha channel of the PNG onto a flat fill so the
   icon recolours with the rest of the UI (hover, theme tweaks)
   instead of being baked white. */
.contribute-fab-icon {
    width: 3.5rem;
    height: 3.5rem;
    display: inline-block;
    flex-shrink: 0;
    background-color: var(--color-primary);
    -webkit-mask-image: url("/static/anonymous-mask.png");
            mask-image: url("/static/anonymous-mask.png");
    -webkit-mask-size: contain;
            mask-size: contain;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-position: center;
            mask-position: center;
    transition: background-color 0.18s ease;
}
.contribute-fab:hover .contribute-fab-icon {
    background-color: var(--color-primary);
    filter: brightness(1.2);
}
.contribute-fab-name {
    letter-spacing: 0.01em;
}

@media (max-width: 768px) {
    .contribute-fab-name { display: none; }
}

/* Lunar machine time β€” fixed bottom-right, single line.
   Date (year Β· moon Β· day-of-moon) on the left, UTC time on the
   right. Clickable: links to /mt (Machine Time spec). */
.lmt-clock {
    position: fixed;
    bottom: 1rem;
    right: 1.25rem;
    z-index: 25;
    display: inline-flex;
    align-items: baseline;
    gap: 0.65rem;
    font-family: var(--font-mono, monospace);
    line-height: 1;
    color: var(--color-primary);
    text-decoration: none;
    text-shadow: 0 0 10px color-mix(in srgb, var(--color-primary) 35%, transparent);
    transition: transform 0.22s cubic-bezier(0.22, 1, 0.36, 1), color 0.18s ease;
}

.lmt-clock:hover {
    color: var(--color-primary);
    transform: translateY(-1px);
    text-shadow: 0 0 16px color-mix(in srgb, var(--color-primary) 60%, transparent);
}

.lmt-clock-time {
    font-size: 1rem;
    font-weight: 500;
    letter-spacing: 0.06em;
    opacity: 0.7;
}

.lmt-clock-date {
    font-size: 1.7rem;
    font-weight: 700;
    letter-spacing: 0.04em;
}

.site-title:hover {
    color: var(--color-primary);
}

.sidebar-nav {
    /* No flex-grow here β€” if .sidebar-nav grows to fill the column,
       there's no free space left for .app-launcher's margin-top:
       auto to consume, and the launcher gets pushed past the
       viewport bottom (visible on mobile where 100vh is shorter).
       Letting .sidebar-nav size to its content leaves the slack
       for the launcher to anchor at the bottom of the sidebar. */
    padding: 0.15rem 0 0.5rem;
}

.sidebar-menu {
    list-style: none;
    margin: 0;
    padding: 0;
}

.sidebar-item a {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.45rem 1rem;
    color: var(--color-text);
    opacity: 0.7;
    font-size: 0.95rem;
    transition:
        opacity 0.15s,
        background 0.15s;
    text-decoration: none;
}

.sidebar-item a:hover {
    opacity: 1;
    background: rgba(255, 255, 255, 0.04);
}

.sidebar-item a.active {
    opacity: 1;
    color: var(--color-primary);
    background: rgba(168, 85, 247, 0.06);
}

.sidebar-icon {
    font-size: 1.1rem;
    width: 1.4rem;
    text-align: center;
    flex-shrink: 0;
}

.sidebar-label {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.sidebar-builtin {
    margin-top: 0.5rem;
    padding-top: 0.5rem;
    border-top: none;
}

.external-icon {
    font-size: 0.75em;
    margin-left: 0.15em;
}

.sidebar-toggle {
    display: none;
    position: fixed;
    top: 0.6rem;
    left: 0.6rem;
    z-index: 40;
    background: color-mix(in srgb, var(--color-bg) 85%, transparent);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    border: 1px solid color-mix(in srgb, var(--color-primary) 30%, transparent);
    border-radius: 6px;
    cursor: pointer;
    padding: 0.55rem 0.6rem;
    flex-direction: column;
    gap: 5px;
    box-shadow: 0 0 12px color-mix(in srgb, var(--color-primary) 18%, transparent);
}

.sidebar-toggle span {
    display: block;
    width: 22px;
    height: 2px;
    background: var(--color-primary);
    transition: transform 0.2s, opacity 0.2s;
}

.sidebar.open + .sidebar-toggle span:nth-child(1),
.sidebar-toggle[aria-expanded="true"] span:nth-child(1) {
    transform: translateY(7px) rotate(45deg);
}
.sidebar.open + .sidebar-toggle span:nth-child(2),
.sidebar-toggle[aria-expanded="true"] span:nth-child(2) {
    opacity: 0;
}
.sidebar.open + .sidebar-toggle span:nth-child(3),
.sidebar-toggle[aria-expanded="true"] span:nth-child(3) {
    transform: translateY(-7px) rotate(-45deg);
}

/* ============================================================
   LAYOUT β€” SIDEBAR + MAIN (see .layout above)
   ============================================================ */

/* ============================================================
   PAGES
   ============================================================ */

.page-header {
    margin-bottom: 2rem;
}

/* Title bar: sticks below the site header so the reader never loses
   the page they're on. Site header (z:30) would otherwise cover it.
   --site-header-h is set from JS on load and resize. Metadata below
   scrolls away with body. */
.page-title-bar {
    position: sticky;
    top: var(--site-header-h, 3rem);
    z-index: 20;
    background: var(--color-bg);
    padding: 0.75rem 0 0.4rem;
    margin: -0.75rem 0 0.5rem;
}

.page-header h1 {
    margin: 0;
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

/* Tab bar: lives after main content in normal flow. When the reader
   scrolls past it, it pins directly under site-header + title-bar
   and stays there while the four sections scroll under it. The
   stacked `top` offset is driven by --sticky-h, set from JS. */
.page-tabs {
    position: sticky;
    top: var(--sticky-h, 6rem);
    z-index: 19;
    background: var(--color-bg);
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
    margin: 2.5rem 0 0;
    padding: 0.5rem 0;
    font-size: 0.9rem;
    border-bottom: 1px solid var(--color-border);
}

.page-tab {
    padding: 0.4rem 0;
    margin-bottom: -1px;
    color: var(--color-text);
    opacity: 0.5;
    text-decoration: none;
    border-bottom: 2px solid transparent;
    transition: opacity 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.page-tab:hover {
    opacity: 0.9;
}

.page-tab.is-active {
    opacity: 1;
    color: var(--color-primary);
    border-bottom-color: var(--color-primary);
}

/* Icon + page name on one line */
.page-icon {
    font-size: 1.6rem;
    line-height: 1;
    flex-shrink: 0;
}

/* Aliases (synonyms) shown under page title */
.page-aliases {
    font-size: 0.8rem;
    opacity: 0.5;
    margin-top: 0.15rem;
}

.page-alias {
    color: var(--color-text);
}

.page-alias + .page-alias::before {
    content: " Β· ";
}

.page-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 0.5rem 0;
}

.tag {
    display: inline-block;
    padding: 0.15em 0.6em;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 1em;
    font-size: 0.8rem;
    color: var(--color-primary);
    transition: background 0.15s;
}

.tag:hover {
    background: var(--color-primary);
    color: #fff;
}

.tag-small {
    font-size: 0.7rem;
    padding: 0.1em 0.4em;
}

.tag-count {
    opacity: 0.6;
}

.crystal {
    display: inline-block;
    padding: 0.15em 0.6em;
    border-radius: 1em;
    font-size: 0.75rem;
    letter-spacing: 0.04em;
    border: 1px solid;
}

.crystal-type {
    color: #b48ead;
    border-color: rgba(180, 142, 173, 0.35);
    background: rgba(180, 142, 173, 0.08);
}

.crystal-domain {
    color: #88c0d0;
    border-color: rgba(136, 192, 208, 0.35);
    background: rgba(136, 192, 208, 0.08);
}

.page-meta-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
    margin-top: 0.5rem;
    font-size: 0.8rem;
    font-family: var(--font-mono);
    opacity: 0.7;
}

.page-focus {
    color: var(--color-primary);
}

.page-visibility {
    padding: 0.05rem 0.45rem;
    border: 1px solid currentColor;
    border-radius: 3px;
    text-transform: lowercase;
    letter-spacing: 0.03em;
}

.page-visibility.is-private {
    color: #e06c75;
}

.page-date {
    font-size: 0.85rem;
    opacity: 0.6;
}

.ns-path {
    color: var(--color-text);
    opacity: 0.45;
    text-decoration: none;
    font-weight: 400;
}
.ns-path:hover {
    opacity: 0.8;
    text-decoration: underline;
}
.ns-sep {
    opacity: 0.3;
    margin: 0 0.1em;
    font-weight: 300;
}

.page-content {
    margin-bottom: 2rem;
}

/* ============================================================
   POST-CONTENT SECTIONS
   Dimensions / Children / Linked References / Local Graph share one
   visual grammar: dimmed uppercase label, flat list underneath, no
   boxes. Each section distinguishes itself by content, not chrome.
   ============================================================ */

.dimensional-peers,
.namespace-children,
.backlinks,
.page-minimap {
    margin-top: 2.5rem;
    padding: 0;
    background: transparent;
    border: none;
    /* Anchor-jumps and smooth-scroll account for the sticky stack
       (site-header + title-bar + tabs-bar). JS sets --sticky-h-full. */
    scroll-margin-top: calc(var(--sticky-h-full, 8rem) + 1rem);
}

.dimensional-peers > h2,
.namespace-children > h2,
.backlinks > h2,
.page-minimap > h2 {
    font-size: 1.35rem;
    font-weight: 700;
    text-transform: none;
    letter-spacing: 0;
    opacity: 1;
    margin: 0 0 1rem 0;
}

.namespace-children ul,
.backlinks-list {
    list-style: none;
    padding: 0;
    margin: 0;
}

.namespace-children li,
.backlinks-list li {
    padding: 0.25rem 0;
    font-size: 0.9rem;
}

.backlink-title {
    font-size: 0.9rem;
}

/* Dimensions uses <details> so content is collapsible, but the summary
   row should look identical to a backlink list item β€” no box, no bg. */
.dimension {
    border: none;
    border-radius: 0;
    margin: 0;
}

.dimension-path {
    display: block;
    padding: 0.25rem 0;
    background: transparent;
    cursor: pointer;
    font-size: 0.9rem;
}

.dimension-path:hover {
    background: transparent;
    opacity: 0.85;
}

.dimension-path a {
    opacity: 0.8;
}

.dimension-content {
    padding: 0.5rem 0 0.5rem 1rem;
    margin: 0.25rem 0 0.5rem 0.25rem;
    border-top: none;
    border-left: 2px solid var(--color-border);
    font-size: 0.9rem;
    opacity: 0.85;
}

.dimension-content h1,
.dimension-content h2,
.dimension-content h3 {
    font-size: 1rem;
    margin-top: 1rem;
}

/* ============================================================
   INDEX PAGE
   ============================================================ */

.index-header {
    margin-bottom: 2rem;
}

.site-description {
    font-size: 1.1rem;
    opacity: 0.7;
    margin-top: 0.5rem;
}

.page-count {
    font-size: 0.85rem;
    opacity: 0.5;
}

.index-search {
    margin-bottom: 2rem;
}

#search-input {
    width: 100%;
    padding: 0.6em 1em;
    font-family: inherit;
    font-size: 1rem;
    border: 1px solid var(--color-border);
    border-radius: 6px;
    background: var(--color-surface);
    color: var(--color-text);
    outline: none;
    transition: border-color 0.15s;
}

#search-input:focus {
    border-color: var(--color-primary);
}

.search-results {
    margin-top: 0.5rem;
}

.search-results a {
    display: block;
    padding: 0.5em 0.75em;
    border-radius: 4px;
}

.search-results a:hover {
    background: var(--color-surface);
}

.search-result-title {
    font-weight: 500;
}

.search-result-excerpt {
    font-size: 0.85rem;
    opacity: 0.6;
    margin-top: 0.2em;
}

.page-list {
    list-style: none;
    padding: 0;
}

.page-list-item {
    padding: 0.5em 0;
    border-bottom: 1px solid var(--color-border);
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.page-list-item:last-child {
    border-bottom: none;
}

.page-list-title {
    font-weight: 500;
}

.page-list-item time {
    font-size: 0.8rem;
    opacity: 0.5;
    white-space: nowrap;
}

.page-icon-small {
    margin-right: 0.3rem;
}

/* ============================================================
   BLOG PAGE β€” TIMELINE
   ============================================================ */

.blog-page {
    position: relative;
    padding-bottom: 4rem;
}

/* Timeline container */
.timeline {
    position: relative;
    padding: 1rem 0;
}

/* Vertical line */
.timeline::before {
    content: "";
    position: absolute;
    left: 50%;
    top: 0;
    bottom: 0;
    width: 2px;
    background: var(--color-border);
    transform: translateX(-50%);
}

/* Each entry: date row + content row */
.timeline-entry {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    gap: 0 1.5rem;
    align-items: start;
    margin-bottom: 2rem;
}

/* Centered date */
.timeline-date {
    grid-column: 1 / -1;
    text-align: center;
    margin-bottom: 0.75rem;
    position: relative;
    z-index: 1;
}

.timeline-date-link {
    display: inline-block;
    background: var(--color-bg);
    padding: 0.3rem 1rem;
    border: 1px solid var(--color-border);
    border-radius: 2rem;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--color-primary);
    text-decoration: none;
    letter-spacing: 0.02em;
    transition: all 0.2s ease;
}

.timeline-date-link:hover {
    background: var(--color-primary);
    color: var(--color-bg);
    border-color: var(--color-primary);
}

/* Content card */
.timeline-content {
    grid-column: 1 / -1;
    width: 100%;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 0.5rem;
    padding: 1rem 1.25rem;
    position: relative;
}

/* Content body β€” rendered HTML with clickable wikilinks */
.timeline-body {
    font-size: 0.92rem;
    line-height: 1.6;
}

.timeline-body p {
    margin: 0.3rem 0;
}

.timeline-body ul,
.timeline-body ol {
    margin: 0.25rem 0;
    padding-left: 1.25rem;
}

.timeline-body li {
    margin-bottom: 0.2rem;
}

/* "read more" link */
.timeline-more {
    display: inline-block;
    margin-top: 0.5rem;
    font-size: 0.82rem;
    color: var(--color-primary);
    opacity: 0.8;
    text-decoration: none;
}

.timeline-more:hover {
    opacity: 1;
    text-decoration: underline;
}

/* Tags inside timeline */
.timeline-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-top: 0.6rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--color-border);
}

/* Mobile: simpler layout */
@media (max-width: 640px) {
    .timeline::before {
        left: 1rem;
    }
    .timeline-entry {
        display: block;
        padding-left: 2.5rem;
        position: relative;
    }
    .timeline-date {
        text-align: left;
        margin-bottom: 0.5rem;
    }
    .timeline-content {
        margin: 0;
    }
}

/* ============================================================
   RESPONSIVE
   ============================================================ */

@media (max-width: 768px) {
    .sidebar {
        transform: translateX(-100%);
        transition: transform 0.25s ease;
    }

    .sidebar.open {
        transform: translateX(0);
    }

    .sidebar-toggle {
        display: flex;
    }

    .layout {
        padding-left: 0;
    }

    .site-header {
        padding-left: 0.75rem;
    }

    .main-content {
        padding: 1rem 0.75rem 5rem;
    }

    /* Make room for the hamburger toggle that lives at top-left
       of the viewport. Without this, the page title icon sits
       directly under the toggle and looks crowded. */
    .page-title-bar {
        padding-left: 3.25rem;
    }

    h1 {
        font-size: 1.5rem;
    }
    h2 {
        font-size: 1.25rem;
    }

    .pages-table {
        font-size: 0.8rem;
    }
    .pages-table .col-rank,
    .pages-table .col-created,
    .pages-table .col-modified,
    .pages-table .col-size,
    .pages-table .col-density,
    .pages-table .col-gravity {
        display: none;
    }
    .pages-table th,
    .pages-table td {
        padding: 0.3rem 0.5rem;
    }
    .col-title a {
        white-space: normal;
        word-break: break-word;
    }
}

/* ============================================================
   TABLE OF CONTENTS
   ============================================================ */

/* Plain block β€” no box, no border, no background. The TOC reads
   as a first-class heading + nested list, the same way the rest of
   the page renders headings and lists. */
.toc {
    margin: 0 0 2rem;
    padding: 0;
    background: none;
    border: none;
}

/* "Contents" inherits the standard h2 typography β€” same weight,
   size, color, line-height as any other section heading. */
.toc h2 {
    margin: 0 0 0.6rem;
}

/* padding-left = bullet inset so the depth=1 dot sits inside the
   column instead of overflowing to the left of the page gutter. */
.toc ul {
    margin: 0;
    padding-left: 0.7rem;
    list-style: none;
}

/* Depth-driven indent. Flat <li> + data-depth keeps the DOM valid
   on pages whose headings don't strictly nest (mixing h2/h3 in any
   order); padding-left is the visual hierarchy. 1.4rem per step is
   wide enough to read at a glance, narrow enough that 5–6 levels
   still fit. */
.toc li {
    margin: 0.2rem 0;
    padding-left: calc((var(--toc-d, 1) - 1) * 1.4rem);
    position: relative;
}

.toc li[data-depth="1"] { --toc-d: 1; }
.toc li[data-depth="2"] { --toc-d: 2; }
.toc li[data-depth="3"] { --toc-d: 3; }
.toc li[data-depth="4"] { --toc-d: 4; }
.toc li[data-depth="5"] { --toc-d: 5; }
.toc li[data-depth="6"] { --toc-d: 6; }

/* Subtle marker dot before each entry. Same shape at every depth β€”
   the indent carries the hierarchy, the dot just anchors the eye. */
.toc li::before {
    content: "Β·";
    position: absolute;
    left: calc((var(--toc-d, 1) - 1) * 1.4rem - 0.7rem);
    color: var(--color-primary);
    opacity: 0.5;
}

/* Synthetic page-title row at the top of every TOC. Slightly
   stronger weight than body entries so the eye reads it as the
   root. No bullet β€” it stands on its own. */
.toc li.toc-title {
    font-weight: 600;
    margin-bottom: 0.35rem;
}
.toc li.toc-title::before {
    content: none;
}
.toc li.toc-title a {
    opacity: 1;
    color: var(--color-primary);
}

.toc a {
    color: var(--color-text);
    opacity: 0.8;
}

.toc a:hover {
    color: var(--color-primary);
    opacity: 1;
}

.heading-anchor {
    display: block;
    position: relative;
    top: -4rem;
    visibility: hidden;
}

/* ============================================================
   ADMONITIONS
   ============================================================ */

.admonition {
    border-left: 4px solid var(--color-primary);
    padding: 1rem 1.25rem;
    margin: 1.5rem 0;
    background: var(--color-surface);
    border-radius: 0 6px 6px 0;
}

.admonition-title {
    font-weight: 600;
    font-size: 0.9rem;
    margin-bottom: 0.5rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.admonition-note {
    border-left-color: var(--color-blue, #3b82f6);
}
.admonition-note .admonition-title {
    color: var(--color-blue, #3b82f6);
}

.admonition-tip {
    border-left-color: var(--color-green, #22c55e);
}
.admonition-tip .admonition-title {
    color: var(--color-green, #22c55e);
}

.admonition-warning {
    border-left-color: #f59e0b;
}
.admonition-warning .admonition-title {
    color: #f59e0b;
}

.admonition-caution {
    border-left-color: var(--color-red, #ef4444);
}
.admonition-caution .admonition-title {
    color: var(--color-red, #ef4444);
}

.admonition-important {
    border-left-color: #a855f7;
}
.admonition-important .admonition-title {
    color: #a855f7;
}

/* ============================================================
   EMBEDS
   ============================================================ */

.embed {
    border-left: 3px solid var(--color-primary);
    padding: 0.75rem 1rem;
    margin: 1rem 0;
    background: var(--color-surface);
    border-radius: 0 6px 6px 0;
}

.embed-header {
    font-weight: 600;
    margin-bottom: 0.5rem;
    font-size: 0.9rem;
}

.embed-header a {
    color: var(--color-primary);
}

.embed-missing {
    border-left-color: var(--color-border);
    opacity: 0.6;
    font-style: italic;
}

/* ============================================================
   BLOCK REFERENCES
   ============================================================ */

.block-ref {
    background: var(--color-surface);
    padding: 0.1em 0.3em;
    border-radius: 3px;
    border-bottom: 1px dashed var(--color-primary);
}

.block-ref-missing {
    opacity: 0.5;
    font-size: 0.85em;
    border-bottom-color: var(--color-border);
}

/* ============================================================
   QUERY RESULTS
   ============================================================ */

.query-results {
    border: 1px solid var(--color-border);
    border-radius: 6px;
    padding: 1rem;
    margin: 1rem 0;
    background: var(--color-surface);
}

.query-results-header {
    font-size: 0.85rem;
    opacity: 0.5;
    margin-bottom: 0.5rem;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.query-results ul {
    margin: 0;
    padding-left: 1.5rem;
}

.query-fallback {
    background: var(--color-surface);
    border: 1px dashed var(--color-border);
    border-radius: 6px;
    padding: 1rem;
    margin: 1rem 0;
}

.query-fallback code {
    font-size: 0.85rem;
}

.query-fallback .query-note {
    font-size: 0.8rem;
    opacity: 0.5;
    margin-top: 0.5rem;
}

/* ============================================================
   TOPICS PAGE
   ============================================================ */

.index-topics {
    margin: 2rem 0;
}

.index-topics h2 {
    margin-bottom: 0.75rem;
}

.index-topics .fullscreen-viz-container {
    height: 400px;
    border-radius: 8px;
    border: 1px solid var(--color-border);
}

/* On index page, topics widget should not break out of container */
.index-page .index-topics {
    position: relative;
    overflow: hidden;
}

/* ============================================================
   FULL-SCREEN VISUALIZATIONS (Graph, Topics)
   ============================================================ */

.fullscreen-viz {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    z-index: 0;
}

.fullscreen-viz-container {
    width: 100vw;
    height: 100vh;
    position: absolute;
    top: 0;
    left: 0;
    overflow: hidden;
}

/* When fullscreen-viz is active, make nav float on top with blur */
body:has(.fullscreen-viz) .site-header {
    background: color-mix(
        in srgb,
        var(--color-bg) 60%,
        transparent
    );
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
}

body:has(.fullscreen-viz) .sidebar {
    background: color-mix(
        in srgb,
        var(--color-bg) 60%,
        transparent
    );
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
}

body:has(.fullscreen-viz) .layout {
    padding-left: 0;
}

body:has(.fullscreen-viz) .main-content {
    padding: 0;
    max-width: none;
}

.graph-stats {
    position: fixed;
    bottom: 5.5rem;
    left: 50%;
    transform: translateX(-50%);
    font-size: 0.8rem;
    color: var(--color-primary);
    background: color-mix(
        in srgb,
        var(--color-bg) 70%,
        transparent
    );
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid
        color-mix(
            in srgb,
            var(--color-primary) 30%,
            transparent
        );
    padding: 0.4rem 1.2rem;
    border-radius: 2rem;
    pointer-events: none;
    z-index: 10;
    letter-spacing: 0.03em;
}

/* Graph filter β€” two-row stack: a dimension switcher above, pills
   for the active dimension below. Selections in inactive
   dimensions persist and still affect the filter; only the pills
   row changes when you switch tabs.
   Constrained to ~62% of viewport width, centered, so the pills
   don't overlap the sidebar menu on the left. */
.graph-filter {
    position: fixed;
    top: 1rem;
    left: 50%;
    transform: translateX(-50%);
    width: 62%;
    max-width: 62vw;
    z-index: 35;
    padding: 0.4rem 1rem 0.3rem;
    pointer-events: none;
}

.graph-filter-tabs {
    display: flex;
    gap: 0.5rem;
    justify-content: center;
    margin-bottom: 0.35rem;
}

.graph-filter-tab {
    pointer-events: auto;
    cursor: pointer;
    font-family: inherit;
    font-size: 0.72rem;
    letter-spacing: 0.04em;
    padding: 0.25rem 0.75rem;
    border-radius: 1rem;
    border: 1px solid color-mix(in srgb, var(--color-text) 18%, transparent);
    background: color-mix(in srgb, var(--color-bg) 70%, transparent);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    color: var(--color-text);
    opacity: 0.55;
    transition: opacity 0.12s ease, border-color 0.12s ease, background 0.12s ease, color 0.12s ease;
}

.graph-filter-tab:hover {
    opacity: 0.9;
}

.graph-filter-tab.has-selection {
    border-color: color-mix(in srgb, var(--color-primary) 55%, transparent);
    color: var(--color-primary);
    opacity: 0.95;
}

.graph-filter-tab.active {
    opacity: 1;
    border-color: var(--color-primary);
    background: color-mix(in srgb, var(--color-primary) 10%, var(--color-bg));
}

.graph-filter-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    padding-bottom: 0.25rem;
    justify-content: center;
}

.graph-filter-pill {
    flex: 0 0 auto;
    font-size: 0.7rem;
    padding: 0.22rem 0.7rem;
    border-radius: 1rem;
    border: 1px solid color-mix(in srgb, var(--color-primary) 30%, transparent);
    background: color-mix(in srgb, var(--color-bg) 70%, transparent);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    color: var(--color-text);
    opacity: 0.7;
    cursor: pointer;
    transition: opacity 0.15s, border-color 0.15s, background 0.15s, color 0.15s;
    font-family: inherit;
    letter-spacing: 0.03em;
    white-space: nowrap;
    pointer-events: auto;
}

.graph-filter-pill:hover {
    opacity: 1;
    border-color: var(--color-primary);
}

.graph-filter-pill.active {
    background: var(--color-primary);
    color: var(--color-bg);
    opacity: 1;
    border-color: var(--color-primary);
}

/* ============================================================
   PAGES DISCOVERY
   ============================================================ */

.pages-discovery {
    padding-bottom: 4rem;
}

.files-stats-top {
    color: var(--color-primary);
    font-size: 0.85rem;
    letter-spacing: 0.03em;
    padding: 0.4rem 0;
    margin-bottom: 0.5rem;
    text-align: center;
}

.pages-table {
    width: 100%;
    /* fixed layout β€” columns take exactly the widths declared below
       and the title column gets whatever is left, so the table never
       grows wider than its container (.main-content's max-width). */
    table-layout: fixed;
    border-collapse: collapse;
    font-size: 0.88rem;
    border-spacing: 0;
}

.pages-table thead {
    position: sticky;
    top: 0;
    z-index: 5;
    background: var(--color-bg);
}

.pages-table th {
    text-align: left;
    padding: 0.5rem 0.75rem;
    font-size: 0.65rem;
    text-transform: lowercase;
    letter-spacing: 0.06em;
    opacity: 0.35;
    font-weight: 400;
    border: none;
    border-bottom: 1px solid
        color-mix(
            in srgb,
            var(--color-text) 6%,
            transparent
        );
    vertical-align: middle;
}

.pages-table td {
    padding: 0.35rem 0.75rem;
    border: none;
    border-bottom: 1px solid
        color-mix(
            in srgb,
            var(--color-text) 3%,
            transparent
        );
    transition: background 0.15s ease;
    vertical-align: middle;
}

.pages-table .page-row {
    /* content-visibility: auto on 22k rows makes hover feel laggy β€”
       the browser is paint-deferring offscreen rows and has to do
       a layout pass before reacting to hover. The cost of always-
       laying-out is small for a flat table; the hover responsiveness
       is worth far more. */
    transition: background 0.05s ease;
}

.pages-table .page-row:hover {
    background: color-mix(
        in srgb,
        var(--color-primary) 4%,
        transparent
    );
}

.pages-table .page-row:hover .col-rank {
    opacity: 0.5;
}

/* Rank column hidden so the file column lines up with the search bar
   above. Row IDs (#r{{rank}}) remain in the DOM for deep links and
   scroll-position hash tracking. */
.col-rank {
    display: none;
}

/* Title cell: first visible column, no left padding so its content
   starts flush at the table's left edge (= search bar left edge). */
.pages-table th.col-title,
.pages-table td.col-title {
    padding-left: 0;
}

/* Title is the elastic column β€” claim leftover table width and clip
   long file paths with ellipsis inside. Hover the cell to read the
   full name via the native title tooltip. */
.col-title {
    width: 100%;
}
.col-title a {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    max-width: 100%;
    overflow: hidden;
    vertical-align: middle;
}
.row-title-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

.col-size,
.col-pagerank,
.col-links,
.col-density,
.col-gravity,
.col-created,
.col-modified {
    white-space: nowrap;
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-size: 0.8rem;
    padding-left: 0.3rem !important;
    padding-right: 0.3rem !important;
}

/* Explicit widths for numeric columns under table-layout: fixed.
   These together with col-title (auto, takes leftover) keep the
   total within the parent's 48rem cap. */
.pages-table .col-created  { width: 3.6rem; }
.pages-table .col-modified { width: 3.6rem; }
.pages-table .col-size     { width: 3.6rem; }
.pages-table .col-links    { width: 2.6rem; }
.pages-table .col-pagerank { width: 2.6rem; }
.pages-table .col-density  { width: 2.8rem; }
.pages-table .col-gravity  { width: 2.8rem; }

.col-density,
.col-gravity {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    letter-spacing: 0.02em;
}

.pages-table th[data-col] {
    cursor: pointer;
    user-select: none;
}
.pages-table th.sort-asc::after { content: ' ↑'; }
.pages-table th.sort-desc::after { content: ' ↓'; }

.col-created,
.col-modified {
    font-family: var(--font-mono);
    font-size: 0.72rem;
    letter-spacing: 0.02em;
    color: #666;
}

.col-gravity {
    padding-right: 0.5rem !important;
}

.pages-table .col-title {
    width: 100%;
    white-space: normal;
    overflow: hidden;
    text-overflow: ellipsis;
}

.col-title a {
    color: var(--color-text);
    text-decoration: none;
    transition: color 0.15s ease;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
}

.col-title .page-icon {
    font-size: 1.1rem;
    line-height: 1;
}

.col-title a:hover {
    color: var(--color-primary);
}

.row-tags {
    margin-left: 0.5rem;
}

.row-tag {
    font-size: 0.65rem;
    opacity: 0.3;
    color: var(--color-text);
    text-decoration: none;
    margin-right: 0.3rem;
    transition: opacity 0.15s ease;
}

.row-tag:hover {
    opacity: 0.7;
    color: var(--color-primary);
}

/* ============================================================
   FILES TABLE
   ============================================================ */

.files-table .col-type {
    width: 3.5rem;
    opacity: 0.35;
    font-size: 0.75rem;
}

.files-table .col-cid {
    width: 5.5rem;
    font-size: 0.75rem;
    opacity: 0.4;
    font-variant-numeric: tabular-nums;
}

.files-table .col-cid code {
    font-size: 0.7rem;
    opacity: 0.7;
    background: none;
    padding: 0;
}

.file-missing {
    opacity: 0.4;
}


Homonyms

cyb/cyb/cyb-portal/style.css

Graph