This commit introduces a new PHP file `icons.php` that defines a set of inline SVG icons to replace the Bootstrap Icons webfont. The icons include common UI elements such as calendar, clock, folder, and tag, as well as theme picker icons like sun, moon, snow, droplet, cup, and half. The SVGs are designed to scale with existing font-size rules and are accessible via the `blowdit_icon` function. Additionally, a mapping function `blowdit_theme_icon` is provided to associate theme keys with their corresponding icon names.
1858 lines
40 KiB
CSS
1858 lines
40 KiB
CSS
/* ========================================
|
|
BLOWDIT — a Bludit theme inspired by Blowfish
|
|
Monochrome palette with light & dark modes
|
|
======================================== */
|
|
|
|
/* ----------------------------------------
|
|
THEME TOKENS — Light Mode (default)
|
|
---------------------------------------- */
|
|
:root {
|
|
color-scheme: light;
|
|
|
|
/* Accent (monochrome: near-black in light mode) */
|
|
--accent: #1f1f1f;
|
|
--accent-hover: #000000;
|
|
--accent-contrast: #ffffff;
|
|
|
|
/* Surfaces */
|
|
--bg-body: #ffffff;
|
|
--bg-surface: #ffffff;
|
|
--bg-subtle: #f5f5f5;
|
|
--bg-hover: #f0f0f0;
|
|
|
|
/* Text */
|
|
--text-primary: #1f1f1f;
|
|
--text-secondary: #595959;
|
|
--text-muted: #8c8c8c;
|
|
|
|
/* Borders */
|
|
--border-color: #e5e5e5;
|
|
--border-subtle: #ededed;
|
|
|
|
/* Chrome (navbar / footer) — fixed dark in both modes */
|
|
--chrome-bg: #1f1f1f;
|
|
--chrome-text: rgba(255, 255, 255, 0.92);
|
|
--chrome-text-hover: #ffffff;
|
|
|
|
/* Shadows */
|
|
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04);
|
|
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
--shadow-lg: 0 12px 28px rgba(0, 0, 0, 0.12);
|
|
|
|
/* Code */
|
|
--code-bg: #f5f5f5;
|
|
--code-text: #1f1f1f;
|
|
|
|
/* Spacing scale */
|
|
--space-xs: 0.5rem;
|
|
--space-sm: 1rem;
|
|
--space-md: 1.5rem;
|
|
--space-lg: 2rem;
|
|
--space-xl: 3rem;
|
|
|
|
/* Radii */
|
|
--radius-sm: 8px;
|
|
--radius-md: 12px;
|
|
--radius-lg: 18px;
|
|
|
|
/* Typography */
|
|
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
--font-size-base: 1.05rem;
|
|
--font-size-sm: 0.875rem;
|
|
--font-size-xs: 0.78rem;
|
|
--line-height-base: 1.7;
|
|
|
|
/* Filter applied to the dark social SVG glyphs (none on light themes) */
|
|
--social-icon-filter: none;
|
|
}
|
|
|
|
/* ----------------------------------------
|
|
THEME TOKENS — Dark Mode
|
|
---------------------------------------- */
|
|
[data-theme="dark"] {
|
|
color-scheme: dark;
|
|
|
|
--accent: #e5e5e5;
|
|
--accent-hover: #ffffff;
|
|
--accent-contrast: #171717;
|
|
|
|
--bg-body: #171717;
|
|
--bg-surface: #1f1f1f;
|
|
--bg-subtle: #262626;
|
|
--bg-hover: #2e2e2e;
|
|
|
|
--text-primary: #ededed;
|
|
--text-secondary: #b0b0b0;
|
|
--text-muted: #808080;
|
|
|
|
--border-color: #333333;
|
|
--border-subtle: #2a2a2a;
|
|
|
|
--chrome-bg: #1f1f1f;
|
|
--chrome-text: rgba(255, 255, 255, 0.88);
|
|
--chrome-text-hover: #ffffff;
|
|
|
|
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4);
|
|
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.45);
|
|
--shadow-lg: 0 12px 28px rgba(0, 0, 0, 0.5);
|
|
|
|
--code-bg: #262626;
|
|
--code-text: #ededed;
|
|
|
|
--social-icon-filter: brightness(0) invert(1);
|
|
}
|
|
|
|
/* ----------------------------------------
|
|
THEME TOKENS — Nord (arctic)
|
|
---------------------------------------- */
|
|
[data-theme="nord"] {
|
|
color-scheme: dark;
|
|
|
|
--accent: #88c0d0;
|
|
--accent-hover: #8fbcbb;
|
|
--accent-contrast: #2e3440;
|
|
|
|
--bg-body: #2e3440;
|
|
--bg-surface: #3b4252;
|
|
--bg-subtle: #434c5e;
|
|
--bg-hover: #4c566a;
|
|
|
|
--text-primary: #eceff4;
|
|
--text-secondary: #d8dee9;
|
|
--text-muted: #8b95a8;
|
|
|
|
--border-color: #434c5e;
|
|
--border-subtle: #3b4252;
|
|
|
|
--chrome-bg: #3b4252;
|
|
--chrome-text: rgba(236, 239, 244, 0.9);
|
|
--chrome-text-hover: #eceff4;
|
|
|
|
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3);
|
|
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.35);
|
|
--shadow-lg: 0 12px 28px rgba(0, 0, 0, 0.4);
|
|
|
|
--code-bg: #3b4252;
|
|
--code-text: #eceff4;
|
|
|
|
--social-icon-filter: brightness(0) invert(1);
|
|
}
|
|
|
|
/* ----------------------------------------
|
|
THEME TOKENS — Dracula
|
|
---------------------------------------- */
|
|
[data-theme="dracula"] {
|
|
color-scheme: dark;
|
|
|
|
--accent: #bd93f9;
|
|
--accent-hover: #caa6fa;
|
|
--accent-contrast: #282a36;
|
|
|
|
--bg-body: #282a36;
|
|
--bg-surface: #343746;
|
|
--bg-subtle: #44475a;
|
|
--bg-hover: #44475a;
|
|
|
|
--text-primary: #f8f8f2;
|
|
--text-secondary: #c4c6d6;
|
|
--text-muted: #6272a4;
|
|
|
|
--border-color: #44475a;
|
|
--border-subtle: #383b4a;
|
|
|
|
--chrome-bg: #343746;
|
|
--chrome-text: rgba(248, 248, 242, 0.9);
|
|
--chrome-text-hover: #f8f8f2;
|
|
|
|
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.35);
|
|
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4);
|
|
--shadow-lg: 0 12px 28px rgba(0, 0, 0, 0.45);
|
|
|
|
--code-bg: #21222c;
|
|
--code-text: #f8f8f2;
|
|
|
|
--social-icon-filter: brightness(0) invert(1);
|
|
}
|
|
|
|
/* ----------------------------------------
|
|
THEME TOKENS — Catppuccin Mocha
|
|
---------------------------------------- */
|
|
[data-theme="catppuccin"] {
|
|
color-scheme: dark;
|
|
|
|
--accent: #cba6f7;
|
|
--accent-hover: #d7bdf9;
|
|
--accent-contrast: #1e1e2e;
|
|
|
|
--bg-body: #1e1e2e;
|
|
--bg-surface: #313244;
|
|
--bg-subtle: #45475a;
|
|
--bg-hover: #585b70;
|
|
|
|
--text-primary: #cdd6f4;
|
|
--text-secondary: #bac2de;
|
|
--text-muted: #a6adc8;
|
|
|
|
--border-color: #45475a;
|
|
--border-subtle: #313244;
|
|
|
|
--chrome-bg: #313244;
|
|
--chrome-text: rgba(205, 214, 244, 0.9);
|
|
--chrome-text-hover: #cdd6f4;
|
|
|
|
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.35);
|
|
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4);
|
|
--shadow-lg: 0 12px 28px rgba(0, 0, 0, 0.45);
|
|
|
|
--code-bg: #181825;
|
|
--code-text: #cdd6f4;
|
|
|
|
--social-icon-filter: brightness(0) invert(1);
|
|
}
|
|
|
|
/* ========================================
|
|
BASE
|
|
======================================== */
|
|
html {
|
|
position: relative;
|
|
min-height: 100%;
|
|
scroll-behavior: smooth;
|
|
background-color: var(--bg-body);
|
|
}
|
|
|
|
body {
|
|
font-family: var(--font-sans);
|
|
padding-top: 56px;
|
|
min-height: 100vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
background-color: var(--bg-body);
|
|
color: var(--text-primary);
|
|
font-size: var(--font-size-base);
|
|
line-height: var(--line-height-base);
|
|
-webkit-font-smoothing: antialiased;
|
|
text-rendering: optimizeLegibility;
|
|
}
|
|
|
|
/* Main content grows so the footer is pushed to the bottom (sticky footer) */
|
|
body > .container,
|
|
body > .container-wide {
|
|
flex: 1 0 auto;
|
|
width: 100%;
|
|
padding-bottom: var(--space-xl);
|
|
}
|
|
|
|
/* Wider container used on article pages that have a ToC sidebar.
|
|
Same gutter behaviour as Bootstrap's .container but with more breathing room. */
|
|
.container-wide {
|
|
padding-right: 15px;
|
|
padding-left: 15px;
|
|
margin-right: auto;
|
|
margin-left: auto;
|
|
max-width: 1400px;
|
|
}
|
|
|
|
/* Align the navbar's inner container with the wider article layout so the
|
|
brand lines up with the ToC and the nav items line up with the right sidebar. */
|
|
body.has-toc .navbar .container {
|
|
max-width: 1400px;
|
|
}
|
|
|
|
img {
|
|
max-width: 100%;
|
|
height: auto;
|
|
}
|
|
|
|
::selection {
|
|
background: var(--accent);
|
|
color: var(--accent-contrast);
|
|
}
|
|
|
|
/* ========================================
|
|
LINKS
|
|
======================================== */
|
|
a {
|
|
color: var(--text-primary);
|
|
text-decoration: none;
|
|
transition: color 0.2s ease;
|
|
}
|
|
|
|
a:hover {
|
|
color: var(--text-secondary);
|
|
text-decoration: none;
|
|
}
|
|
|
|
a:focus-visible {
|
|
outline: 2px solid var(--accent);
|
|
outline-offset: 2px;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
/* ========================================
|
|
TYPOGRAPHY
|
|
======================================== */
|
|
h1, h2, h3, h4, h5, h6 {
|
|
color: var(--text-primary);
|
|
font-weight: 700;
|
|
line-height: 1.25;
|
|
letter-spacing: -0.02em;
|
|
}
|
|
|
|
h1.title {
|
|
font-size: 2.4rem;
|
|
margin-bottom: var(--space-md);
|
|
}
|
|
|
|
h2.title {
|
|
font-size: 1.85rem;
|
|
margin-bottom: var(--space-sm);
|
|
}
|
|
|
|
.content {
|
|
margin-top: var(--space-md);
|
|
}
|
|
|
|
.content h2 { font-size: 1.6rem; margin-top: var(--space-xl); }
|
|
.content h3 { font-size: 1.3rem; margin-top: var(--space-lg); }
|
|
|
|
.content p,
|
|
.content ul,
|
|
.content ol {
|
|
margin-bottom: var(--space-md);
|
|
}
|
|
|
|
.content img {
|
|
border-radius: var(--radius-md);
|
|
margin: var(--space-md) 0;
|
|
}
|
|
|
|
hr {
|
|
border: none;
|
|
border-top: 1px solid var(--border-color);
|
|
margin: var(--space-xl) 0;
|
|
}
|
|
|
|
/* ========================================
|
|
FORMS
|
|
======================================== */
|
|
input,
|
|
textarea,
|
|
select,
|
|
.form-control {
|
|
background-color: var(--bg-surface);
|
|
color: var(--text-primary);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: var(--radius-sm);
|
|
}
|
|
|
|
.form-control:focus {
|
|
background-color: var(--bg-surface);
|
|
color: var(--text-primary);
|
|
border-color: var(--accent);
|
|
box-shadow: 0 0 0 3px rgba(127, 127, 127, 0.18);
|
|
}
|
|
|
|
/* ========================================
|
|
CODE
|
|
======================================== */
|
|
pre,
|
|
code {
|
|
background: var(--code-bg);
|
|
color: var(--code-text);
|
|
border-radius: var(--radius-sm);
|
|
font-family: "JetBrains Mono", "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
|
transition: background-color 0.25s ease, color 0.25s ease;
|
|
}
|
|
|
|
pre {
|
|
border: 1px solid var(--border-color);
|
|
border-left: 3px solid var(--accent);
|
|
padding: var(--space-md);
|
|
overflow-x: auto;
|
|
/* max-width + min-width:0 on flex ancestors is required for overflow-x:auto
|
|
to actually scroll rather than expand or clip the flex column. */
|
|
max-width: 100%;
|
|
white-space: pre;
|
|
}
|
|
|
|
/* Bootstrap flex columns default to min-width:auto, which lets them grow past
|
|
their grid width and breaks overflow-x:auto on pre/code blocks inside them. */
|
|
.row > [class*="col-"] {
|
|
min-width: 0;
|
|
}
|
|
|
|
code {
|
|
display: inline-block;
|
|
padding: 0.15em 0.45em;
|
|
font-size: 0.875em;
|
|
}
|
|
|
|
pre code {
|
|
padding: 0;
|
|
background: transparent;
|
|
border: none;
|
|
}
|
|
|
|
/* Always-visible scrollbar on code blocks.
|
|
-webkit-appearance: none disables macOS overlay/auto-hide behaviour;
|
|
a non-transparent track ensures the slot is always painted. */
|
|
pre {
|
|
scrollbar-width: thin;
|
|
scrollbar-color: var(--text-muted) var(--bg-subtle);
|
|
}
|
|
pre::-webkit-scrollbar {
|
|
-webkit-appearance: none;
|
|
height: 8px;
|
|
}
|
|
pre::-webkit-scrollbar-track {
|
|
background: var(--bg-subtle);
|
|
border-radius: 0 0 var(--radius-sm) var(--radius-sm);
|
|
}
|
|
pre::-webkit-scrollbar-thumb {
|
|
background: var(--text-muted);
|
|
border-radius: 4px;
|
|
}
|
|
pre::-webkit-scrollbar-thumb:hover {
|
|
background: var(--text-secondary);
|
|
}
|
|
|
|
/* ========================================
|
|
TABLES
|
|
======================================== */
|
|
table {
|
|
empty-cells: show;
|
|
border: 1px solid var(--border-color);
|
|
width: 100%;
|
|
font-size: 0.95em;
|
|
margin-bottom: var(--space-md);
|
|
border-radius: var(--radius-sm);
|
|
overflow: hidden;
|
|
border-collapse: collapse;
|
|
}
|
|
|
|
thead {
|
|
background: var(--bg-subtle);
|
|
color: var(--text-primary);
|
|
text-align: left;
|
|
}
|
|
|
|
tbody tr {
|
|
transition: background-color 0.2s ease;
|
|
}
|
|
|
|
tbody tr:hover {
|
|
background-color: var(--bg-hover);
|
|
}
|
|
|
|
th, td {
|
|
padding: 0.75em 1em;
|
|
border-bottom: 1px solid var(--border-color);
|
|
}
|
|
|
|
/* ========================================
|
|
BLOCKQUOTES
|
|
======================================== */
|
|
blockquote {
|
|
padding: var(--space-md) var(--space-lg);
|
|
margin: 0 0 var(--space-lg) 0;
|
|
border-left: 4px solid var(--accent);
|
|
background-color: var(--bg-subtle);
|
|
border-radius: var(--radius-sm);
|
|
color: var(--text-secondary);
|
|
font-style: italic;
|
|
}
|
|
|
|
/* ========================================
|
|
NAVBAR
|
|
======================================== */
|
|
.navbar-modern {
|
|
background: var(--chrome-bg) !important;
|
|
box-shadow: var(--shadow-sm);
|
|
padding-top: 0.5rem;
|
|
padding-bottom: 0.5rem;
|
|
min-height: 56px;
|
|
}
|
|
|
|
.navbar-brand {
|
|
font-weight: 700;
|
|
font-size: 1.1rem;
|
|
letter-spacing: -0.01em;
|
|
}
|
|
|
|
.navbar-brand .text-white {
|
|
color: #ffffff !important;
|
|
}
|
|
|
|
.navbar .nav-link {
|
|
font-size: 0.85rem;
|
|
font-weight: 500;
|
|
color: var(--chrome-text) !important;
|
|
padding: 0.4rem 0.85rem !important;
|
|
border-radius: var(--radius-sm);
|
|
transition: background-color 0.2s ease, color 0.2s ease;
|
|
}
|
|
|
|
.navbar .nav-link:hover {
|
|
background-color: rgba(255, 255, 255, 0.12);
|
|
color: var(--chrome-text-hover) !important;
|
|
}
|
|
|
|
.navbar .nav-link.active {
|
|
background-color: rgba(255, 255, 255, 0.18);
|
|
color: var(--chrome-text-hover) !important;
|
|
}
|
|
|
|
.navbar .nav-link .bi,
|
|
.navbar .nav-link .bd-icon {
|
|
font-size: 1.15rem;
|
|
margin-right: 0;
|
|
vertical-align: -0.05em;
|
|
}
|
|
|
|
/* Social SVG icons (img/<network>.svg) — rendered white on the dark navbar */
|
|
img.nav-svg-icon {
|
|
width: 1.2rem;
|
|
height: 1.2rem;
|
|
filter: brightness(0) invert(1);
|
|
opacity: 0.85;
|
|
transition: opacity 0.2s ease, transform 0.2s ease;
|
|
}
|
|
|
|
.nav-link:hover img.nav-svg-icon {
|
|
opacity: 1;
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
/* Theme toggle button */
|
|
.theme-toggle {
|
|
background: transparent;
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
color: var(--chrome-text) !important;
|
|
border-radius: var(--radius-sm);
|
|
width: 38px;
|
|
height: 38px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
transition: background-color 0.2s ease, border-color 0.2s ease;
|
|
}
|
|
|
|
.theme-toggle:hover {
|
|
background-color: rgba(255, 255, 255, 0.12);
|
|
border-color: rgba(255, 255, 255, 0.4);
|
|
color: #ffffff !important;
|
|
}
|
|
|
|
.theme-toggle .bi,
|
|
.theme-toggle .bd-icon {
|
|
font-size: 1.1rem;
|
|
margin-right: 0;
|
|
}
|
|
|
|
/* ----------------------------------------
|
|
THEME PICKER DROPDOWN
|
|
---------------------------------------- */
|
|
.theme-picker-wrap {
|
|
position: relative;
|
|
}
|
|
|
|
.theme-picker {
|
|
position: absolute;
|
|
right: 0;
|
|
top: calc(100% + 10px);
|
|
display: flex;
|
|
gap: 2px;
|
|
padding: 10px 8px;
|
|
background: #111111;
|
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
border-radius: var(--radius-md);
|
|
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.5);
|
|
z-index: 1051;
|
|
/* hidden by default */
|
|
visibility: hidden;
|
|
opacity: 0;
|
|
transform: translateY(-6px) scale(0.95);
|
|
transition: opacity 0.15s ease, transform 0.15s ease, visibility 0s linear 0.15s;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.theme-picker.is-open {
|
|
visibility: visible;
|
|
opacity: 1;
|
|
transform: translateY(0) scale(1);
|
|
transition: opacity 0.15s ease, transform 0.15s ease, visibility 0s linear 0s;
|
|
pointer-events: auto;
|
|
}
|
|
|
|
.swatch {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 5px;
|
|
background: none;
|
|
border: none;
|
|
padding: 6px 7px;
|
|
cursor: pointer;
|
|
border-radius: 8px;
|
|
transition: background 0.12s;
|
|
}
|
|
|
|
.swatch:hover {
|
|
background: rgba(255, 255, 255, 0.08);
|
|
}
|
|
|
|
/* --sb = swatch background (bg-body of that theme)
|
|
--sa = swatch accent colour */
|
|
.swatch-dot {
|
|
width: 22px;
|
|
height: 22px;
|
|
border-radius: 50%;
|
|
background: var(--sb);
|
|
border: 2px solid var(--sa);
|
|
transition: box-shadow 0.15s;
|
|
}
|
|
|
|
.swatch.is-active .swatch-dot {
|
|
box-shadow: 0 0 0 2px var(--sa), 0 0 0 4px rgba(255, 255, 255, 0.18);
|
|
}
|
|
|
|
.swatch-label {
|
|
font-size: 9px;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
color: rgba(255, 255, 255, 0.45);
|
|
transition: color 0.12s;
|
|
}
|
|
|
|
.swatch.is-active .swatch-label {
|
|
color: rgba(255, 255, 255, 0.9);
|
|
}
|
|
|
|
/* ========================================
|
|
PROFILE HERO (Blowfish-style homepage)
|
|
======================================== */
|
|
.profile {
|
|
padding: var(--space-lg) 0 var(--space-md);
|
|
margin-bottom: var(--space-md);
|
|
border-bottom: 1px solid var(--border-color);
|
|
}
|
|
|
|
.profile-avatar {
|
|
width: 150px;
|
|
height: 150px;
|
|
border-radius: 50%;
|
|
object-fit: cover;
|
|
border: 4px solid var(--bg-surface);
|
|
box-shadow: var(--shadow-md);
|
|
margin-bottom: var(--space-md);
|
|
}
|
|
|
|
.profile-name {
|
|
font-size: 2.1rem;
|
|
margin-bottom: var(--space-xs);
|
|
}
|
|
|
|
.profile-bio {
|
|
color: var(--text-secondary);
|
|
font-size: 1.1rem;
|
|
margin-bottom: var(--space-md);
|
|
}
|
|
|
|
.profile-social {
|
|
display: flex;
|
|
justify-content: center;
|
|
flex-wrap: wrap;
|
|
gap: var(--space-md);
|
|
}
|
|
|
|
.profile-social a {
|
|
display: inline-flex;
|
|
}
|
|
|
|
.profile-social-icon {
|
|
width: 1.4rem;
|
|
height: 1.4rem;
|
|
opacity: 0.65;
|
|
filter: var(--social-icon-filter);
|
|
transition: opacity 0.2s ease, transform 0.2s ease;
|
|
}
|
|
|
|
.profile-social a:hover .profile-social-icon {
|
|
opacity: 1;
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
@media (max-width: 767.98px) {
|
|
.profile-avatar { width: 120px; height: 120px; }
|
|
.profile-name { font-size: 1.7rem; }
|
|
}
|
|
|
|
/* ========================================
|
|
HOME SEARCH (relocated under the hero)
|
|
======================================== */
|
|
.home-search {
|
|
margin: 0 0 var(--space-md);
|
|
}
|
|
|
|
.home-search .plugin,
|
|
.home-search .plugin-content {
|
|
border: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
/* Hide the "Search" label — Popeye-style bare search bar */
|
|
.home-search .plugin-label {
|
|
display: none;
|
|
}
|
|
|
|
.home-search .plugin-content,
|
|
.home-search form {
|
|
display: flex;
|
|
width: 100%;
|
|
gap: var(--space-sm);
|
|
align-items: stretch;
|
|
margin: 0;
|
|
}
|
|
|
|
/* Force the theme palette over the plugin's own / Bootstrap defaults */
|
|
.home-search input,
|
|
.home-search .form-control {
|
|
flex: 1 1 auto;
|
|
margin: 0 !important;
|
|
background-color: var(--bg-surface) !important;
|
|
color: var(--text-primary) !important;
|
|
border: 1px solid var(--border-color) !important;
|
|
border-radius: var(--radius-sm) !important;
|
|
}
|
|
|
|
.home-search input::placeholder,
|
|
.home-search .form-control::placeholder {
|
|
color: var(--text-muted) !important;
|
|
}
|
|
|
|
.home-search button,
|
|
.home-search .btn {
|
|
flex: 0 0 auto;
|
|
width: auto !important; /* cancel Bootstrap's btn-block */
|
|
margin: 0 !important;
|
|
white-space: nowrap;
|
|
background-color: var(--accent) !important;
|
|
border: 1px solid var(--accent) !important;
|
|
color: var(--accent-contrast) !important;
|
|
border-radius: var(--radius-sm) !important;
|
|
}
|
|
|
|
@media (max-width: 575.98px) {
|
|
.home-search .plugin-content,
|
|
.home-search form {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.home-search button,
|
|
.home-search .btn {
|
|
width: 100% !important;
|
|
}
|
|
}
|
|
|
|
/* ========================================
|
|
POST LIST (titles only)
|
|
======================================== */
|
|
.post-list {
|
|
margin-top: var(--space-xl);
|
|
}
|
|
|
|
.post-list-item {
|
|
padding: var(--space-lg) 0;
|
|
border-bottom: 1px solid var(--border-subtle);
|
|
}
|
|
|
|
.post-list-item:first-child {
|
|
padding-top: 0;
|
|
}
|
|
|
|
.post-list-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
/* Stacked: title, then date right beneath it */
|
|
.post-list-title {
|
|
font-size: 1.4rem;
|
|
font-weight: 600;
|
|
margin: 0 0 0.2rem;
|
|
color: var(--text-primary);
|
|
transition: color 0.2s ease;
|
|
}
|
|
|
|
.post-list-head a:hover .post-list-title {
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.post-list-meta {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
color: var(--text-muted);
|
|
font-size: var(--font-size-sm);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.post-list-meta .bi,
|
|
.post-list-meta .bd-icon {
|
|
margin-right: 0.25rem;
|
|
}
|
|
|
|
.post-list-item .post-taxonomy {
|
|
margin-top: var(--space-sm);
|
|
}
|
|
|
|
/* ========================================
|
|
POST CARDS (single page / post)
|
|
======================================== */
|
|
.card-modern {
|
|
background: transparent;
|
|
border: none;
|
|
border-bottom: 1px solid var(--border-color);
|
|
padding-bottom: var(--space-xl);
|
|
margin-bottom: var(--space-xl);
|
|
}
|
|
|
|
.card-modern:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.card-modern .card-img-top {
|
|
border-radius: var(--radius-md);
|
|
}
|
|
|
|
.card-modern .card-body {
|
|
padding: 0;
|
|
margin-top: var(--space-md);
|
|
}
|
|
|
|
.card-modern .title {
|
|
color: var(--text-primary);
|
|
transition: color 0.2s ease;
|
|
}
|
|
|
|
.card-modern a:hover .title {
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.cover-image-wrapper {
|
|
position: relative;
|
|
overflow: hidden;
|
|
border-radius: var(--radius-md);
|
|
margin-bottom: var(--space-md);
|
|
}
|
|
|
|
.cover-image-wrapper .card-img-top {
|
|
transition: transform 0.4s ease;
|
|
}
|
|
|
|
.card-modern:hover .cover-image-wrapper .card-img-top {
|
|
transform: scale(1.03);
|
|
}
|
|
|
|
/* ========================================
|
|
METADATA (date, reading time)
|
|
======================================== */
|
|
.metadata {
|
|
color: var(--text-muted);
|
|
font-size: var(--font-size-sm);
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: var(--space-md);
|
|
align-items: center;
|
|
}
|
|
|
|
.metadata i,
|
|
.metadata .bd-icon {
|
|
color: var(--text-muted);
|
|
margin-right: 0.35rem;
|
|
}
|
|
|
|
/* ========================================
|
|
BUTTONS
|
|
======================================== */
|
|
.btn-primary-gradient {
|
|
background: var(--accent);
|
|
border: none;
|
|
color: var(--accent-contrast);
|
|
padding: 0.6rem 1.3rem;
|
|
border-radius: var(--radius-sm);
|
|
font-weight: 600;
|
|
font-size: var(--font-size-sm);
|
|
transition: background-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
|
|
box-shadow: var(--shadow-sm);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.btn-primary-gradient:hover {
|
|
background: var(--accent-hover);
|
|
color: var(--accent-contrast);
|
|
transform: translateY(-2px);
|
|
box-shadow: var(--shadow-md);
|
|
text-decoration: none;
|
|
}
|
|
|
|
.btn-primary-gradient .bi,
|
|
.btn-primary-gradient .bd-icon {
|
|
margin-right: 0;
|
|
transition: transform 0.2s ease;
|
|
}
|
|
|
|
.btn-primary-gradient:hover .bi,
|
|
.btn-primary-gradient:hover .bd-icon {
|
|
transform: translateX(3px);
|
|
}
|
|
|
|
/* ========================================
|
|
TAXONOMY (tags & categories)
|
|
======================================== */
|
|
.post-taxonomy {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.4rem;
|
|
align-items: center;
|
|
}
|
|
|
|
.taxonomy-badge {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 0.3rem 0.8rem;
|
|
border-radius: 999px;
|
|
font-size: var(--font-size-xs);
|
|
font-weight: 500;
|
|
border: 1px solid var(--border-color);
|
|
color: var(--text-secondary);
|
|
background: transparent;
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.taxonomy-badge .bi,
|
|
.taxonomy-badge .bd-icon {
|
|
margin-right: 0.35rem;
|
|
font-size: 0.85em;
|
|
}
|
|
|
|
.taxonomy-badge:hover {
|
|
background: var(--accent);
|
|
color: var(--accent-contrast);
|
|
border-color: var(--accent);
|
|
}
|
|
|
|
/* ========================================
|
|
PAGINATION
|
|
======================================== */
|
|
.pagination .page-link {
|
|
background: var(--bg-surface);
|
|
border: 1px solid var(--border-color);
|
|
color: var(--text-primary);
|
|
padding: 0.55rem 1.1rem;
|
|
border-radius: var(--radius-sm);
|
|
font-weight: 500;
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.pagination .page-link:hover {
|
|
background: var(--accent);
|
|
color: var(--accent-contrast);
|
|
border-color: var(--accent);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.pagination .page-item.disabled .page-link {
|
|
background: var(--bg-subtle);
|
|
color: var(--text-muted);
|
|
border-color: var(--border-color);
|
|
}
|
|
|
|
/* ========================================
|
|
SIDEBAR + PLUGINS
|
|
======================================== */
|
|
.sidebar-container {
|
|
margin-top: var(--space-xl);
|
|
background: var(--bg-surface);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: var(--radius-lg);
|
|
padding: var(--space-lg);
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
/* Author card inside the sidebar (article view) */
|
|
.sidebar-author {
|
|
padding-bottom: 1.2rem;
|
|
margin-bottom: 1.2rem;
|
|
border-bottom: 1px solid var(--border-subtle);
|
|
text-align: center;
|
|
}
|
|
|
|
.sidebar-author-label {
|
|
display: block;
|
|
font-size: var(--font-size-xs);
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.08em;
|
|
color: var(--text-muted);
|
|
margin-bottom: 0.85rem;
|
|
}
|
|
|
|
.sidebar-author-identity {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 0.6rem;
|
|
}
|
|
|
|
.sidebar-author-avatar {
|
|
width: 100px;
|
|
height: 100px;
|
|
border-radius: 50%;
|
|
object-fit: cover;
|
|
border: 2px solid var(--border-color);
|
|
transition: transform 0.2s ease;
|
|
}
|
|
|
|
.sidebar-author-avatar:hover {
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.sidebar-author-name {
|
|
font-weight: 700;
|
|
font-size: 1.1rem;
|
|
color: var(--text-primary);
|
|
line-height: 1.3;
|
|
}
|
|
|
|
.sidebar-author-bio {
|
|
color: var(--accent);
|
|
font-size: var(--font-size-xs);
|
|
margin: 0.15rem 0 0;
|
|
line-height: 1.4;
|
|
opacity: 0.75;
|
|
text-shadow: 0 0 12px var(--accent);
|
|
}
|
|
|
|
.plugin {
|
|
padding-bottom: 1.2rem;
|
|
margin-bottom: 1.2rem;
|
|
border-bottom: 1px solid var(--border-subtle);
|
|
}
|
|
|
|
.plugin:last-child {
|
|
border-bottom: none;
|
|
margin-bottom: 0;
|
|
padding-bottom: 0;
|
|
}
|
|
|
|
/* The navigation plugin is wrapped in .js-random-nav, so it matches
|
|
.plugin:last-child even when it isn't last — restore its divider/spacing. */
|
|
.js-random-nav .plugin {
|
|
border-bottom: 1px solid var(--border-subtle);
|
|
margin-bottom: 1.2rem;
|
|
padding-bottom: 1.2rem;
|
|
}
|
|
|
|
/* …unless the wrapped navigation really is the last item in the sidebar. */
|
|
.sidebar-container > .js-random-nav:last-child .plugin {
|
|
border-bottom: none;
|
|
margin-bottom: 0;
|
|
padding-bottom: 0;
|
|
}
|
|
|
|
.plugin-label {
|
|
font-size: var(--font-size-xs);
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.08em;
|
|
color: var(--text-muted);
|
|
margin-bottom: var(--space-sm);
|
|
}
|
|
|
|
.plugin ul {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
.plugin ul li {
|
|
padding: 0.15rem 0;
|
|
}
|
|
|
|
.plugin ul li a {
|
|
color: var(--text-secondary);
|
|
display: block;
|
|
transition: color 0.2s ease;
|
|
}
|
|
|
|
.plugin ul li a:hover {
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
/* Tags & categories in sidebar — badge style */
|
|
.plugin.plugin-tags ul,
|
|
.plugin.plugin-categories ul {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.4rem;
|
|
}
|
|
|
|
.plugin.plugin-tags li,
|
|
.plugin.plugin-categories li {
|
|
padding: 0;
|
|
}
|
|
|
|
.plugin.plugin-tags li a,
|
|
.plugin.plugin-categories li a {
|
|
background: var(--bg-subtle);
|
|
color: var(--text-secondary);
|
|
padding: 0.3rem 0.8rem;
|
|
border-radius: 999px;
|
|
font-size: var(--font-size-xs);
|
|
font-weight: 500;
|
|
display: inline-block;
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.plugin.plugin-tags li a:hover,
|
|
.plugin.plugin-categories li a:hover {
|
|
background: var(--accent);
|
|
color: var(--accent-contrast);
|
|
}
|
|
|
|
/* Search plugin — target input directly in case the plugin omits form-control */
|
|
.plugin.plugin-search input,
|
|
.plugin.plugin-search .form-control {
|
|
border: 1px solid var(--border-color) !important;
|
|
border-radius: var(--radius-sm) !important;
|
|
padding: 0.6rem 0.9rem !important;
|
|
background: var(--bg-surface) !important;
|
|
background-color: var(--bg-surface) !important;
|
|
color: var(--text-primary) !important;
|
|
width: 100%;
|
|
}
|
|
|
|
.plugin.plugin-search input::placeholder,
|
|
.plugin.plugin-search .form-control::placeholder {
|
|
color: var(--text-muted) !important;
|
|
}
|
|
|
|
.plugin.plugin-search input:focus,
|
|
.plugin.plugin-search .form-control:focus {
|
|
border-color: var(--accent) !important;
|
|
box-shadow: 0 0 0 3px rgba(127, 127, 127, 0.18) !important;
|
|
outline: none !important;
|
|
background: var(--bg-surface) !important;
|
|
background-color: var(--bg-surface) !important;
|
|
}
|
|
|
|
.plugin.plugin-search button,
|
|
.plugin.plugin-search .btn {
|
|
background: var(--accent) !important;
|
|
background-color: var(--accent) !important;
|
|
border: 1px solid var(--accent) !important;
|
|
color: var(--accent-contrast) !important;
|
|
padding: 0.55rem 1.2rem;
|
|
border-radius: var(--radius-sm);
|
|
font-weight: 600;
|
|
transition: background-color 0.2s ease, transform 0.2s ease;
|
|
margin-top: var(--space-sm);
|
|
}
|
|
|
|
/* Higher-specificity rule for the sidebar context so we beat any plugin-bundled
|
|
CSS that gets loaded after style.css and re-sets the button colour. */
|
|
.sidebar-container .plugin.plugin-search button,
|
|
.sidebar-container .plugin.plugin-search .btn {
|
|
background: var(--accent) !important;
|
|
background-color: var(--accent) !important;
|
|
border-color: var(--accent) !important;
|
|
color: var(--accent-contrast) !important;
|
|
}
|
|
|
|
.plugin.plugin-search button:hover,
|
|
.plugin.plugin-search .btn:hover {
|
|
background: var(--accent-hover) !important;
|
|
background-color: var(--accent-hover) !important;
|
|
border-color: var(--accent-hover) !important;
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
/* ========================================
|
|
FOOTER
|
|
======================================== */
|
|
footer.footer-modern {
|
|
flex-shrink: 0;
|
|
width: 100%;
|
|
min-height: 64px;
|
|
line-height: 1.6;
|
|
background: var(--chrome-bg);
|
|
color: var(--chrome-text);
|
|
padding: 1rem 0;
|
|
font-size: var(--font-size-xs);
|
|
}
|
|
|
|
footer.footer-modern a {
|
|
color: var(--chrome-text);
|
|
transition: color 0.2s ease;
|
|
}
|
|
|
|
footer.footer-modern a:hover {
|
|
color: var(--chrome-text-hover);
|
|
}
|
|
|
|
.mini-logo {
|
|
height: 22px;
|
|
vertical-align: middle;
|
|
filter: brightness(0) invert(1);
|
|
}
|
|
|
|
/* ========================================
|
|
ICON SPACING
|
|
======================================== */
|
|
.bi,
|
|
.bd-icon {
|
|
margin-right: 0.4rem;
|
|
}
|
|
|
|
/* Inline SVG icons: sized to 1em via their width/height attrs, aligned like
|
|
the old icon font, and never shrink inside flex rows. */
|
|
.bd-icon {
|
|
vertical-align: -0.125em;
|
|
flex: 0 0 auto;
|
|
}
|
|
|
|
/* ========================================
|
|
TABBED CODE BLOCKS (```tabs fences)
|
|
======================================== */
|
|
.tab-group {
|
|
margin: var(--space-md) 0;
|
|
border: 1px solid var(--border-color);
|
|
border-radius: var(--radius-sm);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.tab-list {
|
|
display: flex;
|
|
background: var(--bg-subtle);
|
|
border-bottom: 1px solid var(--border-color);
|
|
overflow-x: auto;
|
|
scrollbar-width: none;
|
|
}
|
|
|
|
.tab-list::-webkit-scrollbar { display: none; }
|
|
|
|
.tab-btn {
|
|
padding: 0.45rem 1rem;
|
|
font-size: var(--font-size-sm);
|
|
font-weight: 600;
|
|
font-family: var(--font-sans);
|
|
background: none;
|
|
border: none;
|
|
border-bottom: 2px solid transparent;
|
|
color: var(--text-muted);
|
|
cursor: pointer;
|
|
white-space: nowrap;
|
|
margin-bottom: -1px;
|
|
transition: color 0.15s ease, border-color 0.15s ease;
|
|
}
|
|
|
|
.tab-btn:hover {
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.tab-btn.is-active {
|
|
color: var(--accent);
|
|
border-bottom-color: var(--accent);
|
|
}
|
|
|
|
.tab-panel {
|
|
display: none;
|
|
}
|
|
|
|
.tab-panel.is-active {
|
|
display: block;
|
|
}
|
|
|
|
/* Inherit code-block look; remove double borders with the group container */
|
|
.tab-panel pre {
|
|
margin: 0;
|
|
border: none;
|
|
border-radius: 0;
|
|
border-left: 3px solid var(--accent);
|
|
}
|
|
|
|
/* ========================================
|
|
IMAGE CAROUSEL (```carousel fences)
|
|
======================================== */
|
|
.carousel {
|
|
position: relative;
|
|
margin: var(--space-md) 0;
|
|
border-radius: var(--radius-md);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.carousel-slide {
|
|
display: none;
|
|
position: relative;
|
|
}
|
|
|
|
.carousel-slide.is-active {
|
|
display: block;
|
|
}
|
|
|
|
.carousel-slide img {
|
|
width: 100%;
|
|
height: auto;
|
|
display: block;
|
|
cursor: zoom-in;
|
|
}
|
|
|
|
/* Caption: hidden by default, fades in on hover */
|
|
.carousel-caption {
|
|
position: absolute;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
margin: 0;
|
|
padding: 0.3rem 1rem;
|
|
background: rgba(0, 0, 0, 0.6);
|
|
color: rgba(255, 255, 255, 0.9);
|
|
font-size: 0.75rem;
|
|
text-align: center;
|
|
opacity: 0;
|
|
transition: opacity 0.2s ease;
|
|
}
|
|
|
|
.carousel-slide:hover .carousel-caption {
|
|
opacity: 1;
|
|
}
|
|
|
|
/* Arrows */
|
|
.carousel-arrow {
|
|
position: absolute;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
z-index: 10;
|
|
background: rgba(0, 0, 0, 0.4);
|
|
color: #ffffff;
|
|
border: none;
|
|
width: 36px;
|
|
height: 36px;
|
|
border-radius: 50%;
|
|
font-size: 1.5rem;
|
|
line-height: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
transition: background 0.15s ease;
|
|
}
|
|
|
|
.carousel-arrow:hover { background: rgba(0, 0, 0, 0.75); }
|
|
.carousel-prev { left: 10px; }
|
|
.carousel-next { right: 10px; }
|
|
|
|
/* Dots: overlaid at the bottom of the image, no separate strip */
|
|
.carousel-dots {
|
|
position: absolute;
|
|
bottom: 10px;
|
|
left: 0;
|
|
right: 0;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
gap: 6px;
|
|
pointer-events: none;
|
|
z-index: 5;
|
|
}
|
|
|
|
.carousel-dot {
|
|
pointer-events: auto;
|
|
width: 6px;
|
|
height: 6px;
|
|
border-radius: 50%;
|
|
background: rgba(255, 255, 255, 0.45);
|
|
border: none;
|
|
padding: 0;
|
|
cursor: pointer;
|
|
transition: background 0.15s ease, transform 0.15s ease;
|
|
}
|
|
|
|
.carousel-dot.is-active {
|
|
background: #ffffff;
|
|
transform: scale(1.5);
|
|
}
|
|
|
|
/* ========================================
|
|
LIGHTBOX
|
|
======================================== */
|
|
.lightbox-overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 2000;
|
|
background: rgba(0, 0, 0, 0.88);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: zoom-out;
|
|
opacity: 0;
|
|
visibility: hidden;
|
|
transition: opacity 0.2s ease, visibility 0s linear 0.2s;
|
|
}
|
|
|
|
.lightbox-overlay.is-open {
|
|
opacity: 1;
|
|
visibility: visible;
|
|
transition: opacity 0.2s ease, visibility 0s linear 0s;
|
|
}
|
|
|
|
.lightbox-overlay img {
|
|
max-width: 90vw;
|
|
max-height: 90vh;
|
|
object-fit: contain;
|
|
border-radius: var(--radius-sm);
|
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
|
|
transform: scale(0.94);
|
|
transition: transform 0.2s ease;
|
|
cursor: zoom-out;
|
|
}
|
|
|
|
.lightbox-overlay.is-open img {
|
|
transform: scale(1);
|
|
}
|
|
|
|
.lightbox-arrow {
|
|
position: fixed;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
z-index: 2001;
|
|
background: rgba(255, 255, 255, 0.12);
|
|
color: #ffffff;
|
|
border: none;
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: 50%;
|
|
font-size: 2rem;
|
|
line-height: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
transition: background 0.15s ease;
|
|
}
|
|
|
|
.lightbox-arrow:hover {
|
|
background: rgba(255, 255, 255, 0.28);
|
|
}
|
|
|
|
.lightbox-prev { left: 20px; }
|
|
.lightbox-next { right: 20px; }
|
|
|
|
/* Zoomable image wrapper — JS wraps each content image in this span */
|
|
.zoomable-wrap {
|
|
position: relative;
|
|
display: inline-block;
|
|
cursor: zoom-in;
|
|
overflow: hidden; /* clips both pseudo-elements to image bounds */
|
|
}
|
|
|
|
/* ::before — dark overlay */
|
|
.zoomable-wrap::before {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 0;
|
|
background: rgba(0, 0, 0, 0.38);
|
|
opacity: 0;
|
|
transition: opacity 0.2s ease;
|
|
pointer-events: none;
|
|
z-index: 1;
|
|
}
|
|
|
|
/* ::after — stroke-based magnifier, centred, fixed 36 px.
|
|
Strokes render as vectors so no pixelation at any display size. */
|
|
.zoomable-wrap::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 36px;
|
|
height: 36px;
|
|
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3Cline x1='11' y1='8' x2='11' y2='14'/%3E%3Cline x1='8' y1='11' x2='14' y2='11'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
opacity: 0;
|
|
transition: opacity 0.2s ease;
|
|
pointer-events: none;
|
|
z-index: 2;
|
|
}
|
|
|
|
.zoomable-wrap:hover::before,
|
|
.zoomable-wrap:hover::after {
|
|
opacity: 1;
|
|
}
|
|
|
|
/* ========================================
|
|
TABLE OF CONTENTS (left sidebar, article view)
|
|
======================================== */
|
|
.toc-sidebar {
|
|
margin-top: var(--space-xl);
|
|
position: sticky;
|
|
/* 56px = fixed navbar height; extra gap so the ToC doesn't hug it */
|
|
top: calc(56px + var(--space-sm));
|
|
max-height: calc(100vh - 56px - var(--space-md));
|
|
overflow-y: auto;
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
.toc-title {
|
|
font-size: var(--font-size-xs);
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.08em;
|
|
color: var(--text-muted);
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
|
|
.toc-list {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
.toc-item {
|
|
padding: 0.15rem 0;
|
|
}
|
|
|
|
.toc-h3 {
|
|
padding-left: 0.75rem;
|
|
}
|
|
|
|
.toc-h4 {
|
|
padding-left: 1.5rem;
|
|
}
|
|
|
|
.toc-link {
|
|
color: var(--text-muted);
|
|
text-decoration: none;
|
|
line-height: 1.5;
|
|
display: block;
|
|
transition: color 0.15s ease;
|
|
}
|
|
|
|
.toc-link:hover {
|
|
color: var(--text-primary);
|
|
text-decoration: none;
|
|
}
|
|
|
|
.toc-link.active {
|
|
color: var(--accent);
|
|
font-weight: 600;
|
|
text-decoration: none;
|
|
}
|
|
|
|
/* ========================================
|
|
SKIP-TO-CONTENT LINK (keyboard / a11y)
|
|
======================================== */
|
|
.skip-link {
|
|
position: fixed;
|
|
top: -120px;
|
|
left: 1rem;
|
|
z-index: 3000;
|
|
padding: 0.6rem 1rem;
|
|
background: var(--accent);
|
|
color: var(--accent-contrast);
|
|
border-radius: var(--radius-sm);
|
|
font-weight: 600;
|
|
text-decoration: none;
|
|
box-shadow: var(--shadow-md);
|
|
transition: top 0.15s ease;
|
|
}
|
|
|
|
.skip-link:focus {
|
|
top: 1rem;
|
|
color: var(--accent-contrast);
|
|
text-decoration: none;
|
|
outline: 2px solid var(--accent-contrast);
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
/* The skip target shouldn't paint a focus ring when jumped to */
|
|
#main-content:focus {
|
|
outline: none;
|
|
}
|
|
|
|
/* ========================================
|
|
READING PROGRESS BAR
|
|
======================================== */
|
|
.reading-progress {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 3px;
|
|
z-index: 1040; /* above the fixed-top navbar (Bootstrap z-index 1030) */
|
|
background: transparent;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.reading-progress-fill {
|
|
height: 100%;
|
|
width: 0;
|
|
background: var(--accent);
|
|
transition: width 0.05s linear;
|
|
}
|
|
|
|
/* ========================================
|
|
BACK-TO-TOP BUTTON
|
|
======================================== */
|
|
.back-to-top {
|
|
position: fixed;
|
|
bottom: 1.5rem;
|
|
right: 1.5rem;
|
|
z-index: 1035;
|
|
width: 44px;
|
|
height: 44px;
|
|
padding: 0;
|
|
border: none;
|
|
border-radius: 50%;
|
|
background: var(--accent);
|
|
color: var(--accent-contrast);
|
|
display: grid;
|
|
place-items: center;
|
|
cursor: pointer;
|
|
box-shadow: var(--shadow-md);
|
|
opacity: 0;
|
|
visibility: hidden;
|
|
transform: translateY(10px);
|
|
transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s linear 0.2s, background 0.15s ease;
|
|
}
|
|
|
|
.back-to-top.is-visible {
|
|
opacity: 1;
|
|
visibility: visible;
|
|
transform: translateY(0);
|
|
transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s linear 0s, background 0.15s ease;
|
|
}
|
|
|
|
.back-to-top:hover {
|
|
background: var(--accent-hover);
|
|
}
|
|
|
|
/* Inline SVG arrow — centred in its own viewBox, so no font baseline/bearing
|
|
offsets. grid place-items on the button does the rest. */
|
|
.back-to-top svg {
|
|
display: block;
|
|
}
|
|
|
|
/* ========================================
|
|
COPY-TO-CLIPBOARD ON CODE BLOCKS
|
|
======================================== */
|
|
.code-block {
|
|
position: relative;
|
|
}
|
|
|
|
.code-copy {
|
|
position: absolute;
|
|
top: 0.5rem;
|
|
right: 0.5rem;
|
|
z-index: 2;
|
|
width: 32px;
|
|
height: 32px;
|
|
padding: 0;
|
|
display: grid;
|
|
place-items: center;
|
|
border: 1px solid var(--border-color);
|
|
border-radius: var(--radius-sm);
|
|
background: var(--bg-subtle);
|
|
color: var(--text-secondary);
|
|
cursor: pointer;
|
|
opacity: 0;
|
|
transition: opacity 0.15s ease, color 0.15s ease, background 0.15s ease, border-color 0.15s ease;
|
|
}
|
|
|
|
.code-block:hover .code-copy,
|
|
.code-copy:focus {
|
|
opacity: 1;
|
|
}
|
|
|
|
.code-copy:hover {
|
|
color: var(--text-primary);
|
|
border-color: var(--accent);
|
|
}
|
|
|
|
.code-copy.is-copied {
|
|
opacity: 1;
|
|
color: var(--accent-contrast);
|
|
background: var(--accent);
|
|
border-color: var(--accent);
|
|
}
|
|
|
|
.code-copy svg {
|
|
display: block;
|
|
}
|
|
|
|
/* "Copied" toast — pops up just left of the button */
|
|
.code-copy-toast {
|
|
position: absolute;
|
|
top: 0.5rem;
|
|
right: calc(0.5rem + 32px + 8px); /* clears the 32px button + gap */
|
|
height: 32px;
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0 0.6rem;
|
|
border-radius: var(--radius-sm);
|
|
background: var(--accent);
|
|
color: var(--accent-contrast);
|
|
font-size: var(--font-size-xs);
|
|
font-weight: 600;
|
|
white-space: nowrap;
|
|
pointer-events: none;
|
|
opacity: 0;
|
|
transform: translateX(6px);
|
|
transition: opacity 0.15s ease, transform 0.15s ease;
|
|
}
|
|
|
|
.code-copy-toast.is-visible {
|
|
opacity: 1;
|
|
transform: translateX(0);
|
|
}
|
|
|
|
/* Touch devices have no hover — keep the button discoverable */
|
|
@media (hover: none) {
|
|
.code-copy { opacity: 0.7; }
|
|
}
|
|
|
|
/* ========================================
|
|
GPU HINTS — promote animated layers ahead of time
|
|
======================================== */
|
|
.lightbox-overlay img,
|
|
.carousel-slide img {
|
|
will-change: transform;
|
|
}
|
|
|
|
/* Respect users who prefer reduced motion */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.reading-progress-fill { transition: none; }
|
|
.back-to-top,
|
|
.back-to-top.is-visible { transform: none; }
|
|
}
|
|
|
|
/* ========================================
|
|
SYNTAX HIGHLIGHTING (highlight.js)
|
|
Use only the theme's token colours; keep the code block's own
|
|
background/padding (set on pre / --code-bg) rather than hljs's.
|
|
======================================== */
|
|
pre code.hljs {
|
|
padding: 0 !important;
|
|
background: transparent !important;
|
|
}
|
|
|
|
/* ========================================
|
|
KEYBOARD FOCUS — visible ring for keyboard users only
|
|
======================================== */
|
|
a:focus-visible,
|
|
button:focus-visible,
|
|
.swatch:focus-visible,
|
|
.tab-btn:focus-visible,
|
|
.carousel-arrow:focus-visible,
|
|
.carousel-dot:focus-visible,
|
|
.lightbox-arrow:focus-visible,
|
|
.code-copy:focus-visible,
|
|
.back-to-top:focus-visible {
|
|
outline: 2px solid var(--accent);
|
|
outline-offset: 2px;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
/* Don't show the ring for mouse clicks */
|
|
a:focus:not(:focus-visible),
|
|
button:focus:not(:focus-visible) {
|
|
outline: none;
|
|
}
|
|
|
|
/* ========================================
|
|
PRINT — clean, ink-friendly article output
|
|
======================================== */
|
|
@media print {
|
|
.navbar-modern,
|
|
.footer,
|
|
.sidebar-container,
|
|
.toc-sidebar,
|
|
.skip-link,
|
|
.reading-progress,
|
|
.back-to-top,
|
|
.code-copy,
|
|
.code-copy-toast,
|
|
.theme-picker-wrap {
|
|
display: none !important;
|
|
}
|
|
|
|
html, body {
|
|
background: #ffffff !important;
|
|
color: #000000 !important;
|
|
}
|
|
|
|
/* Use the full page width once the chrome is gone */
|
|
.container,
|
|
.container-wide {
|
|
max-width: 100% !important;
|
|
width: 100% !important;
|
|
}
|
|
|
|
.card-modern {
|
|
box-shadow: none !important;
|
|
border: none !important;
|
|
}
|
|
|
|
a {
|
|
color: #000000 !important;
|
|
text-decoration: underline;
|
|
}
|
|
|
|
/* Keep code blocks readable on paper */
|
|
pre, code {
|
|
background: #f5f5f5 !important;
|
|
color: #000000 !important;
|
|
border-color: #cccccc !important;
|
|
}
|
|
|
|
pre {
|
|
white-space: pre-wrap;
|
|
word-wrap: break-word;
|
|
}
|
|
|
|
/* Avoid awkward page breaks */
|
|
h1, h2, h3, h4 { page-break-after: avoid; }
|
|
pre, blockquote, table, figure, img { page-break-inside: avoid; }
|
|
}
|
|
|
|
/* ========================================
|
|
RESPONSIVE
|
|
======================================== */
|
|
@media (max-width: 767.98px) {
|
|
h1.title { font-size: 1.9rem; }
|
|
h2.title { font-size: 1.5rem; }
|
|
|
|
body { font-size: 1rem; }
|
|
|
|
.sidebar-container {
|
|
position: static;
|
|
margin-top: var(--space-lg);
|
|
}
|
|
|
|
.btn-primary-gradient {
|
|
width: 100%;
|
|
justify-content: center;
|
|
}
|
|
|
|
.navbar-nav .theme-toggle {
|
|
width: 100%;
|
|
margin-top: 0.5rem;
|
|
height: 42px;
|
|
}
|
|
}
|