ultimate-jekyll-manager 1.7.2 → 1.8.1

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.
Files changed (84) hide show
  1. package/CHANGELOG.md +69 -1
  2. package/CLAUDE.md +36 -15
  3. package/README.md +4 -2
  4. package/TODO-AUTH-TESTING.md +1 -1
  5. package/dist/assets/themes/newsflash/README.md +58 -0
  6. package/dist/assets/themes/newsflash/_config.scss +138 -0
  7. package/dist/assets/themes/newsflash/_theme.js +27 -0
  8. package/dist/assets/themes/newsflash/_theme.scss +37 -0
  9. package/dist/assets/themes/newsflash/css/base/_mixins.scss +50 -0
  10. package/dist/assets/themes/newsflash/css/base/_root.scss +134 -0
  11. package/dist/assets/themes/newsflash/css/base/_typography.scss +49 -0
  12. package/dist/assets/themes/newsflash/css/base/_utilities.scss +58 -0
  13. package/dist/assets/themes/newsflash/css/components/_badges.scss +65 -0
  14. package/dist/assets/themes/newsflash/css/components/_buttons.scss +139 -0
  15. package/dist/assets/themes/newsflash/css/components/_cards.scss +52 -0
  16. package/dist/assets/themes/newsflash/css/components/_editorial.scss +182 -0
  17. package/dist/assets/themes/newsflash/css/components/_forms.scss +75 -0
  18. package/dist/assets/themes/newsflash/css/components/_infinite-scroll.scss +102 -0
  19. package/dist/assets/themes/newsflash/css/components/_panels.scss +91 -0
  20. package/dist/assets/themes/newsflash/css/components/_ticker.scss +70 -0
  21. package/dist/assets/themes/newsflash/css/layout/_general.scss +264 -0
  22. package/dist/assets/themes/newsflash/css/layout/_navigation.scss +164 -0
  23. package/dist/assets/themes/newsflash/js/initialize-tooltips.js +20 -0
  24. package/dist/assets/themes/newsflash/js/masthead-scroll.js +29 -0
  25. package/dist/assets/themes/newsflash/pages/404/index.scss +27 -0
  26. package/dist/assets/themes/newsflash/pages/about/index.scss +70 -0
  27. package/dist/assets/themes/newsflash/pages/blog/index.scss +17 -0
  28. package/dist/assets/themes/newsflash/pages/blog/post.js +29 -0
  29. package/dist/assets/themes/newsflash/pages/blog/post.scss +164 -0
  30. package/dist/assets/themes/newsflash/pages/index.scss +159 -0
  31. package/dist/assets/themes/newsflash/pages/pricing/index.scss +194 -0
  32. package/dist/assets/themes/newsflash/pages/test/libraries/layers/index.js +9 -0
  33. package/dist/assets/themes/newsflash/pages/test/libraries/layers/index.scss +7 -0
  34. package/dist/commands/blogify.js +6 -3
  35. package/dist/commands/test.js +34 -5
  36. package/dist/defaults/CLAUDE.md +17 -4
  37. package/dist/defaults/dist/_includes/core/pricing/resolve-plan.html +59 -0
  38. package/dist/defaults/dist/_includes/themes/classy/frontend/sections/footer.html +20 -3
  39. package/dist/defaults/dist/_layouts/themes/classy/admin/core/minimal-viewport-locked.html +1 -1
  40. package/dist/defaults/dist/_layouts/themes/classy/admin/core/minimal.html +1 -1
  41. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/pricing.html +5 -40
  42. package/dist/defaults/dist/_layouts/themes/neobrutalism/frontend/pages/pricing.html +33 -34
  43. package/dist/defaults/dist/_layouts/themes/newsflash/frontend/core/base.html +61 -0
  44. package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/404.html +86 -0
  45. package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/about.html +353 -0
  46. package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/blog/categories/category.html +105 -0
  47. package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/blog/categories/index.html +93 -0
  48. package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/blog/index.html +373 -0
  49. package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/blog/post.html +289 -0
  50. package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/blog/tags/index.html +90 -0
  51. package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/blog/tags/tag.html +107 -0
  52. package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/contact.html +340 -0
  53. package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/index.html +522 -0
  54. package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/pricing.html +485 -0
  55. package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/team/index.html +207 -0
  56. package/dist/defaults/dist/_layouts/themes/newsflash/frontend/pages/team/member.html +134 -0
  57. package/dist/defaults/test/README.md +4 -0
  58. package/dist/gulp/tasks/jekyll.js +4 -2
  59. package/dist/test/runner.js +50 -3
  60. package/dist/test/suites/build/attach-log-file.test.js +102 -0
  61. package/dist/test/suites/build/theme-contract.test.js +173 -0
  62. package/dist/test/utils/extended-mode-warning.js +13 -0
  63. package/dist/utils/attach-log-file.js +70 -43
  64. package/docs/appearance.md +1 -0
  65. package/docs/assets.md +9 -0
  66. package/docs/audit.md +78 -7
  67. package/docs/build-system.md +57 -0
  68. package/docs/common-mistakes.md +15 -0
  69. package/docs/{project-structure.md → directory-structure.md} +1 -1
  70. package/docs/environment-detection.md +1 -1
  71. package/docs/javascript-libraries.md +38 -1
  72. package/docs/layouts-and-pages.md +146 -0
  73. package/docs/local-development.md +1 -8
  74. package/docs/logging.md +30 -0
  75. package/docs/migration.md +131 -0
  76. package/docs/no-inline-scripts.md +304 -0
  77. package/docs/purgecss.md +164 -0
  78. package/docs/seo.md +131 -4
  79. package/docs/templating.md +23 -0
  80. package/docs/test-boot-layer.md +1 -1
  81. package/docs/test-framework.md +56 -8
  82. package/docs/themes.md +254 -13
  83. package/logs/test.log +111 -0
  84. package/package.json +9 -8
@@ -0,0 +1,75 @@
1
+ // Newsflash Theme — Forms
2
+ // Pill inputs framed in ink with a volt focus ring — the newsletter-signup
3
+ // look applied to every form control.
4
+
5
+ .form-control,
6
+ .form-select {
7
+ background-color: var(--bs-body-bg);
8
+ border: var(--nf-border);
9
+ color: var(--bs-body-color);
10
+ border-radius: 50rem;
11
+ font-weight: 500;
12
+ padding: 0.6rem 1.1rem;
13
+ transition: $nf-transition;
14
+
15
+ &::placeholder {
16
+ color: var(--bs-secondary-color);
17
+ opacity: 0.8;
18
+ }
19
+
20
+ &:focus {
21
+ background-color: var(--bs-body-bg);
22
+ border-color: var(--nf-border-color);
23
+ color: var(--bs-body-color);
24
+ box-shadow: 0 0 0 3px var(--nf-volt);
25
+ }
26
+ }
27
+
28
+ // Multiline fields keep the editorial frame radius (a pill textarea is silly)
29
+ textarea.form-control {
30
+ border-radius: var(--bs-border-radius);
31
+ }
32
+
33
+ // Checks + radios: ink frames, vermilion when checked
34
+ .form-check-input {
35
+ border: var(--nf-border);
36
+ background-color: var(--bs-body-bg);
37
+
38
+ &:checked {
39
+ background-color: var(--bs-primary);
40
+ border-color: var(--nf-border-color);
41
+ }
42
+
43
+ &:focus {
44
+ border-color: var(--nf-border-color);
45
+ box-shadow: 0 0 0 3px var(--nf-volt);
46
+ }
47
+ }
48
+
49
+ // Switches keep the pill but pick up the ink frame
50
+ .form-switch .form-check-input {
51
+ border-radius: 50rem;
52
+ }
53
+
54
+ // Labels read as compact editorial labels
55
+ .form-label {
56
+ font-weight: 700;
57
+ font-size: 0.85rem;
58
+ }
59
+
60
+ // Input groups: joined pill — round only the outer corners
61
+ .input-group {
62
+ > .form-control:not(:last-child),
63
+ > .form-select:not(:last-child) {
64
+ border-top-right-radius: 0;
65
+ border-bottom-right-radius: 0;
66
+ }
67
+
68
+ > .btn:last-child {
69
+ border-top-right-radius: 50rem;
70
+ border-bottom-right-radius: 50rem;
71
+ border-top-left-radius: 0;
72
+ border-bottom-left-radius: 0;
73
+ box-shadow: none; // joined controls share one frame; no floating shadow
74
+ }
75
+ }
@@ -0,0 +1,102 @@
1
+ // Newsflash Theme — Infinite Scroll
2
+ // Structural marquee used by the shared layouts (trusted-by logo strip,
3
+ // testimonial scroll). Mostly layout/animation (theme-agnostic); cosmetic
4
+ // bits are tuned to the editorial look (framed cards, paper fade edges).
5
+ // NOTE: this is structural behavior the inherited Classy layouts depend on —
6
+ // see docs/themes.md "Structural vs visual components".
7
+
8
+ .infinite-scroll-wrapper {
9
+ position: relative;
10
+ width: 100vw;
11
+ margin-left: calc(-50vw + 50%);
12
+ overflow: hidden;
13
+
14
+ &.infinite-scroll-fade-edges {
15
+ &::before,
16
+ &::after {
17
+ content: '';
18
+ position: absolute;
19
+ top: 0;
20
+ bottom: 0;
21
+ width: 100px;
22
+ z-index: 10;
23
+ pointer-events: none;
24
+ }
25
+ &::before { left: 0; background: linear-gradient(to right, var(--bs-body-bg), transparent); }
26
+ &::after { right: 0; background: linear-gradient(to left, var(--bs-body-bg), transparent); }
27
+ }
28
+ }
29
+
30
+ .infinite-scroll-track {
31
+ display: flex;
32
+ width: fit-content;
33
+ animation: infinite-scroll var(--infinite-scroll-duration, 30s) linear infinite;
34
+
35
+ &:hover { animation-play-state: paused; }
36
+
37
+ &.gap-sm { gap: 1rem; }
38
+ &.gap-md { gap: 1.5rem; }
39
+ &.gap-lg { gap: 2rem; }
40
+ }
41
+
42
+ .infinite-scroll-item { flex-shrink: 0; }
43
+
44
+ @keyframes infinite-scroll {
45
+ 0% { transform: translateX(0); }
46
+ 100% { transform: translateX(var(--infinite-scroll-distance, -50%)); }
47
+ }
48
+
49
+ @media (prefers-reduced-motion: reduce) {
50
+ .infinite-scroll-track { animation: none; }
51
+ .infinite-scroll-wrapper { overflow-x: auto; }
52
+ }
53
+
54
+ // Logo items — quiet ink-tinted marks that wake on hover
55
+ .infinite-scroll-item--logo {
56
+ padding: 0 3rem;
57
+ display: flex;
58
+ align-items: center;
59
+ height: 60px;
60
+
61
+ img, svg {
62
+ height: 40px;
63
+ width: auto;
64
+ max-width: 150px;
65
+ opacity: 0.65;
66
+ transition: $nf-transition;
67
+ }
68
+
69
+ &:hover img,
70
+ &:hover svg {
71
+ opacity: 1;
72
+ }
73
+ }
74
+
75
+ // Card items (testimonials) — framed paper, pop on hover
76
+ .infinite-scroll-item--card {
77
+ width: 320px;
78
+ padding: 0.5rem 0.5rem 1.5rem 0; // room for the pop shadow
79
+
80
+ .card {
81
+ height: 100%;
82
+
83
+ &:hover {
84
+ @include nf-shadow-pop();
85
+ }
86
+ }
87
+ }
88
+
89
+ @media (max-width: 768px) {
90
+ .infinite-scroll-wrapper.infinite-scroll-fade-edges {
91
+ &::before,
92
+ &::after { width: 50px; }
93
+ }
94
+
95
+ .infinite-scroll-item--logo {
96
+ padding: 0 2rem;
97
+ height: 50px;
98
+ img, svg { height: 30px; }
99
+ }
100
+
101
+ .infinite-scroll-item--card { width: 280px; }
102
+ }
@@ -0,0 +1,91 @@
1
+ // Newsflash Theme — Shared Panels & Numerals
2
+ // Cross-page editorial set pieces: the dark "big read" CTA band (home,
3
+ // pricing, about) and the stroked serif numerals (stats strips, step
4
+ // numbers). Universal semantic class names — no theme prefixes.
5
+
6
+ // ============================================
7
+ // CTA panel — the dark big-read band
8
+ // ============================================
9
+ .cta-panel {
10
+ background: var(--nf-panel-bg);
11
+ color: var(--nf-panel-color);
12
+ border-radius: calc(#{$nf-radius} + 8px);
13
+ padding: clamp(2.5rem, 6vw, 4rem);
14
+ position: relative;
15
+ overflow: hidden;
16
+
17
+ .kicker {
18
+ color: var(--nf-volt);
19
+ }
20
+
21
+ .text-accent {
22
+ color: var(--nf-volt);
23
+ }
24
+ }
25
+
26
+ .cta-title {
27
+ font-style: italic;
28
+ font-weight: 550;
29
+ font-size: clamp(2rem, 3.6vw, 3.2rem);
30
+ letter-spacing: -0.015em;
31
+ line-height: 1.1;
32
+ margin: 0.75rem 0;
33
+ }
34
+
35
+ .cta-desc {
36
+ color: var(--nf-panel-color);
37
+ opacity: 0.72;
38
+ max-width: 36em;
39
+ margin-bottom: 1.75rem;
40
+ }
41
+
42
+ // Spinning vermilion ring field — the band's decorative flourish
43
+ .cta-rings {
44
+ position: absolute;
45
+ right: -120px;
46
+ top: 50%;
47
+ width: 520px;
48
+ aspect-ratio: 1;
49
+ border-radius: 50%;
50
+ background: repeating-radial-gradient(circle, var(--nf-vermilion) 0 2px, transparent 2px 34px);
51
+ opacity: 0.7;
52
+ transform: translateY(-50%);
53
+ animation: cta-rings-spin 60s linear infinite;
54
+ pointer-events: none;
55
+ }
56
+
57
+ @keyframes cta-rings-spin {
58
+ to { transform: translateY(-50%) rotate(360deg); }
59
+ }
60
+
61
+ @media (max-width: 991.98px) {
62
+ .cta-rings { opacity: 0.3; }
63
+ }
64
+
65
+ @media (prefers-reduced-motion: reduce) {
66
+ .cta-rings { animation: none; }
67
+ }
68
+
69
+ // ============================================
70
+ // Feature icon chip — round accent badge for desk cards (features, values,
71
+ // contact methods)
72
+ // ============================================
73
+ .feature-icon {
74
+ width: 3rem;
75
+ height: 3rem;
76
+ flex: none;
77
+ border: var(--nf-border);
78
+ }
79
+
80
+ // ============================================
81
+ // Stroked numerals — stats strips, oversized counters
82
+ // ============================================
83
+ .stat-num {
84
+ font-family: $nf-font-display;
85
+ font-style: italic;
86
+ font-weight: 800;
87
+ font-size: clamp(2.6rem, 5vw, 3.6rem);
88
+ line-height: 1.1;
89
+ color: transparent;
90
+ -webkit-text-stroke: 1.5px var(--nf-ink);
91
+ }
@@ -0,0 +1,70 @@
1
+ // Newsflash Theme — News Ticker
2
+ // The signature strip above the masthead: an ink bar marquee-scrolling the
3
+ // latest headlines with a pulsing live dot. Pure CSS animation — the track
4
+ // content is duplicated once in the layout, so -50% loops seamlessly.
5
+ // Genuinely novel UI (no Bootstrap equivalent) → universal semantic classes.
6
+
7
+ .ticker {
8
+ background: var(--nf-panel-bg);
9
+ color: var(--nf-panel-color);
10
+ overflow: hidden;
11
+ white-space: nowrap;
12
+ font-size: 0.78rem;
13
+ font-weight: 600;
14
+ letter-spacing: 0.03em;
15
+
16
+ a {
17
+ color: inherit;
18
+ text-decoration: none;
19
+
20
+ &:hover {
21
+ color: var(--nf-volt);
22
+ }
23
+ }
24
+ }
25
+
26
+ .ticker-track {
27
+ display: inline-flex;
28
+ padding: 0.55em 0;
29
+ animation: ticker-scroll 36s linear infinite;
30
+ }
31
+
32
+ .ticker:hover .ticker-track {
33
+ animation-play-state: paused;
34
+ }
35
+
36
+ .ticker-item {
37
+ display: inline-flex;
38
+ align-items: center;
39
+ gap: 0.6em;
40
+ padding: 0 1.6em;
41
+
42
+ // Bolt separators render via {% uj_icon %} — keep them volt
43
+ svg, i {
44
+ color: var(--nf-volt);
45
+ }
46
+ }
47
+
48
+ .live-dot {
49
+ width: 7px;
50
+ height: 7px;
51
+ border-radius: 50%;
52
+ flex: none;
53
+ background: var(--nf-vermilion);
54
+ animation: live-pulse 1.4s ease-in-out infinite;
55
+ }
56
+
57
+ @keyframes ticker-scroll {
58
+ to { transform: translateX(-50%); }
59
+ }
60
+
61
+ @keyframes live-pulse {
62
+ 0%, 100% { opacity: 1; transform: scale(1); }
63
+ 50% { opacity: 0.45; transform: scale(0.8); }
64
+ }
65
+
66
+ @media (prefers-reduced-motion: reduce) {
67
+ .ticker-track { animation: none; }
68
+ .live-dot { animation: none; }
69
+ .ticker { overflow-x: auto; }
70
+ }
@@ -0,0 +1,264 @@
1
+ // Newsflash Theme — General Layout
2
+ // Section rhythm, footer, shadow utilities, and overrides for the
3
+ // gradient/accent classes the shared (Classy-derived) layouts emit. The goal:
4
+ // inherited pages (auth, account, team, feedback…) render as flat editorial
5
+ // paper-and-ink — no layout override needed.
6
+
7
+ // ============================================
8
+ // Section rhythm
9
+ // ============================================
10
+ // Shared layouts rely on the theme to provide vertical section padding.
11
+ // The masthead is sticky (in-flow), so no first-section navbar offset is
12
+ // needed — content starts right under the bar.
13
+ section {
14
+ padding-top: clamp(3rem, 6vw, 5rem);
15
+ padding-bottom: clamp(3rem, 6vw, 5rem);
16
+ }
17
+
18
+ // ============================================
19
+ // Gradient neutralization — inherited markup goes flat editorial
20
+ // ============================================
21
+ .bg-gradient-rainbow,
22
+ .gradient-animated,
23
+ .gradient-grain {
24
+ background-image: none !important;
25
+ animation: none !important;
26
+ }
27
+
28
+ // Hero sections ship as .bg-gradient-rainbow with light text; flatten to a
29
+ // paper-2 well closed by an ink rule, and pull the text back to ink.
30
+ section.bg-gradient-rainbow {
31
+ background-color: var(--nf-paper-2) !important;
32
+ color: var(--bs-body-color) !important;
33
+ border-radius: 0 !important;
34
+ border-bottom: var(--nf-border);
35
+ box-shadow: none !important;
36
+
37
+ .text-light,
38
+ &.text-light,
39
+ h1, h2, h3, .lead,
40
+ p {
41
+ color: var(--bs-body-color) !important;
42
+ }
43
+
44
+ // The hero tagline becomes a vermilion kicker line
45
+ .text-uppercase {
46
+ color: var(--bs-primary) !important;
47
+ font-weight: 800;
48
+ letter-spacing: 0.14em;
49
+ opacity: 1 !important;
50
+ }
51
+ }
52
+
53
+ // Gradient-clipped text → solid italic serif vermilion
54
+ .text-gradient-rainbow {
55
+ background: none !important;
56
+ -webkit-background-clip: initial !important;
57
+ background-clip: initial !important;
58
+ -webkit-text-fill-color: currentColor !important;
59
+ color: var(--bs-primary) !important;
60
+ font-family: $nf-font-display;
61
+ font-style: italic;
62
+ }
63
+
64
+ // Decorative gradient borders → hairline ink
65
+ .border-gradient-rainbow {
66
+ border-image: none !important;
67
+ border-color: var(--nf-line) !important;
68
+ }
69
+
70
+ // Glassy panels → framed paper
71
+ .bg-glassy,
72
+ .card.bg-glassy {
73
+ background: var(--bs-card-bg) !important;
74
+ backdrop-filter: none !important;
75
+ -webkit-backdrop-filter: none !important;
76
+ @include nf-border();
77
+ }
78
+
79
+ // Gradient promo/CTA cards (newsletter bands on inherited blog pages) →
80
+ // solid vermilion signup slab with ink frame + hard offset shadow.
81
+ .card.bg-gradient,
82
+ .card.bg-primary {
83
+ background-image: none !important;
84
+ background-color: var(--bs-primary) !important;
85
+ color: #fff !important;
86
+ @include nf-border();
87
+ box-shadow: var(--nf-shadow-hard-hover) !important;
88
+ }
89
+
90
+ // ============================================
91
+ // Shadow utilities → editorial pop (soft depth; frames carry the ink)
92
+ // ============================================
93
+ .shadow-sm { box-shadow: 0 8px 20px -14px rgba(23, 19, 16, 0.35) !important; }
94
+ .shadow { box-shadow: var(--nf-shadow-pop) !important; }
95
+ .shadow-lg { box-shadow: var(--nf-shadow-pop) !important; }
96
+
97
+ // Cards that ship with .border-0 (e.g. the auth card) lose the frame; give it
98
+ // back on elevated cards so the ink outline + pop shadow read as one object.
99
+ .card.border-0.shadow-sm,
100
+ .card.border-0.shadow,
101
+ .card.border-0.shadow-lg {
102
+ @include nf-border();
103
+ }
104
+
105
+ // ============================================
106
+ // Rules — hr reads as a soft editorial divider
107
+ // ============================================
108
+ hr {
109
+ border-top: 1px solid var(--nf-line);
110
+ opacity: 1;
111
+ }
112
+
113
+ // ============================================
114
+ // Footer — the ink slab that closes every page
115
+ // ============================================
116
+ // The shared footer include ships a .bg-body-secondary utility (!important,
117
+ // specificity 0,1,0) — the ink slab needs !important AND the class-qualified
118
+ // selector to beat it (a bare `footer` ties on !important and loses on
119
+ // specificity).
120
+ footer,
121
+ footer.bg-body-secondary {
122
+ background: var(--nf-panel-bg) !important;
123
+ }
124
+
125
+ footer {
126
+ color: var(--nf-panel-color);
127
+
128
+ h1, h2, h3, h4, h5, h6,
129
+ .h1, .h2, .h3, .h4, .h5, .h6 {
130
+ color: var(--nf-panel-color);
131
+ }
132
+
133
+ a {
134
+ color: var(--nf-panel-color);
135
+ opacity: 0.78;
136
+ text-decoration: none;
137
+ transition: $nf-transition;
138
+
139
+ &:hover {
140
+ opacity: 1;
141
+ color: var(--nf-volt);
142
+ }
143
+ }
144
+
145
+ // The inherited (classy) footer brand row ships an avatar + .h5 wordmark;
146
+ // render it as the masthead echo instead — wordmark-only, oversized serif —
147
+ // and keep the description to a column measure.
148
+ .h5 {
149
+ font-family: $nf-font-display;
150
+ font-size: clamp(1.6rem, 2.4vw, 2.1rem);
151
+
152
+ .avatar { display: none; }
153
+ }
154
+
155
+ p { max-width: 24em; }
156
+
157
+ // Inherited links carry .link-muted / .text-body utilities (!important
158
+ // color) — repaint with panel color so they read on the ink slab.
159
+ .text-body { color: var(--nf-panel-color) !important; }
160
+
161
+ .link-muted {
162
+ color: var(--nf-panel-color) !important;
163
+
164
+ &:hover,
165
+ &:focus {
166
+ color: var(--nf-volt) !important;
167
+ text-decoration: none;
168
+ opacity: 1;
169
+ }
170
+ }
171
+
172
+ // Muted footer text keeps panel contrast (utilities would go ink-soft)
173
+ .text-muted,
174
+ .text-body-secondary {
175
+ color: var(--nf-panel-color) !important;
176
+ opacity: 0.6;
177
+ }
178
+
179
+ // Volt uppercase column heads (the editorial footer's section labels).
180
+ // !important on weight: the inherited column heads carry .fw-semibold.
181
+ h6, .h6 {
182
+ font-family: $font-family-sans-serif;
183
+ font-size: 0.72rem;
184
+ font-weight: 800 !important;
185
+ letter-spacing: 0.14em;
186
+ text-transform: uppercase;
187
+ color: var(--nf-volt);
188
+ }
189
+
190
+ // Rules + dividers read as faint panel-color lines on the ink slab
191
+ hr,
192
+ .border-top {
193
+ border-top-color: color-mix(in srgb, var(--nf-panel-color) 18%, transparent) !important;
194
+ }
195
+
196
+ // Outline buttons (language/appearance pickers) carry the panel color — the
197
+ // global ghost-button rule paints body ink, which vanishes on the slab.
198
+ // Doubled attribute + footer scope (0,2,1) beats the global doubled rule.
199
+ [class*="btn-outline-"][class*="btn-outline-"] {
200
+ color: var(--nf-panel-color);
201
+ border-color: color-mix(in srgb, var(--nf-panel-color) 55%, transparent);
202
+ opacity: 1;
203
+ --bs-btn-hover-bg: var(--nf-panel-color);
204
+ --bs-btn-hover-color: var(--nf-panel-bg);
205
+
206
+ &:hover {
207
+ color: var(--nf-panel-bg);
208
+ background: var(--nf-panel-color);
209
+ border-color: var(--nf-panel-color);
210
+ }
211
+ }
212
+ }
213
+
214
+ // ============================================
215
+ // Alerts, accordions, modals — framed paper panels
216
+ // ============================================
217
+ .alert {
218
+ @include nf-border();
219
+ border-radius: var(--bs-border-radius-lg);
220
+ }
221
+
222
+ .accordion-item {
223
+ @include nf-border();
224
+ background: var(--bs-card-bg);
225
+
226
+ // Bootstrap strips the top border on stacked items; ours are framed
227
+ // objects, so force the full frame back on every item.
228
+ &:not(:first-of-type) {
229
+ border-top: var(--nf-border) !important;
230
+ }
231
+ }
232
+
233
+ .accordion-button {
234
+ font-family: $nf-font-display;
235
+ font-weight: 600;
236
+ background: var(--bs-card-bg);
237
+ color: var(--bs-body-color);
238
+
239
+ &:not(.collapsed) {
240
+ background: var(--nf-volt);
241
+ color: var(--nf-volt-ink);
242
+ box-shadow: none;
243
+ }
244
+
245
+ &:focus {
246
+ box-shadow: none;
247
+ border-color: var(--nf-border-color);
248
+ }
249
+ }
250
+
251
+ .modal-content,
252
+ .toast,
253
+ .offcanvas {
254
+ background: var(--bs-card-bg);
255
+ @include nf-border();
256
+ @include nf-shadow-pop();
257
+ }
258
+
259
+ // Back-to-top / floating action buttons stay on-brand
260
+ .btn-back-to-top {
261
+ border-radius: 50rem;
262
+ @include nf-border();
263
+ @include nf-shadow-hard();
264
+ }