@gemboss/website-ui 0.8.0 → 0.10.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/AGENTS.md +8 -3
- package/CHANGELOG.md +110 -0
- package/README.md +5 -5
- package/bin/lib/check.mjs +19 -0
- package/css/effects.css +71 -0
- package/css/footer.css +74 -58
- package/css/header.css +12 -0
- package/css/tokens.css +9 -2
- package/demo/footer.html +1 -1
- package/demo/index.html +1 -0
- package/demo/spotlight.html +291 -0
- package/docs/components.md +62 -10
- package/docs/markup-contract.md +45 -7
- package/docs/theming.md +6 -0
- package/js/website.js +54 -4
- package/js/website.mjs +70 -7
- package/package.json +1 -1
- package/skill/SKILL.md +2 -0
- package/system.json +16 -4
- package/templates/footer.html +11 -9
- package/website.css +168 -61
- package/website.min.css +1 -1
package/website.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @gemboss/website-ui 0.
|
|
1
|
+
/*! @gemboss/website-ui 0.10.0 | generated by scripts/build.mjs from the files in css/. Edit those and not this file */
|
|
2
2
|
/* @gemboss/website-ui tokens. GENERATED from system.json by scripts/build.mjs: do not edit by hand. */
|
|
3
3
|
:root {
|
|
4
4
|
color-scheme: light;
|
|
@@ -70,6 +70,13 @@
|
|
|
70
70
|
--gw-ease: cubic-bezier(.22,1,.36,1);
|
|
71
71
|
--gw-reveal-ms: 700ms;
|
|
72
72
|
--gw-spin-ms: 700ms;
|
|
73
|
+
--gw-spot-radius: 260px;
|
|
74
|
+
--gw-spot-radius-action: 120px;
|
|
75
|
+
--gw-spot-strength: 22%;
|
|
76
|
+
--gw-spot-strength-action: 38%;
|
|
77
|
+
--gw-spot-strength-stage: 40%;
|
|
78
|
+
--gw-spot-ms: 550ms;
|
|
79
|
+
--gw-spot-ease: cubic-bezier(.16,1,.3,1);
|
|
73
80
|
--gw-wash-pink: #ff6f5e;
|
|
74
81
|
--gw-wash-violet: #7c8af7;
|
|
75
82
|
--gw-wash-sky: #43b0ff;
|
|
@@ -88,7 +95,7 @@
|
|
|
88
95
|
--gw-surface: #ffffff;
|
|
89
96
|
--gw-ink: #1b1c1e;
|
|
90
97
|
--gw-ink-2: #3b3d42;
|
|
91
|
-
--gw-muted: #
|
|
98
|
+
--gw-muted: #6a6d73;
|
|
92
99
|
--gw-line: #e5e7ea;
|
|
93
100
|
--gw-line-strong: #d5d8dd;
|
|
94
101
|
--gw-line-control: #85888e;
|
|
@@ -180,7 +187,7 @@
|
|
|
180
187
|
--gw-surface: #ffffff;
|
|
181
188
|
--gw-ink: #1b1c1e;
|
|
182
189
|
--gw-ink-2: #3b3d42;
|
|
183
|
-
--gw-muted: #
|
|
190
|
+
--gw-muted: #6a6d73;
|
|
184
191
|
--gw-line: #e5e7ea;
|
|
185
192
|
--gw-line-strong: #d5d8dd;
|
|
186
193
|
--gw-line-control: #85888e;
|
|
@@ -1215,22 +1222,38 @@ html.is-menu-open .gw-header:has(> .gw-mobile-menu.is-drill) { position: sticky;
|
|
|
1215
1222
|
.gw-header__actions > .gw-lang { display: block; }
|
|
1216
1223
|
}
|
|
1217
1224
|
|
|
1225
|
+
/* ---- 0.10.0: the theme toggle's two icons ---------------------------------------------------------------------------
|
|
1226
|
+
A toggle may hold two icons: .gw-theme-toggle__moon, shown in light, and .gw-theme-toggle__sun, shown in dark, as
|
|
1227
|
+
gemboss.ai draws them. The theme in force picks one: data-theme on <html>, else the system's. A toggle with one plain
|
|
1228
|
+
<svg> stays as it is. */
|
|
1229
|
+
.gw-theme-toggle__sun { display: none; }
|
|
1230
|
+
:root[data-theme="dark"] .gw-theme-toggle__sun { display: block; }
|
|
1231
|
+
:root[data-theme="dark"] .gw-theme-toggle__moon { display: none; }
|
|
1232
|
+
@media (prefers-color-scheme: dark) {
|
|
1233
|
+
:root:not([data-theme="light"]) .gw-theme-toggle__sun { display: block; }
|
|
1234
|
+
:root:not([data-theme="light"]) .gw-theme-toggle__moon { display: none; }
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1218
1237
|
/* footer: brand block, link columns, bottom row. One hairline on top, white like the page. */
|
|
1219
1238
|
.gw-footer {
|
|
1220
|
-
padding-block: 64px
|
|
1239
|
+
padding-block: 64px 32px;
|
|
1221
1240
|
border-top: 1px solid var(--gw-line);
|
|
1222
|
-
background:
|
|
1241
|
+
background: transparent;
|
|
1223
1242
|
color: var(--gw-ink);
|
|
1224
1243
|
font-size: 14px;
|
|
1225
1244
|
}
|
|
1226
1245
|
.gw-footer__grid {
|
|
1227
1246
|
display: grid;
|
|
1228
1247
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
1229
|
-
gap: 32px
|
|
1230
|
-
}
|
|
1231
|
-
.gw-footer__brand {
|
|
1232
|
-
.gw-footer__brand
|
|
1233
|
-
.gw-
|
|
1248
|
+
gap: 32px;
|
|
1249
|
+
}
|
|
1250
|
+
.gw-footer__brand p { max-width: 30ch; margin-top: 16px; color: var(--gw-muted); line-height: 22px; }
|
|
1251
|
+
.gw-footer__brand .gw-brand { display: flex; min-height: 34px; font-family: var(--gw-font); line-height: 1.6; }
|
|
1252
|
+
.gw-footer__brand .gw-brand__mark { width: 34px; height: 34px; border-radius: var(--gw-radius-8); }
|
|
1253
|
+
/* the brand's name, the column headings and the wordmark are set in the kit's face (--gw-font), whatever the page
|
|
1254
|
+
does to its body or its h2 (a page in Chinese puts PingFang first; gemboss.ai keeps Inter for these three); a
|
|
1255
|
+
heading is balanced, like gemboss.ai's main pages */
|
|
1256
|
+
.gw-footer__heading { margin: 0 0 12px; color: var(--gw-ink); font-family: var(--gw-font); font-size: 13px; font-weight: 600; letter-spacing: 0; line-height: 1.6; text-wrap: balance; }
|
|
1234
1257
|
/* an optional newsletter form in the brand block (0.6.0): label, the inline row (field and button), a note on what the
|
|
1235
1258
|
reader agrees to; only when the form is wired to a list that keeps the address */
|
|
1236
1259
|
.gw-footer__newsletter { margin-top: 24px; }
|
|
@@ -1243,33 +1266,35 @@ html.is-menu-open .gw-header:has(> .gw-mobile-menu.is-drill) { position: sticky;
|
|
|
1243
1266
|
flex-wrap: wrap;
|
|
1244
1267
|
align-items: center;
|
|
1245
1268
|
justify-content: space-between;
|
|
1246
|
-
gap:
|
|
1247
|
-
margin-top:
|
|
1269
|
+
gap: 12px 24px;
|
|
1270
|
+
margin-top: 32px;
|
|
1248
1271
|
padding-top: 24px;
|
|
1249
1272
|
border-top: 1px solid var(--gw-line);
|
|
1250
1273
|
color: var(--gw-muted);
|
|
1251
1274
|
font-size: 13px;
|
|
1252
1275
|
}
|
|
1253
1276
|
.gw-footer__bottom:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
|
|
1254
|
-
.gw-footer__bottom ul { display: flex; flex-wrap: wrap; gap:
|
|
1277
|
+
.gw-footer__bottom ul { display: flex; flex-wrap: wrap; gap: 16px; margin: 0; padding: 0; list-style: none; }
|
|
1255
1278
|
.gw-footer__bottom a {
|
|
1256
1279
|
display: inline-flex;
|
|
1257
1280
|
align-items: center;
|
|
1258
1281
|
justify-content: center;
|
|
1259
|
-
min-width: 24px;
|
|
1260
|
-
min-height: 24px;
|
|
1261
1282
|
color: var(--gw-muted);
|
|
1262
1283
|
}
|
|
1263
1284
|
.gw-footer__bottom a:hover { color: var(--gw-ink); }
|
|
1264
|
-
@media (
|
|
1265
|
-
.gw-footer__grid {
|
|
1285
|
+
@media (max-width: 620px) {
|
|
1286
|
+
.gw-footer__grid { gap: 24px 20px; }
|
|
1287
|
+
.gw-footer__brand { grid-column: 1 / -1; }
|
|
1266
1288
|
}
|
|
1267
|
-
@media (min-width:
|
|
1268
|
-
.gw-footer__grid { grid-auto-flow: column; grid-template-columns: minmax(0, 1.6fr); grid-auto-columns: minmax(0, 1fr); }
|
|
1269
|
-
.gw-footer__brand { grid-column: auto; }
|
|
1289
|
+
@media (min-width: 1081px) {
|
|
1290
|
+
.gw-footer__grid { grid-auto-flow: column; grid-template-columns: minmax(0, 1.6fr); grid-auto-columns: minmax(0, 1fr); gap: 32px 24px; }
|
|
1270
1291
|
}
|
|
1271
1292
|
@media (max-width: 768px), (pointer: coarse) {
|
|
1293
|
+
.gw-footer__brand .gw-brand { min-height: var(--gw-tap-min); }
|
|
1294
|
+
.gw-footer__col a { display: flex; align-items: center; min-height: var(--gw-tap-min); padding-block: 0; }
|
|
1272
1295
|
.gw-footer__bottom a { min-height: var(--gw-tap-min); }
|
|
1296
|
+
.gw-footer__bottom ul:last-child a { min-width: var(--gw-tap-min); }
|
|
1297
|
+
.gw-footer__bottom ul:not(:last-child) { gap: 8px 16px; }
|
|
1273
1298
|
}
|
|
1274
1299
|
|
|
1275
1300
|
/* ---- 0.7.0: the footer as gemboss.ai has it (issue #71) ---------------------------------------------------------------
|
|
@@ -1283,16 +1308,14 @@ html.is-menu-open .gw-header:has(> .gw-mobile-menu.is-drill) { position: sticky;
|
|
|
1283
1308
|
touch. */
|
|
1284
1309
|
.gw-footer__ask { margin-top: 24px; }
|
|
1285
1310
|
/* the row may run wider than the brand's sentence (four names on one line at 390px) */
|
|
1286
|
-
.gw-
|
|
1287
|
-
.gw-footer__brand:has(> .gw-footer__ask) > :not(.gw-footer__ask) { max-width: 320px; }
|
|
1288
|
-
.gw-footer__ask > .gw-footer__ask-label { margin: 0; color: var(--gw-muted); font-size: 13px; }
|
|
1311
|
+
.gw-footer__ask > .gw-footer__ask-label { margin: 0; color: var(--gw-muted); font-size: 13px; line-height: 22px; }
|
|
1289
1312
|
.gw-footer__ask > ul { display: flex; flex-wrap: wrap; gap: 0 16px; margin: 4px 0 0; padding: 0; list-style: none; }
|
|
1290
1313
|
.gw-footer__ask a { display: block; padding-block: 4px; color: var(--gw-ink); line-height: 20px; }
|
|
1291
|
-
.gw-footer__ask a:hover { text-decoration: underline;
|
|
1314
|
+
.gw-footer__ask a:hover { text-decoration: underline; }
|
|
1292
1315
|
|
|
1293
1316
|
/* the mark after a link that leaves the site (↗, drawn), aria-hidden; the words "(opens in a new tab)" are a separate
|
|
1294
1317
|
.gw-visually-hidden span, only when the link has target="_blank" */
|
|
1295
|
-
.gw-footer__ext {
|
|
1318
|
+
.gw-footer__ext:empty {
|
|
1296
1319
|
display: inline-block;
|
|
1297
1320
|
width: 12px;
|
|
1298
1321
|
height: 12px;
|
|
@@ -1302,6 +1325,7 @@ html.is-menu-open .gw-header:has(> .gw-mobile-menu.is-drill) { position: sticky;
|
|
|
1302
1325
|
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3.5 8.5l5-5M4.5 3.5h4v4' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 12px no-repeat;
|
|
1303
1326
|
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M3.5 8.5l5-5M4.5 3.5h4v4' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / 12px no-repeat;
|
|
1304
1327
|
}
|
|
1328
|
+
.gw-footer__ext:not(:empty) { color: var(--gw-muted); font-weight: 400; }
|
|
1305
1329
|
|
|
1306
1330
|
/* Folds: <details class="gw-footer__fold"> inside a .gw-footer__col (or a .gw-footer__family), its <summary> holding the
|
|
1307
1331
|
.gw-footer__heading, then the <ul>. Below 768px each is a 52px row with a chevron, folded unless the markup says
|
|
@@ -1310,48 +1334,52 @@ html.is-menu-open .gw-header:has(> .gw-mobile-menu.is-drill) { position: sticky;
|
|
|
1310
1334
|
(::details-content; website.js also sets `open` there, for browsers without it). */
|
|
1311
1335
|
.gw-footer__fold > summary { list-style: none; }
|
|
1312
1336
|
.gw-footer__fold > summary::-webkit-details-marker { display: none; }
|
|
1313
|
-
.gw-footer__fold .gw-footer__heading { margin: 0;
|
|
1337
|
+
.gw-footer__fold .gw-footer__heading { margin: 0; }
|
|
1314
1338
|
.gw-footer__fold > ul { margin: 0; padding: 0; list-style: none; }
|
|
1315
1339
|
.gw-footer__col > .gw-footer__fold a { line-height: 20px; }
|
|
1316
|
-
@media (min-width:
|
|
1340
|
+
@media (min-width: 621px) {
|
|
1317
1341
|
.gw-footer__fold > summary { display: contents; }
|
|
1318
1342
|
.gw-footer__col > .gw-footer__fold .gw-footer__heading { margin-bottom: 12px; }
|
|
1319
1343
|
}
|
|
1320
|
-
@media (min-width:
|
|
1344
|
+
@media (min-width: 621px) {
|
|
1321
1345
|
.gw-footer__fold::details-content { content-visibility: visible; }
|
|
1322
1346
|
}
|
|
1323
|
-
@media (max-width:
|
|
1347
|
+
@media (max-width: 620px) {
|
|
1324
1348
|
.gw-footer__grid:has(> .gw-footer__col > .gw-footer__fold) { grid-template-columns: minmax(0, 1fr); row-gap: 0; }
|
|
1325
1349
|
.gw-footer__grid:has(> .gw-footer__col > .gw-footer__fold) > .gw-footer__brand { padding-bottom: 24px; }
|
|
1326
|
-
.
|
|
1350
|
+
/* a column's hairline on top, the last column's below too, a family row's below (gemboss.ai's order: a focused
|
|
1351
|
+
row's ring passes under the next row's line) */
|
|
1352
|
+
.gw-footer__col:has(> .gw-footer__fold) { border-top: 1px solid var(--gw-line); }
|
|
1353
|
+
.gw-footer__col:has(> .gw-footer__fold):last-child,
|
|
1327
1354
|
.gw-footer__family { border-bottom: 1px solid var(--gw-line); }
|
|
1328
|
-
.gw-footer__brand + .gw-footer__col:has(> .gw-footer__fold) { border-top: 1px solid var(--gw-line); }
|
|
1329
1355
|
.gw-footer__fold > summary {
|
|
1330
1356
|
display: flex;
|
|
1331
1357
|
align-items: center;
|
|
1332
1358
|
justify-content: space-between;
|
|
1333
1359
|
gap: 16px;
|
|
1334
|
-
min-height:
|
|
1335
|
-
padding
|
|
1360
|
+
min-height: 52px;
|
|
1361
|
+
padding: 0;
|
|
1336
1362
|
cursor: pointer;
|
|
1337
1363
|
}
|
|
1338
1364
|
.gw-footer__fold > summary::after {
|
|
1339
1365
|
content: "";
|
|
1340
1366
|
flex: none;
|
|
1341
|
-
width:
|
|
1342
|
-
height:
|
|
1343
|
-
|
|
1344
|
-
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1367
|
+
width: 8px;
|
|
1368
|
+
height: 8px;
|
|
1369
|
+
margin-right: 4px;
|
|
1370
|
+
border-right: 2px solid var(--gw-muted);
|
|
1371
|
+
border-bottom: 2px solid var(--gw-muted);
|
|
1372
|
+
rotate: 45deg;
|
|
1373
|
+
translate: 0 -2px;
|
|
1347
1374
|
}
|
|
1348
|
-
.gw-footer__fold[open] > summary::after {
|
|
1375
|
+
.gw-footer__fold[open] > summary::after { rotate: 225deg; translate: 0 2px; }
|
|
1376
|
+
.gw-footer__fold > summary:focus-visible { outline: 2px solid var(--gw-accent); outline-offset: 2px; border-radius: var(--gw-radius-6); }
|
|
1349
1377
|
.gw-footer__fold > ul { padding-bottom: 12px; }
|
|
1350
1378
|
.gw-footer__col > .gw-footer__fold a { display: flex; align-items: center; min-height: var(--gw-tap-min); }
|
|
1351
1379
|
}
|
|
1352
1380
|
/* desktop: the brand's track is as wide as gemboss.ai draws it beside five columns (1.2 parts of 6.2, after five 24px
|
|
1353
1381
|
gaps), whatever the number of columns, so a family row can line up with it; the columns share the rest */
|
|
1354
|
-
@media (min-width:
|
|
1382
|
+
@media (min-width: 1081px) {
|
|
1355
1383
|
.gw-footer__grid:has(> .gw-footer__col > .gw-footer__fold) { grid-template-columns: minmax(0, calc((100% - 120px) * 6 / 31)); }
|
|
1356
1384
|
}
|
|
1357
1385
|
|
|
@@ -1361,6 +1389,8 @@ html.is-menu-open .gw-header:has(> .gw-mobile-menu.is-drill) { position: sticky;
|
|
|
1361
1389
|
the items start where the first column starts. */
|
|
1362
1390
|
.gw-footer__family .gw-footer__heading { color: var(--gw-muted); font-weight: 500; }
|
|
1363
1391
|
.gw-footer__family .gw-footer__fold > ul { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
|
|
1392
|
+
.gw-footer__family .gw-footer__fold > ul > li { display: flex; }
|
|
1393
|
+
.gw-footer__family .gw-footer__fold > ul > li > a { flex: 1 1 auto; }
|
|
1364
1394
|
.gw-footer__family a {
|
|
1365
1395
|
display: flex;
|
|
1366
1396
|
flex-direction: column;
|
|
@@ -1374,14 +1404,18 @@ html.is-menu-open .gw-header:has(> .gw-mobile-menu.is-drill) { position: sticky;
|
|
|
1374
1404
|
.gw-footer__family a:hover { background: var(--gw-bg-soft); }
|
|
1375
1405
|
.gw-footer__family b { font-size: 14px; font-weight: 600; line-height: 20px; }
|
|
1376
1406
|
.gw-footer__family i { color: var(--gw-muted); font-size: 13px; font-style: normal; line-height: 18px; }
|
|
1377
|
-
@media (min-width:
|
|
1407
|
+
@media (min-width: 621px) {
|
|
1378
1408
|
.gw-footer__family { margin-top: 48px; padding-top: 32px; border-top: 1px solid var(--gw-line); }
|
|
1379
1409
|
.gw-footer__family .gw-footer__heading { margin-bottom: 12px; }
|
|
1380
|
-
.gw-footer__family .gw-footer__fold > ul { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 4px 16px;
|
|
1410
|
+
.gw-footer__family .gw-footer__fold > ul { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 4px 16px; }
|
|
1381
1411
|
.gw-footer__family a { min-height: 0; padding: 8px 12px; }
|
|
1382
1412
|
.gw-footer__family + .gw-footer__bottom { margin-top: 32px; }
|
|
1383
1413
|
}
|
|
1384
|
-
@media (min-width:
|
|
1414
|
+
@media (min-width: 621px) and (max-width: 768px) {
|
|
1415
|
+
.gw-footer__family a { min-height: var(--gw-tap-min); }
|
|
1416
|
+
}
|
|
1417
|
+
@media (min-width: 1081px) {
|
|
1418
|
+
.gw-footer__family .gw-footer__fold > ul { margin-left: -12px; }
|
|
1385
1419
|
.gw-footer__family { display: flow-root; }
|
|
1386
1420
|
.gw-footer__family .gw-footer__heading { float: left; width: calc((100% - 120px) * 6 / 31); margin: 8px 0 0; }
|
|
1387
1421
|
.gw-footer__family .gw-footer__fold > ul { margin-left: calc((100% - 120px) * 6 / 31 + 12px); }
|
|
@@ -1389,7 +1423,7 @@ html.is-menu-open .gw-header:has(> .gw-mobile-menu.is-drill) { position: sticky;
|
|
|
1389
1423
|
|
|
1390
1424
|
/* The theme toggle in the bottom row (.gw-theme-toggle with data-gw-theme-toggle, wired like the header's): a 32px
|
|
1391
1425
|
square with one hairline, its hit area 40px, 44px on touch. It pushes itself and the social links to the right.
|
|
1392
|
-
|
|
1426
|
+
Up to 620px the copyright and the toggle share the first line, legal and social links the second. */
|
|
1393
1427
|
.gw-footer__toggle {
|
|
1394
1428
|
position: relative;
|
|
1395
1429
|
width: 32px;
|
|
@@ -1400,12 +1434,15 @@ html.is-menu-open .gw-header:has(> .gw-mobile-menu.is-drill) { position: sticky;
|
|
|
1400
1434
|
}
|
|
1401
1435
|
.gw-footer__toggle::after { content: ""; position: absolute; inset: -5px; }
|
|
1402
1436
|
.gw-footer__toggle svg { width: 18px; height: 18px; }
|
|
1437
|
+
/* on hover the hairline turns ink and the icon the accent, the ground stays (gemboss.ai's); colour only, 0.15s */
|
|
1438
|
+
.gw-footer__toggle { transition: border-color 0.15s, color 0.15s; }
|
|
1439
|
+
.gw-footer__toggle:hover { border-color: var(--gw-ink); background: var(--gw-surface); color: var(--gw-accent); }
|
|
1403
1440
|
@media (max-width: 768px), (pointer: coarse) {
|
|
1404
1441
|
.gw-footer__toggle { width: var(--gw-tap-min); height: var(--gw-tap-min); }
|
|
1405
1442
|
}
|
|
1406
|
-
@media (max-width:
|
|
1407
|
-
.gw-footer__bottom:has(> .gw-footer__toggle) {
|
|
1408
|
-
.gw-footer__bottom:has(> .gw-footer__toggle) > :first-child { flex: 1 1 calc(100% -
|
|
1443
|
+
@media (max-width: 620px) {
|
|
1444
|
+
.gw-footer__bottom:has(> .gw-footer__toggle) { gap: 8px 16px; }
|
|
1445
|
+
.gw-footer__bottom:has(> .gw-footer__toggle) > :first-child { flex: 1 1 calc(100% - 64px); }
|
|
1409
1446
|
.gw-footer__bottom > .gw-footer__toggle { order: 1; margin: 0; }
|
|
1410
1447
|
.gw-footer__bottom:has(> .gw-footer__toggle) > ul { order: 2; }
|
|
1411
1448
|
.gw-footer__family + .gw-footer__bottom,
|
|
@@ -1415,39 +1452,37 @@ html.is-menu-open .gw-header:has(> .gw-mobile-menu.is-drill) { position: sticky;
|
|
|
1415
1452
|
/* The wordmark: the brand's mark and name, large and faint, the footer's last line. Decoration (aria-hidden="true"):
|
|
1416
1453
|
the name is already in the brand block. Its size follows the viewport (--gw-display-wordmark) and it never makes the
|
|
1417
1454
|
page scroll sideways: a name too long for the line is cut. The mark is drawn in currentColor (an inline SVG); an
|
|
1418
|
-
<img> mark keeps its own colours, so leave it out and keep the name.
|
|
1419
|
-
|
|
1420
|
-
|
|
1455
|
+
<img> mark keeps its own colours, so leave it out and keep the name. Values are gemboss.ai's, 1:1 (Chris 26/10/2026):
|
|
1456
|
+
weight 800, 48px above, nothing below but the footer's own 32px, the mark 7vw wide (40–96px), hidden up to 620px.
|
|
1457
|
+
The footer is not painted (transparent on the page), so the breathing gate reads it as part of the page. */
|
|
1421
1458
|
.gw-footer__wordmark {
|
|
1422
1459
|
display: flex;
|
|
1423
1460
|
align-items: center;
|
|
1424
1461
|
justify-content: center;
|
|
1425
1462
|
gap: 24px;
|
|
1426
|
-
padding-block: 48px
|
|
1463
|
+
padding-block: 48px 0;
|
|
1427
1464
|
color: color-mix(in srgb, var(--gw-ink) 10%, transparent);
|
|
1465
|
+
font-family: var(--gw-font);
|
|
1428
1466
|
font-size: var(--gw-display-wordmark);
|
|
1429
|
-
font-weight:
|
|
1467
|
+
font-weight: 800;
|
|
1430
1468
|
letter-spacing: var(--gw-display-tracking);
|
|
1431
1469
|
line-height: 1;
|
|
1432
1470
|
white-space: nowrap;
|
|
1433
1471
|
user-select: none;
|
|
1434
1472
|
}
|
|
1435
1473
|
.gw-footer__wordmark > svg,
|
|
1436
|
-
.gw-footer__wordmark > img { flex: none; width:
|
|
1474
|
+
.gw-footer__wordmark > img { flex: none; width: clamp(40px, 7vw, 96px); height: auto; }
|
|
1437
1475
|
.gw-footer__wordmark > span { min-width: 0; overflow-x: clip; }
|
|
1438
|
-
@media (max-width:
|
|
1439
|
-
@media (max-width:
|
|
1476
|
+
@media (max-width: 640px) { .gw-footer__wordmark { gap: 16px; } }
|
|
1477
|
+
@media (max-width: 620px) { .gw-footer__wordmark { display: none; } }
|
|
1440
1478
|
|
|
1441
1479
|
@media (max-width: 768px), (pointer: coarse) {
|
|
1442
1480
|
.gw-footer__ask a { display: flex; align-items: center; min-height: var(--gw-tap-min); padding-block: 0; }
|
|
1443
1481
|
}
|
|
1444
|
-
@media (prefers-reduced-motion: reduce) {
|
|
1445
|
-
.gw-footer__fold > summary::after { transition: none; }
|
|
1446
|
-
}
|
|
1447
1482
|
/* a forced-colours screen drops backgrounds: draw the chevron and the ↗ in its text colour */
|
|
1448
1483
|
@media (forced-colors: active) {
|
|
1449
|
-
.gw-footer__ext
|
|
1450
|
-
.gw-footer__fold > summary::after {
|
|
1484
|
+
.gw-footer__ext:empty { forced-color-adjust: none; background: CanvasText; }
|
|
1485
|
+
.gw-footer__fold > summary::after { border-color: CanvasText; }
|
|
1451
1486
|
}
|
|
1452
1487
|
|
|
1453
1488
|
/* sections: hero, logos, stats, features, split, showcase, steps, quote, pricing, CTA. */
|
|
@@ -2229,3 +2264,75 @@ a.gw-rating-badge:hover { border-color: var(--gw-line-strong); }
|
|
|
2229
2264
|
.gw-rating-badge__body { display: grid; gap: 2px; min-width: 0; }
|
|
2230
2265
|
.gw-rating-badge__score { display: flex; align-items: center; gap: 8px; color: var(--gw-ink); font-size: 16px; font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
2231
2266
|
.gw-rating-badge__count { color: var(--gw-muted); }
|
|
2267
|
+
|
|
2268
|
+
/* effects: the spotlight (0.9.0, Chris 25/10/2026, issue #74), a named hover effect for the things you click.
|
|
2269
|
+
data-gw-spot on an <a href>, a <button>, or a card that holds exactly one of them (a plan card, the mega menu's
|
|
2270
|
+
feature card, the primary button of a night band, a .gw-card--link): a soft light in the brand's accent follows the
|
|
2271
|
+
pointer over it. Opt-in per element: a page without the attribute does not change.
|
|
2272
|
+
website.js wires the host (data-gw-ready="spot") and puts .gw-spot on the element that carries the light: the host,
|
|
2273
|
+
or the one its data-gw-spot="<selector>" names inside it. It writes the pointer's place there (--gw-spot-x,
|
|
2274
|
+
--gw-spot-y, trailing it a little every frame); this file draws the light on its ::before, under its words, in its
|
|
2275
|
+
own corners (border-radius: inherit, so the radius profile holds), and grows the light's radius on hover.
|
|
2276
|
+
- Pointer devices only: nothing here applies without (hover: hover) and (pointer: fine).
|
|
2277
|
+
- Keyboard focus (on the host, or on the one action of a card) shows the same light, centred.
|
|
2278
|
+
- Reduced motion: the light stands still, centred, and appears without a transition.
|
|
2279
|
+
- Words under the light: while a thing is lit, its muted words read --gw-ink-2 (the token, set as --gw-muted on the
|
|
2280
|
+
host), so they keep 4.5:1 at the light's middle; they turn back once the light has gone. A colour, no movement.
|
|
2281
|
+
- Without @property or color-mix(), website.js puts .gw-spot nowhere: no light at all, nothing half drawn.
|
|
2282
|
+
- One colour, the accent family, as the page has it (the -dark family in dark mode and in night bands). Never on a
|
|
2283
|
+
heading or running text: a heading stays ink, with no gradient and no accent words (check: spot-target).
|
|
2284
|
+
The radius is the one animated property that is neither transform nor opacity: it repaints one layer of one
|
|
2285
|
+
element. Registered, so it can transition; inherited, so the ::before (and an inner element) reads the host's. */
|
|
2286
|
+
@property --gw-spot-r {
|
|
2287
|
+
syntax: "<length>";
|
|
2288
|
+
inherits: true;
|
|
2289
|
+
initial-value: 0px;
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
@media (hover: hover) and (pointer: fine) {
|
|
2293
|
+
/* the host: no light at rest (a lit card does not light a lit button inside it), the light's size on hover and focus */
|
|
2294
|
+
[data-gw-ready~="spot"] { --gw-spot-r: 0px; }
|
|
2295
|
+
[data-gw-ready~="spot"]:is(:hover, :focus-visible) { --gw-spot-r: var(--gw-spot-radius); --gw-muted: var(--gw-ink-2); }
|
|
2296
|
+
/* a card whose one action has the keyboard focus; a rule of its own, so a browser without :has() drops only it */
|
|
2297
|
+
[data-gw-ready~="spot"]:has(:focus-visible) { --gw-spot-r: var(--gw-spot-radius); --gw-muted: var(--gw-ink-2); }
|
|
2298
|
+
.gw-btn[data-gw-ready~="spot"]:is(:hover, :focus-visible) { --gw-spot-r: var(--gw-spot-radius-action); }
|
|
2299
|
+
|
|
2300
|
+
/* the element that carries the light: positioned for its layer, isolated so the layer sits under its words.
|
|
2301
|
+
:where(), so a site's own position for an inner element wins. */
|
|
2302
|
+
:where(.gw-spot) { position: relative; isolation: isolate; }
|
|
2303
|
+
/* the light. Not on a busy button: its ::before is the ring. */
|
|
2304
|
+
.gw-spot:not([aria-busy="true"])::before {
|
|
2305
|
+
--gw-spot-mix: var(--gw-spot-strength);
|
|
2306
|
+
content: "";
|
|
2307
|
+
position: absolute;
|
|
2308
|
+
inset: 0;
|
|
2309
|
+
z-index: -1;
|
|
2310
|
+
border-radius: inherit;
|
|
2311
|
+
background: radial-gradient(circle var(--gw-spot-r) at var(--gw-spot-x, 50%) var(--gw-spot-y, 50%), color-mix(in srgb, var(--gw-accent) var(--gw-spot-mix), transparent), transparent 70%);
|
|
2312
|
+
pointer-events: none;
|
|
2313
|
+
transition: --gw-spot-r var(--gw-spot-ms) var(--gw-spot-ease);
|
|
2314
|
+
}
|
|
2315
|
+
/* a button is small and filled: a stronger light. Its words keep their contrast: the light is the accent over the
|
|
2316
|
+
button's fill, so under the accent action profile it lies between the accent and the accent-strong, the two fills
|
|
2317
|
+
the build checks the words on. */
|
|
2318
|
+
.gw-btn.gw-spot:not([aria-busy="true"])::before { --gw-spot-mix: var(--gw-spot-strength-action); }
|
|
2319
|
+
/* the mega menu's feature card is the Stage, a night ground: a stronger light, or it would not show */
|
|
2320
|
+
.gw-mega__feature.gw-spot:not([aria-busy="true"])::before { --gw-spot-mix: var(--gw-spot-strength-stage); }
|
|
2321
|
+
|
|
2322
|
+
/* words under the light: lit, muted words are ink-2 at once (rules above); back to muted only once the light has gone
|
|
2323
|
+
out (a colour that waits the light's time, no movement). :where(), so a part's own transition wins. */
|
|
2324
|
+
:where([data-gw-ready~="spot"] *) { transition: color 0s linear var(--gw-spot-ms); }
|
|
2325
|
+
:where([data-gw-ready~="spot"]:is(:hover, :focus-visible) *) { transition-delay: 0s; }
|
|
2326
|
+
:where([data-gw-ready~="spot"]:has(:focus-visible) *) { transition-delay: 0s; }
|
|
2327
|
+
|
|
2328
|
+
/* keyboard focus: the light in the middle, so focus is never weaker than hover */
|
|
2329
|
+
:is([data-gw-ready~="spot"]:focus-visible, [data-gw-ready~="spot"]:focus-visible *).gw-spot::before { --gw-spot-x: 50%; --gw-spot-y: 50%; }
|
|
2330
|
+
:is([data-gw-ready~="spot"]:has(:focus-visible), [data-gw-ready~="spot"]:has(:focus-visible) *).gw-spot::before { --gw-spot-x: 50%; --gw-spot-y: 50%; }
|
|
2331
|
+
}
|
|
2332
|
+
|
|
2333
|
+
/* reduced motion: a still light in the middle, there at once and gone at once, the words with it (website.js follows
|
|
2334
|
+
no pointer then) */
|
|
2335
|
+
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: reduce) {
|
|
2336
|
+
.gw-spot:not([aria-busy="true"])::before { --gw-spot-x: 50%; --gw-spot-y: 50%; transition: none; }
|
|
2337
|
+
:where([data-gw-ready~="spot"] *) { transition: none; }
|
|
2338
|
+
}
|