@refrakt-md/lumina 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/base.css CHANGED
@@ -19,6 +19,7 @@
19
19
  @import './styles/layouts/blog.css';
20
20
  @import './styles/layouts/on-this-page.css';
21
21
  @import './styles/layouts/search.css';
22
+ @import './styles/layouts/theme-toggle.css';
22
23
  @import './styles/layouts/version-switcher.css';
23
24
  @import './styles/layouts/split.css';
24
25
 
@@ -200,6 +200,69 @@
200
200
  "{content}"
201
201
  ]
202
202
  },
203
+ "Badge": {
204
+ "block": "badge",
205
+ "root": ".rf-badge",
206
+ "dataRune": "badge",
207
+ "childOrder": [
208
+ "{content}"
209
+ ]
210
+ },
211
+ "Collection": {
212
+ "block": "collection",
213
+ "root": ".rf-collection",
214
+ "dataRune": "collection",
215
+ "childOrder": [
216
+ "{content}"
217
+ ]
218
+ },
219
+ "Relationships": {
220
+ "block": "relationships",
221
+ "root": ".rf-relationships",
222
+ "dataRune": "relationships",
223
+ "childOrder": [
224
+ "{content}"
225
+ ]
226
+ },
227
+ "Aggregate": {
228
+ "block": "aggregate",
229
+ "root": ".rf-aggregate",
230
+ "dataRune": "aggregate",
231
+ "childOrder": [
232
+ "{content}"
233
+ ]
234
+ },
235
+ "Progress": {
236
+ "block": "progress",
237
+ "root": ".rf-progress",
238
+ "dataRune": "progress",
239
+ "childOrder": [
240
+ "{content}"
241
+ ],
242
+ "modifiers": {
243
+ "sentiment": {
244
+ "source": "meta",
245
+ "classPattern": ".rf-progress--{value}",
246
+ "dataAttribute": "data-sentiment"
247
+ }
248
+ }
249
+ },
250
+ "Card": {
251
+ "block": "card",
252
+ "root": ".rf-card",
253
+ "dataRune": "card",
254
+ "childOrder": [
255
+ "{content}"
256
+ ],
257
+ "modifiers": {
258
+ "layout": {
259
+ "source": "meta",
260
+ "default": "stacked",
261
+ "classPattern": ".rf-card--{value}",
262
+ "dataAttribute": "data-layout"
263
+ }
264
+ }
265
+ },
203
266
  "Embed": {
204
267
  "block": "embed",
205
268
  "root": ".rf-embed",
package/index.css CHANGED
@@ -19,6 +19,7 @@
19
19
  @import './styles/layouts/blog.css';
20
20
  @import './styles/layouts/on-this-page.css';
21
21
  @import './styles/layouts/search.css';
22
+ @import './styles/layouts/theme-toggle.css';
22
23
  @import './styles/layouts/version-switcher.css';
23
24
  @import './styles/layouts/split.css';
24
25
  @import './styles/layouts/plan.css';
@@ -41,6 +42,7 @@
41
42
  @import './styles/runes/annotate.css';
42
43
  @import './styles/runes/audio.css';
43
44
  @import './styles/runes/api.css';
45
+ @import './styles/runes/card.css';
44
46
  @import './styles/runes/badge.css';
45
47
  @import './styles/runes/bento.css';
46
48
  @import './styles/runes/blog.css';
@@ -49,6 +51,10 @@
49
51
  @import './styles/runes/budget.css';
50
52
  @import './styles/runes/cast.css';
51
53
  @import './styles/runes/character.css';
54
+ @import './styles/runes/collection.css';
55
+ @import './styles/runes/relationships.css';
56
+ @import './styles/runes/aggregate.css';
57
+ @import './styles/runes/progress.css';
52
58
  @import './styles/runes/changelog.css';
53
59
  @import './styles/runes/chart.css';
54
60
  @import './styles/runes/compare.css';
@@ -117,11 +123,6 @@
117
123
  @import './styles/runes/bug.css';
118
124
  @import './styles/runes/decision.css';
119
125
  @import './styles/runes/milestone.css';
120
- @import './styles/runes/backlog.css';
121
- @import './styles/runes/decision-log.css';
122
126
  @import './styles/runes/plan-progress.css';
123
- @import './styles/runes/plan-activity.css';
124
127
  @import './styles/runes/plan-ref.css';
125
- @import './styles/runes/plan-relationships.css';
126
128
  @import './styles/runes/plan-history.css';
127
- @import './styles/runes/plan-entity-tabs.css';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@refrakt-md/lumina",
3
3
  "description": "Lumina theme for refrakt.md — design tokens, CSS, identity transform, and layout configs",
4
- "version": "0.15.0",
4
+ "version": "0.16.0",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -83,9 +83,9 @@
83
83
  "build": "tsc"
84
84
  },
85
85
  "dependencies": {
86
- "@refrakt-md/runes": "0.15.0",
87
- "@refrakt-md/transform": "0.15.0",
88
- "@refrakt-md/types": "0.15.0"
86
+ "@refrakt-md/runes": "0.16.0",
87
+ "@refrakt-md/transform": "0.16.0",
88
+ "@refrakt-md/types": "0.16.0"
89
89
  },
90
90
  "devDependencies": {
91
91
  "postcss": "^8.4.0"
@@ -100,6 +100,15 @@
100
100
  background: var(--rf-color-bg, #fff);
101
101
  z-index: 5;
102
102
  }
103
+ /* Match the sticky offset in the natural-flow position too. The fixed header
104
+ * is out of flow so `.rf-docs-body` starts at viewport top; without this
105
+ * margin, the sidebar's static position sits at Y=0 with its top 3.125rem
106
+ * hidden under the header — visible only when the page scrolls enough for
107
+ * sticky to activate. Scoped to `:has(.rf-docs-header)` so layouts without a
108
+ * header don't gain an unwanted top gap. */
109
+ html:has(.rf-docs-header) .rf-docs-sidebar {
110
+ margin-top: 3.125rem;
111
+ }
103
112
  .rf-docs-sidebar::-webkit-scrollbar {
104
113
  width: 0;
105
114
  }
@@ -190,6 +199,8 @@ html:has(.rf-docs-header) {
190
199
  padding: 0.625rem 1rem;
191
200
  border-bottom: 1px solid var(--rf-color-border);
192
201
  background: var(--rf-color-bg, #fff);
202
+ max-width: 100%;
203
+ overflow: hidden;
193
204
  }
194
205
  .rf-docs-toolbar__hamburger {
195
206
  background: none;
@@ -206,6 +217,12 @@ html:has(.rf-docs-header) {
206
217
  .rf-docs-toolbar__breadcrumb {
207
218
  font-size: 0.8rem;
208
219
  color: var(--rf-color-muted);
220
+ /* `flex: 1 1 0` (basis 0, not auto) so the breadcrumb shrinks against
221
+ * the available space rather than starting at its full content width —
222
+ * combined with `min-width: 0` this is what triggers ellipsis on long
223
+ * titles. Without basis 0 a long page title overflows the toolbar and
224
+ * forces horizontal page scroll on narrow viewports. */
225
+ flex: 1 1 0;
209
226
  min-width: 0;
210
227
  overflow: hidden;
211
228
  text-overflow: ellipsis;
@@ -0,0 +1,67 @@
1
+ /* Theme-mode toggle button (SPEC-073).
2
+ *
3
+ * Layout chrome — a peer of the search trigger, placed in the header/toolbar.
4
+ * The `theme-toggle` behavior cycles auto → light → dark and reflects the
5
+ * current preference onto the button as `data-theme-pref`; the icon below
6
+ * tracks that. Ported verbatim from the former `ThemeToggle.svelte` so the
7
+ * look is unchanged, with the icon selector rekeyed from a swapped class to
8
+ * the `data-theme-pref` attribute. */
9
+ .rf-theme-toggle {
10
+ display: inline-flex;
11
+ align-items: center;
12
+ justify-content: center;
13
+ width: 2rem;
14
+ height: 2rem;
15
+ padding: 0;
16
+ border: 1px solid var(--rf-color-border);
17
+ border-radius: var(--rf-radius-md);
18
+ background: transparent;
19
+ color: var(--rf-color-text);
20
+ cursor: pointer;
21
+ transition: background-color 120ms ease, border-color 120ms ease;
22
+ }
23
+ .rf-theme-toggle:hover {
24
+ background: var(--rf-color-surface-hover);
25
+ }
26
+ .rf-theme-toggle:focus-visible {
27
+ outline: 2px solid var(--rf-color-primary);
28
+ outline-offset: 2px;
29
+ }
30
+
31
+ /* In the header chrome the brand/nav and the controls are flex siblings
32
+ * arranged by `order` (search is order 1, nav/menu order 3). Without this the
33
+ * toggle defaults to order 0 and groups with the brand on the left; order 2
34
+ * places it just after the right-aligned search trigger — matching the former
35
+ * component. The plan toolbar arranges by DOM order, so it isn't included. */
36
+ .rf-header__inner .rf-theme-toggle,
37
+ .rf-docs-header__inner .rf-theme-toggle,
38
+ .rf-blog-header__inner .rf-theme-toggle {
39
+ order: 2;
40
+ }
41
+
42
+ /* Hidden on tint-locked pages: the saved preference is preserved but ignored
43
+ * while the lock is active (SPEC-052), so a toggle that does nothing would be
44
+ * confusing. Pure CSS — the lock lands on <html> at SSR and on client nav. */
45
+ html[data-tint-lock="true"] .rf-theme-toggle {
46
+ display: none;
47
+ }
48
+
49
+ /* Icon — a currentColor-tinted mask. Defaults to the auto glyph so the SSR'd
50
+ * button shows an icon before the behavior sets data-theme-pref; the light and
51
+ * dark glyphs swap in by state. */
52
+ .rf-theme-toggle__icon {
53
+ display: inline-block;
54
+ width: 1rem;
55
+ height: 1rem;
56
+ background: currentColor;
57
+ mask-size: contain;
58
+ mask-repeat: no-repeat;
59
+ mask-position: center;
60
+ mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='10'/><path d='M12 2a10 10 0 0 0 0 20Z' fill='currentColor'/></svg>");
61
+ }
62
+ .rf-theme-toggle[data-theme-pref="light"] .rf-theme-toggle__icon {
63
+ mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='12' cy='12' r='4'/><path d='M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41'/></svg>");
64
+ }
65
+ .rf-theme-toggle[data-theme-pref="dark"] .rf-theme-toggle__icon {
66
+ mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z'/></svg>");
67
+ }
@@ -1,9 +1,6 @@
1
1
  /* Accordion */
2
- .rf-accordion {
3
- border: 1px solid var(--rf-color-border);
4
- border-radius: var(--rf-radius-md);
5
- overflow: hidden;
6
- }
2
+ /* No wrapping border / radius — the only chrome is the divider between items. */
3
+ .rf-accordion {}
7
4
  .rf-accordion__preamble {
8
5
  margin-bottom: 1.5rem;
9
6
  }
@@ -52,35 +49,69 @@
52
49
  .rf-accordion-item__header {
53
50
  display: flex;
54
51
  align-items: center;
55
- justify-content: space-between;
56
- padding: 0.875rem 1.25rem;
52
+ gap: 0.5rem;
53
+ padding: 0.875rem 0rem;
57
54
  font-weight: 600;
58
55
  font-size: 0.95rem;
59
56
  cursor: pointer;
60
57
  user-select: none;
61
- transition: background-color 200ms ease;
62
58
  list-style: none;
63
59
  }
64
60
  .rf-accordion-item__header::-webkit-details-marker { display: none; }
65
61
  .rf-accordion-item__header::marker { display: none; content: ''; }
66
- .rf-accordion-item__header:hover {
67
- background: var(--rf-color-surface);
68
- }
69
- .rf-accordion-item__header::after {
70
- content: '';
62
+ /* Chevron in front of the label — points right when collapsed, rotates 90°
63
+ * to point down when expanded. SVG mask so the glyph is crisp at any size
64
+ * and tints with the muted text color. */
65
+ .rf-accordion-item__header::before {
66
+ content: '';
71
67
  flex-shrink: 0;
72
- color: var(--rf-color-muted);
68
+ width: 0.75rem;
69
+ height: 0.75rem;
70
+ background: var(--rf-color-muted);
71
+ mask-size: contain;
72
+ mask-repeat: no-repeat;
73
+ mask-position: center;
74
+ mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='9 18 15 12 9 6'/></svg>");
73
75
  transition: transform 200ms ease;
74
76
  }
75
- .rf-accordion-item[open] .rf-accordion-item__header::after {
76
- transform: rotate(180deg);
77
+ .rf-accordion-item[open] .rf-accordion-item__header::before {
78
+ transform: rotate(90deg);
77
79
  }
78
80
  .rf-accordion-item__title {
79
81
  flex: 1;
80
82
  min-width: 0;
81
83
  }
84
+ /* Per-group count for collection/relationships group-display="accordion".
85
+ * Inherits the header's font (weight + color) so the count reads as part
86
+ * of the same label. The header's `gap` handles spacing — no margins needed. */
87
+ .rf-accordion-item__count {
88
+ flex-shrink: 0;
89
+ }
90
+ /* Animate the panel's collapse / expand. `::details-content` (the auto-generated
91
+ * wrapper for non-summary content) + `interpolate-size: allow-keywords` lets the
92
+ * height transition between 0 and auto. `transition-behavior: allow-discrete`
93
+ * on `content-visibility` keeps the panel reachable during the open animation.
94
+ * Browsers without `::details-content` support fall back to the native snap. */
95
+ .rf-accordion-item {
96
+ interpolate-size: allow-keywords;
97
+ }
98
+ .rf-accordion-item::details-content {
99
+ block-size: 0;
100
+ overflow: hidden;
101
+ transition: block-size 250ms ease, content-visibility 250ms allow-discrete;
102
+ }
103
+ .rf-accordion-item[open]::details-content {
104
+ block-size: auto;
105
+ }
82
106
  .rf-accordion-item__body {
83
107
  padding: 0 1.25rem 1rem;
108
+ }
109
+ /* Prose styles for the standalone accordion rune's bodies (paragraphs,
110
+ * lists, etc.). Scoped to `:not(:has([data-block]))` so they don't bleed into
111
+ * block-content children like cards inside a collection/relationships
112
+ * `group-display="accordion"` panel, where the card's own typography
113
+ * should govern. */
114
+ .rf-accordion-item__body:not(:has([data-block])) {
84
115
  font-size: 0.925rem;
85
116
  line-height: 1.65;
86
117
  color: var(--rf-color-muted);
@@ -88,3 +119,12 @@
88
119
  .rf-accordion-item__body p:last-child {
89
120
  margin-bottom: 0;
90
121
  }
122
+ /* Block-content children — collection / relationships mark their body-template
123
+ * items `data-block`. A panel housing them (group-display="accordion") switches
124
+ * to a flex column with the small gap so cards get the same spacing they'd have
125
+ * under a heading-grouped layout. */
126
+ .rf-accordion-item__body:has([data-block]) {
127
+ display: flex;
128
+ flex-direction: column;
129
+ gap: var(--rf-spacing-sm);
130
+ }
@@ -0,0 +1,75 @@
1
+ /* Aggregate — counts and per-group breakdowns from the registry (SPEC-076).
2
+ *
3
+ * Two modes share the same block class. The no-body inline form is a `<span>`
4
+ * carrying the integer; the body-zoned form is a `<section>` rendering
5
+ * preamble / per-group template / fallback. Most of the visual weight inside
6
+ * the body comes from other runes (`progress`, `badge`, `humanize` text), so
7
+ * this stylesheet sticks to container chrome and the inline single-number
8
+ * form.
9
+ */
10
+
11
+ /* Block container — vertical rhythm matches collection / relationships. The
12
+ * bare selector also matches the inline form's `<span>`, but vertical margin
13
+ * on an inline span has no effect so it's safe to share. */
14
+ .rf-aggregate {
15
+ margin: var(--rf-spacing-md) 0;
16
+ }
17
+
18
+ /* Inline single-number form — `<span class="rf-aggregate" data-aggregate="count">`.
19
+ * Tabular nums keep digits aligned when the count changes between renders;
20
+ * weight tuned to stand out modestly in prose. */
21
+ .rf-aggregate[data-aggregate='count'] {
22
+ font-variant-numeric: tabular-nums;
23
+ font-weight: 600;
24
+ color: var(--rf-color-text);
25
+ }
26
+
27
+ /* Preamble — totals chrome above the breakdown (e.g. a progress bar). Trim
28
+ * first/last-child block margins so the rune's outer rhythm wins over the
29
+ * template's inner block margins (same pattern collection uses). */
30
+ .rf-aggregate__preamble > :first-child { margin-top: 0; }
31
+ .rf-aggregate__preamble > :last-child { margin-bottom: 0; }
32
+
33
+ /* Empty state — replaces the breakdown when the query yields nothing. */
34
+ .rf-aggregate__empty {
35
+ color: var(--rf-color-muted);
36
+ font-size: 0.9375em;
37
+ }
38
+ .rf-aggregate__empty > :first-child { margin-top: 0; }
39
+ .rf-aggregate__empty > :last-child { margin-bottom: 0; }
40
+
41
+ /* Items — either the per-group breakdown row, or the ungrouped single-render
42
+ * slot. Default stack handles the ungrouped case (one block element from the
43
+ * body). With groups, switch to a wrapping inline row so per-group renders
44
+ * (badges, pills, …) flow across the page. */
45
+ .rf-aggregate__items {
46
+ display: flex;
47
+ flex-direction: column;
48
+ gap: var(--rf-spacing-sm);
49
+ margin-top: var(--rf-spacing-sm);
50
+ }
51
+ .rf-aggregate__items:has(.rf-aggregate__group) {
52
+ flex-direction: row;
53
+ flex-wrap: wrap;
54
+ gap: var(--rf-spacing-xs) var(--rf-spacing-sm);
55
+ }
56
+
57
+ /* Direct children of __items (the ungrouped template render) — trim outer
58
+ * block margins so the rune's outer rhythm wins, same pattern as preamble /
59
+ * empty. */
60
+ .rf-aggregate__items > :first-child { margin-top: 0; }
61
+ .rf-aggregate__items > :last-child { margin-bottom: 0; }
62
+
63
+ /* Per-group block — wraps one render of the template. `data-block` is set
64
+ * by the resolver so we trim its outer block margins (the items-row `gap`
65
+ * governs inter-group spacing) — same pattern collection / relationships
66
+ * use for `__item[data-block]`. */
67
+ .rf-aggregate__group {
68
+ min-width: 0;
69
+ }
70
+ .rf-aggregate__group[data-block] > :first-child {
71
+ margin-top: 0;
72
+ }
73
+ .rf-aggregate__group[data-block] > :last-child {
74
+ margin-bottom: 0;
75
+ }
@@ -1,33 +1,37 @@
1
- /* Badge rune — standalone inline pill that visually stands out from prose.
1
+ /* Badge rune — standalone inline chip.
2
2
  *
3
- * Unlike the metadata badges that appear inside card chrome (plan items,
4
- * recipe meta, work item headers), standalone {% badge %} runes are meant
5
- * to pop. They reuse the universal `--meta-color` set by
6
- * dimensions/metadata.css's sentiment rules, applied as a sentiment-tinted
7
- * border, a low-opacity sentiment-tinted background, and sentiment-coloured
8
- * text. The leading sentiment dot from metadata.css is dropped — the
9
- * background tint already carries the sentiment cue.
3
+ * Built atop the metadata pill base (`data-meta-type`), but reshaped to the
4
+ * compact chip look used by plan-progress status counts: **no border**,
5
+ * solid-ish sentiment-tinted background, smaller font + tighter padding, and
6
+ * a small corner radius. Tints come from the universal `--meta-color` set by
7
+ * `dimensions/metadata.css`'s sentiment rules (positive / caution / negative /
8
+ * neutral success / warning / danger / muted). Background is a `color-mix`
9
+ * of that color, so the chip stays sentiment-flexible — any value the theme
10
+ * gives `--meta-color` works without per-status selectors.
10
11
  *
11
- * rank="primary" boosts emphasis with a heavier background tint and a
12
- * bolder weight, useful for "Popular" / "Featured" labels that should
13
- * dominate over their neighbours.
12
+ * `rank="primary"` boosts emphasis with a heavier tint and bolder weight,
13
+ * useful for labels that should dominate over neighbours.
14
14
  */
15
15
 
16
16
  .rf-badge {
17
17
  vertical-align: baseline;
18
- border-color: var(--meta-color, var(--rf-color-border));
19
- background: color-mix(in srgb, var(--meta-color, transparent) 12%, transparent);
20
- color: var(--meta-color, var(--rf-color-text));
18
+ border: none;
19
+ background: color-mix(in srgb, var(--meta-color, var(--rf-color-muted)) 10%, transparent);
20
+ color: var(--meta-color, var(--rf-color-muted));
21
+ font-size: 0.75rem;
21
22
  font-weight: 500;
23
+ padding: 0.125rem 0.5rem;
24
+ border-radius: var(--rf-radius-sm);
25
+ white-space: nowrap;
22
26
  }
23
27
 
24
28
  .rf-badge[data-meta-rank="primary"] {
25
- background: color-mix(in srgb, var(--meta-color, transparent) 22%, transparent);
29
+ background: color-mix(in srgb, var(--meta-color, var(--rf-color-muted)) 28%, transparent);
26
30
  font-weight: 600;
27
31
  }
28
32
 
29
33
  /* Drop the sentiment dot — the background tint carries the cue for
30
- * standalone badges, and the dot competes visually with adjacent prose. */
34
+ * standalone chips, and the dot competes visually with adjacent prose. */
31
35
  .rf-badge::before {
32
36
  display: none;
33
37
  }
@@ -0,0 +1,83 @@
1
+ /* card — generic content card (SPEC-070).
2
+ *
3
+ * Borderless (no outline) but with a soft surface fill — the surface + the
4
+ * structure (media split, footer separator) carry the card, no border needed.
5
+ * The media|content split + responsive collapse + mobile full-bleed media
6
+ * header come from the shared layouts/split.css (keyed off data-layout /
7
+ * data-section="media" / data-name="content" / data-media-position).
8
+ */
9
+
10
+ .rf-card {
11
+ position: relative; /* anchor for the stretched link + media bleed */
12
+ padding: var(--rune-padding, var(--rf-spacing-md));
13
+ border-radius: var(--rf-radius-md);
14
+ background: var(--rf-color-surface);
15
+ }
16
+
17
+ .rf-card:hover {
18
+ background: var(--rf-color-surface-hover);
19
+ }
20
+
21
+ .rf-card__body > :first-child { margin-top: 0; }
22
+ .rf-card__body > :last-child { margin-bottom: 0; }
23
+
24
+ /* Eyebrow — a leading paragraph immediately before a heading, the same kicker
25
+ * treatment as page-section / recipe. */
26
+ .rf-card__eyebrow {
27
+ font-size: 0.8rem;
28
+ font-weight: 600;
29
+ letter-spacing: 0.06em;
30
+ text-transform: uppercase;
31
+ color: var(--rf-color-primary);
32
+ margin: 0 0 0.375rem;
33
+ }
34
+
35
+ /* The body's leading heading is the card title; drop its prose top margin so it
36
+ * hugs the eyebrow (or the top of the card) instead of opening a gap. */
37
+ .rf-card__title {
38
+ margin-top: 0;
39
+ }
40
+
41
+ .rf-card__footer {
42
+ margin-top: var(--rf-spacing-sm);
43
+ padding-top: var(--rf-spacing-sm);
44
+ border-top: 1px solid var(--rf-color-border);
45
+ font-size: 0.8125em;
46
+ color: var(--rf-color-muted);
47
+ }
48
+
49
+ /* In a collection grid, each row stretches its items to the tallest (see
50
+ * collection.css); these rules make a stacked card spend that height usefully:
51
+ * the card becomes a column, its content fills the leftover height, and the
52
+ * body grows so footers drop to a common baseline across the row. Scoped to
53
+ * stacked cards so a split card's grid (layouts/split.css) is left intact. */
54
+ .rf-collection[data-layout='grid'] .rf-card[data-layout='stacked'] {
55
+ display: flex;
56
+ flex-direction: column;
57
+ /* The grid/flex wrapper stretches the card, but a nested flex container
58
+ * doesn't always pass that height to its own flex children; pin it so the
59
+ * content/body flex:1 reliably fills. */
60
+ height: 100%;
61
+ }
62
+ .rf-collection[data-layout='grid'] .rf-card[data-layout='stacked'] > .rf-card__content {
63
+ flex: 1;
64
+ }
65
+ .rf-collection[data-layout='grid'] .rf-card[data-layout='stacked'] .rf-card__content {
66
+ display: flex;
67
+ flex-direction: column;
68
+ }
69
+ .rf-collection[data-layout='grid'] .rf-card[data-layout='stacked'] .rf-card__body {
70
+ flex: 1;
71
+ }
72
+
73
+ /* Whole-card stretched link — covers the card; real links in body/footer
74
+ * sit above it via position:relative. */
75
+ .rf-card__link {
76
+ position: absolute;
77
+ inset: 0;
78
+ z-index: 0;
79
+ }
80
+ .rf-card a:not(.rf-card__link) {
81
+ position: relative;
82
+ z-index: 1;
83
+ }
@@ -0,0 +1,166 @@
1
+ /* Collection — list / grid / table / cards of registry entities (SPEC-070).
2
+ *
3
+ * The block is layout-agnostic; the `data-layout` attribute on the wrapper
4
+ * selects the arrangement. Built-in items carry `.rf-collection__card` /
5
+ * `__item`; a body template emits its own markup inside `.rf-collection__item`.
6
+ */
7
+
8
+ .rf-collection {
9
+ margin: var(--rf-spacing-md) 0;
10
+ }
11
+
12
+ /* Preamble — rendered above items only when the query is non-empty, so a
13
+ * heading/intro can live inside the rune and vanish with an empty section. */
14
+ .rf-collection__preamble > :first-child { margin-top: 0; }
15
+
16
+ /* Empty state — shown in place of items when the query yields nothing. */
17
+ .rf-collection__empty {
18
+ color: var(--rf-color-muted);
19
+ font-size: 0.9375em;
20
+ }
21
+
22
+ .rf-collection__items {
23
+ display: flex;
24
+ flex-direction: column;
25
+ gap: var(--rf-spacing-sm);
26
+ }
27
+
28
+ /* grid arranges items into responsive columns; list stays a stacked flow.
29
+ * Item chrome (a card box, bare row, …) comes from the item, not the layout. */
30
+ .rf-collection[data-layout='grid'] .rf-collection__items {
31
+ display: grid;
32
+ grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
33
+ gap: var(--rf-spacing-md);
34
+ align-items: stretch;
35
+ }
36
+
37
+ /* Uniform heights per row: grid stretches each item to the tallest in its row;
38
+ * a body-template item then lets its card fill that stretched height. */
39
+ .rf-collection[data-layout='grid'] .rf-collection__item {
40
+ display: flex;
41
+ }
42
+ .rf-collection[data-layout='grid'] .rf-collection__item > * {
43
+ flex: 1;
44
+ min-width: 0;
45
+ }
46
+
47
+ /* Grouped grid: the grid belongs to each group (its items are the cells), not
48
+ * to __items — whose children are the group blocks (heading groups) or the
49
+ * accordion wrapper. Left on __items, each group becomes a single cell, i.e. a
50
+ * column per group. So when __items holds groups it stacks them, and each
51
+ * group becomes the grid instead, with the group title spanning every column. */
52
+ .rf-collection[data-layout='grid'] .rf-collection__items:has(.rf-collection__group),
53
+ .rf-collection[data-layout='grid'] .rf-collection__items:has(.rf-accordion) {
54
+ display: flex;
55
+ flex-direction: column;
56
+ gap: var(--rf-spacing-md);
57
+ }
58
+ .rf-collection[data-layout='grid'] .rf-collection__group {
59
+ display: grid;
60
+ grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
61
+ gap: var(--rf-spacing-md);
62
+ align-items: stretch;
63
+ }
64
+ .rf-collection[data-layout='grid'] .rf-collection__group-title {
65
+ grid-column: 1 / -1;
66
+ }
67
+
68
+ .rf-collection__item {
69
+ min-width: 0;
70
+ }
71
+
72
+ .rf-collection__card {
73
+ display: flex;
74
+ flex-direction: column;
75
+ gap: var(--rf-spacing-xs);
76
+ padding: var(--rf-spacing-md);
77
+ border: 1px solid var(--rf-color-border);
78
+ border-radius: var(--rf-radius-md);
79
+ background: var(--rf-color-surface);
80
+ }
81
+
82
+ .rf-collection__card:hover {
83
+ background: var(--rf-color-surface-hover);
84
+ }
85
+
86
+ .rf-collection__title {
87
+ font-weight: 600;
88
+ color: var(--rf-color-text);
89
+ text-decoration: none;
90
+ }
91
+
92
+ .rf-collection__title:hover {
93
+ color: var(--rf-color-primary);
94
+ }
95
+
96
+ .rf-collection__field {
97
+ font-size: 0.875em;
98
+ color: var(--rf-color-muted);
99
+ }
100
+
101
+ /* grouping */
102
+ .rf-collection__group {
103
+ margin-top: var(--rf-spacing-md);
104
+ display: flex;
105
+ flex-direction: column;
106
+ /* Tight default for short item rows (the built-in title link, no body). */
107
+ gap: var(--rf-spacing-xs);
108
+ }
109
+ /* Modestly roomier when items carry a body template (block content — a card,
110
+ * callout, list, …), so they don't collide. Body-template items are marked
111
+ * `data-block` by the resolver; the built-in inline title rows are not.
112
+ * Mirrors the rf-relationships rule. */
113
+ .rf-collection__group:has(.rf-collection__item[data-block]) {
114
+ gap: var(--rf-spacing-sm);
115
+ }
116
+
117
+ /* Block-content item — let the body template (card / callout / etc.) fill the
118
+ * container instead of shrink-fitting. No-op in grid mode (items there are
119
+ * already `display: flex` with `flex: 1` on the child). Trim the outer margins
120
+ * of the template's first/last block so the group `gap` controls inter-item
121
+ * spacing — works for any block content, not a wrapper element. */
122
+ .rf-collection__item[data-block] {
123
+ display: block;
124
+ }
125
+ .rf-collection__item[data-block] > :first-child {
126
+ margin-top: 0;
127
+ }
128
+ .rf-collection__item[data-block] > :last-child {
129
+ margin-bottom: 0;
130
+ }
131
+
132
+ .rf-collection__group-title {
133
+ margin: 0 0 var(--rf-spacing-sm);
134
+ font-size: 0.875em;
135
+ text-transform: uppercase;
136
+ letter-spacing: 0.05em;
137
+ color: var(--rf-color-muted);
138
+ }
139
+
140
+ /* table layout */
141
+ .rf-collection__table {
142
+ width: 100%;
143
+ border-collapse: collapse;
144
+ }
145
+
146
+ .rf-collection__table th,
147
+ .rf-collection__table td {
148
+ padding: var(--rf-spacing-sm);
149
+ text-align: left;
150
+ border-bottom: 1px solid var(--rf-color-border);
151
+ }
152
+
153
+ .rf-collection__table th {
154
+ font-size: 0.875em;
155
+ text-transform: uppercase;
156
+ letter-spacing: 0.05em;
157
+ color: var(--rf-color-muted);
158
+ }
159
+
160
+ /* Heading-template cells (`layout="table"` body) wrap their content as
161
+ * markdown paragraphs, which pick up the global `p { margin-bottom: 1rem }`
162
+ * and leave dead space inside each row. Drop it inside the table — table
163
+ * cells don't want paragraph spacing. */
164
+ .rf-collection__table td p {
165
+ margin: 0;
166
+ }
@@ -47,7 +47,12 @@
47
47
  }
48
48
  .rf-conversation-message__body p { margin: 0; }
49
49
  .rf-conversation-message__body p + p { margin-top: 0.5rem; }
50
- .rf-conversation-message__body > span[property],
50
+ /* The speaker name lives inline-bold inside the bubble (the explicit form
51
+ * authors it that way, and the named-speakers form injects it the same way),
52
+ * so the property-carrier span is data only — hide it. Same for the meta
53
+ * tags the engine consumes. `data-field` is the post-engine attribute set by
54
+ * createComponentRenderable. */
55
+ .rf-conversation-message > span[data-field="speaker"],
56
+ .rf-conversation-message > meta[data-field] { display: none; }
57
+ .rf-conversation-message__body > span[data-field],
51
58
  .rf-conversation-message__body > meta { display: none; }
52
- .rf-conversation-message > span[property="speaker"],
53
- .rf-conversation-message > meta[property] { display: none; }
@@ -67,6 +67,21 @@
67
67
  color: var(--rf-color-muted);
68
68
  }
69
69
 
70
+ /* Items with an inline badge (recognised by the nav schema and tagged with
71
+ * data-name="badge") sit on one row so the badge rides alongside the link
72
+ * instead of wrapping to the next line — the link's `display: block` would
73
+ * otherwise eat the full row. */
74
+ .rf-nav-item:has(> .rf-badge[data-name="badge"]) {
75
+ display: flex;
76
+ align-items: center;
77
+ gap: 0.375rem;
78
+ padding: 0;
79
+ }
80
+ .rf-nav-item:has(> .rf-badge[data-name="badge"]) > .rf-nav-item__link {
81
+ flex: 0 1 auto;
82
+ min-width: 0;
83
+ }
84
+
70
85
  /* ─── Collapsible groups (vertical sidebar) ───────────────────────────── */
71
86
 
72
87
  .rf-nav--collapsible .rf-nav-group h2,
@@ -0,0 +1,49 @@
1
+ /* progress — generic completion bar (SPEC-072 / WORK-285).
2
+ *
3
+ * Presentational: the fill width is driven by the `--rf-progress` custom
4
+ * property set on the root from the computed percent. Generalized from the
5
+ * former plan milestone progress bar.
6
+ */
7
+
8
+ .rf-progress {
9
+ display: flex;
10
+ flex-wrap: wrap;
11
+ align-items: baseline;
12
+ gap: var(--rf-spacing-xs) var(--rf-spacing-sm);
13
+ margin: var(--rf-spacing-sm) 0;
14
+ }
15
+
16
+ .rf-progress__label {
17
+ font-weight: 600;
18
+ color: var(--rf-color-text);
19
+ }
20
+
21
+ .rf-progress__value {
22
+ font-weight: 600;
23
+ color: var(--rf-color-text);
24
+ margin-left: auto;
25
+ }
26
+
27
+ .rf-progress__track {
28
+ flex-basis: 100%;
29
+ height: 0.5rem;
30
+ border-radius: var(--rf-radius-pill, 999px);
31
+ background: var(--rf-color-surface-hover);
32
+ overflow: hidden;
33
+ }
34
+
35
+ .rf-progress__fill {
36
+ display: block;
37
+ height: 100%;
38
+ width: var(--rf-progress, 0%);
39
+ border-radius: inherit;
40
+ background: var(--rf-color-primary);
41
+ transition: width 0.3s ease;
42
+ }
43
+
44
+ /* Sentiment variants tint the fill; the neutral default uses the primary.
45
+ * Token names follow the sentiment scheme in dimensions/metadata.css
46
+ * (positive→success, caution→warning, negative→danger). */
47
+ .rf-progress--positive .rf-progress__fill { background: var(--rf-color-success); }
48
+ .rf-progress--caution .rf-progress__fill { background: var(--rf-color-warning); }
49
+ .rf-progress--negative .rf-progress__fill { background: var(--rf-color-danger); }
@@ -0,0 +1,132 @@
1
+ /* Relationships — an entity's graph edges, grouped by kind (SPEC-072).
2
+ *
3
+ * Generic over kind: the rune emits `data-kind` on each item and a humanized
4
+ * group heading per kind. Mirrors collection's list chrome.
5
+ */
6
+
7
+ .rf-relationships {
8
+ margin: var(--rf-spacing-md) 0;
9
+ }
10
+
11
+ .rf-relationships__preamble > :first-child { margin-top: 0; }
12
+
13
+ .rf-relationships__empty {
14
+ color: var(--rf-color-muted);
15
+ font-size: 0.9375em;
16
+ }
17
+
18
+ .rf-relationships__items {
19
+ display: flex;
20
+ flex-direction: column;
21
+ gap: var(--rf-spacing-sm);
22
+ }
23
+
24
+ /* grid arranges edges into responsive columns; list stays a stacked flow.
25
+ * Mirrors collection: item chrome comes from the item (the card built-in, or
26
+ * a {% card %} in the body template), not the layout. */
27
+ .rf-relationships[data-layout='grid'] .rf-relationships__items {
28
+ display: grid;
29
+ grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
30
+ gap: var(--rf-spacing-md);
31
+ align-items: stretch;
32
+ }
33
+ .rf-relationships[data-layout='grid'] .rf-relationships__item {
34
+ display: flex;
35
+ }
36
+ .rf-relationships[data-layout='grid'] .rf-relationships__item > * {
37
+ flex: 1;
38
+ min-width: 0;
39
+ }
40
+
41
+ /* Grouped grid: the grid belongs to each group (its items are the cells), not
42
+ * to __items — whose children are the group blocks (or the accordion wrapper).
43
+ * Left on __items, each group collapses into a single column. So __items stacks
44
+ * the groups and each group becomes the grid, with its title spanning. */
45
+ .rf-relationships[data-layout='grid'] .rf-relationships__items:has(.rf-relationships__group),
46
+ .rf-relationships[data-layout='grid'] .rf-relationships__items:has(.rf-accordion) {
47
+ display: flex;
48
+ flex-direction: column;
49
+ gap: var(--rf-spacing-md);
50
+ }
51
+ .rf-relationships[data-layout='grid'] .rf-relationships__group {
52
+ display: grid;
53
+ grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
54
+ gap: var(--rf-spacing-md);
55
+ align-items: stretch;
56
+ }
57
+ .rf-relationships[data-layout='grid'] .rf-relationships__group-title {
58
+ grid-column: 1 / -1;
59
+ }
60
+
61
+ .rf-relationships__group {
62
+ margin-top: var(--rf-spacing-md);
63
+ display: flex;
64
+ flex-direction: column;
65
+ /* Tight default for the built-in inline shape (title + field spans). */
66
+ gap: var(--rf-spacing-xs);
67
+ }
68
+ .rf-relationships__group:first-child {
69
+ margin-top: 0;
70
+ }
71
+ /* Modestly roomier when items carry a body template (block content — more than
72
+ * a one-liner). Body-template items are marked `data-block` by the resolver;
73
+ * the built-in inline title+field rows are not. */
74
+ .rf-relationships__group:has(.rf-relationships__item[data-block]) {
75
+ gap: var(--rf-spacing-sm);
76
+ }
77
+
78
+ .rf-relationships__group-title {
79
+ margin: 0 0 var(--rf-spacing-sm);
80
+ font-size: 0.875em;
81
+ text-transform: uppercase;
82
+ letter-spacing: 0.05em;
83
+ color: var(--rf-color-muted);
84
+ }
85
+
86
+ .rf-relationships__item {
87
+ display: flex;
88
+ flex-wrap: wrap;
89
+ align-items: baseline;
90
+ gap: var(--rf-spacing-xs) var(--rf-spacing-sm);
91
+ }
92
+ /* Block-content item — let the body template (card / callout / etc.) fill the
93
+ * container width instead of shrink-fitting. Same `data-block` signal as the
94
+ * group spacing above; trim the template's outer block margins so the group
95
+ * `gap` governs spacing (works for any block content, not a wrapper element). */
96
+ .rf-relationships__item[data-block] {
97
+ display: block;
98
+ }
99
+ .rf-relationships__item[data-block] > :first-child {
100
+ margin-top: 0;
101
+ }
102
+ .rf-relationships__item[data-block] > :last-child {
103
+ margin-bottom: 0;
104
+ }
105
+
106
+ /* Card chrome for the grid built-in (mirrors collection's __card). */
107
+ .rf-relationships__card {
108
+ display: flex;
109
+ flex-direction: column;
110
+ gap: var(--rf-spacing-xs);
111
+ padding: var(--rf-spacing-md);
112
+ border: 1px solid var(--rf-color-border);
113
+ border-radius: var(--rf-radius-md);
114
+ background: var(--rf-color-surface);
115
+ }
116
+ .rf-relationships__card:hover {
117
+ background: var(--rf-color-surface-hover);
118
+ }
119
+
120
+ .rf-relationships__title {
121
+ font-weight: 600;
122
+ color: var(--rf-color-text);
123
+ text-decoration: none;
124
+ }
125
+ .rf-relationships__title:hover {
126
+ color: var(--rf-color-primary);
127
+ }
128
+
129
+ .rf-relationships__field {
130
+ font-size: 0.875em;
131
+ color: var(--rf-color-muted);
132
+ }
@@ -1,108 +0,0 @@
1
- /* Backlog */
2
-
3
- /* Group sections */
4
- .rf-backlog__group {
5
- margin-bottom: 1.5rem;
6
- }
7
- .rf-backlog__group-title {
8
- font-size: 0.8125rem;
9
- font-weight: 700;
10
- text-transform: uppercase;
11
- letter-spacing: 0.05em;
12
- color: var(--rf-color-muted);
13
- margin: 0 0 0.5rem;
14
- padding-bottom: 0.375rem;
15
- border-bottom: 1px solid var(--rf-color-border);
16
- }
17
-
18
- /* Cards — mini rune surface */
19
- .rf-backlog__card {
20
- display: flex;
21
- flex-direction: column;
22
- gap: 0.375rem;
23
- padding: 0.75rem 1rem;
24
- border-radius: var(--rf-radius-md);
25
- background: var(--rf-color-surface);
26
- margin-bottom: 0.5rem;
27
- transition: box-shadow 200ms ease;
28
- }
29
- .rf-backlog__card:hover {
30
- box-shadow: var(--rf-shadow-md);
31
- }
32
-
33
- /* Card link — suppress underline */
34
- .rf-backlog__card-link {
35
- text-decoration: none;
36
- color: inherit;
37
- display: flex;
38
- flex-direction: column;
39
- gap: 0.375rem;
40
- }
41
- .rf-backlog__card-link:hover {
42
- text-decoration: none;
43
- }
44
-
45
- /* ─── Header row: ID left, status + progress right ──────────────────── */
46
- .rf-backlog__card [data-section="header"] {
47
- display: flex;
48
- align-items: center;
49
- justify-content: space-between;
50
- gap: 0.5rem;
51
- margin-bottom: 0;
52
- }
53
- .rf-backlog__card-header-left,
54
- .rf-backlog__card-header-right {
55
- display: inline-flex;
56
- align-items: center;
57
- gap: 0.5rem;
58
- }
59
-
60
- /* Strip borders and pill styling from header badges — doubled class for specificity */
61
- .rf-backlog__card [data-section="header"] [data-meta-type][data-meta-type] {
62
- border: none;
63
- padding: 0;
64
- border-radius: 0;
65
- }
66
-
67
- /* Suppress sentiment circles in header */
68
- .rf-backlog__card [data-section="header"] [data-meta-sentiment]::before {
69
- display: none;
70
- }
71
-
72
- /* ─── Body: title ───────────────────────────────────────────────────── */
73
- .rf-backlog__card [data-section="title"] {
74
- font-size: 0.9375rem;
75
- font-weight: 600;
76
- }
77
-
78
- /* ─── Footer row: uniform secondary pills ───────────────────────────── */
79
- .rf-backlog__card [data-section="footer"] {
80
- display: flex;
81
- flex-wrap: wrap;
82
- align-items: center;
83
- gap: 0.375rem;
84
- }
85
-
86
- /* Normalize all footer pills to same size regardless of rank/type */
87
- .rf-backlog__card [data-section="footer"] [data-meta-type] {
88
- font-size: 0.75rem;
89
- padding: 0.25em 0.75em;
90
- opacity: 1;
91
- }
92
-
93
- /* Checklist progress badge — plain text in header.
94
- Override checklist dimension styles that match [data-checked]. */
95
- .rf-backlog__card-progress {
96
- font-family: var(--rf-font-mono);
97
- font-size: 0.75rem;
98
- font-weight: 600;
99
- color: var(--rf-color-muted);
100
- border: none;
101
- padding: 0;
102
- padding-left: 0;
103
- margin-left: 0;
104
- position: static;
105
- }
106
- .rf-backlog__card-progress::before {
107
- display: none;
108
- }
@@ -1,46 +0,0 @@
1
- /* Decision Log */
2
-
3
- /* Entry list */
4
- .rf-decision-log__list {
5
- list-style: none;
6
- padding: 0;
7
- margin: 0;
8
- }
9
- .rf-decision-log__entry {
10
- display: flex;
11
- flex-direction: column;
12
- gap: 0.25rem;
13
- padding: 0.75rem 1rem;
14
- border-radius: var(--rf-radius-md);
15
- background: var(--rf-color-surface);
16
- margin-bottom: 0.5rem;
17
- transition: box-shadow 200ms ease;
18
- }
19
- .rf-decision-log__entry:hover {
20
- box-shadow: var(--rf-shadow-md);
21
- }
22
-
23
- /* Entry link — suppress underline */
24
- .rf-decision-log__link {
25
- text-decoration: none;
26
- color: inherit;
27
- display: flex;
28
- flex-direction: column;
29
- gap: 0.25rem;
30
- }
31
- .rf-decision-log__link:hover {
32
- text-decoration: none;
33
- }
34
-
35
- /* Entry title — smaller than full rune */
36
- .rf-decision-log__entry [data-section="title"] {
37
- font-size: 0.9375rem;
38
- font-weight: 600;
39
- }
40
-
41
- /* Superseded/deprecated entries — muted with strikethrough */
42
- .rf-decision-log__entry[data-status="superseded"] [data-section="title"],
43
- .rf-decision-log__entry[data-status="deprecated"] [data-section="title"] {
44
- color: var(--rf-color-muted);
45
- text-decoration: line-through;
46
- }
@@ -1,87 +0,0 @@
1
- /* Plan Activity */
2
- .rf-plan-activity__list {
3
- list-style: none;
4
- padding: 0;
5
- margin: 0;
6
- }
7
- .rf-plan-activity__entry {
8
- display: flex;
9
- align-items: baseline;
10
- gap: 0.5rem;
11
- padding: 0.25rem 0.5rem;
12
- border-radius: var(--rf-radius-sm);
13
- font-size: 0.8125rem;
14
- }
15
- .rf-plan-activity__entry:nth-child(odd) {
16
- background: var(--rf-color-surface);
17
- }
18
- .rf-plan-activity__date {
19
- font-family: var(--rf-font-mono);
20
- font-size: 0.75rem;
21
- color: var(--rf-color-muted);
22
- white-space: nowrap;
23
- }
24
- .rf-plan-activity__type {
25
- font-size: 0.75rem;
26
- text-transform: uppercase;
27
- letter-spacing: 0.05em;
28
- color: var(--rf-color-muted);
29
- min-width: 4rem;
30
- }
31
- .rf-plan-activity__id {
32
- font-family: var(--rf-font-mono);
33
- font-size: 0.75rem;
34
- color: var(--rf-color-primary);
35
- white-space: nowrap;
36
- }
37
- .rf-plan-activity__status {
38
- font-size: 0.75rem;
39
- padding: 0.0625rem 0.375rem;
40
- border-radius: var(--rf-radius-sm);
41
- white-space: nowrap;
42
- }
43
-
44
- /* Status badge colors */
45
- .rf-plan-activity__status[data-status="done"],
46
- .rf-plan-activity__status[data-status="fixed"],
47
- .rf-plan-activity__status[data-status="accepted"] {
48
- color: var(--rf-color-success);
49
- background: var(--rf-color-success-bg);
50
- }
51
- .rf-plan-activity__status[data-status="in-progress"],
52
- .rf-plan-activity__status[data-status="confirmed"],
53
- .rf-plan-activity__status[data-status="review"] {
54
- color: var(--rf-color-warning);
55
- background: var(--rf-color-warning-bg);
56
- }
57
- .rf-plan-activity__status[data-status="ready"],
58
- .rf-plan-activity__status[data-status="reported"],
59
- .rf-plan-activity__status[data-status="proposed"] {
60
- color: var(--rf-color-info);
61
- background: var(--rf-color-info-bg);
62
- }
63
- .rf-plan-activity__status[data-status="blocked"] {
64
- color: var(--rf-color-danger);
65
- background: var(--rf-color-danger-bg);
66
- }
67
- .rf-plan-activity__status[data-status="draft"] {
68
- color: var(--rf-color-muted);
69
- background: var(--rf-color-surface-active);
70
- }
71
- .rf-plan-activity__title {
72
- color: var(--rf-color-text);
73
- overflow: hidden;
74
- text-overflow: ellipsis;
75
- white-space: nowrap;
76
- }
77
- .rf-plan-activity__link {
78
- display: flex;
79
- align-items: baseline;
80
- gap: 0.5rem;
81
- color: inherit;
82
- text-decoration: none;
83
- width: 100%;
84
- }
85
- .rf-plan-activity__link:hover .rf-plan-activity__title {
86
- color: var(--rf-color-primary);
87
- }
@@ -1,88 +0,0 @@
1
- /* Plan Entity Tabs — page-level tab navigation for spec/work/bug/decision pages */
2
-
3
- .rf-plan-entity-tabs {
4
- margin-block-start: var(--rf-spacing-lg, 1.5rem);
5
- }
6
-
7
- /* Tab bar — underline style matching the core tabs rune */
8
- .rf-plan-entity-tabs__tabs {
9
- display: flex;
10
- border-bottom: 1px solid var(--rf-color-border, #dee2e6);
11
- gap: 0;
12
- }
13
-
14
- /* Tab buttons */
15
- .rf-plan-entity-tabs__tab {
16
- flex: 0 0 auto;
17
- padding: 0.625rem 1.25rem;
18
- font-size: 0.875rem;
19
- font-weight: 500;
20
- font-family: inherit;
21
- color: var(--rf-color-muted, #868e96);
22
- background: none;
23
- border: none;
24
- border-bottom: 2px solid transparent;
25
- cursor: pointer;
26
- transition: color 200ms ease, border-color 200ms ease;
27
- white-space: nowrap;
28
- }
29
-
30
- .rf-plan-entity-tabs__tab:hover {
31
- color: var(--rf-color-text, #212529);
32
- }
33
-
34
- .rf-plan-entity-tabs__tab--active,
35
- .rf-plan-entity-tabs__tab[data-state="active"] {
36
- color: var(--rf-color-primary, #228be6);
37
- border-bottom-color: var(--rf-color-primary, #228be6);
38
- font-weight: 600;
39
- }
40
-
41
- /* Panels container */
42
- .rf-plan-entity-tabs__panels {
43
- padding: 0;
44
- margin: 0;
45
- }
46
-
47
- /* Individual panel */
48
- .rf-plan-entity-tabs__panel {
49
- padding-block-start: var(--rf-spacing-lg, 1.5rem);
50
- }
51
-
52
- .rf-plan-entity-tabs__panel > *:first-child {
53
- margin-top: 0;
54
- }
55
-
56
- .rf-plan-entity-tabs__panel > *:last-child {
57
- margin-bottom: 0;
58
- }
59
-
60
- /* When inside entity tabs, relationships and history sections don't need
61
- their own heading — the tab label provides the context */
62
- .rf-plan-entity-tabs__panel .rf-plan-relationships__heading,
63
- .rf-plan-entity-tabs__panel .rf-plan-history__heading {
64
- display: none;
65
- }
66
-
67
- /* Remove top margin, padding, and border from relationships/history sections when inside a tab panel */
68
- .rf-plan-entity-tabs__panel .rf-plan-relationships,
69
- .rf-plan-entity-tabs__panel .rf-plan-history {
70
- margin-block-start: 0;
71
- padding-block-start: 0;
72
- border-block-start: none;
73
- }
74
-
75
- /* ---- TOC dimming when non-Overview tab is active ---- */
76
-
77
- /* Desktop TOC: dim when a non-overview tab is active */
78
- :has([data-active-tab]:not([data-active-tab="overview"])) .rf-plan-toc {
79
- opacity: 0.3;
80
- pointer-events: none;
81
- transition: opacity 200ms ease;
82
- }
83
-
84
- :has([data-active-tab="overview"]) .rf-plan-toc {
85
- opacity: 1;
86
- pointer-events: auto;
87
- transition: opacity 200ms ease;
88
- }
@@ -1,40 +0,0 @@
1
- /* Plan relationships section */
2
-
3
- .rf-plan-relationships {
4
- margin-top: 2rem;
5
- padding-top: 1.5rem;
6
- border-top: 1px solid var(--rf-color-border, #dee2e6);
7
- }
8
-
9
- .rf-plan-relationships__heading {
10
- font-size: 1.125rem;
11
- font-weight: 600;
12
- color: var(--rf-color-text, #212529);
13
- margin: 0 0 1rem;
14
- }
15
-
16
- .rf-plan-relationships__group {
17
- margin-bottom: 1rem;
18
- }
19
-
20
- .rf-plan-relationships__group-title {
21
- font-size: 0.8125rem;
22
- font-weight: 600;
23
- text-transform: uppercase;
24
- letter-spacing: 0.04em;
25
- color: var(--rf-color-muted, #6c757d);
26
- margin: 0 0 0.375rem;
27
- }
28
-
29
- .rf-plan-relationships__group[data-kind="blocked-by"] .rf-plan-relationships__group-title {
30
- color: var(--rf-color-danger, #ff6b6b);
31
- }
32
-
33
- .rf-plan-relationships__group[data-kind="blocks"] .rf-plan-relationships__group-title {
34
- color: var(--rf-color-warning, #f59f00);
35
- }
36
-
37
- .rf-plan-relationships__cards {
38
- display: flex;
39
- flex-direction: column;
40
- }