@conduction/docusaurus-preset 0.1.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/MISSING_COMPONENTS.md +109 -0
- package/README.md +171 -0
- package/package.json +59 -0
- package/src/components/AgentTrace/AgentTrace.jsx +128 -0
- package/src/components/AgentTrace/AgentTrace.module.css +115 -0
- package/src/components/AppMock/AppMock.jsx +86 -0
- package/src/components/AppMock/AppMock.module.css +629 -0
- package/src/components/AppMock/variants/DeciDeskMock.jsx +71 -0
- package/src/components/AppMock/variants/DocuDeskMock.jsx +69 -0
- package/src/components/AppMock/variants/LarpingAppMock.jsx +59 -0
- package/src/components/AppMock/variants/MyDashBiMock.jsx +135 -0
- package/src/components/AppMock/variants/MyDashMock.jsx +96 -0
- package/src/components/AppMock/variants/MyDashTilesMock.jsx +103 -0
- package/src/components/AppMock/variants/MyDashWidgetsMock.jsx +123 -0
- package/src/components/AppMock/variants/NLDesignMock.jsx +70 -0
- package/src/components/AppMock/variants/OpenCatalogiMock.jsx +61 -0
- package/src/components/AppMock/variants/OpenConnectorMock.jsx +83 -0
- package/src/components/AppMock/variants/OpenRegisterMock.jsx +100 -0
- package/src/components/AppMock/variants/OpenWooMock.jsx +61 -0
- package/src/components/AppMock/variants/PipelinQMock.jsx +88 -0
- package/src/components/AppMock/variants/ProcestMock.jsx +87 -0
- package/src/components/AppMock/variants/SoftwareCatalogMock.jsx +71 -0
- package/src/components/AppMock/variants/ZaakAfhandelAppMock.jsx +71 -0
- package/src/components/AppsGrid/AppsGrid.jsx +84 -0
- package/src/components/AppsGrid/AppsGrid.module.css +46 -0
- package/src/components/AppsPreview/AppsPreview.jsx +85 -0
- package/src/components/AppsPreview/AppsPreview.module.css +128 -0
- package/src/components/Clients/Clients.jsx +205 -0
- package/src/components/Clients/Clients.module.css +166 -0
- package/src/components/ComposeBlock/ComposeBlock.jsx +70 -0
- package/src/components/ComposeBlock/ComposeBlock.module.css +74 -0
- package/src/components/ConductionBg/ConductionBg.jsx +150 -0
- package/src/components/ConductionBg/ConductionBg.module.css +41 -0
- package/src/components/ContentCard/ContentCard.jsx +126 -0
- package/src/components/ContentCard/ContentCard.module.css +84 -0
- package/src/components/ContentDetailHero/ContentDetailHero.jsx +136 -0
- package/src/components/ContentDetailHero/ContentDetailHero.module.css +96 -0
- package/src/components/ContentTypeFilter/ContentTypeFilter.jsx +103 -0
- package/src/components/ContentTypeFilter/ContentTypeFilter.module.css +60 -0
- package/src/components/ContentTypeFilter/contentTypes.js +58 -0
- package/src/components/CookieCli/CookieCli.jsx +223 -0
- package/src/components/CookieCli/CookieCli.module.css +166 -0
- package/src/components/CtaBanner/CtaBanner.jsx +61 -0
- package/src/components/CtaBanner/CtaBanner.module.css +65 -0
- package/src/components/DetailHero/DetailHero.jsx +143 -0
- package/src/components/DetailHero/DetailHero.module.css +154 -0
- package/src/components/Diagrams/Diagrams.jsx +148 -0
- package/src/components/EmployeeCard/EmployeeCard.jsx +127 -0
- package/src/components/EmployeeCard/EmployeeCard.module.css +144 -0
- package/src/components/ExternalAppShelf/ExternalAppShelf.jsx +61 -0
- package/src/components/ExternalAppShelf/ExternalAppShelf.module.css +90 -0
- package/src/components/FAQ/FAQ.jsx +42 -0
- package/src/components/FAQ/FAQ.module.css +74 -0
- package/src/components/FacetedFilters/FacetedFilters.jsx +125 -0
- package/src/components/FacetedFilters/FacetedFilters.module.css +133 -0
- package/src/components/FeatureGrid/FeatureGrid.jsx +94 -0
- package/src/components/FeatureGrid/FeatureGrid.module.css +114 -0
- package/src/components/FeatureList/FeatureList.jsx +54 -0
- package/src/components/FeatureList/FeatureList.module.css +52 -0
- package/src/components/FeaturedCard/FeaturedCard.jsx +101 -0
- package/src/components/FeaturedCard/FeaturedCard.module.css +98 -0
- package/src/components/GameModal/GameModal.jsx +197 -0
- package/src/components/GameModal/GameModal.module.css +184 -0
- package/src/components/Hero/Hero.jsx +101 -0
- package/src/components/Hero/Hero.module.css +95 -0
- package/src/components/HexBackground/HexBackground.jsx +56 -0
- package/src/components/HexBackground/HexBackground.module.css +73 -0
- package/src/components/HexNetwork/HexNetwork.jsx +141 -0
- package/src/components/HexNetwork/HexNetwork.module.css +187 -0
- package/src/components/HexRain/HexRain.jsx +81 -0
- package/src/components/HowSteps/HowSteps.jsx +57 -0
- package/src/components/HowSteps/HowSteps.module.css +52 -0
- package/src/components/ManagedCommonGround/ManagedCommonGround.jsx +78 -0
- package/src/components/ManagedCommonGround/ManagedCommonGround.module.css +16 -0
- package/src/components/NewsletterCta/NewsletterCta.jsx +83 -0
- package/src/components/NewsletterCta/NewsletterCta.module.css +103 -0
- package/src/components/PairCard/PairCard.jsx +58 -0
- package/src/components/PairCard/PairCard.module.css +54 -0
- package/src/components/PartnerCard/PartnerCard.jsx +130 -0
- package/src/components/PartnerCard/PartnerCard.module.css +198 -0
- package/src/components/PartnerDirectory/PartnerDirectory.jsx +122 -0
- package/src/components/PartnerDirectory/PartnerDirectory.module.css +25 -0
- package/src/components/PartnerSidecard/PartnerSidecard.jsx +116 -0
- package/src/components/PartnerSidecard/PartnerSidecard.module.css +185 -0
- package/src/components/Pipeline/Pipeline.jsx +198 -0
- package/src/components/Pipeline/Pipeline.module.css +206 -0
- package/src/components/PlatformDiagram/PlatformDiagram.jsx +110 -0
- package/src/components/PlatformOverview/PlatformOverview.jsx +68 -0
- package/src/components/PlatformOverview/PlatformOverview.module.css +71 -0
- package/src/components/ReferenceCard/ReferenceCard.jsx +44 -0
- package/src/components/ReferenceCard/ReferenceCard.module.css +57 -0
- package/src/components/RelatedPosts/RelatedPosts.jsx +58 -0
- package/src/components/RelatedPosts/RelatedPosts.module.css +51 -0
- package/src/components/RotatingCards/RotatingCards.jsx +98 -0
- package/src/components/RotatingCards/RotatingCards.module.css +153 -0
- package/src/components/Showcase/Showcase.jsx +129 -0
- package/src/components/Showcase/Showcase.module.css +168 -0
- package/src/components/SolutionCard/SolutionCard.jsx +83 -0
- package/src/components/SolutionCard/SolutionCard.module.css +99 -0
- package/src/components/StatsStrip/StatsStrip.jsx +38 -0
- package/src/components/StatsStrip/StatsStrip.module.css +53 -0
- package/src/components/WidgetShelf/WidgetShelf.jsx +67 -0
- package/src/components/WidgetShelf/WidgetShelf.module.css +73 -0
- package/src/components/index.js +96 -0
- package/src/components/primitives/AuthorByline.jsx +85 -0
- package/src/components/primitives/AuthorByline.module.css +57 -0
- package/src/components/primitives/BrandCitation.jsx +71 -0
- package/src/components/primitives/Button.jsx +46 -0
- package/src/components/primitives/Button.module.css +88 -0
- package/src/components/primitives/Card.jsx +42 -0
- package/src/components/primitives/Card.module.css +42 -0
- package/src/components/primitives/Eyebrow.jsx +37 -0
- package/src/components/primitives/Eyebrow.module.css +19 -0
- package/src/components/primitives/HexBullet.jsx +37 -0
- package/src/components/primitives/HexBullet.module.css +16 -0
- package/src/components/primitives/HexThumbnail.jsx +70 -0
- package/src/components/primitives/HexThumbnail.module.css +45 -0
- package/src/components/primitives/Pill.jsx +42 -0
- package/src/components/primitives/Pill.module.css +30 -0
- package/src/components/primitives/Section.jsx +51 -0
- package/src/components/primitives/Section.module.css +31 -0
- package/src/components/primitives/SectionHead.jsx +36 -0
- package/src/components/primitives/SectionHead.module.css +43 -0
- package/src/components/primitives/index.js +22 -0
- package/src/css/brand.css +158 -0
- package/src/css/tokens.css +12 -0
- package/src/data/app-downloads.js +42 -0
- package/src/diagrams/README.md +74 -0
- package/src/diagrams/cn-domain-tree.js +105 -0
- package/src/diagrams/cn-hex-prism.js +163 -0
- package/src/diagrams/cn-hex.js +181 -0
- package/src/diagrams/cn-honeycomb-bg.js +135 -0
- package/src/diagrams/cn-pipeline.js +150 -0
- package/src/diagrams/cn-platform.js +156 -0
- package/src/diagrams/cn-side-box.js +104 -0
- package/src/diagrams/index.js +28 -0
- package/src/index.js +183 -0
- package/src/theme/Footer/index.jsx +516 -0
- package/src/theme/MDXPage/index.jsx +134 -0
- package/src/theme/Navbar/index.jsx +120 -0
- package/src/theme/Navbar/styles.module.css +114 -0
- package/src/theme/brand.jsx +63 -0
- package/src/theme.js +45 -0
- package/src/utils/lazyScript.js +37 -0
- package/static/img/favicon.svg +14 -0
- package/static/img/honeycomb-scatter.svg +23 -0
- package/static/img/honeycomb-watermark.svg +108 -0
- package/static/img/logo-dark.svg +11 -0
- package/static/img/logo.svg +14 -0
- package/static/img/nextcloud-logo.svg +5 -0
- package/static/lib/canal-footer.css +418 -0
- package/static/lib/canal-footer.js +499 -0
- package/static/lib/clients-flow.js +317 -0
- package/static/lib/conduction-bg.css +50 -0
- package/static/lib/conduction-bg.js +122 -0
- package/static/lib/hex-rain.css +128 -0
- package/static/lib/hex-rain.js +284 -0
- package/static/lib/kade-cyclist.css +264 -0
- package/static/lib/kade-cyclist.js +420 -0
- package/static/lib/logo-memory.css +219 -0
- package/static/lib/logo-memory.js +540 -0
- package/static/lib/platform-diagram.css +458 -0
- package/static/lib/platform-diagram.js +414 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
/* Kade Cyclist: hidden minigame on the canal-footer kade strip. First
|
|
2
|
+
click on a drifting kade bike clears the ambient kade traffic, drops
|
|
3
|
+
the player cyclist under the blue Conduction house in the skyline,
|
|
4
|
+
and starts a slow-then-ramping dodge round. Hazards (bike → scooter
|
|
5
|
+
→ car → bus → truck → tram) unlock progressively as the player racks
|
|
6
|
+
up dodges, and both spawn rate + drift speed creep up.
|
|
7
|
+
|
|
8
|
+
↑/W and ↓/S switch between the top and bottom lane. Collision ends
|
|
9
|
+
the round, fires `connext:gameend` on window so the GameModal picks
|
|
10
|
+
it up, and the modal's "Play again" button fires `connext:gamereplay`
|
|
11
|
+
which we listen for to re-run the round. The ambient kade is
|
|
12
|
+
restored when the round ends. */
|
|
13
|
+
(function () {
|
|
14
|
+
const PLAYER_COLLISION_X_TOL = 24; // px tolerance around the player x
|
|
15
|
+
const HAZARD_BASE_MS = 6000; // very slow first hazards (drift duration)
|
|
16
|
+
const HAZARD_FLOOR_MS = 1900; // cap at hardest difficulty
|
|
17
|
+
const SPAWN_INITIAL_MS = 4500; // very slow first spawn interval
|
|
18
|
+
const SPAWN_FLOOR_MS = 700; // cap at hardest spawn interval
|
|
19
|
+
|
|
20
|
+
/* Difficulty tiers — kind unlocks happen at fixed dodge counts. The
|
|
21
|
+
speed/spawn multipliers grow continuously per dodge; tiers only
|
|
22
|
+
control which kinds are eligible to spawn. Vehicles get
|
|
23
|
+
progressively bigger and harder to read at speed. */
|
|
24
|
+
const KIND_TIERS = [
|
|
25
|
+
{ atDodges: 0, kinds: ['bike'] },
|
|
26
|
+
{ atDodges: 3, kinds: ['bike', 'scooter'] },
|
|
27
|
+
{ atDodges: 6, kinds: ['bike', 'scooter', 'car'] },
|
|
28
|
+
{ atDodges: 10, kinds: ['bike', 'scooter', 'car', 'bus'] },
|
|
29
|
+
{ atDodges: 15, kinds: ['bike', 'scooter', 'car', 'bus', 'truck'] },
|
|
30
|
+
{ atDodges: 22, kinds: ['bike', 'scooter', 'car', 'bus', 'truck', 'tram'] },
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
function currentKinds(dodges) {
|
|
34
|
+
let active = KIND_TIERS[0].kinds;
|
|
35
|
+
for (let i = 0; i < KIND_TIERS.length; i++) {
|
|
36
|
+
if (dodges >= KIND_TIERS[i].atDodges) active = KIND_TIERS[i].kinds;
|
|
37
|
+
}
|
|
38
|
+
return active;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); }
|
|
42
|
+
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
43
|
+
|
|
44
|
+
/* Inline SVG library. Kept in JS so the runtime doesn't depend on the
|
|
45
|
+
host page's <template> blocks. Sizes match the kade band's 16px
|
|
46
|
+
content height; viewBoxes leave a small bleed above for handlebars,
|
|
47
|
+
poles, and exhaust. */
|
|
48
|
+
function buildHazardSvg(kind) {
|
|
49
|
+
if (kind === 'bike') {
|
|
50
|
+
return (
|
|
51
|
+
'<svg class="kc-hazard-svg kc-hk-bike" width="22" height="16" viewBox="0 -2 22 18" aria-hidden="true">' +
|
|
52
|
+
'<g stroke="#0A172F" stroke-width="1.4" fill="none" stroke-linecap="round">' +
|
|
53
|
+
'<circle cx="4" cy="12" r="3"/>' +
|
|
54
|
+
'<circle cx="18" cy="12" r="3"/>' +
|
|
55
|
+
'<line x1="4" y1="12" x2="11" y2="6"/>' +
|
|
56
|
+
'<line x1="11" y1="6" x2="18" y2="12"/>' +
|
|
57
|
+
'<line x1="11" y1="6" x2="14" y2="12"/>' +
|
|
58
|
+
'<line x1="11" y1="6" x2="11" y2="3"/>' +
|
|
59
|
+
'<line x1="11" y1="3" x2="14" y2="6"/>' +
|
|
60
|
+
'</g>' +
|
|
61
|
+
'<circle cx="11" cy="1.5" r="1.6" fill="#0A172F"/>' +
|
|
62
|
+
'</svg>'
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
if (kind === 'scooter') {
|
|
66
|
+
return (
|
|
67
|
+
'<svg class="kc-hazard-svg kc-hk-scooter" width="22" height="16" viewBox="0 -2 22 18" aria-hidden="true">' +
|
|
68
|
+
'<g stroke="#3A3F4B" stroke-width="1.4" fill="none" stroke-linecap="round">' +
|
|
69
|
+
'<circle cx="5" cy="12" r="3"/>' +
|
|
70
|
+
'<circle cx="17" cy="12" r="3"/>' +
|
|
71
|
+
'<path d="M 5,12 L 11,12 L 14,5 L 17,12" stroke-width="1.6"/>' +
|
|
72
|
+
'<line x1="14" y1="5" x2="16" y2="2"/>' +
|
|
73
|
+
'<line x1="14" y1="5" x2="12" y2="2"/>' +
|
|
74
|
+
'</g>' +
|
|
75
|
+
'</svg>'
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
if (kind === 'car') {
|
|
79
|
+
return (
|
|
80
|
+
'<svg class="kc-hazard-svg kc-hk-car" width="38" height="16" viewBox="0 0 38 16" aria-hidden="true">' +
|
|
81
|
+
'<rect x="0" y="6" width="38" height="6" rx="2" fill="#0A172F"/>' +
|
|
82
|
+
'<path d="M 5,6 L 9,2 L 29,2 L 33,6 Z" fill="#0A172F"/>' +
|
|
83
|
+
'<rect x="11" y="3" width="6" height="3" fill="rgba(255,255,255,0.4)"/>' +
|
|
84
|
+
'<rect x="20" y="3" width="6" height="3" fill="rgba(255,255,255,0.4)"/>' +
|
|
85
|
+
'<circle cx="9" cy="13" r="2" fill="#C8482F"/>' +
|
|
86
|
+
'<circle cx="29" cy="13" r="2" fill="#C8482F"/>' +
|
|
87
|
+
'</svg>'
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
if (kind === 'bus') {
|
|
91
|
+
/* GVB-style city bus, side profile. Cobalt body, white windows. */
|
|
92
|
+
return (
|
|
93
|
+
'<svg class="kc-hazard-svg kc-hk-bus" width="58" height="16" viewBox="0 -1 58 17" aria-hidden="true">' +
|
|
94
|
+
'<rect x="0" y="2" width="58" height="11" rx="2" fill="#21468B"/>' +
|
|
95
|
+
'<rect x="2" y="4" width="6" height="4" fill="rgba(255,255,255,0.85)"/>' +
|
|
96
|
+
'<rect x="10" y="4" width="6" height="4" fill="rgba(255,255,255,0.85)"/>' +
|
|
97
|
+
'<rect x="18" y="4" width="6" height="4" fill="rgba(255,255,255,0.85)"/>' +
|
|
98
|
+
'<rect x="26" y="4" width="6" height="4" fill="rgba(255,255,255,0.85)"/>' +
|
|
99
|
+
'<rect x="34" y="4" width="6" height="4" fill="rgba(255,255,255,0.85)"/>' +
|
|
100
|
+
'<rect x="42" y="4" width="6" height="4" fill="rgba(255,255,255,0.85)"/>' +
|
|
101
|
+
'<rect x="50" y="4" width="6" height="4" fill="rgba(255,255,255,0.85)"/>' +
|
|
102
|
+
'<rect x="50" y="9" width="6" height="2" fill="#F77F0E"/>' +
|
|
103
|
+
'<circle cx="10" cy="13" r="2" fill="#0A172F"/>' +
|
|
104
|
+
'<circle cx="48" cy="13" r="2" fill="#0A172F"/>' +
|
|
105
|
+
'</svg>'
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
if (kind === 'truck') {
|
|
109
|
+
/* Cab + container, two sets of wheels. Cobalt cab, white box. */
|
|
110
|
+
return (
|
|
111
|
+
'<svg class="kc-hazard-svg kc-hk-truck" width="68" height="16" viewBox="0 0 68 16" aria-hidden="true">' +
|
|
112
|
+
'<rect x="0" y="3" width="42" height="9" fill="#FFFFFF" stroke="#0A172F" stroke-width="0.8"/>' +
|
|
113
|
+
'<rect x="42" y="5" width="20" height="7" fill="#21468B"/>' +
|
|
114
|
+
'<path d="M 62,5 L 66,8 L 62,8 Z" fill="#21468B"/>' +
|
|
115
|
+
'<rect x="46" y="6" width="6" height="3" fill="rgba(255,255,255,0.5)"/>' +
|
|
116
|
+
'<circle cx="8" cy="13" r="2" fill="#0A172F"/>' +
|
|
117
|
+
'<circle cx="22" cy="13" r="2" fill="#0A172F"/>' +
|
|
118
|
+
'<circle cx="50" cy="13" r="2" fill="#0A172F"/>' +
|
|
119
|
+
'<circle cx="60" cy="13" r="2" fill="#0A172F"/>' +
|
|
120
|
+
'</svg>'
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
/* tram — GVB Amsterdam, blue body with yellow band, overhead pole */
|
|
124
|
+
return (
|
|
125
|
+
'<svg class="kc-hazard-svg kc-hk-tram" width="80" height="16" viewBox="0 -3 80 19" aria-hidden="true">' +
|
|
126
|
+
'<line x1="40" y1="-3" x2="40" y2="2" stroke="#3A3F4B" stroke-width="0.8"/>' +
|
|
127
|
+
'<rect x="0" y="2" width="80" height="11" rx="1" fill="#21468B"/>' +
|
|
128
|
+
'<rect x="0" y="9" width="80" height="2" fill="#F4D04A"/>' +
|
|
129
|
+
'<rect x="2" y="4" width="5" height="4" fill="rgba(255,255,255,0.9)"/>' +
|
|
130
|
+
'<rect x="9" y="4" width="5" height="4" fill="rgba(255,255,255,0.9)"/>' +
|
|
131
|
+
'<rect x="16" y="4" width="5" height="4" fill="rgba(255,255,255,0.9)"/>' +
|
|
132
|
+
'<rect x="23" y="4" width="5" height="4" fill="rgba(255,255,255,0.9)"/>' +
|
|
133
|
+
'<rect x="32" y="4" width="5" height="4" fill="rgba(255,255,255,0.9)"/>' +
|
|
134
|
+
'<rect x="39" y="4" width="5" height="4" fill="rgba(255,255,255,0.9)"/>' +
|
|
135
|
+
'<rect x="46" y="4" width="5" height="4" fill="rgba(255,255,255,0.9)"/>' +
|
|
136
|
+
'<rect x="53" y="4" width="5" height="4" fill="rgba(255,255,255,0.9)"/>' +
|
|
137
|
+
'<rect x="60" y="4" width="5" height="4" fill="rgba(255,255,255,0.9)"/>' +
|
|
138
|
+
'<rect x="67" y="4" width="5" height="4" fill="rgba(255,255,255,0.9)"/>' +
|
|
139
|
+
'<rect x="74" y="4" width="4" height="4" fill="rgba(255,255,255,0.9)"/>' +
|
|
140
|
+
'<circle cx="14" cy="13" r="1.6" fill="#0A172F"/>' +
|
|
141
|
+
'<circle cx="22" cy="13" r="1.6" fill="#0A172F"/>' +
|
|
142
|
+
'<circle cx="58" cy="13" r="1.6" fill="#0A172F"/>' +
|
|
143
|
+
'<circle cx="66" cy="13" r="1.6" fill="#0A172F"/>' +
|
|
144
|
+
'</svg>'
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Player bike: like the ki-bike-1 outline but with an orange jersey
|
|
149
|
+
so it reads as the player tile, not ambient traffic. */
|
|
150
|
+
function buildPlayerSvg() {
|
|
151
|
+
return (
|
|
152
|
+
'<svg class="kc-player-svg" width="22" height="16" viewBox="0 -2 22 18" aria-hidden="true">' +
|
|
153
|
+
'<g stroke="#0A172F" stroke-width="1.4" fill="none" stroke-linecap="round">' +
|
|
154
|
+
'<circle cx="4" cy="12" r="3"/>' +
|
|
155
|
+
'<circle cx="18" cy="12" r="3"/>' +
|
|
156
|
+
'<line x1="4" y1="12" x2="11" y2="6"/>' +
|
|
157
|
+
'<line x1="11" y1="6" x2="18" y2="12"/>' +
|
|
158
|
+
'<line x1="11" y1="6" x2="14" y2="12"/>' +
|
|
159
|
+
'<line x1="11" y1="6" x2="11" y2="3"/>' +
|
|
160
|
+
'<line x1="11" y1="3" x2="14" y2="6"/>' +
|
|
161
|
+
'</g>' +
|
|
162
|
+
'<circle cx="11" cy="1.5" r="1.6" fill="var(--c-orange-knvb, #F77F0E)"/>' +
|
|
163
|
+
'<circle cx="4" cy="12" r="1.6" fill="var(--c-orange-knvb, #F77F0E)"/>' +
|
|
164
|
+
'<circle cx="18" cy="12" r="1.6" fill="var(--c-orange-knvb, #F77F0E)"/>' +
|
|
165
|
+
'</svg>'
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function findRoot() { return document.querySelector('.canal-footer'); }
|
|
170
|
+
|
|
171
|
+
/* Per-root state stash so the connext:gamereplay listener can find
|
|
172
|
+
the right root and tear-down can restore the ambient items. */
|
|
173
|
+
const ACTIVE = new WeakMap();
|
|
174
|
+
|
|
175
|
+
function hydrate() {
|
|
176
|
+
const root = findRoot();
|
|
177
|
+
if (!root) return;
|
|
178
|
+
if (root.dataset.kadeHydrated === '1') return;
|
|
179
|
+
const kade = root.querySelector('.kade');
|
|
180
|
+
if (!kade) return;
|
|
181
|
+
root.dataset.kadeHydrated = '1';
|
|
182
|
+
|
|
183
|
+
const onBikeClick = function (e) {
|
|
184
|
+
if (root.dataset.kadeActive === '1') return;
|
|
185
|
+
const target = e.target.closest('.ki-bike-1, .ki-bike-2');
|
|
186
|
+
if (!target || !kade.contains(target)) return;
|
|
187
|
+
e.preventDefault();
|
|
188
|
+
e.stopPropagation();
|
|
189
|
+
startGame(root, kade);
|
|
190
|
+
};
|
|
191
|
+
kade.addEventListener('click', onBikeClick, true);
|
|
192
|
+
|
|
193
|
+
/* Modal "Play again" → re-run the round. */
|
|
194
|
+
window.addEventListener('connext:gamereplay', function (e) {
|
|
195
|
+
if (e.detail && e.detail.id === 'kade-cyclist') {
|
|
196
|
+
if (root.dataset.kadeActive === '1') tearDownActive(root, kade);
|
|
197
|
+
setTimeout(function () { startGame(root, kade); }, 80);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
/* Modal "Close" → tear down the game stage and restore ambient. */
|
|
201
|
+
window.addEventListener('connext:gameclose', function (e) {
|
|
202
|
+
if (e.detail && e.detail.id === 'kade-cyclist') {
|
|
203
|
+
if (root.dataset.kadeActive === '1') tearDownActive(root, kade);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function startGame(root, kade) {
|
|
209
|
+
root.dataset.kadeActive = '1';
|
|
210
|
+
|
|
211
|
+
/* Clear the ambient kade — actually remove the items, not just
|
|
212
|
+
pause them, so the field reads as a clean playfield. We stash
|
|
213
|
+
the cleared markup on the kade-items container so tearDown can
|
|
214
|
+
restore it. */
|
|
215
|
+
const kadeItems = kade.querySelector('.kade-items');
|
|
216
|
+
const ambientHTML = kadeItems ? kadeItems.innerHTML : '';
|
|
217
|
+
if (kadeItems) kadeItems.innerHTML = '';
|
|
218
|
+
|
|
219
|
+
/* Compute player x: align under the blue Conduction house in the
|
|
220
|
+
skyline. The skyline is randomised at every page load by canal-
|
|
221
|
+
footer.js but always marks the centre house with .house-
|
|
222
|
+
conduction. Fall back to 14% of viewport if the marker isn't
|
|
223
|
+
present yet. */
|
|
224
|
+
const blueHouse = root.querySelector('.house-conduction');
|
|
225
|
+
const rootRect = root.getBoundingClientRect();
|
|
226
|
+
let playerCenterX;
|
|
227
|
+
if (blueHouse) {
|
|
228
|
+
const r = blueHouse.getBoundingClientRect();
|
|
229
|
+
playerCenterX = (r.left + r.width / 2) - rootRect.left;
|
|
230
|
+
} else {
|
|
231
|
+
playerCenterX = rootRect.width * 0.14 + 11;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const stage = document.createElement('div');
|
|
235
|
+
stage.className = 'kc-stage';
|
|
236
|
+
stage.innerHTML =
|
|
237
|
+
'<div class="kc-track" data-track>' +
|
|
238
|
+
'<div class="kc-lane kc-lane-top" data-lane="top"></div>' +
|
|
239
|
+
'<div class="kc-lane kc-lane-bottom" data-lane="bottom"></div>' +
|
|
240
|
+
'<div class="kc-player" data-player>' + buildPlayerSvg() + '</div>' +
|
|
241
|
+
'</div>' +
|
|
242
|
+
'<div class="kc-hud">' +
|
|
243
|
+
'<div class="kc-score-block"><span class="kc-score-num" data-score>0</span><span class="kc-score-label">Score</span></div>' +
|
|
244
|
+
'<div class="kc-controls" aria-hidden="true">' +
|
|
245
|
+
'<kbd>↑</kbd>/<kbd>W</kbd> · <kbd>↓</kbd>/<kbd>S</kbd>' +
|
|
246
|
+
'</div>' +
|
|
247
|
+
'<button type="button" class="kc-close" data-close aria-label="Stop spel">×</button>' +
|
|
248
|
+
'</div>';
|
|
249
|
+
kade.appendChild(stage);
|
|
250
|
+
|
|
251
|
+
const trackEl = stage.querySelector('[data-track]');
|
|
252
|
+
const playerEl = stage.querySelector('[data-player]');
|
|
253
|
+
const scoreSpan = stage.querySelector('[data-score]');
|
|
254
|
+
const closeBtn = stage.querySelector('[data-close]');
|
|
255
|
+
|
|
256
|
+
/* Position the player centred on the blue-house x. CSS has the
|
|
257
|
+
player at left: var(--kc-player-x). */
|
|
258
|
+
stage.style.setProperty('--kc-player-x', (playerCenterX - 11) + 'px');
|
|
259
|
+
|
|
260
|
+
let lane = 'bottom';
|
|
261
|
+
playerEl.dataset.lane = lane;
|
|
262
|
+
|
|
263
|
+
let score = 0;
|
|
264
|
+
let speedMul = 1;
|
|
265
|
+
let spawnTimer = null;
|
|
266
|
+
let rafId = null;
|
|
267
|
+
let over = false;
|
|
268
|
+
const hazards = [];
|
|
269
|
+
const startTime = performance.now();
|
|
270
|
+
|
|
271
|
+
function setLane(next) {
|
|
272
|
+
if (over) return;
|
|
273
|
+
if (lane === next) return;
|
|
274
|
+
lane = next;
|
|
275
|
+
playerEl.dataset.lane = next;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function spawnHazard() {
|
|
279
|
+
if (over) return;
|
|
280
|
+
/* Pick a kind from the currently-unlocked tier — heavier
|
|
281
|
+
vehicles are still possible at low scores, but only the
|
|
282
|
+
starter set. */
|
|
283
|
+
const kinds = currentKinds(score);
|
|
284
|
+
const kind = kinds[(Math.random() * kinds.length) | 0];
|
|
285
|
+
const hazardLane = Math.random() < 0.5 ? 'top' : 'bottom';
|
|
286
|
+
const hazard = document.createElement('div');
|
|
287
|
+
hazard.className = 'kc-hazard';
|
|
288
|
+
hazard.dataset.lane = hazardLane;
|
|
289
|
+
hazard.dataset.kind = kind;
|
|
290
|
+
hazard.innerHTML = buildHazardSvg(kind);
|
|
291
|
+
const dur = clamp(HAZARD_BASE_MS / speedMul, HAZARD_FLOOR_MS, HAZARD_BASE_MS);
|
|
292
|
+
hazard.style.animationDuration = dur + 'ms';
|
|
293
|
+
trackEl.appendChild(hazard);
|
|
294
|
+
const entry = {
|
|
295
|
+
el: hazard, lane: hazardLane, kind: kind,
|
|
296
|
+
startedAt: performance.now(), durationMs: dur, scored: false,
|
|
297
|
+
};
|
|
298
|
+
hazards.push(entry);
|
|
299
|
+
hazard.addEventListener('animationend', function () {
|
|
300
|
+
if (!entry.scored && !over) {
|
|
301
|
+
entry.scored = true;
|
|
302
|
+
score++;
|
|
303
|
+
scoreSpan.textContent = String(score);
|
|
304
|
+
/* Smooth ramp: per dodge bump speed and spawn rate. The
|
|
305
|
+
clamp in spawn / drift duration formulas caps it. */
|
|
306
|
+
speedMul = clamp(speedMul + 0.05, 1, 2.6);
|
|
307
|
+
}
|
|
308
|
+
hazard.remove();
|
|
309
|
+
const idx = hazards.indexOf(entry);
|
|
310
|
+
if (idx >= 0) hazards.splice(idx, 1);
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function scheduleSpawn() {
|
|
315
|
+
if (over) return;
|
|
316
|
+
const interval = clamp(SPAWN_INITIAL_MS / speedMul, SPAWN_FLOOR_MS, SPAWN_INITIAL_MS);
|
|
317
|
+
spawnTimer = setTimeout(function () {
|
|
318
|
+
spawnHazard();
|
|
319
|
+
scheduleSpawn();
|
|
320
|
+
}, interval);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function checkCollisions() {
|
|
324
|
+
if (over) return;
|
|
325
|
+
const trackRect = trackEl.getBoundingClientRect();
|
|
326
|
+
const playerRect = playerEl.getBoundingClientRect();
|
|
327
|
+
const playerCx = playerRect.left + playerRect.width / 2 - trackRect.left;
|
|
328
|
+
for (let i = 0; i < hazards.length; i++) {
|
|
329
|
+
const h = hazards[i];
|
|
330
|
+
if (h.scored) continue;
|
|
331
|
+
if (h.lane !== lane) continue;
|
|
332
|
+
const hr = h.el.getBoundingClientRect();
|
|
333
|
+
const hazardCx = hr.left + hr.width / 2 - trackRect.left;
|
|
334
|
+
/* Use the hazard's actual width as the X tolerance so a tram
|
|
335
|
+
is harder to dodge late than a bike — generous gameplay
|
|
336
|
+
while still rewarding swap timing. */
|
|
337
|
+
const tol = Math.max(PLAYER_COLLISION_X_TOL, hr.width / 2 + 6);
|
|
338
|
+
if (Math.abs(hazardCx - playerCx) < tol) {
|
|
339
|
+
gameOver();
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
rafId = requestAnimationFrame(checkCollisions);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function gameOver() {
|
|
347
|
+
if (over) return;
|
|
348
|
+
over = true;
|
|
349
|
+
if (spawnTimer) { clearTimeout(spawnTimer); spawnTimer = null; }
|
|
350
|
+
if (rafId) { cancelAnimationFrame(rafId); rafId = null; }
|
|
351
|
+
hazards.forEach(function (h) { h.el.style.animationPlayState = 'paused'; });
|
|
352
|
+
playerEl.classList.add('crashed');
|
|
353
|
+
const elapsedSec = Math.round((performance.now() - startTime) / 1000);
|
|
354
|
+
/* Fire on window so the GameModal picks it up. The modal handles
|
|
355
|
+
the win/loss copy + replay button. */
|
|
356
|
+
window.dispatchEvent(new CustomEvent('connext:gameend', {
|
|
357
|
+
detail: {
|
|
358
|
+
id: 'kade-cyclist',
|
|
359
|
+
won: false,
|
|
360
|
+
score: score,
|
|
361
|
+
summary: score + ' dodges · ' + elapsedSec + 's',
|
|
362
|
+
title: 'Bots.',
|
|
363
|
+
subtitle: 'You crashed into a vehicle on the kade. Try again — start slow, watch the lane.',
|
|
364
|
+
}
|
|
365
|
+
}));
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function tearDown() {
|
|
369
|
+
tearDownActive(root, kade);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function onKey(e) {
|
|
373
|
+
if (e.key === 'Escape') { tearDown(); return; }
|
|
374
|
+
if (over) return;
|
|
375
|
+
if (e.key === 'ArrowUp' || e.key === 'w' || e.key === 'W') { setLane('top'); e.preventDefault(); }
|
|
376
|
+
else if (e.key === 'ArrowDown' || e.key === 's' || e.key === 'S') { setLane('bottom'); e.preventDefault(); }
|
|
377
|
+
}
|
|
378
|
+
document.addEventListener('keydown', onKey);
|
|
379
|
+
closeBtn.addEventListener('click', tearDown);
|
|
380
|
+
|
|
381
|
+
ACTIVE.set(root, {
|
|
382
|
+
ambientHTML: ambientHTML,
|
|
383
|
+
stage: stage,
|
|
384
|
+
onKey: onKey,
|
|
385
|
+
cleanup: function () {
|
|
386
|
+
if (spawnTimer) clearTimeout(spawnTimer);
|
|
387
|
+
if (rafId) cancelAnimationFrame(rafId);
|
|
388
|
+
over = true;
|
|
389
|
+
},
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
if (reduceMotion) speedMul = 1.4;
|
|
393
|
+
|
|
394
|
+
/* Start the round. */
|
|
395
|
+
scheduleSpawn();
|
|
396
|
+
rafId = requestAnimationFrame(checkCollisions);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function tearDownActive(root, kade) {
|
|
400
|
+
const state = ACTIVE.get(root);
|
|
401
|
+
if (!state) return;
|
|
402
|
+
if (state.cleanup) state.cleanup();
|
|
403
|
+
if (state.onKey) document.removeEventListener('keydown', state.onKey);
|
|
404
|
+
if (state.stage && state.stage.parentNode) state.stage.parentNode.removeChild(state.stage);
|
|
405
|
+
/* Restore the ambient kade items. */
|
|
406
|
+
const kadeItems = kade.querySelector('.kade-items');
|
|
407
|
+
if (kadeItems && state.ambientHTML) kadeItems.innerHTML = state.ambientHTML;
|
|
408
|
+
delete root.dataset.kadeActive;
|
|
409
|
+
ACTIVE.delete(root);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
window.KadeCyclist = window.KadeCyclist || {};
|
|
413
|
+
window.KadeCyclist.hydrate = hydrate;
|
|
414
|
+
|
|
415
|
+
if (document.readyState === 'loading') {
|
|
416
|
+
document.addEventListener('DOMContentLoaded', hydrate);
|
|
417
|
+
} else {
|
|
418
|
+
hydrate();
|
|
419
|
+
}
|
|
420
|
+
})();
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/* Logo Memory in-place styles. Pairs with logo-memory.js.
|
|
2
|
+
The runtime uses the actual <a class="hex"> anchors that already
|
|
3
|
+
render in the marquee — no overlay div, no separately-built tiles.
|
|
4
|
+
This CSS handles the visual phases:
|
|
5
|
+
1. .marquee.lm-active pause tracks + restore colour
|
|
6
|
+
2. .marquee.lm-overflow let the 6-row cluster extend below
|
|
7
|
+
the marquee's original 3-row band
|
|
8
|
+
3. .lm-tile / .lm-flipper 3D rotateY flip (front=logo, back=
|
|
9
|
+
cobalt + Conduction C-in-hex)
|
|
10
|
+
4. .lm-back-up flipped to back (the face-down state
|
|
11
|
+
before the player reveals)
|
|
12
|
+
5. .lm-matched orange ring + pulse on a matched pair
|
|
13
|
+
6. .lm-hud score pill above the cluster
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/* ============ Phase 1+2: pause + colourise + overflow ============ */
|
|
17
|
+
|
|
18
|
+
.marquee.lm-active .track,
|
|
19
|
+
[data-memory-marquee].lm-active .track,
|
|
20
|
+
[data-memory-marquee].lm-active > div > div {
|
|
21
|
+
animation-play-state: paused !important;
|
|
22
|
+
}
|
|
23
|
+
[data-memory-marquee].lm-active a img {
|
|
24
|
+
filter: none !important;
|
|
25
|
+
opacity: 1 !important;
|
|
26
|
+
transition: filter 220ms ease, opacity 220ms ease;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.marquee.lm-overflow,
|
|
30
|
+
[data-memory-marquee].lm-overflow {
|
|
31
|
+
overflow: visible !important;
|
|
32
|
+
-webkit-mask-image: none !important;
|
|
33
|
+
mask-image: none !important;
|
|
34
|
+
min-height: 760px;
|
|
35
|
+
position: relative;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/* ============ Phase 3: tile flip structure ============ */
|
|
39
|
+
|
|
40
|
+
.lm-tile {
|
|
41
|
+
perspective: 900px;
|
|
42
|
+
cursor: pointer;
|
|
43
|
+
}
|
|
44
|
+
/* The runtime moves the original img into .lm-front during enhance,
|
|
45
|
+
so the tile's only direct children are the .lm-flipper. The hex's
|
|
46
|
+
::before (cobalt-50 ring) + ::after (white inner) still render
|
|
47
|
+
behind the flipper as the resting frame. */
|
|
48
|
+
|
|
49
|
+
.lm-flipper {
|
|
50
|
+
position: absolute;
|
|
51
|
+
inset: 0;
|
|
52
|
+
transform-style: preserve-3d;
|
|
53
|
+
transition: transform 600ms cubic-bezier(0.65, 0, 0.35, 1);
|
|
54
|
+
z-index: 3;
|
|
55
|
+
}
|
|
56
|
+
.lm-tile.lm-back-up .lm-flipper {
|
|
57
|
+
transform: rotateY(180deg);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.lm-face {
|
|
61
|
+
position: absolute;
|
|
62
|
+
inset: 0;
|
|
63
|
+
display: grid;
|
|
64
|
+
place-items: center;
|
|
65
|
+
-webkit-backface-visibility: hidden;
|
|
66
|
+
backface-visibility: hidden;
|
|
67
|
+
clip-path: var(--hex-pointy-top, polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Front: transparent so the existing .hex::before (cobalt-50 ring) +
|
|
71
|
+
.hex::after (white interior) keep showing through. The img sits on
|
|
72
|
+
top. */
|
|
73
|
+
.lm-front {
|
|
74
|
+
background: transparent;
|
|
75
|
+
}
|
|
76
|
+
.lm-front img {
|
|
77
|
+
max-width: 60%;
|
|
78
|
+
max-height: 50%;
|
|
79
|
+
object-fit: contain;
|
|
80
|
+
filter: none;
|
|
81
|
+
opacity: 1;
|
|
82
|
+
transition: opacity 220ms ease;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* Back: solid brand cobalt with the Conduction C-in-hex avatar. No
|
|
86
|
+
inset frame — the hex clip-path of .lm-face already shapes the
|
|
87
|
+
tile, and a darker outer ring read as a black border. */
|
|
88
|
+
.lm-back {
|
|
89
|
+
transform: rotateY(180deg);
|
|
90
|
+
background: var(--c-cobalt-500, #21468B);
|
|
91
|
+
}
|
|
92
|
+
.lm-back-svg {
|
|
93
|
+
width: 56%;
|
|
94
|
+
height: 56%;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* ============ Matched state ============ */
|
|
98
|
+
|
|
99
|
+
/* Replace the cobalt-50 outer ring with KNVB orange when matched.
|
|
100
|
+
Inner white (::after, 1.5px inset) still covers the interior, so
|
|
101
|
+
the visible orange is just the rim — the same shape as the rest
|
|
102
|
+
of the hex pattern, no layout shift. */
|
|
103
|
+
.lm-tile.lm-matched::before {
|
|
104
|
+
background: var(--c-orange-knvb, #F77F0E) !important;
|
|
105
|
+
}
|
|
106
|
+
.lm-tile.lm-matched {
|
|
107
|
+
animation: lmMatchPulse 700ms cubic-bezier(0.34, 1.56, 0.64, 1) 1;
|
|
108
|
+
}
|
|
109
|
+
@keyframes lmMatchPulse {
|
|
110
|
+
0% { transform: translateY(0) scale(1); }
|
|
111
|
+
35% { transform: translateY(0) scale(1.08); }
|
|
112
|
+
100% { transform: translateY(0) scale(1); }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* Hover hint while face-down: NEVER touch the flipper's transform.
|
|
116
|
+
Animating between rotateY(180deg) and rotateY(180deg) <translate>
|
|
117
|
+
triggers quaternion-decomposition ambiguity at the 180° pole, and
|
|
118
|
+
browsers sometimes interpolate through rotateY(0) — which would
|
|
119
|
+
reveal the front face on hover and let the player cheat the game.
|
|
120
|
+
Use filter only; the tile is only "pushed" face-up via the click
|
|
121
|
+
handler, never via hover or focus. */
|
|
122
|
+
.lm-tile.lm-back-up:not(.lm-matched):hover {
|
|
123
|
+
filter: drop-shadow(0 4px 8px rgba(33, 70, 139, 0.28));
|
|
124
|
+
}
|
|
125
|
+
.lm-tile.lm-back-up:focus-visible {
|
|
126
|
+
outline: 2px solid var(--c-orange-knvb, #F77F0E);
|
|
127
|
+
outline-offset: 4px;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* ============ Phase 6: HUD ============ */
|
|
131
|
+
|
|
132
|
+
.lm-hud {
|
|
133
|
+
position: absolute;
|
|
134
|
+
top: 12px;
|
|
135
|
+
left: 50%;
|
|
136
|
+
transform: translateX(-50%);
|
|
137
|
+
display: flex;
|
|
138
|
+
align-items: center;
|
|
139
|
+
gap: 10px;
|
|
140
|
+
z-index: 20;
|
|
141
|
+
font-family: var(--conduction-typography-font-family-body, 'Figtree', system-ui, sans-serif);
|
|
142
|
+
pointer-events: auto;
|
|
143
|
+
animation: lmHudIn 360ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
|
|
144
|
+
}
|
|
145
|
+
@keyframes lmHudIn {
|
|
146
|
+
from { opacity: 0; transform: translateX(-50%) translateY(-12px); }
|
|
147
|
+
to { opacity: 1; transform: translateX(-50%) translateY(0); }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.lm-counter,
|
|
151
|
+
.lm-moves {
|
|
152
|
+
background: var(--c-cobalt-900, #0A172F);
|
|
153
|
+
color: white;
|
|
154
|
+
padding: 8px 16px;
|
|
155
|
+
border-radius: 999px;
|
|
156
|
+
font-size: 13px;
|
|
157
|
+
font-weight: 600;
|
|
158
|
+
letter-spacing: 0.02em;
|
|
159
|
+
}
|
|
160
|
+
.lm-counter [data-matched] {
|
|
161
|
+
color: var(--c-orange-knvb, #F77F0E);
|
|
162
|
+
font-weight: 700;
|
|
163
|
+
margin-right: 2px;
|
|
164
|
+
}
|
|
165
|
+
.lm-moves [data-moves] {
|
|
166
|
+
font-weight: 700;
|
|
167
|
+
margin-right: 2px;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.lm-close {
|
|
171
|
+
width: 36px;
|
|
172
|
+
height: 36px;
|
|
173
|
+
border-radius: 50%;
|
|
174
|
+
border: 1px solid var(--c-cobalt-900, #0A172F);
|
|
175
|
+
background: white;
|
|
176
|
+
color: var(--c-cobalt-900, #0A172F);
|
|
177
|
+
font-size: 22px;
|
|
178
|
+
line-height: 1;
|
|
179
|
+
cursor: pointer;
|
|
180
|
+
display: grid;
|
|
181
|
+
place-items: center;
|
|
182
|
+
font-family: inherit;
|
|
183
|
+
transition: background 150ms ease, color 150ms ease, transform 150ms ease;
|
|
184
|
+
}
|
|
185
|
+
.lm-close:hover,
|
|
186
|
+
.lm-close:focus-visible {
|
|
187
|
+
background: var(--c-cobalt-900, #0A172F);
|
|
188
|
+
color: white;
|
|
189
|
+
transform: scale(1.05);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* ============ Reduced motion ============ */
|
|
193
|
+
|
|
194
|
+
@media (prefers-reduced-motion: reduce) {
|
|
195
|
+
.lm-flipper,
|
|
196
|
+
.lm-tile.lm-matched,
|
|
197
|
+
.lm-hud,
|
|
198
|
+
.marquee.lm-active .hexLogo,
|
|
199
|
+
.marquee.lm-active a.hex img,
|
|
200
|
+
[data-memory-marquee].lm-active .hexLogo,
|
|
201
|
+
[data-memory-marquee].lm-active a.hex img {
|
|
202
|
+
transition: none !important;
|
|
203
|
+
animation: none !important;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* ============ Responsive: keep the cluster legible on small screens ============ */
|
|
208
|
+
|
|
209
|
+
@media (max-width: 720px) {
|
|
210
|
+
.marquee.lm-overflow,
|
|
211
|
+
[data-memory-marquee].lm-overflow {
|
|
212
|
+
min-height: 600px;
|
|
213
|
+
}
|
|
214
|
+
.lm-counter,
|
|
215
|
+
.lm-moves {
|
|
216
|
+
padding: 6px 12px;
|
|
217
|
+
font-size: 12px;
|
|
218
|
+
}
|
|
219
|
+
}
|