Spaces:
Sleeping
Sleeping
| /* ----------------------------------------------------------------------- | |
| Article content styles | |
| Shared between editor and published version. | |
| Uses CSS variables from tokens.css for light/dark theming. | |
| ----------------------------------------------------------------------- */ | |
| /* Base typography */ | |
| .tiptap { | |
| outline: none; | |
| font-family: var(--default-font-family); | |
| font-size: 1rem; | |
| line-height: 1.7; | |
| color: var(--text-color); | |
| max-width: 780px; | |
| margin-left: auto; | |
| margin-right: auto; | |
| padding: 0 32px 0 64px; | |
| min-height: 100%; | |
| overflow-wrap: break-word; | |
| word-break: break-word; | |
| } | |
| .tiptap > *:first-child { | |
| margin-top: 0; | |
| } | |
| /* Headings | |
| Sizes / weights / margins mirror the published rules | |
| (`.content-grid main h2/h3/h4` in `_base.css`) so the editor view matches | |
| the final rendering. In the published HTML these `.tiptap` rules are | |
| overridden by the higher-specificity `.content-grid main ...` rules, but | |
| the values stay identical - keeping them here is the source of truth | |
| for the editor-only context (which lacks the `.content-grid main` wrapper). */ | |
| .tiptap h1 { | |
| font-size: 2rem; | |
| font-weight: 700; | |
| color: var(--text-heading); | |
| line-height: 1.2; | |
| margin: 2em 0 0.4em; | |
| letter-spacing: -0.02em; | |
| } | |
| .tiptap h1:first-child { | |
| margin-top: 0; | |
| } | |
| .tiptap h2 { | |
| font-weight: 600; | |
| font-size: clamp(22px, 2.6vw, 32px); | |
| line-height: 1.2; | |
| margin: var(--spacing-10) 0 var(--spacing-5); | |
| padding-bottom: var(--spacing-2); | |
| border-bottom: 1px solid var(--border-color); | |
| } | |
| .tiptap h3 { | |
| font-weight: 700; | |
| font-size: clamp(18px, 2.1vw, 22px); | |
| line-height: 1.25; | |
| margin: var(--spacing-8) 0 var(--spacing-4); | |
| } | |
| .tiptap h4 { | |
| font-weight: 600; | |
| font-size: 16px; | |
| line-height: 1.2; | |
| margin: var(--spacing-6) 0 var(--spacing-4); | |
| text-transform: uppercase; | |
| } | |
| /* Paragraphs */ | |
| .tiptap p { | |
| margin: 0.5em 0; | |
| } | |
| /* Lists */ | |
| .tiptap ul, | |
| .tiptap ol { | |
| padding-left: 1.5rem; | |
| margin: 0.5em 0; | |
| } | |
| .tiptap li { | |
| margin: 0.25em 0; | |
| } | |
| .tiptap li p { | |
| margin: 0.15em 0; | |
| } | |
| /* Blockquote */ | |
| .tiptap blockquote { | |
| border-left: 3px solid var(--border-color); | |
| padding-left: 1rem; | |
| margin: 1em 0; | |
| color: var(--muted-color); | |
| } | |
| .tiptap blockquote p { | |
| margin: 0.4em 0; | |
| } | |
| /* Inline code */ | |
| .tiptap code { | |
| font-size: 14px; | |
| font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; | |
| border-radius: 0.3em; | |
| border: 1px solid var(--border-color); | |
| color: var(--text-color); | |
| font-weight: 400; | |
| line-height: 1.5; | |
| } | |
| .tiptap p code { | |
| white-space: nowrap; | |
| padding: calc(var(--spacing-1) / 3) calc(var(--spacing-1) / 2); | |
| } | |
| /* ============================================================================ | |
| Code blocks - Shiki-powered syntax highlighting | |
| ============================================================================ | |
| Both the editor (PM decorations, `code-block-shiki.tsx`) and the publisher | |
| (static HTML, `transformers/highlight-code.ts`) emit the same markup: | |
| <pre class="shiki ..." data-lang="python" | |
| style="--shiki-light:...;--shiki-dark:..."> | |
| <code class="language-python"> | |
| <span class="code-line-num">1</span> | |
| <span style="--shiki-light:#d73a49">const</span> foo = ... | |
| <span class="code-line-num">2</span> | |
| ... | |
| </code> | |
| </pre> | |
| - Shiki is configured with `defaultColor: false`, so tokens carry BOTH | |
| theme colors as CSS vars. We pick the active var based on | |
| `html[data-theme=...]` for zero-JS live theme switching. | |
| - Line numbers are physical markup (`<span class="code-line-num">`) rather | |
| than CSS counters. In the editor PM widget decorations inject them; in | |
| the published HTML the highlight-code transformer does the same. Placing | |
| them as the first child of each line means they sit on the first visual | |
| row of soft-wrapped lines automatically - no absolute positioning math. | |
| - The language label comes from a `::after` pseudo on `<pre>` using | |
| `attr(data-lang)`, so no extra DOM node exists. */ | |
| .tiptap pre { | |
| --code-line-num-width: 2.5em; | |
| --code-line-num-gap: 1em; | |
| --code-padding-left: calc(var(--code-line-num-width) + var(--code-line-num-gap) + var(--spacing-1)); | |
| --code-padding-right: var(--spacing-3); | |
| position: relative; | |
| /* Override Shiki's inline background-color with our surface color. */ | |
| background-color: var(--surface-bg) ; | |
| border: 1px solid var(--border-color); | |
| border-radius: 6px; | |
| padding: var(--spacing-2) 0; | |
| margin: 0 0 var(--block-spacing-y); | |
| font-size: 14px; | |
| overflow: hidden; | |
| } | |
| .tiptap pre code { | |
| display: block; | |
| background: none; | |
| border: none; | |
| padding: 0 var(--code-padding-right) 0 var(--code-padding-left); | |
| color: var(--text-color); | |
| font-size: 14px; | |
| line-height: 1.6; | |
| /* Soft-wrap long lines instead of horizontal scrollbars. */ | |
| white-space: pre-wrap; | |
| word-break: break-word; | |
| overflow-wrap: anywhere; | |
| } | |
| /* Line number widget. Uses negative left margin to sit inside the padding | |
| area, and a matching right margin that offsets the negative one so the | |
| span is zero-width in the layout. Wrapped rows naturally align with | |
| `padding-left` (no number prefix on continuation rows). */ | |
| .tiptap pre .code-line-num { | |
| display: inline-block; | |
| width: var(--code-line-num-width); | |
| margin-left: calc(-1 * (var(--code-line-num-width) + var(--code-line-num-gap))); | |
| margin-right: var(--code-line-num-gap); | |
| text-align: right; | |
| color: var(--muted-color); | |
| opacity: 0.45; | |
| user-select: none; | |
| pointer-events: none; | |
| font-variant-numeric: tabular-nums; | |
| } | |
| /* Shiki token colors. `var(..., inherit)` is critical so non-token spans | |
| (line wrappers, widgets, selection highlights) inherit the parent's | |
| color instead of falling back to black. */ | |
| .tiptap pre code span { | |
| color: var(--shiki-light, inherit); | |
| } | |
| html[data-theme="dark"] .tiptap pre code span { | |
| color: var(--shiki-dark, inherit); | |
| } | |
| /* Language label, top-right. Pure CSS, no DOM footprint. */ | |
| .tiptap pre[data-lang]::after { | |
| content: attr(data-lang); | |
| position: absolute; | |
| top: 0.5em; | |
| right: 0.75em; | |
| font-size: 11px; | |
| line-height: 1; | |
| font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; | |
| letter-spacing: 0.04em; | |
| text-transform: uppercase; | |
| color: var(--muted-color); | |
| opacity: 0.55; | |
| user-select: none; | |
| pointer-events: none; | |
| } | |
| /* Horizontal rule */ | |
| .tiptap hr { | |
| border: none; | |
| border-top: 1px solid var(--border-color); | |
| margin: 2em 0; | |
| } | |
| /* Tables - aligned with template _table.css | |
| Uses separate+spacing-0 instead of collapse so border-radius works on the table itself | |
| (the template puts radius on a .table-scroll wrapper which TipTap doesn't have). */ | |
| .tiptap table { | |
| border-collapse: separate; | |
| border-spacing: 0; | |
| table-layout: auto; | |
| width: 100%; | |
| margin: 0 0 var(--block-spacing-y); | |
| border: 1px solid var(--border-color); | |
| border-radius: var(--table-border-radius); | |
| overflow: hidden; | |
| background-color: var(--surface-bg); | |
| } | |
| .tiptap th, | |
| .tiptap td { | |
| border-bottom: 1px solid var(--border-color); | |
| border-right: 1px solid var(--border-color); | |
| padding: 6px 8px; | |
| font-size: 15px; | |
| white-space: nowrap; | |
| word-break: auto-phrase; | |
| vertical-align: top; | |
| } | |
| .tiptap th:last-child, | |
| .tiptap td:last-child { | |
| border-right: none; | |
| } | |
| .tiptap th { | |
| background: var(--table-header-bg); | |
| font-weight: 600; | |
| padding-top: 10px; | |
| padding-bottom: 10px; | |
| } | |
| .tiptap tbody tr:nth-child(odd) td { | |
| background: var(--table-row-odd-bg); | |
| } | |
| .tiptap tbody tr:last-child td { | |
| border-bottom: none; | |
| } | |
| /* Links - aligned with template (_base.css .content-grid main a) */ | |
| .tiptap a, | |
| .tiptap .editor-link { | |
| color: var(--primary-color); | |
| text-decoration: none; | |
| border-bottom: 1px solid color-mix(in srgb, var(--primary-color) 40%, transparent); | |
| text-underline-offset: 2px; | |
| transition: border-color 0.15s; | |
| } | |
| .tiptap a:hover, | |
| .tiptap .editor-link:hover { | |
| color: var(--primary-color-hover); | |
| border-bottom-color: color-mix(in srgb, var(--primary-color) 70%, transparent); | |
| } | |
| /* Bold */ | |
| .tiptap strong { | |
| font-weight: 600; | |
| color: var(--text-strong); | |
| } | |
| /* Images */ | |
| .tiptap img { | |
| max-width: 100%; | |
| height: auto; | |
| border-radius: 8px; | |
| margin: 1em 0; | |
| } | |
| /* Math - inline */ | |
| .tiptap [data-type="inline-math"] .tiptap-mathematics-render { | |
| padding: 0 0.2em; | |
| border-radius: 3px; | |
| transition: background 0.15s; | |
| } | |
| .tiptap [data-type="inline-math"] .tiptap-mathematics-render:hover { | |
| background: var(--code-bg); | |
| } | |
| /* Math - block */ | |
| .tiptap [data-type="block-math"] { | |
| margin: 1em 0; | |
| } | |
| .tiptap [data-type="block-math"] .tiptap-mathematics-render { | |
| display: flex; | |
| justify-content: center; | |
| padding: 1rem 0; | |
| border-radius: 8px; | |
| transition: background 0.15s; | |
| } | |
| .tiptap [data-type="block-math"] .tiptap-mathematics-render:hover { | |
| background: var(--bg-hover); | |
| } | |
| /* KaTeX color */ | |
| .tiptap .katex { | |
| color: var(--katex-color); | |
| } | |
| /* Citation inline node */ | |
| .tiptap .citation-node, | |
| .citation-node { | |
| position: relative; | |
| color: var(--primary-color); | |
| cursor: default; | |
| font-size: 0.9em; | |
| white-space: nowrap; | |
| text-decoration: underline; | |
| text-decoration-color: color-mix(in srgb, var(--primary-color) 30%, transparent); | |
| text-underline-offset: 2px; | |
| transition: text-decoration-color 0.15s; | |
| } | |
| .tiptap .citation-node:hover, | |
| .citation-node:hover { | |
| text-decoration-color: color-mix(in srgb, var(--primary-color) 70%, transparent); | |
| } | |
| /* Citation hover tooltip */ | |
| .citation-tooltip { | |
| position: absolute; | |
| bottom: calc(100% + 8px); | |
| left: 50%; | |
| transform: translateX(-50%); | |
| background: var(--bg-tooltip); | |
| border: 1px solid var(--border-color); | |
| border-radius: 8px; | |
| padding: 0.75rem; | |
| min-width: 240px; | |
| max-width: 320px; | |
| box-shadow: var(--shadow-lg); | |
| z-index: 100; | |
| white-space: normal; | |
| pointer-events: auto; | |
| } | |
| .citation-tooltip-title { | |
| color: var(--text-heading-secondary); | |
| font-size: 0.8125rem; | |
| font-weight: 500; | |
| line-height: 1.4; | |
| margin-bottom: 0.35rem; | |
| } | |
| .citation-tooltip-journal { | |
| color: var(--muted-color); | |
| font-size: 0.75rem; | |
| font-style: italic; | |
| line-height: 1.3; | |
| } | |
| .citation-tooltip-doi { | |
| color: var(--text-tertiary); | |
| font-size: 0.7rem; | |
| font-family: "SFMono-Regular", "Fira Code", monospace; | |
| margin-top: 0.35rem; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| .citation-tooltip-remove { | |
| display: block; | |
| margin-top: 0.5rem; | |
| margin-left: auto; | |
| padding: 0.25rem 0.6rem; | |
| border: none; | |
| border-radius: 4px; | |
| background: var(--danger-bg); | |
| color: var(--danger); | |
| font-size: 0.7rem; | |
| font-family: inherit; | |
| cursor: pointer; | |
| transition: background 0.15s; | |
| } | |
| .citation-tooltip-remove:hover { | |
| background: var(--danger-bg-hover); | |
| } | |
| /* Bibliography block - hidden in editor body, displayed in footer instead */ | |
| .tiptap .bibliography-block { | |
| display: none; | |
| } | |
| .bibliography-block { | |
| margin: 2em 0 1em; | |
| padding-top: 1.5em; | |
| border-top: 1px solid var(--border-color); | |
| } | |
| .bibliography-title { | |
| font-size: 1.3rem; | |
| font-weight: 600; | |
| color: var(--text-heading-secondary); | |
| margin: 0 0 1em; | |
| } | |
| .bibliography-empty { | |
| color: var(--text-faint); | |
| font-size: 0.875rem; | |
| font-style: italic; | |
| } | |
| .bibliography-content { | |
| font-size: 0.875rem; | |
| line-height: 1.7; | |
| color: var(--muted-color); | |
| } | |
| .bibliography-content .csl-entry { | |
| margin-bottom: 0.75em; | |
| padding-left: 1.5em; | |
| text-indent: -1.5em; | |
| } | |
| .bibliography-content .csl-entry i { | |
| font-style: italic; | |
| } | |
| /* ----------------------------------------------------------------------- | |
| Custom component blocks (shared between editor and published page) | |
| Neutral selectors [data-component="..."] work in both contexts. | |
| ----------------------------------------------------------------------- */ | |
| .component-content > *:first-child { | |
| margin-top: 0; | |
| } | |
| .component-content > *:last-child { | |
| margin-bottom: 0; | |
| } | |
| /* --- Note --- */ | |
| [data-component="note"] { | |
| background: var(--surface-bg); | |
| border-left: 2px solid var(--border-color); | |
| border-top-right-radius: 8px; | |
| border-bottom-right-radius: 8px; | |
| padding: 20px 18px; | |
| margin: var(--block-spacing-y, 1em) 0; | |
| } | |
| [data-component="note"][variant="info"] { | |
| border-left-color: #f39c12; | |
| background: color-mix(in oklab, #f39c12 10%, var(--surface-bg)); | |
| } | |
| [data-component="note"][variant="success"] { | |
| border-left-color: #2ecc71; | |
| background: color-mix(in oklab, #2ecc71 8%, var(--surface-bg)); | |
| } | |
| [data-component="note"][variant="danger"] { | |
| border-left-color: #e74c3c; | |
| background: color-mix(in oklab, #e74c3c 8%, var(--surface-bg)); | |
| } | |
| [data-component="note"] .note__title { | |
| font-size: 16px; | |
| font-weight: 600; | |
| color: var(--text-color); | |
| margin-bottom: 6px; | |
| } | |
| [data-component="note"] .component-content { | |
| font-size: 0.95rem; | |
| } | |
| [data-component="note"] > *:first-child { margin-top: 0; } | |
| [data-component="note"] > *:last-child { margin-bottom: 0; } | |
| /* --- Quote block (decorative) --- */ | |
| [data-component="quoteBlock"] { | |
| position: relative; | |
| margin: 32px 0; | |
| max-width: 600px; | |
| padding: 0; | |
| border: none; | |
| background: none; | |
| } | |
| [data-component="quoteBlock"]::before { | |
| content: '\201C'; | |
| position: absolute; | |
| top: -24px; | |
| left: -30px; | |
| font-size: 8rem; | |
| font-weight: 400; | |
| color: var(--text-color); | |
| opacity: 0.05; | |
| z-index: -1; | |
| line-height: 1; | |
| pointer-events: none; | |
| } | |
| [data-component="quoteBlock"] .quote-text, | |
| [data-component="quoteBlock"] .component-content { | |
| font-size: 1.5rem; | |
| line-height: 1.4; | |
| font-weight: 400; | |
| letter-spacing: -0.01em; | |
| color: var(--text-color); | |
| } | |
| [data-component="quoteBlock"] .quote-footer { | |
| font-size: 0.875rem; | |
| color: var(--muted-color); | |
| margin-top: 12px; | |
| } | |
| [data-component="quoteBlock"] .quote-author::before { | |
| content: '\2014\00A0'; | |
| font-style: normal; | |
| } | |
| [data-component="quoteBlock"] .quote-author, | |
| [data-component="quoteBlock"] .quote-source { | |
| font-weight: 500; | |
| font-style: italic; | |
| color: var(--text-color); | |
| opacity: 0.85; | |
| } | |
| [data-component="quoteBlock"] > *:first-child { margin-top: 0; } | |
| [data-component="quoteBlock"] > *:last-child { margin-bottom: 0; } | |
| /* --- Sidenote (margin note) --- */ | |
| [data-component="sidenote"] { | |
| font-size: 0.9rem; | |
| color: var(--muted-color); | |
| margin: 0; | |
| padding: 0; | |
| position: relative; | |
| } | |
| [data-component="sidenote"] .component-content > *:first-child { margin-top: 0; } | |
| [data-component="sidenote"] .component-content > *:last-child { margin-bottom: 0; } | |
| /* Editor: position sidenote in the right gutter of the .content-grid. | |
| The 3-col content-grid reserves a 200px track on the right of the | |
| 680px content column, separated by 32px gap. We float the sidenote | |
| right and push it into that track with a negative margin. | |
| Breakpoint is aligned with the TOC (1100px) so both collapse together. */ | |
| .editor-app .tiptap .node-sidenote { | |
| float: right; | |
| clear: right; | |
| width: 200px; | |
| margin-right: calc(-200px - 32px); | |
| margin-top: 0; | |
| margin-bottom: 0.5em; | |
| margin-left: 16px; | |
| border-radius: 6px; | |
| transition: background 0.15s; | |
| position: relative; | |
| } | |
| .editor-app .tiptap .node-sidenote:hover { | |
| background: var(--bg-hover); | |
| } | |
| .editor-app .tiptap [data-component="sidenote"] .sidenote-content-area { | |
| padding: 4px 8px; | |
| } | |
| .editor-app .tiptap .node-sidenote .sidenote-badge { | |
| position: absolute; | |
| top: 2px; | |
| right: 4px; | |
| font-size: 10px; | |
| color: var(--text-faint); | |
| opacity: 0; | |
| transition: opacity 0.15s; | |
| pointer-events: none; | |
| user-select: none; | |
| } | |
| .editor-app .tiptap .node-sidenote:hover .sidenote-badge { | |
| opacity: 1; | |
| } | |
| /* Responsive: below 1100px the TOC collapses, so the right gutter is | |
| used by the content column. Stack sidenotes inline to match. */ | |
| @media (max-width: 1100px) { | |
| .editor-app .tiptap .node-sidenote { | |
| float: none; | |
| clear: none; | |
| width: auto; | |
| margin: 0.75em 0; | |
| padding: 0 30px; | |
| border-left: 2px solid var(--border-color); | |
| border-radius: 0; | |
| } | |
| .editor-app .tiptap [data-component="sidenote"] .sidenote-content-area { | |
| padding: 0; | |
| } | |
| .editor-app .tiptap .node-sidenote .sidenote-badge { | |
| display: none; | |
| } | |
| } | |
| /* --- Reference / figure --- */ | |
| [data-component="reference"] { | |
| margin: 0 0 var(--spacing-4, 1rem); | |
| } | |
| [data-component="reference"] .reference__caption { | |
| text-align: left; | |
| font-size: 0.9rem; | |
| color: var(--muted-color); | |
| margin-top: 6px; | |
| } | |
| [data-component="reference"] .component-content { | |
| margin-bottom: 0; | |
| } | |
| [data-component="reference"] > *:first-child { margin-top: 0; } | |
| [data-component="reference"] > *:last-child { margin-bottom: 0; } | |
| /* --- Stack / multi-column layout --- */ | |
| [data-component="stack"] { | |
| display: grid; | |
| gap: 1rem; | |
| margin: var(--block-spacing-y, 1.5em) 0; | |
| width: 100%; | |
| max-width: 100%; | |
| box-sizing: border-box; | |
| } | |
| [data-component="stack"][data-layout="2-column"] { grid-template-columns: repeat(2, 1fr); } | |
| [data-component="stack"][data-layout="3-column"] { grid-template-columns: repeat(3, 1fr); } | |
| [data-component="stack"][data-layout="4-column"] { grid-template-columns: repeat(4, 1fr); } | |
| [data-component="stack"][data-layout="auto"] { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); } | |
| [data-component="stack"]:not([data-layout]) { grid-template-columns: repeat(2, 1fr); } | |
| [data-component="stack"][data-gap="small"] { gap: 0.5rem; } | |
| [data-component="stack"][data-gap="large"] { gap: 2rem; } | |
| [data-type="stack-column"] { min-width: 0; overflow: hidden; word-wrap: break-word; overflow-wrap: break-word; } | |
| [data-type="stack-column"] > *:first-child { margin-top: 0; } | |
| [data-type="stack-column"] > *:last-child { margin-bottom: 0; } | |
| @media (max-width: 768px) { | |
| [data-component="stack"][data-layout="2-column"], | |
| [data-component="stack"][data-layout="3-column"], | |
| [data-component="stack"][data-layout="4-column"], | |
| [data-component="stack"][data-layout="auto"], | |
| [data-component="stack"]:not([data-layout]) { grid-template-columns: 1fr ; } | |
| } | |
| @media (min-width: 769px) and (max-width: 1100px) { | |
| [data-component="stack"][data-layout="3-column"], | |
| [data-component="stack"][data-layout="4-column"], | |
| [data-component="stack"][data-layout="auto"] { grid-template-columns: repeat(2, 1fr) ; } | |
| } | |
| /* --- Accordion --- */ | |
| details[data-component="accordion"] { | |
| border: 1px solid var(--border-color); | |
| border-radius: var(--table-border-radius, 8px); | |
| background: var(--surface-bg); | |
| padding: 0; | |
| margin: 0 0 var(--spacing-4, 1em); | |
| transition: box-shadow 180ms ease, border-color 180ms ease; | |
| } | |
| details[data-component="accordion"][open] { | |
| border-color: color-mix(in oklab, var(--border-color), var(--primary-color) 20%); | |
| } | |
| details[data-component="accordion"] > summary { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| gap: 4px; | |
| padding: var(--spacing-2, 0.5rem) var(--spacing-3, 0.75rem); | |
| cursor: pointer; | |
| font-weight: 600; | |
| color: var(--text-color); | |
| list-style: none; | |
| user-select: none; | |
| } | |
| details[data-component="accordion"][open] > summary { | |
| border-bottom: 1px solid var(--border-color); | |
| } | |
| details[data-component="accordion"] > summary::-webkit-details-marker { display: none; } | |
| details[data-component="accordion"] > summary::marker { content: ""; } | |
| details[data-component="accordion"] > summary::after { | |
| content: ''; | |
| display: inline-block; | |
| width: 0.5em; | |
| height: 0.5em; | |
| border-right: 2px solid currentColor; | |
| border-bottom: 2px solid currentColor; | |
| transform: rotate(-45deg); | |
| transition: transform 220ms ease; | |
| opacity: 0.6; | |
| flex-shrink: 0; | |
| } | |
| details[data-component="accordion"][open] > summary::after { | |
| transform: rotate(45deg); | |
| } | |
| details[data-component="accordion"] > .accordion-content { | |
| padding: 8px; | |
| } | |
| details[data-component="accordion"] > .accordion-content > *:first-child { margin-top: 0; } | |
| details[data-component="accordion"] > .accordion-content > *:last-child { margin-bottom: 0; } | |
| /* --- Wide / full-width containers (editor) --- | |
| * | |
| * Strategy: the breakout is applied to the OUTER TipTap node wrapper | |
| * (`.node-wide`, `.node-fullWidth`), which is a plain <div> with no inline | |
| * styles. The INNER `[data-component]` holds the visual chrome (padding, | |
| * background, border-radius) and is always `width: 100%` of the breakout | |
| * box. | |
| * | |
| * The content column is viewport-centered (symmetric 3-col grid with equal | |
| * TOC/sidenote widths), so `50% ↔ 50vw` math lands on the viewport center. | |
| * | |
| * !important guards the horizontal margins against any future inline-style | |
| * leak from React / NodeViewWrapper – those are a classic footgun because | |
| * the `margin` shorthand silently resets marginLeft/marginRight to 0. */ | |
| .editor-app .tiptap .node-wide, | |
| .editor-app .tiptap .node-fullWidth { | |
| box-sizing: border-box; | |
| position: relative; | |
| z-index: var(--z-elevated); | |
| margin-block: 1.25em; | |
| clear: both; | |
| } | |
| .editor-app .tiptap .node-wide { | |
| width: min(1100px, 100vw - var(--content-padding-x, 16px) * 4); | |
| margin-left: 50% ; | |
| margin-right: 0 ; | |
| transform: translateX(-50%); | |
| } | |
| .editor-app .tiptap .node-fullWidth { | |
| width: 100vw; | |
| margin-left: calc(50% - 50vw) ; | |
| margin-right: calc(50% - 50vw) ; | |
| } | |
| /* Visual chrome lives on the inner wrapper, which always fills the breakout */ | |
| .editor-app .tiptap [data-component="wide"], | |
| .editor-app .tiptap [data-component="fullWidth"] { | |
| box-sizing: border-box; | |
| width: 100%; | |
| background-color: var(--surface-bg); | |
| padding: calc(var(--content-padding-x, 16px) * 2); | |
| border-radius: calc(var(--button-radius, 6px) * 2); | |
| border: 1px solid var(--border-color); | |
| } | |
| .editor-app .tiptap [data-component="wide"] > *:first-child, | |
| .editor-app .tiptap [data-component="fullWidth"] > *:first-child { | |
| margin-top: 0; | |
| } | |
| .editor-app .tiptap [data-component="wide"] > *:last-child, | |
| .editor-app .tiptap [data-component="fullWidth"] > *:last-child { | |
| margin-bottom: 0; | |
| } | |
| .editor-app .tiptap [data-component="fullWidth"] { | |
| border-radius: 0; | |
| border-left: none; | |
| border-right: none; | |
| } | |
| .editor-app .tiptap [data-component="fullWidth"] figure figcaption { | |
| text-align: center ; | |
| } | |
| /* On narrow screens (≤1100px), the grid collapses to a single column and the | |
| * content column spans the viewport minus gutters. Keep the blocks in-flow | |
| * instead of breaking out. */ | |
| @media (max-width: 1100px) { | |
| .editor-app .tiptap .node-wide, | |
| .editor-app .tiptap .node-fullWidth { | |
| width: 100%; | |
| margin-left: 0 ; | |
| margin-right: 0 ; | |
| transform: none; | |
| } | |
| .editor-app .tiptap [data-component="wide"], | |
| .editor-app .tiptap [data-component="fullWidth"] { | |
| padding: var(--content-padding-x, 16px); | |
| } | |
| } | |
| /* Glossary inline node */ | |
| .tiptap .glossary-node, | |
| .glossary-node { | |
| position: relative; | |
| display: inline; | |
| border-bottom: 1px dashed color-mix(in srgb, var(--primary-color) 50%, transparent); | |
| color: var(--primary-color); | |
| cursor: help; | |
| } | |
| .glossary-tooltip { | |
| position: absolute; | |
| bottom: calc(100% + 6px); | |
| left: 50%; | |
| transform: translateX(-50%); | |
| background: var(--bg-tooltip); | |
| border: 1px solid var(--border-color); | |
| border-radius: 8px; | |
| padding: 10px 14px; | |
| min-width: 200px; | |
| max-width: 320px; | |
| z-index: 50; | |
| box-shadow: var(--shadow-lg); | |
| } | |
| .glossary-tooltip-term { | |
| font-weight: 700; | |
| font-size: 13px; | |
| color: var(--text-heading-secondary); | |
| margin-bottom: 4px; | |
| } | |
| .glossary-tooltip-def { | |
| font-size: 12px; | |
| color: var(--muted-color); | |
| line-height: 1.5; | |
| } | |
| .glossary-tooltip-remove { | |
| margin-top: 8px; | |
| background: none; | |
| border: none; | |
| color: var(--danger); | |
| font-size: 11px; | |
| cursor: pointer; | |
| padding: 0; | |
| opacity: 0.8; | |
| } | |
| .glossary-tooltip-remove:hover { | |
| opacity: 1; | |
| } | |
| /* Footnote inline node */ | |
| .footnote-node { | |
| position: relative; | |
| display: inline; | |
| } | |
| .tiptap .footnote-marker, | |
| .footnote-marker { | |
| color: var(--primary-color); | |
| cursor: pointer; | |
| font-size: 0.75em; | |
| font-weight: 700; | |
| margin: 0 1px; | |
| } | |
| .footnote-tooltip { | |
| position: absolute; | |
| bottom: calc(100% + 6px); | |
| left: 50%; | |
| transform: translateX(-50%); | |
| background: var(--bg-tooltip); | |
| border: 1px solid var(--border-color); | |
| border-radius: 8px; | |
| padding: 10px 14px; | |
| min-width: 220px; | |
| max-width: 360px; | |
| z-index: 50; | |
| box-shadow: var(--shadow-lg); | |
| } | |
| .footnote-tooltip-content { | |
| font-size: 12px; | |
| color: var(--muted-color); | |
| line-height: 1.5; | |
| } | |
| /* Stack wrapper - override the published [data-component="stack"] grid | |
| because the editor uses its own StackView grid inside .stack-grid. */ | |
| .stack-wrapper { | |
| display: block ; | |
| width: 100%; | |
| } | |
| .stack-container { | |
| border: 1px solid rgba(255, 255, 255, 0.15); | |
| border-radius: 8px; | |
| background: rgba(255, 255, 255, 0.03); | |
| margin: 0.75em 0; | |
| overflow: hidden; | |
| } | |
| :root:not([data-theme="dark"]) .stack-container, | |
| [data-theme="light"] .stack-container { | |
| border-color: #ddd; | |
| background: #f9f9f9; | |
| } | |
| .stack-header { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| padding: 6px 12px; | |
| border-bottom: 1px solid rgba(255, 255, 255, 0.15); | |
| user-select: none; | |
| } | |
| :root:not([data-theme="dark"]) .stack-header, | |
| [data-theme="light"] .stack-header { | |
| border-bottom-color: #ddd; | |
| } | |
| .stack-header-icon { | |
| font-size: 14px; | |
| line-height: 1; | |
| } | |
| .stack-header-label { | |
| font-size: 13px; | |
| font-weight: 600; | |
| color: var(--text-tertiary); | |
| } | |
| .stack-header-select { | |
| font-size: 12px; | |
| padding: 3px 24px 3px 8px; | |
| } | |
| /* Stack columns layout: | |
| .stack-grid is the CSS grid container (set via inline style in StackView). | |
| Everything between .stack-grid and [data-type="stack-column"] must be | |
| display:contents so columns become actual grid items regardless of | |
| how many intermediate wrappers TipTap/ProseMirror/Y.js inserts. */ | |
| .stack-grid > *, | |
| .stack-grid > * > *, | |
| .stack-grid > * > * > * { | |
| display: contents; | |
| } | |
| .stack-columns { | |
| counter-reset: stack-col; | |
| } | |
| /* Editor-only: dashed borders, hover, and placeholder for stack columns */ | |
| .editor-app [data-component="stack"] [data-type="stack-column"] { | |
| display: block ; | |
| border: 2px dashed rgba(255, 255, 255, 0.2); | |
| border-radius: 6px; | |
| padding: 12px; | |
| min-height: 80px; | |
| background: rgba(255, 255, 255, 0.05); | |
| position: relative; | |
| counter-increment: stack-col; | |
| transition: border-color 0.15s, background 0.15s; | |
| } | |
| :root:not([data-theme="dark"]) .editor-app [data-component="stack"] [data-type="stack-column"], | |
| [data-theme="light"] .editor-app [data-component="stack"] [data-type="stack-column"] { | |
| border-color: #bbb; | |
| background: rgba(0, 0, 0, 0.03); | |
| } | |
| .editor-app [data-component="stack"] [data-type="stack-column"]:hover { | |
| border-color: var(--primary-color); | |
| background: color-mix(in srgb, var(--primary-color) 10%, transparent); | |
| } | |
| .editor-app [data-component="stack"] [data-type="stack-column"]:has(> p:only-child > .ProseMirror-trailingBreak:only-child)::before, | |
| .editor-app [data-component="stack"] [data-type="stack-column"]:has(> p:only-child:empty)::before { | |
| content: "Column " counter(stack-col); | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| color: var(--text-faint); | |
| font-size: 13px; | |
| font-weight: 500; | |
| pointer-events: none; | |
| } | |
| .editor-app [data-component="stack"] [data-type="stack-column"] > *:first-child { | |
| margin-top: 0; | |
| } | |
| .editor-app [data-component="stack"] [data-type="stack-column"] > *:last-child { | |
| margin-bottom: 0; | |
| } | |