From b3d5dd036d66f6445f28dbbe5f3b8e8475a8fb14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Wed, 26 Aug 2026 18:17:02 +0200 Subject: [PATCH 01/67] Port the content block to the design system's class vocabulary Blocks now carry the classes ui_kits/app/app.css targets, replacing the Bootstrap grid ones: row-fluid block -> block ldh-block main span7 -> main ldh-block-body well -> ldh-block-head 'block' and 'main' stay alongside the new names: 62 and 11 CSR handler patterns anchor on those tokens, so dropping them would unhook drag-drop, inline editing and the block toolbars. The header gains the two flex children app.css expects - a titles group (type list as badge, h2.ttl, description as span.sub instead of a

) and an .actions group holding the timestamp and actions. bs2:Left and bs2:Right emitted reserved grid columns for backlinks, related resources and parallax navigation. The design system has no columns; that content belongs to a block-scoped drawer, so they now render inside div.ldh-drawer. They stay unconditional because CSR fills them later. Two CSR call sites had to follow the markup: - view.xsl took the nav placeholders with a child step, which the drawer wrapper broke - faceted search would have lost its facets and parallax its controls, silently, since an empty for-each is not an error. - chart.xsl and query.xsl lift the header out before replacing block content via ./div/div[@class = 'well'], declared as="element()", so the rename would have been a runtime type error rather than a silent miss. Still to port: bs2:Left/bs2:Right internals to the drawer's own structure, the drawer's open/close behaviour, bs2:Actions buttons to .tb, the YouTube header's inner markup, and dropping app.css itself in - without which this renders unstyled. Co-Authored-By: Claude Opus 5 (1M context) --- .../xsl/bootstrap/2.3.2/client/block.xsl | 36 +++++----- .../bootstrap/2.3.2/client/block/chart.xsl | 16 ++--- .../bootstrap/2.3.2/client/block/query.xsl | 10 +-- .../xsl/bootstrap/2.3.2/client/block/view.xsl | 22 +++--- .../2.3.2/imports/services/youtube.xsl | 2 +- .../xsl/bootstrap/2.3.2/resource.xsl | 67 +++++++++++-------- 6 files changed, 82 insertions(+), 71 deletions(-) diff --git a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block.xsl b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block.xsl index 9cb2569592..4889d3009a 100644 --- a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block.xsl +++ b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block.xsl @@ -187,7 +187,7 @@ exclude-result-prefixes="#all" [contains-token(@class, 'block')] predicate excludes their inner (class='row-fluid' from resource.xsl:518). --> - + @@ -239,7 +239,7 @@ exclude-result-prefixes="#all" - + @@ -269,7 +269,7 @@ exclude-result-prefixes="#all" - + @@ -331,7 +331,7 @@ exclude-result-prefixes="#all" - + @@ -352,7 +352,7 @@ exclude-result-prefixes="#all" - + - + @@ -442,7 +442,7 @@ exclude-result-prefixes="#all" - + @@ -470,7 +470,7 @@ exclude-result-prefixes="#all" - + @@ -506,7 +506,7 @@ exclude-result-prefixes="#all" - + @@ -522,7 +522,7 @@ exclude-result-prefixes="#all" - + @@ -536,8 +536,8 @@ exclude-result-prefixes="#all" the imported edit.xsl - shadows the editor's own block DnD (edit.xsl), killing its drop marks and reorder. Excluding the editor lets those events fall through to the editor's handlers. --> - - + + @@ -555,8 +555,8 @@ exclude-result-prefixes="#all" - - + + @@ -572,8 +572,8 @@ exclude-result-prefixes="#all" - - + + @@ -595,8 +595,8 @@ exclude-result-prefixes="#all" - - + + diff --git a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/chart.xsl b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/chart.xsl index 0f2976dd47..d5cd87ee79 100644 --- a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/chart.xsl +++ b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/chart.xsl @@ -255,7 +255,7 @@ exclude-result-prefixes="#all" - + @@ -384,7 +384,7 @@ exclude-result-prefixes="#all" ldh:render-chart - + @@ -489,7 +489,7 @@ exclude-result-prefixes="#all" - + @@ -525,7 +525,7 @@ exclude-result-prefixes="#all" - + @@ -559,7 +559,7 @@ exclude-result-prefixes="#all" - + @@ -639,9 +639,9 @@ exclude-result-prefixes="#all" - + - + @@ -721,7 +721,7 @@ exclude-result-prefixes="#all" - + diff --git a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/query.xsl b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/query.xsl index a1944e0e3a..ee3cf1f29c 100644 --- a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/query.xsl +++ b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/query.xsl @@ -108,7 +108,7 @@ exclude-result-prefixes="#all" - + @@ -171,7 +171,7 @@ exclude-result-prefixes="#all" ldh:render-query - + @@ -271,7 +271,7 @@ exclude-result-prefixes="#all" - + @@ -375,7 +375,7 @@ exclude-result-prefixes="#all" - + @@ -438,7 +438,7 @@ exclude-result-prefixes="#all" - + diff --git a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/view.xsl b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/view.xsl index 752a54ec9d..6f9b7c2e85 100644 --- a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/view.xsl +++ b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/client/block/view.xsl @@ -62,7 +62,7 @@ exclude-result-prefixes="#all" - + @@ -882,7 +882,7 @@ exclude-result-prefixes="#all" - + @@ -912,7 +912,7 @@ exclude-result-prefixes="#all" - + @@ -1528,7 +1528,7 @@ exclude-result-prefixes="#all" - + @@ -1633,7 +1633,7 @@ exclude-result-prefixes="#all" - + @@ -1684,7 +1684,7 @@ exclude-result-prefixes="#all" - + @@ -1960,7 +1960,7 @@ exclude-result-prefixes="#all" - + @@ -1968,7 +1968,7 @@ exclude-result-prefixes="#all" - + @@ -2307,9 +2307,9 @@ exclude-result-prefixes="#all" - + - + @@ -2543,7 +2543,7 @@ exclude-result-prefixes="#all" - + ldh:refresh-view block URI: cache found: diff --git a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/imports/services/youtube.xsl b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/imports/services/youtube.xsl index a9b807fcfc..8501fd1690 100644 --- a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/imports/services/youtube.xsl +++ b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/imports/services/youtube.xsl @@ -49,7 +49,7 @@ exclude-result-prefixes="#all" - +

diff --git a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/resource.xsl b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/resource.xsl index 0e8b366bf1..a561af5ac3 100644 --- a/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/resource.xsl +++ b/src/main/webapp/static/com/atomgraph/linkeddatahub/xsl/bootstrap/2.3.2/resource.xsl @@ -662,17 +662,18 @@ extension-element-prefixes="ixsl" - + + - + -
+
@@ -688,8 +689,6 @@ extension-element-prefixes="ixsl" - -
@@ -733,17 +732,24 @@ extension-element-prefixes="ixsl"
- -
+ + +
- + - + -
+
@@ -751,28 +757,33 @@ extension-element-prefixes="ixsl" - - - - -

- - - - - -

+ +
+ - -

- -

-
+

+ + + + + +

- -
+ + + + + +
+ +
+ + + +
+
From 6b17fc9fec6d89f28d58c87dc297c996a8afd305 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martynas=20Jusevi=C4=8Dius?= Date: Wed, 26 Aug 2026 19:51:36 +0200 Subject: [PATCH 02/67] Vendor the design system's CSS and fonts, link them after Bootstrap colors_and_type.css (tokens), app.css (components), retro.css (m3 skin) copied from the design system export; fonts.css @font-faces the vendored woff2s - Geist, Geist Mono, Instrument Serif (latin subsets) and the Material Symbols Rounded variable icon font - so no external font requests. The root element takes data-retro="m3" data-theme="light", which the skin keys on. Linked after the Bootstrap stylesheets so the design system wins where both target the same elements; the Bootstrap links come out at the end of the port. Co-Authored-By: Claude Fable 5 --- .../com/atomgraph/linkeddatahub/css/app.css | 3379 +++++++++++++++++ .../linkeddatahub/css/colors_and_type.css | 426 +++ .../com/atomgraph/linkeddatahub/css/fonts.css | 54 + .../linkeddatahub/css/fonts/geist-mono.woff2 | Bin 0 -> 23108 bytes .../linkeddatahub/css/fonts/geist.woff2 | Bin 0 -> 29288 bytes .../css/fonts/instrument-serif-italic.woff2 | Bin 0 -> 15684 bytes .../css/fonts/instrument-serif.woff2 | Bin 0 -> 15040 bytes .../css/fonts/material-symbols-rounded.woff2 | Bin 0 -> 5358720 bytes .../com/atomgraph/linkeddatahub/css/retro.css | 1417 +++++++ .../xsl/bootstrap/2.3.2/client/block.xsl | 38 +- .../bootstrap/2.3.2/client/block/chart.xsl | 14 +- .../bootstrap/2.3.2/client/block/query.xsl | 8 +- .../xsl/bootstrap/2.3.2/client/block/view.xsl | 18 +- .../xsl/bootstrap/2.3.2/layout.xsl | 14 +- .../xsl/bootstrap/2.3.2/resource.xsl | 28 +- 15 files changed, 5342 insertions(+), 54 deletions(-) create mode 100644 src/main/webapp/static/com/atomgraph/linkeddatahub/css/app.css create mode 100644 src/main/webapp/static/com/atomgraph/linkeddatahub/css/colors_and_type.css create mode 100644 src/main/webapp/static/com/atomgraph/linkeddatahub/css/fonts.css create mode 100644 src/main/webapp/static/com/atomgraph/linkeddatahub/css/fonts/geist-mono.woff2 create mode 100644 src/main/webapp/static/com/atomgraph/linkeddatahub/css/fonts/geist.woff2 create mode 100644 src/main/webapp/static/com/atomgraph/linkeddatahub/css/fonts/instrument-serif-italic.woff2 create mode 100644 src/main/webapp/static/com/atomgraph/linkeddatahub/css/fonts/instrument-serif.woff2 create mode 100644 src/main/webapp/static/com/atomgraph/linkeddatahub/css/fonts/material-symbols-rounded.woff2 create mode 100644 src/main/webapp/static/com/atomgraph/linkeddatahub/css/retro.css diff --git a/src/main/webapp/static/com/atomgraph/linkeddatahub/css/app.css b/src/main/webapp/static/com/atomgraph/linkeddatahub/css/app.css new file mode 100644 index 0000000000..1978b5dbcc --- /dev/null +++ b/src/main/webapp/static/com/atomgraph/linkeddatahub/css/app.css @@ -0,0 +1,3379 @@ +/* ========================================================================= + LinkedDataHub App UI kit — composition styles + Builds on ../../colors_and_type.css and the core primitives. Keep tokens here + to a minimum; this file is for layout / component skins. + ========================================================================= */ + +/* Core primitives (incl. the .msi icon base) — app pages link app.css directly, + so the dependency is declared here rather than assumed from styles.css. */ +@import url('../core/core.css'); + +* { box-sizing: border-box; } +html, body { margin: 0; padding: 0; } +body { background: var(--bg-page); color: var(--fg-1); font-family: var(--font-sans); } + +a { color: var(--fg-link); text-decoration: none; } +a:hover { text-decoration: underline; text-underline-offset: 2px; } + +button { font-family: inherit; cursor: pointer; } + +/* Material Symbols (.msi) now lives in ui_kits/core/core.css — the icon + primitive is core-level. app.css inherits it via styles.css. */ + +/* ---------- App shell ---------- */ +.app { + min-height: 100vh; + display: flex; + flex-direction: column; +} + +/* Top header — dark accent gradient with knowledge-graph dot pattern overlay */ +.ldh-header { + position: relative; + height: 56px; + padding: 0 var(--sp-6); + display: grid; + grid-template-columns: 220px 1fr auto; + align-items: center; + gap: var(--sp-6); + background: var(--grad-deep); + color: #fff; + flex-shrink: 0; + isolation: isolate; /* contain ::before stacking without clipping descendants */ +} +.ldh-header::before { + content: ""; + position: absolute; inset: 0; + background-image: radial-gradient(circle at 1px 1px, rgba(255,255,255,0.10) 1px, transparent 1.4px); + background-size: 22px 22px; + pointer-events: none; + opacity: 0.55; + z-index: -1; /* sit under header children so dropdowns can overflow downward */ +} +.ldh-header > * { position: relative; } + +.ldh-wordmark { + display: flex; align-items: center; gap: var(--sp-2); + font-family: var(--font-sans); + font-weight: var(--fw-semibold); + font-size: 16px; + letter-spacing: var(--tracking-snug); + color: #fff; + text-decoration: none; +} +.ldh-wordmark .mark { + width: 24px; height: 24px; + border-radius: 6px; + background: linear-gradient(135deg, var(--ldh-blue-400) 0%, var(--ldh-violet-400) 100%); + position: relative; + box-shadow: inset 0 0 0 1px rgba(255,255,255,0.18); +} +.ldh-wordmark .mark::before, .ldh-wordmark .mark::after { + content: ""; position: absolute; border-radius: 50%; background: rgba(255,255,255,0.9); +} +.ldh-wordmark .mark::before { width: 6px; height: 6px; top: 4px; left: 4px; } +.ldh-wordmark .mark::after { width: 4px; height: 4px; bottom: 5px; right: 5px; } + +.ldh-address { + display: flex; align-items: center; + height: 36px; + min-width: 0; + width: 100%; + max-width: 720px; + padding: 0 12px; + background: rgba(255,255,255,0.08); + border: 1px solid rgba(255,255,255,0.14); + border-radius: var(--r-md); + color: #fff; + gap: 8px; + justify-self: center; +} +.ldh-address:focus-within { + background: rgba(255,255,255,0.16); + border-color: rgba(255,255,255,0.32); + box-shadow: 0 0 0 4px rgba(77, 148, 248, 0.18); +} +.ldh-address .msi { color: rgba(255,255,255,0.72); } +.ldh-address input { + flex: 1; + min-width: 0; + background: transparent; + border: 0; + outline: 0; + color: #fff; + font-family: var(--font-mono); + letter-spacing: 0.02em; + font-size: var(--fs-sm); +} +.ldh-address input::placeholder { color: rgba(255,255,255,0.55); font-family: var(--font-mono); letter-spacing: 0.02em; } + +.ldh-search { + display: flex; align-items: center; + height: 36px; + background: rgba(255,255,255,0.10); + border: 1px solid rgba(255,255,255,0.14); + border-radius: var(--r-md); + padding: 0 var(--sp-3); + gap: var(--sp-2); + color: rgba(255,255,255,0.92); + transition: background var(--dur-base) var(--ease-out-soft), border-color var(--dur-base) var(--ease-out-soft); + max-width: 720px; + width: 100%; + justify-self: center; +} +.ldh-search:focus-within { + background: rgba(255,255,255,0.16); + border-color: rgba(255,255,255,0.32); + box-shadow: 0 0 0 4px rgba(77, 148, 248, 0.18); +} +.ldh-search .msi { color: rgba(255,255,255,0.72); } +.ldh-search input { + flex: 1; + background: transparent; + border: 0; + outline: 0; + color: #fff; + font-family: var(--font-sans); + font-size: var(--fs-sm); +} +.ldh-search input::placeholder { color: rgba(255,255,255,0.55); font-family: var(--font-mono); letter-spacing: 0.02em; } +.ldh-search kbd { + font-family: var(--font-mono); + font-size: 10px; + color: rgba(255,255,255,0.55); + padding: 2px 6px; + border: 1px solid rgba(255,255,255,0.18); + border-radius: 4px; +} + +.ldh-header-actions { display: flex; align-items: center; gap: var(--sp-2); } +.ldh-icon-btn { + width: 36px; height: 36px; + display: inline-flex; align-items: center; justify-content: center; + border-radius: var(--r-md); + background: transparent; + border: 0; + color: rgba(255,255,255,0.85); + transition: background var(--dur-fast) var(--ease-out-soft), color var(--dur-fast) var(--ease-out-soft); +} +.ldh-icon-btn:hover { background: rgba(255,255,255,0.10); color: #fff; } +.ldh-icon-btn.has-dot { position: relative; } +.ldh-icon-btn.has-dot::after { + content: ""; position: absolute; top: 8px; right: 9px; width: 7px; height: 7px; border-radius: 50%; + background: var(--ldh-blue-400); box-shadow: 0 0 0 2px rgba(10,45,87,0.95); +} + +.ldh-avatar { + width: 32px; height: 32px; + border-radius: 50%; + background: linear-gradient(135deg, var(--ldh-blue-400), var(--ldh-violet-400)); + color: #fff; + display: inline-flex; align-items: center; justify-content: center; + font-size: var(--fs-xs); + font-weight: var(--fw-semibold); + letter-spacing: 0.04em; + margin-left: var(--sp-1); + cursor: pointer; + border: 2px solid transparent; + transition: box-shadow var(--dur-fast) var(--ease-out-soft), border-color var(--dur-fast) var(--ease-out-soft); +} +.ldh-avatar:hover, +.ldh-avatar.is-open { + border-color: rgba(255,255,255,0.55); + box-shadow: 0 0 0 3px rgba(77, 148, 248, 0.22); +} + +/* User dropdown — popover anchored under the header avatar */ +.ldh-avatar-wrap { position: relative; display: inline-flex; } +.ldh-user-menu { + position: absolute; + top: calc(100% + 8px); + right: 0; + min-width: 248px; + background: var(--bg-card); + border: 1px solid var(--border-default); + border-radius: var(--r-md); + box-shadow: 0 16px 40px -12px rgba(15, 23, 42, 0.28), 0 4px 12px -4px rgba(15, 23, 42, 0.18); + padding: var(--sp-2); + z-index: 70; + animation: menu-pop var(--dur-fast) var(--ease-out-soft); + color: var(--fg-1); +} +@keyframes menu-pop { + from { opacity: 0; transform: translateY(-4px) scale(0.98); } + to { opacity: 1; transform: translateY(0) scale(1); } +} +.ldh-user-menu .umhead { + display: flex; align-items: center; gap: var(--sp-2); + padding: var(--sp-2); +} +.ldh-user-menu .umavatar { + width: 36px; height: 36px; + border-radius: 50%; + background: linear-gradient(135deg, var(--ldh-blue-400), var(--ldh-violet-400)); + color: #fff; + display: inline-flex; align-items: center; justify-content: center; + font-size: var(--fs-xs); + font-weight: var(--fw-semibold); + letter-spacing: 0.04em; + flex-shrink: 0; +} +.ldh-user-menu .umid { min-width: 0; } +.ldh-user-menu .umname { + font-weight: var(--fw-semibold); + font-size: var(--fs-sm); + color: var(--fg-1); + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; +} +.ldh-user-menu .umemail { + font-size: 11px; + color: var(--fg-2); + font-family: var(--font-mono); + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; +} +.ldh-user-menu .umdiv { + height: 1px; + background: var(--border-default); + margin: var(--sp-1) 0; +} +.ldh-user-menu .umitem { + display: flex; align-items: center; gap: var(--sp-2); + width: 100%; + padding: var(--sp-2); + border: 0; + background: transparent; + border-radius: var(--r-sm); + cursor: pointer; + font-size: var(--fs-sm); + color: var(--fg-1); + text-align: left; + font-family: inherit; +} +.ldh-user-menu .umitem:hover { background: var(--surface-2); } +.ldh-user-menu .umitem.is-danger { color: var(--ldh-red-600, #b42318); } +.ldh-user-menu .umitem.is-danger:hover { background: rgba(180, 35, 24, 0.08); } +.ldh-user-menu .umitem .msi { color: inherit; } + +/* ---------- Tabs row (dataspaces) ---------- */ +.ldh-tabs { + display: flex; + align-items: stretch; + gap: 2px; + padding: 0 var(--sp-6); + background: var(--surface-1); + border-bottom: 1px solid var(--border-default); + height: 40px; +} +.ldh-tab { + display: inline-flex; align-items: center; gap: var(--sp-2); + padding: 0 var(--sp-4); + font-size: var(--fs-sm); + font-weight: var(--fw-medium); + color: var(--fg-muted); + border: 0; + background: transparent; + border-bottom: 2px solid transparent; + transition: color var(--dur-fast) var(--ease-out-soft), border-color var(--dur-fast) var(--ease-out-soft); +} +.ldh-tab:hover { color: var(--fg-1); } +.ldh-tab.is-active { color: var(--fg-1); border-bottom-color: var(--ldh-blue-500); } +.ldh-tab .close { + width: 18px; height: 18px; display: inline-flex; align-items: center; justify-content: center; + border-radius: var(--r-xs); color: var(--fg-hint); +} +.ldh-tab .close:hover { background: var(--bg-hover); color: var(--fg-1); } + +/* ---------- Action bar (Add / Breadcrumb / Toolbar) ---------- */ +/* Action bar Create dropdown — Container / Item picker. */ +.ldh-add-wrap { position: relative; display: inline-flex; } +.ldh-add-wrap .ldh-btn.is-open .caret { transform: rotate(180deg); } +.ldh-add-wrap .ldh-btn .caret { transition: transform var(--dur-fast) var(--ease-out-soft); } +.ldh-add-menu { + position: absolute; + top: calc(100% + 6px); + left: 0; + min-width: 280px; + background: var(--bg-card); + border: 1px solid var(--border-default); + border-radius: var(--r-md); + box-shadow: 0 16px 40px -12px rgba(15, 23, 42, 0.28), 0 4px 12px -4px rgba(15, 23, 42, 0.18); + padding: var(--sp-2); + z-index: 50; + animation: menu-pop var(--dur-fast) var(--ease-out-soft); +} +.ldh-add-menu .hd { + padding: 6px 8px 4px; + font-family: var(--font-mono); + font-size: 10px; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--fg-muted); +} +.ldh-add-menu .it { + display: grid; + grid-template-columns: 32px 1fr; + align-items: center; + gap: 10px; + width: 100%; + padding: 8px; + border: 0; + background: transparent; + border-radius: var(--r-sm); + cursor: pointer; + font: inherit; + text-align: left; + color: var(--fg-1); +} +.ldh-add-menu .it:hover { background: var(--surface-1); } +.ldh-add-menu .it .ico { + width: 32px; height: 32px; + display: inline-flex; align-items: center; justify-content: center; + border-radius: var(--r-sm); + background: var(--bg-accent-quiet); + color: var(--ldh-blue-700); +} +.ldh-add-menu .it .body { display: flex; flex-direction: column; min-width: 0; } +.ldh-add-menu .it .body .lbl { + font-size: var(--fs-sm); + font-weight: var(--fw-medium); + color: var(--fg-1); +} +.ldh-add-menu .it .body .sub { + font-size: var(--fs-xs); + color: var(--fg-2); +} + +.ldh-actionbar { + display: grid; + grid-template-columns: auto 1fr auto; + align-items: center; + gap: var(--sp-6); + padding: var(--sp-3) var(--sp-6); + background: var(--surface-0); + border-bottom: 1px solid var(--border-default); + flex-shrink: 0; +} + +/* Primary button (Add / Create / Save) */ +.ldh-btn { + display: inline-flex; align-items: center; gap: var(--sp-2); + height: 36px; + padding: 0 var(--sp-4); + border-radius: var(--r-md); + font-family: var(--font-sans); + font-size: var(--fs-sm); + font-weight: var(--fw-medium); + border: 1px solid transparent; + transition: background var(--dur-fast) var(--ease-out-soft), box-shadow var(--dur-fast) var(--ease-out-soft), transform 80ms var(--ease-out-soft); + background: var(--bg-accent); + color: var(--fg-on-accent); +} +.ldh-btn:hover { background: var(--bg-accent-hover); } +.ldh-btn:active { transform: translateY(1px); } +.ldh-btn:focus-visible { outline: 0; box-shadow: var(--shadow-glow-blue); } +.ldh-btn.is-ghost { + background: transparent; + color: var(--fg-2); + border-color: var(--border-default); +} +.ldh-btn.is-ghost:hover { background: var(--bg-hover); color: var(--fg-1); } +.ldh-btn.is-quiet { + background: transparent; + color: var(--fg-2); + border-color: transparent; + padding: 0 var(--sp-3); + height: 32px; +} +.ldh-btn.is-quiet:hover { background: var(--bg-hover); color: var(--fg-1); } +.ldh-btn .caret { font-size: 16px; opacity: 0.85; } + +/* Breadcrumb */ +.ldh-breadcrumb { + display: flex; align-items: center; gap: var(--sp-2); + font-size: var(--fs-sm); + color: var(--fg-muted); + min-width: 0; +} +.ldh-breadcrumb .crumb { + display: inline-flex; align-items: center; gap: var(--sp-2); + padding: 4px 8px; + border-radius: var(--r-sm); + color: var(--fg-link); + transition: background var(--dur-fast) var(--ease-out-soft); + white-space: nowrap; +} +.ldh-breadcrumb .crumb:hover { background: var(--surface-2); text-decoration: none; } +.ldh-breadcrumb .crumb .msi { color: var(--fg-muted); font-size: 18px; } +.ldh-breadcrumb .crumb.is-current { color: var(--fg-1); font-weight: var(--fw-medium); } +.ldh-breadcrumb .sep { color: var(--fg-hint); opacity: 0.6; } + +/* ---------- Breadcrumb · Drive (Google-Drive-flavored, default) ---------- + Roomy horizontal path. Ancestors are quiet medium-weight links with a hover + pill; the current resource is large + bold with a dropdown caret. */ +.ldh-bc-drive { + display: flex; align-items: center; + gap: 2px; + min-width: 0; +} +.ldh-bc-drive .bc-d-up { + display: inline-flex; align-items: center; + padding: 4px 9px; + border-radius: var(--r-md); + color: var(--fg-2); + font-size: var(--fs-base); + font-weight: var(--fw-medium); + white-space: nowrap; + transition: background var(--dur-fast) var(--ease-out-soft), color var(--dur-fast) var(--ease-out-soft); +} +.ldh-bc-drive .bc-d-up:hover { + background: var(--surface-2); + color: var(--fg-1); + text-decoration: none; +} +.ldh-bc-drive .bc-d-sep { + color: var(--fg-hint); + font-size: 18px; + opacity: 0.7; + flex-shrink: 0; +} +.ldh-bc-drive .bc-d-here { + display: inline-flex; align-items: center; + padding: 4px 9px; + border-radius: var(--r-md); + font: inherit; + color: var(--fg-1); + min-width: 0; +} +.ldh-bc-drive .bc-d-name { + font-size: var(--fs-base); + font-weight: var(--fw-medium); + color: var(--fg-1); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + min-width: 0; +} + +/* ---------- Breadcrumb · Stack (modern hero) ---------- + Two stacked lines in the middle of the actionbar: + · Parent path — tiny uppercase mono "you came from here" trail. + · Current — large bold name + icon chip + optional class badge. + Reads as a "you are here" hero rather than a list of equal links. */ +.ldh-bc-stack { + display: flex; flex-direction: column; + justify-content: center; + gap: 2px; + min-width: 0; + line-height: 1.2; +} +.ldh-bc-stack .bc-path { + display: flex; align-items: center; gap: 4px; + font-family: var(--font-mono); + font-size: 10px; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--fg-muted); + min-width: 0; +} +.ldh-bc-stack .bc-up { + display: inline-flex; align-items: center; gap: 4px; + padding: 1px 4px; + border-radius: 4px; + color: inherit; +} +.ldh-bc-stack .bc-up:hover { + background: var(--surface-2); + color: var(--fg-2); + text-decoration: none; +} +.ldh-bc-stack .bc-up .msi { font-size: 13px; opacity: 0.7; } +.ldh-bc-stack .bc-sep { + color: var(--fg-hint); + opacity: 0.55; + font-size: 11px; +} +.ldh-bc-stack .bc-here { + display: inline-flex; align-items: center; gap: 8px; + min-width: 0; +} +.ldh-bc-stack .bc-here-ico { + width: 24px; height: 24px; + border-radius: 6px; + background: var(--bg-accent-quiet); + color: var(--ldh-blue-700); + display: inline-flex; align-items: center; justify-content: center; + flex-shrink: 0; +} +.ldh-bc-stack .bc-here-ico .msi { font-size: 16px; } +.ldh-bc-stack .bc-here-name { + font-size: 15px; + font-weight: var(--fw-semibold); + color: var(--fg-1); + letter-spacing: -0.005em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + min-width: 0; +} +.ldh-bc-stack .bc-here-badge { + font-family: var(--font-mono); + font-size: 10px; + letter-spacing: 0.02em; + color: var(--ldh-blue-700); + background: var(--bg-accent-quiet); + border: 1px solid var(--ldh-blue-100); + padding: 1px 6px; + border-radius: var(--r-pill); + flex-shrink: 0; +} + +/* ---------- Breadcrumb · Pills ---------- */ +.ldh-bc-pills { + display: flex; align-items: center; gap: 4px; + flex-wrap: wrap; + min-width: 0; +} +.ldh-bc-pills .bc-pill { + display: inline-flex; align-items: center; gap: 6px; + padding: 4px 10px; + height: 26px; + border-radius: var(--r-pill); + background: var(--surface-1); + border: 1px solid transparent; + color: var(--fg-2); + font-size: var(--fs-sm); + white-space: nowrap; + transition: background var(--dur-fast) var(--ease-out-soft), color var(--dur-fast) var(--ease-out-soft); +} +.ldh-bc-pills .bc-pill:hover { background: var(--bg-hover); color: var(--fg-1); text-decoration: none; } +.ldh-bc-pills .bc-pill.is-current { + background: var(--bg-accent-quiet); + border-color: var(--ldh-blue-100); + color: var(--ldh-blue-700); + font-weight: var(--fw-medium); +} +.ldh-bc-pills .bc-pill .msi { font-size: 14px; opacity: 0.85; } +.ldh-bc-pills .bc-sep { + color: var(--fg-hint); + font-size: 14px; + opacity: 0.7; +} + +/* ---------- Breadcrumb · Compact (iOS-style back + current) ---------- */ +.ldh-bc-compact { + display: inline-flex; align-items: center; gap: 8px; + min-width: 0; +} +.ldh-bc-compact .bc-back { + display: inline-flex; align-items: center; gap: 2px; + padding: 4px 8px 4px 4px; + border-radius: var(--r-sm); + color: var(--fg-link); + font-size: var(--fs-sm); + font-weight: var(--fw-medium); + white-space: nowrap; +} +.ldh-bc-compact .bc-back:hover { background: var(--surface-2); text-decoration: none; } +.ldh-bc-compact .bc-back .msi { font-size: 14px; } +.ldh-bc-compact .bc-divider { + width: 1px; height: 20px; + background: var(--border-default); +} +.ldh-bc-compact .bc-here-compact { + display: inline-flex; align-items: center; gap: 6px; + font-size: var(--fs-sm); + font-weight: var(--fw-semibold); + color: var(--fg-1); + white-space: nowrap; +} +.ldh-bc-compact .bc-here-compact .msi { + color: var(--ldh-blue-700); +} + +/* The Stack style is taller than the rest. Adjust actionbar vertical padding + to keep the row visually balanced regardless of style. */ +.ldh-actionbar--bc-stack { padding-top: var(--sp-2); padding-bottom: var(--sp-2); } +.ldh-actionbar--bc-drive { padding-top: var(--sp-2); padding-bottom: var(--sp-2); } + +/* ───────────────────────────────────────────────────────────────────────── + Tweak-driven overrides — toggled by data-attributes on . + Set by index.html based on the Tweaks panel state. + ───────────────────────────────────────────────────────────────────────── */ + +/* Predicate IRIs — "always" pins the IRI label above the human label even + when the row isn't hovered. */ +html[data-tweak-pred-iris="always"] .ldh-prop-group > .label .pred-iri { + opacity: 1; + transform: translateY(0); + pointer-events: auto; + user-select: text; +} + +/* Density — compact tightens row padding across the property editor. */ +html[data-tweak-density="compact"] .ldh-prop-row > .value, +html[data-tweak-density="compact"] .ldh-prop-row > .row-actions { + padding-top: 4px; + padding-bottom: 4px; +} +html[data-tweak-density="compact"] .ldh-prop-group > .label { + padding-top: 4px; + padding-bottom: 4px; +} +html[data-tweak-density="compact"] .ldh-actionbar { + padding-top: 4px; + padding-bottom: 4px; +} +html[data-tweak-density="compact"] .ldh-block-body { padding: var(--sp-3) 0; } + +/* Header style — subtle = solid dark navy, no gradient. Flat = light card. */ +html[data-tweak-header="subtle"] .ldh-header { + background: var(--ldh-blue-900, #0a1230); +} +html[data-tweak-header="subtle"] .ldh-header::before { opacity: 0; } +html[data-tweak-header="flat"] .ldh-header { + background: var(--bg-card); + color: var(--fg-1); + border-bottom: 1px solid var(--border-default); +} +html[data-tweak-header="flat"] .ldh-header::before { opacity: 0; } +html[data-tweak-header="flat"] .ldh-wordmark { color: var(--fg-1); } +html[data-tweak-header="flat"] .ldh-wordmark .mark { + background: linear-gradient(135deg, var(--ldh-blue-400), var(--ldh-violet-400)); +} +html[data-tweak-header="flat"] .ldh-address { + background: var(--surface-1); + border-color: var(--border-default); + color: var(--fg-1); +} +html[data-tweak-header="flat"] .ldh-address input { color: var(--fg-1); } +html[data-tweak-header="flat"] .ldh-address input::placeholder { color: var(--fg-muted); } +html[data-tweak-header="flat"] .ldh-address .msi { color: var(--fg-muted); } +html[data-tweak-header="flat"] .ldh-avatar { border: 2px solid var(--border-default); } + +/* Block edges — sharp = no radius, borderless = no border + no radius. */ +html[data-tweak-block-edges="sharp"] .ldh-block, +html[data-tweak-block-edges="sharp"] .ldh-view { + border-radius: 0; +} +html[data-tweak-block-edges="borderless"] .ldh-block, +html[data-tweak-block-edges="borderless"] .ldh-view { + border: 0; + border-radius: 0; + box-shadow: none; +} +html[data-tweak-block-edges="borderless"] .ldh-block-head { + border-bottom: 1px solid var(--border-default); +} + +/* Flow — the document treatment: blocks dissolve their card frame (no fill, + border, radius or shadow); the data they contain (grid cards, chart plots, + statement rows) keeps its own surfaces. Consecutive blocks are separated by + a hairline + even vertical rhythm, so prose and structured views interleave + as one continuous reading surface rather than a stack of widgets. */ +html[data-tweak-block-edges="flow"] .ldh-block, +html[data-tweak-block-edges="flow"] .ldh-view { + background: transparent; + border: 0; + border-radius: 0; + box-shadow: none; +} +html[data-tweak-block-edges="flow"] .ldh-block:hover { + box-shadow: none; + transform: none; +} +html[data-tweak-block-edges="flow"] .ldh-content { gap: 0; } +html[data-tweak-block-edges="flow"] .ldh-block-row { padding: var(--sp-6) 0; } +html[data-tweak-block-edges="flow"] .ldh-block-row + .ldh-block-row { + border-top: 1px solid var(--border-default); +} +/* …but data-visualisation bodies still want a white ground — a chart plotted + straight onto the tinted floor reads as unanchored. Plate the chart body + (its own controls/canvas padding supplies the inset) so the plot sits on a + clean surface while prose blocks stay transparent. */ +html[data-tweak-block-edges="flow"] .ldh-block-body:has(> .chart-canvas), +html[data-tweak-block-edges="flow"] .ldh-block-body:has(> .chart-controls) { + background: var(--surface-0); + border: 1px solid var(--border-default); + border-radius: var(--r-md); + padding: 0; + overflow: hidden; +} +/* Resource blocks are structured data surfaces too: the statement list — and + especially a media-header block, whose full-bleed image must keep its corners + clipped — stay on a white card in flow mode (like charts), so the description + doesn't float transparent over the tinted floor. */ +html[data-tweak-block-edges="flow"] .ldh-block:has(.ldh-edit-form) { + background: var(--bg-card); + border: 1px solid var(--border-default); + border-radius: var(--r-lg); + overflow: hidden; +} + +/* Action toolbar (right cluster) */ +.ldh-toolbar { + display: flex; align-items: center; gap: var(--sp-1); + color: var(--fg-muted); +} +.ldh-toolbar .ts { + font-family: var(--font-mono); + font-size: var(--fs-xs); + color: var(--fg-hint); + margin-right: var(--sp-3); + white-space: nowrap; +} +.ldh-toolbar .tb { + width: 36px; height: 36px; + display: inline-flex; align-items: center; justify-content: center; + background: transparent; + border: 0; border-radius: var(--r-sm); + color: var(--fg-2); + transition: background var(--dur-fast) var(--ease-out-soft), color var(--dur-fast) var(--ease-out-soft); +} +.ldh-toolbar .tb:hover { background: var(--bg-hover); color: var(--fg-1); } +.ldh-toolbar .tb.is-danger:hover { background: var(--danger-50); color: var(--danger-500); } +.ldh-toolbar .divider { + width: 1px; height: 20px; background: var(--border-default); margin: 0 var(--sp-2); +} + +/* ─── Redesigned action cluster (declutter): mode · contextual primary · ⋯ ── */ +.ldh-actions { display: flex; align-items: center; gap: var(--sp-1); color: var(--fg-muted); } +.ldh-actions-div { width: 1px; height: 20px; background: var(--border-default); margin: 0 var(--sp-2); } +.ldh-actions .ldh-btn .msi { font-size: 18px; } + +/* Document dateTime as quiet metadata — never a control */ +.ldh-ab-ts { + display: inline-flex; align-items: center; gap: 4px; + font-family: var(--font-mono); font-size: var(--fs-xs); + color: var(--fg-hint); white-space: nowrap; flex-shrink: 0; +} +.ldh-ab-ts .msi { font-size: 14px; opacity: 0.7; } + +/* Overflow kebab */ +.ldh-of-wrap { position: relative; display: inline-flex; } +.ldh-of-wrap .tb { + width: 36px; height: 36px; + display: inline-flex; align-items: center; justify-content: center; + border: 0; background: transparent; border-radius: var(--r-md); + color: var(--fg-muted); cursor: pointer; + transition: background var(--dur-fast) var(--ease-out-soft), color var(--dur-fast) var(--ease-out-soft); +} +.ldh-of-wrap .tb:hover, .ldh-of-wrap.is-open .tb { background: var(--bg-hover); color: var(--fg-1); } +.ldh-of-menu { + position: absolute; top: calc(100% + 6px); right: 0; z-index: 50; + min-width: 184px; padding: 6px; + background: var(--surface-0); border: 1px solid var(--border-default); + border-radius: var(--r-md); box-shadow: var(--shadow-lg); + display: flex; flex-direction: column; gap: 2px; +} +.ldh-of-menu .it { + display: flex; align-items: center; gap: var(--sp-2); + padding: 8px 10px; border: 0; background: transparent; cursor: pointer; + border-radius: var(--r-sm); font: inherit; font-size: var(--fs-sm); + color: var(--fg-2); text-align: left; +} +.ldh-of-menu .it:hover { background: var(--bg-hover); color: var(--fg-1); } +.ldh-of-menu .it.is-danger { color: var(--danger-500); } +.ldh-of-menu .it.is-danger:hover { background: var(--danger-50); } +.ldh-of-menu .it .msi { font-size: 18px; } +.ldh-of-menu .it-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; } +.ldh-of-menu .it-hint { font-size: var(--fs-xs); color: var(--ink-4); } +.ldh-of-menu .it:hover .it-hint { color: var(--fg-hint); } +.ldh-of-menu .it.is-danger .it-hint { color: var(--ink-4); } +.ldh-of-div { height: 1px; background: var(--border-default); margin: 4px 2px; } + +/* Mid zone (breadcrumb + timestamp) for zoned/contextual single-row layouts. + The timestamp pushes to the right edge of the zone (just before the actions), + so the "last modified" stamp reads as right-aligned metadata. */ +.ldh-actionbar--zoned .ab-mid, +.ldh-actionbar--contextual .ab-mid { + display: flex; align-items: center; gap: var(--sp-4); min-width: 0; +} +.ldh-actionbar--zoned .ab-mid .ldh-ab-ts, +.ldh-actionbar--contextual .ab-mid .ldh-ab-ts { margin-left: auto; padding-left: var(--sp-4); } + +/* TWO-TIER — location line above, controls line below */ +.ldh-actionbar--twotier { + display: flex; flex-direction: column; align-items: stretch; gap: var(--sp-2); +} +.ldh-actionbar--twotier .ab-context { + display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); min-width: 0; +} +.ldh-actionbar--twotier .ab-controls { display: flex; align-items: center; gap: var(--sp-4); } +.ldh-actionbar--twotier .ab-spacer { flex: 1; } + +/* CONTEXTUAL — minimal at rest; secondary actions (Edit + ⋯) reveal on hover/ + focus of the bar. Mode switcher stays visible as the one persistent control. */ +.ldh-actionbar--contextual .ldh-actions .ldh-btn, +.ldh-actionbar--contextual .ldh-actions .ldh-of-wrap, +.ldh-actionbar--contextual .ldh-actions .ldh-actions-div { + opacity: 0; transition: opacity var(--dur-base) var(--ease-out-soft); +} +.ldh-actionbar--contextual:hover .ldh-actions .ldh-btn, +.ldh-actionbar--contextual:focus-within .ldh-actions .ldh-btn, +.ldh-actionbar--contextual:hover .ldh-actions .ldh-of-wrap, +.ldh-actionbar--contextual:focus-within .ldh-actions .ldh-of-wrap, +.ldh-actionbar--contextual:hover .ldh-actions .ldh-actions-div, +.ldh-actionbar--contextual:focus-within .ldh-actions .ldh-actions-div { opacity: 1; } + +/* Mode switcher — segmented dropdown */ +.ldh-mode { + position: relative; + display: inline-flex; align-items: center; gap: var(--sp-2); + height: 36px; + padding: 0 var(--sp-3); + background: var(--surface-2); + border: 1px solid var(--border-default); + border-radius: var(--r-md); + color: var(--fg-2); + font-size: var(--fs-sm); + font-weight: var(--fw-medium); + margin-right: var(--sp-2); +} +.ldh-mode .label-row { display: inline-flex; align-items: center; gap: var(--sp-2); } +.ldh-mode .label { letter-spacing: 0.02em; } +.ldh-mode .modes-pop { + position: absolute; top: calc(100% + 6px); right: 0; + min-width: 220px; + background: var(--surface-0); + border: 1px solid var(--border-default); + border-radius: var(--r-md); + box-shadow: var(--shadow-lg); + padding: var(--sp-1); + display: none; + z-index: 50; +} +.ldh-mode.is-open .modes-pop { display: block; } +.ldh-mode .mi { + display: flex; align-items: center; gap: var(--sp-3); + padding: var(--sp-2) var(--sp-3); + font-size: var(--fs-sm); color: var(--fg-2); + border-radius: var(--r-sm); + width: 100%; + background: transparent; border: 0; + cursor: pointer; + text-align: left; +} +.ldh-mode .mi:hover { background: var(--bg-hover); color: var(--fg-1); } +.ldh-mode .mi.is-active { background: var(--ldh-blue-50); color: var(--ldh-blue-700); font-weight: var(--fw-medium); } +.ldh-mode .mi .msi { color: var(--fg-muted); } +.ldh-mode .mi.is-active .msi { color: var(--ldh-blue-600); } +.ldh-mode .mi .desc { display: block; font-size: 11px; color: var(--fg-hint); font-weight: 400; margin-top: 1px; } +.ldh-mode .mi .label-col { flex: 1; } + +/* ---------- Main layout ---------- */ +.ldh-main { + flex: 1; + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: var(--sp-5); + padding: var(--sp-5) var(--sp-6); + max-width: var(--container-xl); + width: 100%; + margin: 0 auto; + align-items: start; +} +.ldh-main.is-flush { padding: var(--sp-3) var(--sp-3); max-width: none; } +.ldh-content { + display: flex; flex-direction: column; gap: var(--sp-6); + min-width: 0; +} + +/* Per-view-block row: the block-scoped drawer (backlinks/related/parallax) + slides in from the right edge of the block and overlays its right portion. + The drawer is scoped to the block's height — no global push, no body scroll + hijack. The row stays display:block so its content keeps full width when + the drawer is closed AND when it's open. */ +.ldh-block-row { position: relative; } +.ldh-block-row > .row-main { min-width: 0; } + +/* Backlinks / related panel */ +.ldh-panel { + background: var(--bg-card); + border: 1px solid var(--border-default); + border-radius: var(--r-lg); + overflow: hidden; +} +.ldh-panel .ph { + display: flex; align-items: center; justify-content: space-between; + padding: var(--sp-3) var(--sp-4); + background: var(--surface-1); + border-bottom: 1px solid var(--border-default); +} +.ldh-panel .ph .title { + font-family: var(--font-mono); + font-size: 11px; + letter-spacing: var(--tracking-wide); + text-transform: uppercase; + color: var(--fg-muted); +} +.ldh-panel.is-collapsed .pb { display: none; } +.ldh-panel.is-collapsed .ph { border-bottom: 0; } +.ldh-panel .pb { padding: var(--sp-3) var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-2); } +.ldh-panel .pb a { display: block; font-size: var(--fs-sm); padding: 4px 0; color: var(--fg-2); } +.ldh-panel .pb a:hover { color: var(--fg-link); } +.ldh-panel .pb .empty { font-size: var(--fs-sm); color: var(--fg-hint); padding: 4px 0; } + +/* ---------- Footer ---------- */ +.ldh-footer { + margin-top: var(--sp-12); + padding: var(--sp-10) var(--sp-6) var(--sp-8); + background: var(--surface-1); + border-top: 1px solid var(--border-default); +} +.ldh-footer .cols { + max-width: var(--container-xl); + margin: 0 auto; + display: grid; + grid-template-columns: 1.4fr repeat(4, 1fr); + gap: var(--sp-8); +} +.ldh-footer .col .ftitle { + font-family: var(--font-mono); + font-size: 11px; + letter-spacing: var(--tracking-wide); + text-transform: uppercase; + color: var(--fg-muted); + margin: 0 0 var(--sp-3); +} +.ldh-footer .col a { display: block; padding: 4px 0; font-size: var(--fs-sm); color: var(--fg-2); } +.ldh-footer .col a:hover { color: var(--fg-link); } +.ldh-footer .brand-col p { + font-size: var(--fs-sm); color: var(--fg-muted); line-height: var(--lh-base); max-width: 280px; margin: var(--sp-3) 0 0; +} +.ldh-footer .legal { + max-width: var(--container-xl); + margin: var(--sp-8) auto 0; + padding-top: var(--sp-4); + border-top: 1px solid var(--border-default); + display: flex; justify-content: space-between; + font-size: var(--fs-xs); + color: var(--fg-hint); + font-family: var(--font-mono); +} + +/* ---------- Generic Block (view block wrapper) ---------- */ +.ldh-block { + background: var(--bg-card); + border: 1px solid var(--border-default); + border-radius: var(--r-lg); + overflow: hidden; + transition: box-shadow var(--dur-base) var(--ease-out-soft), border-color var(--dur-base) var(--ease-out-soft); +} +.ldh-block:hover { box-shadow: var(--shadow-sm); } +.ldh-block.is-quiet { border: 0; background: transparent; } +.ldh-block.is-quiet:hover { box-shadow: none; } +.ldh-block-head { + display: flex; align-items: flex-start; justify-content: space-between; + padding: var(--sp-5) var(--sp-6) var(--sp-3); + gap: var(--sp-4); +} +.ldh-block-head .ttl { font-size: var(--fs-xl); font-weight: var(--fw-semibold); color: var(--fg-1); margin: 0; line-height: var(--lh-snug); letter-spacing: var(--tracking-snug); } +/* Hero text block — the lead resource's title reads at page-title scale, not + block-heading scale, so the top of a document anchors the page. */ +.ldh-block-head .ttl--hero { font-size: var(--fs-3xl); font-weight: var(--fw-bold); line-height: var(--lh-tight); letter-spacing: var(--tracking-tight); } +.ldh-block-head .sub { display: block; font-size: var(--fs-sm); color: var(--fg-muted); margin-top: 4px; font-weight: 400; } +.ldh-block-head .actions { display: flex; gap: var(--sp-1); } +.ldh-block-head .actions .tb { + width: 32px; height: 32px; + display: inline-flex; align-items: center; justify-content: center; + background: transparent; border: 0; border-radius: var(--r-sm); + color: var(--fg-muted); +} +.ldh-block-head .actions .tb:hover { background: var(--bg-hover); color: var(--fg-1); } +/* Delete (×) — quiet at rest, danger on hover. The block-removal affordance. */ +.ldh-block-head .actions .tb-danger:hover { background: var(--danger-50); color: var(--danger-500); } + +/* Links button — opens block-scoped drawer (backlinks / related / parallax) */ +.ldh-block-head .actions .tb-links, +.hero-actions .tb-links { + width: auto; + min-width: 32px; + padding: 0 8px 0 6px; + gap: 4px; + background: var(--bg-accent-quiet); + color: var(--ldh-blue-700); + border: 1px solid var(--ldh-blue-100); +} +.ldh-block-head .actions .tb-links:hover, +.hero-actions .tb-links:hover { + background: var(--ldh-blue-100); + color: var(--ldh-blue-700); +} +.ldh-block-head .actions .tb-links.is-on, +.hero-actions .tb-links.is-on { + background: var(--ldh-blue-600); + color: #fff; + border-color: var(--ldh-blue-600); +} + +.hero-actions { + position: absolute; + top: var(--sp-4); + right: var(--sp-4); + display: flex; + gap: var(--sp-1); + z-index: 2; +} +.hero-actions .tb-links { + height: 32px; + display: inline-flex; align-items: center; + border-radius: var(--r-sm); + background: rgba(255,255,255,0.72); + backdrop-filter: blur(6px); + border-color: rgba(255,255,255,0.85); +} + +/* Block drawer — fixed to the viewport's right edge so it sits FLUSH against + the page edge regardless of where the block lives inside the centered + .ldh-main container. Top/height are set inline by the React component to + match the anchor block's bounding rect, so the drawer stays block-scoped + vertically. */ +.ldh-drawer { + position: fixed; + right: 0; + width: min(360px, 92vw); + background: var(--surface-0); + border: 0; + border-left: 1px solid var(--border-default); + border-radius: 0; + box-shadow: -8px 0 24px -22px rgba(15, 23, 42, 0.16); + display: flex; flex-direction: column; + z-index: 25; + animation: drawer-slide var(--dur-base) var(--ease-out-soft); + overflow: hidden; +} +@keyframes drawer-slide { + from { transform: translateX(100%); opacity: 0; } + to { transform: translateX(0); opacity: 1; } +} + +/* Edge-hover sensor — invisible hot-zone flush to the viewport's right edge, + vertically scoped to its block (top/height set inline). Reaching it slides + the drawer out, no click needed. A faint handle peeks in so the gesture is + discoverable. */ +.ldh-edge-sensor { + position: fixed; + right: 0; + width: 20px; + z-index: 24; + display: flex; align-items: center; justify-content: flex-end; + cursor: pointer; +} +.ldh-edge-handle { + display: flex; align-items: center; justify-content: center; + width: 17px; height: 54px; + border-radius: var(--r-pill) 0 0 var(--r-pill); + background: var(--surface-0); + border: 1px solid var(--border-default); + border-right: 0; + color: var(--fg-hint); + box-shadow: -8px 0 18px -14px rgba(15, 23, 42, 0.28); + opacity: 0.5; + transform: translateX(7px); + transition: opacity var(--dur-base) var(--ease-out-soft), + transform var(--dur-base) var(--ease-out-soft), + color var(--dur-fast) var(--ease-out-soft), + border-color var(--dur-fast) var(--ease-out-soft); +} +.ldh-edge-handle .msi { font-size: 18px; } +.ldh-edge-sensor:hover .ldh-edge-handle { + opacity: 1; + transform: translateX(0); + color: var(--accent); + border-color: var(--accent); +} +.ldh-drawer .dh { + display: flex; align-items: flex-start; justify-content: space-between; + padding: var(--sp-5) var(--sp-5) var(--sp-4); + border-bottom: 1px solid var(--border-default); +} +.ldh-drawer .dh .eyebrow { + font-family: var(--font-mono); font-size: 10px; + letter-spacing: var(--tracking-wide); text-transform: uppercase; + color: var(--fg-muted); +} +.ldh-drawer .dh h3 { + margin: 4px 0 0; + font-size: var(--fs-lg); + font-weight: var(--fw-semibold); + letter-spacing: var(--tracking-snug); + color: var(--fg-1); +} +.ldh-drawer .dh .close { + width: 32px; height: 32px; + background: transparent; border: 0; border-radius: var(--r-sm); + color: var(--fg-muted); + display: inline-flex; align-items: center; justify-content: center; + cursor: pointer; +} +.ldh-drawer .dh .close:hover { background: var(--bg-hover); color: var(--fg-1); } + +.ldh-drawer .db { + flex: 1; + overflow: auto; + padding: var(--sp-3) var(--sp-3) var(--sp-5); + display: flex; flex-direction: column; gap: var(--sp-4); +} +.ldh-drawer .dgroup { display: flex; flex-direction: column; gap: 2px; } +.ldh-drawer .dh2 { + display: flex; align-items: baseline; justify-content: space-between; + padding: var(--sp-2) var(--sp-2) 4px; + font-family: var(--font-mono); font-size: 10px; + letter-spacing: var(--tracking-wide); text-transform: uppercase; + color: var(--fg-muted); +} +.ldh-drawer .dh2 em { + font-style: normal; + font-family: var(--font-mono); font-size: 10px; + color: var(--fg-hint); + background: var(--surface-2); + padding: 1px 6px; border-radius: var(--r-pill); + letter-spacing: 0; +} +.ldh-drawer .drow { + display: flex; align-items: center; gap: 8px; + padding: 6px 8px; + border-radius: var(--r-sm); + text-decoration: none; + color: var(--fg-2); + font-size: var(--fs-sm); + transition: background var(--dur-fast) var(--ease-out-soft); +} +.ldh-drawer .drow:hover { background: var(--bg-hover); color: var(--fg-1); text-decoration: none; } +.ldh-drawer .drow .msi { color: var(--fg-hint); flex-shrink: 0; } +.ldh-drawer .drow .lbl { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.ldh-drawer .drow .tag { + font-family: var(--font-mono); font-size: 9.5px; color: var(--fg-hint); + background: var(--surface-2); padding: 1px 6px; border-radius: var(--r-pill); +} +.ldh-drawer .drow.is-current { + background: var(--bg-accent-quiet); + color: var(--ldh-blue-700); + font-weight: var(--fw-medium); +} +.ldh-drawer .drow.is-current .msi { color: var(--ldh-blue-600); } +.ldh-drawer .drow.is-current .tag { background: var(--surface-0); color: var(--ldh-blue-600); } + +.ldh-drawer .df { + display: flex; align-items: center; justify-content: space-between; + padding: var(--sp-3) var(--sp-4); + border-top: 1px solid var(--border-default); + background: var(--surface-1); + font-family: var(--font-mono); + font-size: var(--fs-xs); + color: var(--fg-muted); +} +.ldh-block-body { padding: 0 var(--sp-6) var(--sp-6); } +.ldh-block-body.is-flush { padding: 0; } +.ldh-block-foot { + display: flex; align-items: center; justify-content: space-between; + padding: var(--sp-3) var(--sp-6); + border-top: 1px solid var(--border-default); + background: var(--surface-1); +} + +/* Section header — for grouping blocks in ContentMode */ +.ldh-section { + display: flex; flex-direction: column; gap: var(--sp-2); + margin-top: var(--sp-4); +} +.ldh-section .eyebrow { font-family: var(--font-mono); font-size: 11px; letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--ldh-blue-600); } +.ldh-section h2 { margin: 0; font-size: var(--fs-2xl); font-weight: var(--fw-semibold); letter-spacing: var(--tracking-snug); color: var(--fg-1); } +.ldh-section p { margin: 4px 0 0; color: var(--fg-2); max-width: 56ch; } + +/* Hero block — uses pastel gradient for the document intro */ +.ldh-hero { + position: relative; + background: var(--grad-aurora); + border: 1px solid var(--border-default); + border-radius: var(--r-lg); + padding: var(--sp-10) var(--sp-8); + overflow: hidden; +} +.ldh-hero::before { + content: ""; position: absolute; inset: 0; + background-image: radial-gradient(circle at 1px 1px, rgba(11,14,20,0.06) 1px, transparent 1.4px); + background-size: 24px 24px; + opacity: 0.7; + pointer-events: none; +} +.ldh-hero > * { position: relative; } + +/* Dark theme: pastel hero would strand light text on a light wash — swap to the + dark accent gradient (governance: pastels/gradients are hero-only anyway). */ +:root[data-theme="dark"] .ldh-hero { + background: var(--grad-deep); + border-color: var(--line-2); +} +:root[data-theme="dark"] .ldh-hero::before { + background-image: radial-gradient(circle at 1px 1px, rgba(255,255,255,0.08) 1px, transparent 1.4px); +} +.ldh-hero .eyebrow { font-family: var(--font-mono); font-size: 11px; letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--ldh-violet-700); } +.ldh-hero h1 { + margin: var(--sp-2) 0 var(--sp-3); + font-size: var(--fs-4xl); + font-weight: var(--fw-semibold); + letter-spacing: var(--tracking-tight); + color: var(--fg-1); + line-height: var(--lh-tight); +} +.ldh-hero h1 em { font-style: italic; font-family: var(--font-display); font-weight: 400; } +.ldh-hero .lede { font-size: var(--fs-md); color: var(--ink-2); max-width: 56ch; line-height: var(--lh-base); margin: 0; } +.ldh-hero .lede + p { margin-top: var(--sp-3); } + +/* Chart controls (re-styled - -
+ -
-