8 Commits

Author SHA1 Message Date
47606ac0a8 feat: darker dedicated favicon (#6a8018) for tab contrast
Logo (navbar) keeps the acid #d8ff3e; only the favicon uses a darker
acid for legibility on light browser tabs. Geometry identical.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 15:33:40 +01:00
16a0861f77 fix: absolute favicon path (/icons not ./icons) for SPA deep routes
Relative ./icons resolved against the current route (e.g. /markdown/1 ->
/markdown/icons/...) and hit the SPA fallback, so the favicon never loaded.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 15:26:30 +01:00
h z
f4787d0a27 Merge pull request 'feat: add 'agent' to API key role options' (#4) from feat/agent-role into main
Reviewed-on: #4
2026-05-17 14:10:05 +00:00
40f051bc77 feat: add 'agent' to API key role options
Keeps the Create API Key modal aligned with the backend allowlist
(admin|creator|user|agent).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 15:06:28 +01:00
h z
9f2faed91f Merge pull request 'feat/lab-terminal-theme' (#3) from feat/lab-terminal-theme into main
Reviewed-on: #3
2026-05-17 13:16:40 +00:00
a98badf653 style: bump base font-size 17px -> 18px
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 14:12:15 +01:00
f9d618d0dd feat: self-host all external assets + larger base font
- Vendor Google Fonts: 23 woff2 + localized fonts.css under
  public/fonts/, referenced via /fonts/fonts.css. Drop the
  googleapis/gstatic preconnects and the (now unused) Font Awesome
  CDN link. No runtime external resource requests remain.
- Dockerfile copies public/fonts into the nginx image.
- html base font-size 16 -> 17px (all rem UI text scales up slightly).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 14:09:46 +01:00
cbac2b0771 feat: lab-terminal theme + new logo
Adopt the Hangman Lab visual language (adapted from the Gitea
STYLE.md, Gitea-only mechanics excluded):

- New acid SVG mark as favicon (?v cache-bust) and navbar logo.
- Single acid accent (#d8ff3e); retune globals.css tokens to the
  ink/panel/line/text/dim/danger palette; drop the cyan/violet pair
  (secondary -> same acid so existing components stay on-brand).
- Fonts: IBM Plex Mono for body/UI/mono, Major Mono Display for the
  lowercase brand lockup; Tailwind fontFamily + content fonts updated.
- Site-wide fixed 48px blueprint-grid backdrop + one acid sheen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 14:05:16 +01:00
34 changed files with 370 additions and 66 deletions

View File

@@ -19,6 +19,7 @@ COPY package*.json ./
COPY --from=build-stage /app/dist /usr/share/nginx/html COPY --from=build-stage /app/dist /usr/share/nginx/html
COPY --from=build-stage /app/public/icons /usr/share/nginx/html/icons COPY --from=build-stage /app/public/icons /usr/share/nginx/html/icons
COPY --from=build-stage /app/public/fonts /usr/share/nginx/html/fonts
COPY nginx.conf /etc/nginx/conf.d/default.conf COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY BuildConfig.sh /docker-entrypoint.d/10-build-config.sh COPY BuildConfig.sh /docker-entrypoint.d/10-build-config.sh
RUN chmod a+x /docker-entrypoint.d/10-build-config.sh RUN chmod a+x /docker-entrypoint.d/10-build-config.sh

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

207
public/fonts/fonts.css Normal file
View File

@@ -0,0 +1,207 @@
/* cyrillic-ext */
@font-face {
font-family: 'IBM Plex Mono';
font-style: italic;
font-weight: 400;
font-display: swap;
src: url(./-F6pfjptAgt5VM-kVkqdyU8n1ioa2Hdgv-s.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'IBM Plex Mono';
font-style: italic;
font-weight: 400;
font-display: swap;
src: url(./-F6pfjptAgt5VM-kVkqdyU8n1ioa0Xdgv-s.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* vietnamese */
@font-face {
font-family: 'IBM Plex Mono';
font-style: italic;
font-weight: 400;
font-display: swap;
src: url(./-F6pfjptAgt5VM-kVkqdyU8n1ioa2ndgv-s.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'IBM Plex Mono';
font-style: italic;
font-weight: 400;
font-display: swap;
src: url(./-F6pfjptAgt5VM-kVkqdyU8n1ioa23dgv-s.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'IBM Plex Mono';
font-style: italic;
font-weight: 400;
font-display: swap;
src: url(./-F6pfjptAgt5VM-kVkqdyU8n1ioa1Xdg.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./-F63fjptAgt5VM-kVkqdyU8n1iIq129k.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./-F63fjptAgt5VM-kVkqdyU8n1isq129k.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* vietnamese */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./-F63fjptAgt5VM-kVkqdyU8n1iAq129k.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./-F63fjptAgt5VM-kVkqdyU8n1iEq129k.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./-F63fjptAgt5VM-kVkqdyU8n1i8q1w.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3twJwl1FgtIU.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3twJwlRFgtIU.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* vietnamese */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3twJwl9FgtIU.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3twJwl5FgtIU.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3twJwlBFgg.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* cyrillic-ext */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3vAOwl1FgtIU.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3vAOwlRFgtIU.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* vietnamese */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3vAOwl9FgtIU.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3vAOwl5FgtIU.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'IBM Plex Mono';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(./-F6qfjptAgt5VM-kVkqdyU8n3vAOwlBFgg.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* vietnamese */
@font-face {
font-family: 'Major Mono Display';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./RWmVoLyb5fEqtsfBX9PDZIGr2tFubRh7AneREnc.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Major Mono Display';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./RWmVoLyb5fEqtsfBX9PDZIGr2tFubRh7A3eREnc.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Major Mono Display';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./RWmVoLyb5fEqtsfBX9PDZIGr2tFubRh7DXeR.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

View File

@@ -0,0 +1,44 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 733.000000 733.000000" role="img" aria-label="Hangman Lab">
<g transform="translate(0.000000,733.000000) scale(0.100000,-0.100000)"
fill="#6a8018" stroke="none">
<path d="M812 6860 c-66 -40 -73 -66 -70 -242 3 -141 5 -159 24 -185 43 -58
63 -63 262 -64 l182 -1 0 -2694 0 -2694 -290 0 c-314 0 -332 -3 -380 -54 -24
-27 -25 -32 -28 -199 -3 -195 4 -218 71 -250 32 -16 257 -17 3084 -17 2955 0
3049 1 3083 19 65 34 75 66 75 236 0 186 -16 224 -104 254 -25 8 -682 11
-2512 11 l-2479 0 0 1998 0 1997 698 697 697 697 670 -3 c369 -2 680 -4 693
-5 22 -1 22 -1 22 -269 l0 -268 84 -12 c47 -7 103 -19 125 -27 41 -14 41 -14
43 283 l3 297 631 1 c698 2 673 -1 714 67 18 28 20 51 20 187 0 173 -8 201
-72 240 -33 20 -56 20 -2623 20 -2567 0 -2590 0 -2623 -20z m1725 -500 c-1 -3
-183 -185 -404 -404 l-403 -399 0 405 0 406 405 -1 c223 -1 404 -4 402 -7z
M4283 5701 c-459 -125 -690 -624 -483 -1046 67 -138 196 -266 335 -335 281
-139 612 -93 837 118 235 219 297 575 152 870 -85 174 -235 306 -427 375 -111
40 -302 48 -414 18z m267 -211 c95 -15 201 -70 274 -144 273 -274 173 -723
-189 -851 -93 -33 -236 -35 -326 -5 -202 68 -340 249 -356 465 -18 242 153
471 394 529 71 18 119 19 203 6z M5945 4716 c-56 -25 -80 -61 -80 -122 0 -48
4 -57 36 -90 88 -88 219 -29 219 98 0 45 -34 96 -75 114 -42 17 -61 17 -100 0z
M5675 4466 c-60 -26 -73 -109 -26 -157 62 -61 161 -19 161 70 0 74 -68 118
-135 87z M3747 4397 c-10 -7 -226 -223 -479 -482 -307 -313 -467 -483 -479
-510 -35 -75 -17 -177 38 -227 39 -35 114 -61 160 -54 100 13 104 17 583 554
l145 162 3 -404 c3 -455 15 -342 -137 -1186 -76 -423 -101 -587 -101 -664 0
-190 182 -307 377 -242 62 21 136 95 152 152 6 22 29 188 52 369 57 466 109
839 118 848 4 4 52 6 106 5 l99 -3 67 -210 c38 -115 68 -220 69 -232 0 -12
-38 -152 -85 -311 -47 -159 -85 -302 -85 -317 0 -128 109 -225 255 -225 109 1
185 42 228 125 26 51 237 683 237 710 0 11 -54 182 -121 380 -121 360 -121
360 -134 605 -8 135 -14 292 -14 350 l0 105 152 -158 c84 -87 166 -167 182
-177 44 -29 98 -26 135 8 38 35 187 379 173 401 -5 8 -34 20 -64 26 -59 11
-38 -8 -393 362 -89 93 -100 97 -174 59 -59 -30 -161 -62 -229 -71 -52 -7 -53
-7 -53 -41 0 -44 -21 -84 -61 -118 -38 -32 -41 0 32 -455 l52 -325 -82 -78
c-45 -43 -85 -78 -89 -78 -4 0 -42 35 -84 78 l-77 77 34 215 c19 118 46 289
61 379 26 164 26 164 -5 188 -42 33 -54 58 -61 121 -5 54 -5 54 -92 87 -98 38
-184 89 -243 145 -76 70 -123 87 -168 57z M5504 4170 c-74 -30 -69 -170 6
-170 19 0 20 -7 20 -123 0 -122 0 -122 -54 -262 -30 -77 -97 -243 -150 -369
-106 -252 -113 -287 -73 -347 46 -70 38 -69 560 -69 327 0 475 3 490 11 69 35
107 109 88 172 -5 17 -62 142 -126 277 -225 470 -215 443 -215 586 0 114 2
124 18 124 57 0 72 101 23 151 -29 29 -29 29 -298 28 -147 0 -278 -4 -289 -9z
m376 -307 c1 -148 1 -148 81 -333 101 -231 98 -222 68 -216 -13 3 -116 22
-229 42 -112 19 -208 37 -212 40 -4 2 18 75 48 160 54 156 54 156 54 305 l0
149 95 0 95 0 0 -147z m-219 -593 c22 0 49 -42 49 -75 0 -46 -29 -75 -74 -75
-37 0 -76 36 -76 71 0 46 45 94 78 83 8 -2 18 -4 23 -4z m327 -126 c53 -59 -7
-144 -80 -113 -54 22 -65 83 -22 125 30 31 67 27 102 -12z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -0,0 +1,44 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 733.000000 733.000000" role="img" aria-label="Hangman Lab">
<g transform="translate(0.000000,733.000000) scale(0.100000,-0.100000)"
fill="#d8ff3e" stroke="none">
<path d="M812 6860 c-66 -40 -73 -66 -70 -242 3 -141 5 -159 24 -185 43 -58
63 -63 262 -64 l182 -1 0 -2694 0 -2694 -290 0 c-314 0 -332 -3 -380 -54 -24
-27 -25 -32 -28 -199 -3 -195 4 -218 71 -250 32 -16 257 -17 3084 -17 2955 0
3049 1 3083 19 65 34 75 66 75 236 0 186 -16 224 -104 254 -25 8 -682 11
-2512 11 l-2479 0 0 1998 0 1997 698 697 697 697 670 -3 c369 -2 680 -4 693
-5 22 -1 22 -1 22 -269 l0 -268 84 -12 c47 -7 103 -19 125 -27 41 -14 41 -14
43 283 l3 297 631 1 c698 2 673 -1 714 67 18 28 20 51 20 187 0 173 -8 201
-72 240 -33 20 -56 20 -2623 20 -2567 0 -2590 0 -2623 -20z m1725 -500 c-1 -3
-183 -185 -404 -404 l-403 -399 0 405 0 406 405 -1 c223 -1 404 -4 402 -7z
M4283 5701 c-459 -125 -690 -624 -483 -1046 67 -138 196 -266 335 -335 281
-139 612 -93 837 118 235 219 297 575 152 870 -85 174 -235 306 -427 375 -111
40 -302 48 -414 18z m267 -211 c95 -15 201 -70 274 -144 273 -274 173 -723
-189 -851 -93 -33 -236 -35 -326 -5 -202 68 -340 249 -356 465 -18 242 153
471 394 529 71 18 119 19 203 6z M5945 4716 c-56 -25 -80 -61 -80 -122 0 -48
4 -57 36 -90 88 -88 219 -29 219 98 0 45 -34 96 -75 114 -42 17 -61 17 -100 0z
M5675 4466 c-60 -26 -73 -109 -26 -157 62 -61 161 -19 161 70 0 74 -68 118
-135 87z M3747 4397 c-10 -7 -226 -223 -479 -482 -307 -313 -467 -483 -479
-510 -35 -75 -17 -177 38 -227 39 -35 114 -61 160 -54 100 13 104 17 583 554
l145 162 3 -404 c3 -455 15 -342 -137 -1186 -76 -423 -101 -587 -101 -664 0
-190 182 -307 377 -242 62 21 136 95 152 152 6 22 29 188 52 369 57 466 109
839 118 848 4 4 52 6 106 5 l99 -3 67 -210 c38 -115 68 -220 69 -232 0 -12
-38 -152 -85 -311 -47 -159 -85 -302 -85 -317 0 -128 109 -225 255 -225 109 1
185 42 228 125 26 51 237 683 237 710 0 11 -54 182 -121 380 -121 360 -121
360 -134 605 -8 135 -14 292 -14 350 l0 105 152 -158 c84 -87 166 -167 182
-177 44 -29 98 -26 135 8 38 35 187 379 173 401 -5 8 -34 20 -64 26 -59 11
-38 -8 -393 362 -89 93 -100 97 -174 59 -59 -30 -161 -62 -229 -71 -52 -7 -53
-7 -53 -41 0 -44 -21 -84 -61 -118 -38 -32 -41 0 32 -455 l52 -325 -82 -78
c-45 -43 -85 -78 -89 -78 -4 0 -42 35 -84 78 l-77 77 34 215 c19 118 46 289
61 379 26 164 26 164 -5 188 -42 33 -54 58 -61 121 -5 54 -5 54 -92 87 -98 38
-184 89 -243 145 -76 70 -123 87 -168 57z M5504 4170 c-74 -30 -69 -170 6
-170 19 0 20 -7 20 -123 0 -122 0 -122 -54 -262 -30 -77 -97 -243 -150 -369
-106 -252 -113 -287 -73 -347 46 -70 38 -69 560 -69 327 0 475 3 490 11 69 35
107 109 88 172 -5 17 -62 142 -126 277 -225 470 -215 443 -215 586 0 114 2
124 18 124 57 0 72 101 23 151 -29 29 -29 29 -298 28 -147 0 -278 -4 -289 -9z
m376 -307 c1 -148 1 -148 81 -333 101 -231 98 -222 68 -216 -13 3 -116 22
-229 42 -112 19 -208 37 -212 40 -4 2 18 75 48 160 54 156 54 156 54 305 l0
149 95 0 95 0 0 -147z m-219 -593 c22 0 49 -42 49 -75 0 -46 -29 -75 -74 -75
-37 0 -76 36 -76 71 0 46 45 94 78 83 8 -2 18 -4 23 -4z m327 -126 c53 -59 -7
-144 -80 -113 -54 22 -65 83 -22 125 30 31 67 27 102 -12z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -5,20 +5,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="color-scheme" content="dark"> <meta name="color-scheme" content="dark">
<title>Hangman Lab</title> <title>Hangman Lab</title>
<link rel="icon" type="image/png" href="./icons/logo.png"> <link rel="icon" type="image/svg+xml" href="/icons/hangman-lab-favicon.svg?v=hl-20260517b">
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="stylesheet" href="/fonts/fonts.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
<link
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"
rel="stylesheet"
/>
</body> </body>
</html> </html>

View File

@@ -12,7 +12,7 @@
} }
code { code {
font-family: ui-monospace, "JetBrains Mono", "Courier New", Courier, monospace; font-family: ui-monospace, "IBM Plex Mono", "Courier New", Courier, monospace;
background-color: hsl(var(--muted)); background-color: hsl(var(--muted));
color: hsl(var(--foreground)); color: hsl(var(--foreground));
padding: 2px 6px; padding: 2px 6px;

View File

@@ -34,7 +34,7 @@
.markdown-preview h4, .markdown-preview h4,
.markdown-preview h5, .markdown-preview h5,
.markdown-preview h6 { .markdown-preview h6 {
font-family: "JetBrains Mono", ui-monospace, monospace; font-family: "IBM Plex Mono", ui-monospace, monospace;
font-weight: 600; font-weight: 600;
color: hsl(var(--foreground)); color: hsl(var(--foreground));
line-height: 1.3; line-height: 1.3;
@@ -108,7 +108,7 @@
/* Inline code */ /* Inline code */
.markdown-preview :not(pre) > code { .markdown-preview :not(pre) > code {
font-family: "JetBrains Mono", ui-monospace, monospace; font-family: "IBM Plex Mono", ui-monospace, monospace;
font-size: 0.85em; font-size: 0.85em;
background-color: hsl(var(--muted)); background-color: hsl(var(--muted));
color: hsl(var(--primary)); color: hsl(var(--primary));
@@ -128,7 +128,7 @@
box-shadow: inset 0 0 40px -20px hsl(var(--primary) / 0.25); box-shadow: inset 0 0 40px -20px hsl(var(--primary) / 0.25);
} }
.markdown-preview pre code { .markdown-preview pre code {
font-family: "JetBrains Mono", ui-monospace, monospace; font-family: "IBM Plex Mono", ui-monospace, monospace;
font-size: 0.85rem; font-size: 0.85rem;
background: transparent; background: transparent;
border: none; border: none;
@@ -150,7 +150,7 @@
} }
.markdown-preview th { .markdown-preview th {
background-color: hsl(var(--muted)); background-color: hsl(var(--muted));
font-family: "JetBrains Mono", ui-monospace, monospace; font-family: "IBM Plex Mono", ui-monospace, monospace;
font-size: 0.8rem; font-size: 0.8rem;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.04em; letter-spacing: 0.04em;

View File

@@ -12,7 +12,7 @@ import { Button } from '../ui/button';
import { Input, Label } from '../ui/input'; import { Input, Label } from '../ui/input';
// Must match the backend allowlist (admin|creator|user). // Must match the backend allowlist (admin|creator|user).
const AVAILABLE_ROLES = ['user', 'creator', 'admin']; const AVAILABLE_ROLES = ['user', 'creator', 'agent', 'admin'];
const SELECT_CLASS = const SELECT_CLASS =
"flex h-9 w-full rounded-md border border-input bg-background/60 px-3 py-1 text-sm text-foreground transition-colors focus-visible:outline-none focus-visible:border-primary/60 focus-visible:ring-2 focus-visible:ring-ring/40 disabled:cursor-not-allowed disabled:opacity-50"; "flex h-9 w-full rounded-md border border-input bg-background/60 px-3 py-1 text-sm text-foreground transition-colors focus-visible:outline-none focus-visible:border-primary/60 focus-visible:ring-2 focus-visible:ring-ring/40 disabled:cursor-not-allowed disabled:opacity-50";

View File

@@ -124,13 +124,13 @@ const MainNavigation = () => {
className="group flex items-center gap-2.5 pr-3" className="group flex items-center gap-2.5 pr-3"
> >
<img <img
src="/icons/logo.png" src="/icons/hangman-lab-logo.svg"
alt="Logo" alt="Hangman Lab"
className="h-7 w-7 rounded" className="h-7 w-7"
/> />
<span className="font-mono text-sm font-semibold tracking-tight"> <span className="font-display text-base leading-none tracking-tight">
<span className="text-foreground">HANGMAN</span> <span className="text-foreground">hangman</span>
<span className="neon-text">//LAB</span> <span className="neon-text"> lab</span>
</span> </span>
</Link> </Link>

View File

@@ -2,37 +2,40 @@
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
/* Dark-tech design tokens (single dark theme). HSL triplets so Tailwind /* Lab-terminal theme — adapted from the Gitea STYLE.md for this React app.
color utilities and shadcn-style primitives resolve via hsl(var(--x)). */ Single acid accent (#d8ff3e), dark blueprint surfaces, monospace UI.
HSL triplets so Tailwind / shadcn-style primitives resolve hsl(var(--x)). */
:root, :root,
.dark { .dark {
--background: 222 32% 6%; --background: 216 24% 4%; /* #080a0d ink */
--foreground: 210 22% 92%; --foreground: 217 35% 86%; /* #cdd8e8 text */
--surface: 222 28% 8.5%; --surface: 214 29% 8%; /* #0f141b panel */
--card: 222 26% 9.5%; --card: 214 29% 8%;
--card-foreground: 210 22% 92%; --card-foreground: 217 38% 95%; /* #e9f0fa strong text */
--muted: 222 20% 14%; --muted: 214 27% 12%; /* hovered panel */
--muted-foreground: 215 16% 60%; --muted-foreground: 216 18% 48%; /* #647691 dim / eyebrow */
--accent: 222 24% 13%; --accent: 214 27% 11%; /* row/element hover wash base */
--accent-foreground: 210 22% 92%; --accent-foreground: 217 35% 86%;
--border: 220 18% 18%; --border: 213 28% 16%; /* #1d2733 lines / dividers */
--input: 220 18% 16%; --input: 213 28% 14%;
--ring: 186 92% 56%; --ring: 72 100% 62%; /* acid focus ring */
/* cyan primary, violet secondary — the neon accents */ /* THE accent — one acid, used for links/primary/active/focus. */
--primary: 186 92% 56%; --primary: 72 100% 62%; /* #d8ff3e */
--primary-foreground: 222 47% 6%; --primary-foreground: 96 24% 5%; /* #0c0f08 text on acid */
--secondary: 258 90% 68%; /* No second hue (STYLE: "one acid"). Secondary == acid so any
--secondary-foreground: 0 0% 100%; component using it stays on-brand. */
--secondary: 72 100% 62%;
--secondary-foreground: 96 24% 5%;
--destructive: 0 72% 56%; --destructive: 3 100% 66%; /* #ff5a52 errors only */
--destructive-foreground: 0 0% 100%; --destructive-foreground: 0 0% 100%;
--radius: 0.5rem; --radius: 0.75rem;
} }
* { * {
@@ -45,25 +48,37 @@ body,
height: 100%; height: 100%;
} }
html {
background-color: hsl(var(--background));
/* Larger base so all rem-based UI text scales up. */
font-size: 18px;
}
body { body {
margin: 0; margin: 0;
background-color: hsl(var(--background)); background-color: transparent;
color: hsl(var(--foreground)); color: hsl(var(--foreground));
font-family: Inter, ui-sans-serif, system-ui, sans-serif; font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, monospace;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
} }
/* Ambient grid + glow backdrop for the app shell */ /* Site-wide blueprint grid backdrop (STYLE §11.2): fixed, behind content,
48px #1d2733 lines at low opacity, radial-masked, plus one acid sheen. */
body::before { body::before {
content: ""; content: "";
position: fixed; position: fixed;
inset: 0; inset: 0;
z-index: -1;
pointer-events: none; pointer-events: none;
z-index: 0; background-color: hsl(var(--background));
background: background-image:
radial-gradient(60rem 60rem at 110% -10%, hsl(258 90% 68% / 0.10), transparent 60%), radial-gradient(1000px 560px at 78% -8%, hsl(72 100% 62% / 0.10), transparent 60%),
radial-gradient(50rem 50rem at -10% 110%, hsl(186 92% 56% / 0.08), transparent 60%); linear-gradient(hsl(213 28% 16% / 0.16) 1px, transparent 1px),
linear-gradient(90deg, hsl(213 28% 16% / 0.16) 1px, transparent 1px);
background-size: 100% 100%, 48px 48px, 48px 48px;
-webkit-mask-image: radial-gradient(ellipse 90% 80% at 50% 30%, #000 55%, transparent 100%);
mask-image: radial-gradient(ellipse 90% 80% at 50% 30%, #000 55%, transparent 100%);
} }
::selection { ::selection {
@@ -71,7 +86,6 @@ body::before {
color: hsl(var(--foreground)); color: hsl(var(--foreground));
} }
/* Slim techy scrollbars */
* { * {
scrollbar-width: thin; scrollbar-width: thin;
scrollbar-color: hsl(var(--border)) transparent; scrollbar-color: hsl(var(--border)) transparent;
@@ -89,16 +103,24 @@ body::before {
} }
@layer utilities { @layer utilities {
/* Display lockup font — always lowercase per STYLE §3. */
.font-display {
font-family: "Major Mono Display", "IBM Plex Mono", monospace;
text-transform: lowercase;
}
.neon-text { .neon-text {
color: hsl(var(--primary)); color: hsl(var(--primary));
text-shadow: 0 0 12px hsl(var(--primary) / 0.55); text-shadow: 0 0 44px hsl(var(--primary) / 0.38);
} }
/* kept for compatibility with components that used the old violet
accent — now the same single acid. */
.neon-text-violet { .neon-text-violet {
color: hsl(var(--secondary)); color: hsl(var(--primary));
text-shadow: 0 0 12px hsl(var(--secondary) / 0.55); text-shadow: 0 0 44px hsl(var(--primary) / 0.38);
} }
.glass { .glass {
background: hsl(var(--surface) / 0.7); background: hsl(214 29% 8% / 0.55);
backdrop-filter: blur(10px); backdrop-filter: blur(16px) saturate(150%);
border: 1px solid hsl(var(--primary) / 0.16);
} }
} }

View File

@@ -42,15 +42,10 @@ module.exports = {
sm: "calc(var(--radius) - 4px)", sm: "calc(var(--radius) - 4px)",
}, },
fontFamily: { fontFamily: {
sans: ["Inter", "ui-sans-serif", "system-ui", "sans-serif"], // Lab-terminal: IBM Plex Mono is the body/UI/mono face.
mono: [ sans: ["IBM Plex Mono", "ui-monospace", "SFMono-Regular", "monospace"],
"JetBrains Mono", mono: ["IBM Plex Mono", "ui-monospace", "SFMono-Regular", "monospace"],
"Fira Code", display: ["Major Mono Display", "IBM Plex Mono", "monospace"],
"ui-monospace",
"SFMono-Regular",
"Menlo",
"monospace",
],
}, },
boxShadow: { boxShadow: {
glow: "0 0 0 1px hsl(var(--primary) / 0.25), 0 0 24px -6px hsl(var(--primary) / 0.45)", glow: "0 0 0 1px hsl(var(--primary) / 0.25), 0 0 24px -6px hsl(var(--primary) / 0.45)",