style: light theme and hand-inked logo matching acampada26s.net

The previous pass kept the dark base and the red icons, which did not
read as the acampada. Now:

- Light theme: white page, black ink and borders, black buttons with a
  hard neon offset shadow, neon for focus and the active nav item;
  h1/h2 in Anton with the stepped neon shadow of the site headings.
- Nav, empty-state, spinner and pattern assets in black; platform icons
  keep their dark disc with a neon stroke.
- New mark: three dome tents wired from their doors into a switch with
  an antenna, drawn in the inked style of the acampada logo (curved
  poles, ball ends, hatching) over a neon offset copy. LogoMark renders
  it inline; visible product name is T-HUB.
- docs/brand: t-hub.svg and a lockup with the Anton wordmark converted
  to outlines so it renders without the font.
- Public submission page moved to the light palette.

Checked with headless Chromium screenshots (offline sample data) on
desktop and mobile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
hacklab 2026-10-15 14:40:02 +02:00
parent 533a0fb1c0
commit cfabbf20ef
49 changed files with 369 additions and 286 deletions

View file

@ -1,51 +1,62 @@
import type { ReactElement } from "react";
/**
* The Tactical Media Broadcast Hub mark, inline.
*
* Source of truth: `public/brand/tactical-media-broadcast-hub.svg` — the arrows-from-circle
* mark, tilted −17.5° counterclockwise so the top arrow points upper-left
* (nothing static; everything points outward). The strokes use `currentColor`
* so the mark always rides the workspace accent, which in the warm palette
* lands within a hair of the brand red (#FF1610 ≈ oklch(0.63 0.24 29) vs the
* token's oklch(0.62 0.22 30)). Geometry is copied 1:1 from the asset on its
* 1700×1700 grid, minus the black plate and the screen-print distress paths
* (those only read on pure black at full size).
* The T-HUB mark, inline: three dome tents in a circle, each with a cable
* out of its door into a switch with an antenna. Drawn in the hand-inked
* style of the acampada26s.net mark, ink in `currentColor` over a neon
* offset copy. Source: docs/brand/t-hub.svg (same 400×400 geometry).
*/
const TENTS: ReadonlyArray<readonly [number, number]> = [[105, 150], [295, 150], [200, 340]];
function Tent({ x, y }: { x: number; y: number }): ReactElement {
return (
<g transform={`translate(${x},${y})`}>
<path fill="none" d="M-58 0 Q-56 -66 -1 -72 Q55 -66 58 0" strokeWidth="7" />
<path fill="none" d="M-64 1 Q0 -3 64 1" strokeWidth="6" />
<path fill="none" d="M-44 0 Q-30 -58 -1 -72" strokeWidth="4.5" />
<path fill="none" d="M44 0 Q30 -58 -1 -72" strokeWidth="4.5" />
<path d="M-15 0 Q-14 -30 0 -33 Q14 -30 15 0 Z" strokeWidth="4" />
<path fill="none" d="M-50 -14 L-41 -25 M-50 -4 L-37 -19 M-43 -1 L-33 -13 M-52 -27 L-45 -36" strokeWidth="2.6" />
<path fill="none" d="M28 -50 L37 -42 M24 -40 L36 -30 M22 -29 L34 -19" strokeWidth="2.6" />
<circle cx="-64" cy="1" r="5.5" stroke="none" />
<circle cx="64" cy="1" r="5.5" stroke="none" />
<circle cx="-1" cy="-73" r="5.5" stroke="none" />
</g>
);
}
function Mark({ paper }: { paper: string }): ReactElement {
return (
<>
<path fill="none" d="M105 150 Q112 206 168 214" strokeWidth="5" />
<path fill="none" d="M295 150 Q288 206 232 214" strokeWidth="5" />
<path fill="none" d="M200 318 Q197 280 200 236" strokeWidth="5" />
<path fill="none" d="M200 200 Q198 160 200 118" strokeWidth="6" />
<path fill="none" d="M184 98 Q200 82 216 98" strokeWidth="5" />
<path fill="none" d="M171 88 Q200 58 229 88" strokeWidth="5" />
<circle cx="200" cy="112" r="7" stroke="none" />
<path d="M168 201 L233 200 L234 235 L167 236 Z" strokeWidth="6" fill={paper} />
<path d="M179 212 h9 v11 h-9 z M196 212 h9 v11 h-9 z M213 212 h9 v11 h-9 z" stroke="none" />
{TENTS.map(([x, y]) => <Tent key={`${x}-${y}`} x={x} y={y} />)}
</>
);
}
export function LogoMark({
size = 32,
className = "text-accent",
title = "Tactical Media Broadcast Hub",
className = "text-text",
title = "T-HUB",
}: {
size?: number;
className?: string;
title?: string;
}): ReactElement {
return (
<svg
width={size}
height={size}
viewBox="0 0 1700 1700"
role="img"
aria-label={title}
className={className}
>
<g transform="rotate(-17.5 850 850)">
<g fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round">
<circle cx="850" cy="850" r="350" strokeWidth="74" />
<path d="M 850 405 L 464.77 1269.52" strokeWidth="88" />
<path d="M 850 405 L 1235.23 1269.52" strokeWidth="88" />
<path d="M 850 405 L 850 350" strokeWidth="88" />
<path d="M 345 850 L 1355 850" strokeWidth="88" />
</g>
<g fill="currentColor">
<polygon points="850,240 925,385 775,385" />
<polygon points="235,850 380,775 380,925" />
<polygon points="1465,850 1320,925 1320,775" />
<polygon points="420,1370 410.51,1207.03 547.52,1268.08" />
<polygon points="1280,1370 1152.48,1268.08 1289.49,1207.03" />
</g>
<svg width={size} height={size} viewBox="0 0 400 400" role="img" aria-label={title} className={className}>
<g strokeLinecap="round" strokeLinejoin="round">
<g transform="translate(7,7)" stroke="#00e5ff" fill="#00e5ff"><Mark paper="#fff" /></g>
<g stroke="currentColor" fill="currentColor"><Mark paper="#fff" /></g>
</g>
</svg>
);
@ -112,10 +123,14 @@ export function Logo({ size = 32, withText = true }: { size?: number; withText?:
<span className={`inline-flex ${stacked ? "flex-col items-center gap-3" : "items-center gap-2.5"}`}>
<LogoMark size={size} />
<span
className={`font-semibold tracking-tight text-text ${stacked ? "text-2xl" : ""}`}
style={stacked ? undefined : { fontSize: `${Math.max(13, Math.round(size * 0.45))}px` }}
className={`uppercase tracking-wide text-text ${stacked ? "text-4xl" : ""}`}
style={{
fontFamily: "var(--font-title)",
textShadow: "0.04em 0.04em 0 var(--color-neon)",
...(stacked ? {} : { fontSize: `${Math.max(16, Math.round(size * 0.6))}px` }),
}}
>
Tactical Media Broadcast Hub
T-HUB
</span>
</span>
);