added more complex cybersigilism generator
This commit is contained in:
@@ -185,7 +185,7 @@ export default function PostList({ posts: initialPosts, isAdmin = false }: Props
|
||||
</span>
|
||||
)}
|
||||
{post.draft && (
|
||||
<span className="plate-tag-mini" style={{ left: 18, right: 'auto', background: 'var(--peach)', color: 'var(--crust)' }}>
|
||||
<span className="plate-tag-mini plate-tag-mini--draft">
|
||||
Sketch
|
||||
</span>
|
||||
)}
|
||||
|
||||
+197
-74
@@ -1,110 +1,233 @@
|
||||
/*
|
||||
* cybersigil — modular neo-tribal sigil builder.
|
||||
* cybersigil — chaotic neo-tribal sigil generator.
|
||||
*
|
||||
* Cybersigilism's whole trick is hard vertical symmetry: author only the
|
||||
* right half, mirror it about a central spine. We keep a small library of
|
||||
* hand-drawn glyph blocks (blade / hook / thorn / loop / dagger / antenna /
|
||||
* fang / comet), each authored in local coords with the spine at x=0 growing
|
||||
* into +x. `buildCybersigil` randomly stacks 3–5 of them down the Y axis,
|
||||
* wraps the column in two <g>s (identity + scale(-1 1)) for the mirror, and
|
||||
* emits an inert SVG string.
|
||||
* Cybersigilism's signature is dense, fractal, barbed linework with hard
|
||||
* vertical symmetry. We grow it procedurally: a wavering central spine spawns
|
||||
* recursive curved limbs, each scattering barbs and thin filament shadows and
|
||||
* occasionally terminating in a small hand-drawn motif. The whole right-leaning
|
||||
* tangle is mirrored about x=0 (scale(-1 1)); a spine that wobbles in +x while
|
||||
* its mirror wobbles in −x weaves the two halves into one symmetric growth.
|
||||
*
|
||||
* Carving: every <path> carries pathLength="1" so the CSS draw-on animation
|
||||
* (stroke-dasharray/dashoffset in global.css, scoped to `.cybersigil`) needs
|
||||
* no JS length measurement. Each path gets an inline `--i` so strokes carve
|
||||
* top-to-bottom in sequence.
|
||||
*
|
||||
* Output is decorative and self-generated (no user input) — safe to inject
|
||||
* via innerHTML / dangerouslySetInnerHTML. Inert under every non-cybersigil
|
||||
* theme because all styling is `.cybersigil`-scoped.
|
||||
* Strokes carry pathLength="1" so the CSS draw-on ("carve") animation needs no
|
||||
* JS length measurement; an inline `--i` staggers the carve into waves.
|
||||
* Output is decorative and self-generated (no user input) — safe to inject via
|
||||
* innerHTML. Inert under non-cybersigil themes (all styling `.cybersigil`-scoped).
|
||||
*/
|
||||
|
||||
export interface Glyph {
|
||||
/** max extent from the spine on +x */
|
||||
w: number;
|
||||
/** vertical run of the block */
|
||||
h: number;
|
||||
/** path data, spine at x=0, y in [0,h] */
|
||||
/** path data, local coords, anchored near origin */
|
||||
d: string;
|
||||
}
|
||||
|
||||
// Right-half building blocks. Each starts/ends near the spine (x≈0) so the
|
||||
// standalone spine line knits the column into one continuous growth.
|
||||
// Small hand-drawn motifs used as limb-tip flourishes — the deliberate,
|
||||
// "carved on purpose" punctuation amid the procedural chaos.
|
||||
export const GLYPHS: readonly Glyph[] = [
|
||||
// blade — long sweep that hooks back, two cast-off barbs
|
||||
{ w: 30, h: 36, d: 'M0 2 Q30 8 25 22 Q21 33 0 35 M5 11 L15 5 M19 17 L28 13' },
|
||||
// hook — tight inward claw with a tip spur
|
||||
{ w: 26, h: 29, d: 'M0 5 Q24 3 24 16 Q24 27 7 28 Q0 28 3 21 M0 5 L6 0' },
|
||||
// thorn cluster — spine run throwing three barbs
|
||||
{ w: 17, h: 30, d: 'M0 0 L0 30 M0 6 L15 2 M0 15 L17 11 M0 24 L12 22' },
|
||||
// loop — teardrop eye off the spine
|
||||
{ w: 29, h: 30, d: 'M0 4 Q28 1 28 17 Q28 30 12 29 Q4 29 6 21 Q9 16 16 18 M0 4 L0 30' },
|
||||
// dagger — crossbar over a descending blade
|
||||
{ w: 19, h: 34, d: 'M0 0 L0 34 M0 9 L19 9 M0 9 L14 3 M0 22 L11 27' },
|
||||
// antenna — rising spike with a split tip
|
||||
{ w: 25, h: 30, d: 'M0 24 Q7 12 19 4 M19 4 L15 0 M19 4 L25 2 M0 24 L0 30' },
|
||||
// fang — bold curved fang with an inner notch
|
||||
{ w: 23, h: 38, d: 'M0 0 Q21 9 22 24 Q22 35 11 37 M0 0 L0 38 M8 14 Q15 18 15 27' },
|
||||
// comet — node trailing two barbs
|
||||
{ w: 30, h: 32, d: 'M0 8 Q17 3 23 12 Q27 19 20 24 Q13 30 6 25 M23 12 L30 9 M20 24 L27 30 M0 0 L0 32' },
|
||||
{ w: 18, h: 22, d: 'M0 0 Q16 5 14 14 Q12 21 0 22 M4 7 L10 3' },
|
||||
{ w: 16, h: 16, d: 'M0 8 Q14 0 15 8 Q14 16 0 8 M8 2 L8 14' },
|
||||
{ w: 14, h: 24, d: 'M0 0 L0 24 M0 6 L12 2 M0 14 L13 10 M0 21 L9 19' },
|
||||
{ w: 20, h: 18, d: 'M0 9 Q10 -2 19 4 M19 4 L15 0 M19 4 L20 9 M0 9 L4 16' },
|
||||
{ w: 15, h: 20, d: 'M0 0 Q14 6 13 13 Q12 19 0 19 M7 8 Q11 11 11 16' },
|
||||
] as const;
|
||||
|
||||
export interface SigilOptions {
|
||||
/** how many blocks to stack; default random 3–5 */
|
||||
/** rough number of primary branch nodes; default random 6–10 */
|
||||
count?: number;
|
||||
/** injectable RNG (0..1); default Math.random */
|
||||
rng?: () => number;
|
||||
}
|
||||
|
||||
const PAD = 6;
|
||||
/** blocks knit slightly so the spine reads unbroken */
|
||||
const OVERLAP = 4;
|
||||
const H = 200; // internal canvas height (viewBox scales it to fit)
|
||||
const PAD = 14;
|
||||
const MAX_PATHS = 110; // safety ceiling; real density is bounded by the params
|
||||
|
||||
function pickIndices(n: number, total: number, rng: () => number): number[] {
|
||||
const pool = Array.from({ length: total }, (_, i) => i);
|
||||
for (let i = pool.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(rng() * (i + 1));
|
||||
[pool[i], pool[j]] = [pool[j], pool[i]];
|
||||
}
|
||||
return pool.slice(0, n);
|
||||
}
|
||||
type Pt = [number, number];
|
||||
|
||||
/**
|
||||
* Build a single mirrored sigil and return it as an SVG markup string.
|
||||
* Random per call unless a deterministic `rng` is supplied.
|
||||
*/
|
||||
export function buildCybersigil(opts: SigilOptions = {}): string {
|
||||
const rng = opts.rng ?? Math.random;
|
||||
const count = opts.count ?? 3 + Math.floor(rng() * 3); // 3–5
|
||||
const chosen = pickIndices(count, GLYPHS.length, rng).map((i) => GLYPHS[i]);
|
||||
const rnd = (a: number, b: number) => a + rng() * (b - a);
|
||||
const pick = <T>(a: readonly T[]): T => a[Math.floor(rng() * a.length)];
|
||||
const n = (v: number) => {
|
||||
const r = Math.round(v * 10) / 10;
|
||||
return Object.is(r, -0) ? '0' : String(r);
|
||||
};
|
||||
|
||||
let y = 0;
|
||||
let maxW = 0;
|
||||
let bottom = 0;
|
||||
const groups: string[] = [];
|
||||
const parts: string[] = [];
|
||||
let strokeCount = 0;
|
||||
let maxX = 24;
|
||||
|
||||
chosen.forEach((g, idx) => {
|
||||
groups.push(
|
||||
`<g transform="translate(0 ${y})"><path d="${g.d}" pathLength="1" style="--i:${idx + 1}"/></g>`,
|
||||
const track = (x: number) => {
|
||||
const ax = Math.abs(x);
|
||||
if (ax > maxX) maxX = ax;
|
||||
};
|
||||
const emit = (d: string, cls: string) => {
|
||||
if (strokeCount >= MAX_PATHS) return;
|
||||
parts.push(
|
||||
`<path class="${cls}" d="${d}" pathLength="1" style="--i:${strokeCount % 16}"/>`,
|
||||
);
|
||||
bottom = Math.max(bottom, y + g.h);
|
||||
maxW = Math.max(maxW, g.w);
|
||||
y += g.h - OVERLAP;
|
||||
});
|
||||
strokeCount++;
|
||||
};
|
||||
|
||||
const half = groups.join('');
|
||||
const spine = `<line class="cs-sig-spine" x1="0" y1="0" x2="0" y2="${bottom}" pathLength="1" style="--i:0"/>`;
|
||||
// Catmull-Rom → cubic Bézier through an ordered point list (organic sweep).
|
||||
const spline = (pts: Pt[]): string => {
|
||||
if (pts.length < 2) return '';
|
||||
let d = `M${n(pts[0][0])} ${n(pts[0][1])}`;
|
||||
for (let i = 0; i < pts.length - 1; i++) {
|
||||
const p0 = pts[i - 1] ?? pts[i];
|
||||
const p1 = pts[i];
|
||||
const p2 = pts[i + 1];
|
||||
const p3 = pts[i + 2] ?? p2;
|
||||
const c1: Pt = [p1[0] + (p2[0] - p0[0]) / 6, p1[1] + (p2[1] - p0[1]) / 6];
|
||||
const c2: Pt = [p2[0] - (p3[0] - p1[0]) / 6, p2[1] - (p3[1] - p1[1]) / 6];
|
||||
d += `C${n(c1[0])} ${n(c1[1])} ${n(c2[0])} ${n(c2[1])} ${n(p2[0])} ${n(p2[1])}`;
|
||||
track(c1[0]);
|
||||
track(c2[0]);
|
||||
track(p2[0]);
|
||||
}
|
||||
return d;
|
||||
};
|
||||
|
||||
const minX = -(maxW + PAD);
|
||||
const vbW = 2 * (maxW + PAD);
|
||||
const minY = -PAD;
|
||||
const vbH = bottom + 2 * PAD;
|
||||
// A short spike, optionally kinked into a fang.
|
||||
const barb = (base: Pt, ang: number, len: number) => {
|
||||
const tip: Pt = [base[0] + Math.cos(ang) * len, base[1] + Math.sin(ang) * len];
|
||||
if (tip[0] < -3) tip[0] = -3;
|
||||
track(base[0]);
|
||||
track(tip[0]);
|
||||
if (rng() < 0.45) {
|
||||
const mid: Pt = [
|
||||
(base[0] + tip[0]) / 2 + Math.cos(ang + Math.PI / 2) * len * 0.3,
|
||||
(base[1] + tip[1]) / 2 + Math.sin(ang + Math.PI / 2) * len * 0.3,
|
||||
];
|
||||
emit(
|
||||
`M${n(base[0])} ${n(base[1])}Q${n(mid[0])} ${n(mid[1])} ${n(tip[0])} ${n(tip[1])}`,
|
||||
'cs-sig-barb',
|
||||
);
|
||||
} else {
|
||||
emit(`M${n(base[0])} ${n(base[1])}L${n(tip[0])} ${n(tip[1])}`, 'cs-sig-barb');
|
||||
}
|
||||
};
|
||||
|
||||
const ornament = (at: Pt, ang: number) => {
|
||||
const g = pick(GLYPHS);
|
||||
const s = rnd(0.45, 0.85);
|
||||
const deg = (ang * 180) / Math.PI + rnd(-25, 25);
|
||||
track(at[0] + g.w * s);
|
||||
parts.push(
|
||||
`<g transform="translate(${n(at[0])} ${n(at[1])}) rotate(${n(deg)}) scale(${n(s)})">` +
|
||||
`<path class="cs-sig-orn" d="${g.d}" pathLength="1" style="--i:${strokeCount % 16}"/></g>`,
|
||||
);
|
||||
strokeCount++;
|
||||
};
|
||||
|
||||
// Recursive limb: a curved sweep out from (ox,oy) along `ang`, hooking back,
|
||||
// scattering barbs, shedding a filament shadow, branching, sometimes tipped
|
||||
// with a motif.
|
||||
const limb = (ox: number, oy: number, ang: number, scale: number, depth: number) => {
|
||||
if (strokeCount >= MAX_PATHS) return;
|
||||
const L = scale * rnd(34, 64);
|
||||
const dx = Math.cos(ang) * L;
|
||||
const dy = Math.sin(ang) * L;
|
||||
const peak: Pt = [ox + dx, oy + dy];
|
||||
const mid: Pt = [
|
||||
ox + dx * 0.45 + Math.cos(ang + Math.PI / 2) * rnd(-10, 14),
|
||||
oy + dy * 0.45 + Math.sin(ang + Math.PI / 2) * rnd(-10, 14),
|
||||
];
|
||||
// hook back toward the spine
|
||||
const hook: Pt = [
|
||||
peak[0] - Math.cos(ang) * L * rnd(0.3, 0.55),
|
||||
peak[1] + Math.sin(ang + 0.7) * L * rnd(0.25, 0.5),
|
||||
];
|
||||
const tail: Pt = [Math.max(-2, hook[0] - L * rnd(0.2, 0.4)), hook[1] + rnd(-6, 10)];
|
||||
const pts: Pt[] = [[ox, oy], mid, peak, hook, tail];
|
||||
emit(spline(pts), 'cs-sig-main');
|
||||
|
||||
// terminal spike off the outermost point
|
||||
barb(peak, ang + rnd(-0.5, 0.5), scale * rnd(8, 18));
|
||||
|
||||
// filament shadow trailing the main sweep
|
||||
if (rng() < 0.4) {
|
||||
const off = rnd(2, 6);
|
||||
emit(
|
||||
spline(
|
||||
pts.map(
|
||||
([x, y]) =>
|
||||
[x + Math.cos(ang + Math.PI / 2) * off, y + Math.sin(ang + Math.PI / 2) * off] as Pt,
|
||||
),
|
||||
),
|
||||
'cs-sig-fil',
|
||||
);
|
||||
}
|
||||
|
||||
// barb scatter along the chord
|
||||
const nb = 1 + Math.floor(rng() * 2);
|
||||
for (let k = 0; k < nb; k++) {
|
||||
const t = (k + 1) / (nb + 1);
|
||||
const seg = t < 0.5 ? [pts[0], pts[2], t * 2] : [pts[2], pts[4], (t - 0.5) * 2];
|
||||
const a = seg[0] as Pt;
|
||||
const b = seg[1] as Pt;
|
||||
const tt = seg[2] as number;
|
||||
const base: Pt = [a[0] + (b[0] - a[0]) * tt, a[1] + (b[1] - a[1]) * tt];
|
||||
const side = k % 2 ? 1 : -1;
|
||||
barb(base, ang + side * rnd(0.6, 1.3), scale * rnd(6, 16));
|
||||
}
|
||||
|
||||
// recurse — one child curls off the mid/peak region
|
||||
if (depth > 0 && rng() < 0.55) {
|
||||
const from = rng() < 0.5 ? mid : peak;
|
||||
limb(
|
||||
from[0],
|
||||
from[1],
|
||||
ang + (rng() < 0.5 ? 1 : -1) * rnd(0.5, 1.2),
|
||||
scale * rnd(0.42, 0.6),
|
||||
depth - 1,
|
||||
);
|
||||
}
|
||||
|
||||
// motif flourish at a terminal tip
|
||||
if (depth === 0 && rng() < 0.3) ornament(peak, ang);
|
||||
};
|
||||
|
||||
// ── Wavering spine: a curve from top to bottom, gently bowing in +x.
|
||||
const spineNodes = 5 + Math.floor(rng() * 3);
|
||||
const spinePts: Pt[] = [];
|
||||
for (let i = 0; i <= spineNodes; i++) {
|
||||
const y = (H * i) / spineNodes;
|
||||
const x = i === 0 || i === spineNodes ? 0 : rnd(0, 11);
|
||||
spinePts.push([x, y]);
|
||||
}
|
||||
emit(spline(spinePts), 'cs-sig-main');
|
||||
|
||||
// ── Branch nodes ride the spine and throw limbs outward. Nodes are
|
||||
// inset from the very ends and spread the full height so growth flows
|
||||
// down the whole trunk rather than clumping at the top.
|
||||
const nodes = opts.count ?? 7 + Math.floor(rng() * 3); // 7–9
|
||||
for (let i = 0; i < nodes; i++) {
|
||||
const t = 0.08 + (0.86 * (i + rnd(-0.25, 0.25))) / (nodes - 1);
|
||||
const tc = Math.max(0.05, Math.min(0.95, t));
|
||||
const si = Math.min(spinePts.length - 2, Math.floor(tc * (spinePts.length - 1)));
|
||||
const sf = tc * (spinePts.length - 1) - si;
|
||||
const a = spinePts[si];
|
||||
const b = spinePts[si + 1];
|
||||
const node: Pt = [a[0] + (b[0] - a[0]) * sf, a[1] + (b[1] - a[1]) * sf];
|
||||
// later nodes lean downward so the lower trunk fills out
|
||||
const bias = -0.25 + tc * 0.7;
|
||||
const limbs = 1 + Math.floor(rng() * 2);
|
||||
for (let l = 0; l < limbs; l++) {
|
||||
const ang = bias + rnd(-0.55, 0.55);
|
||||
limb(node[0], node[1], ang, rnd(0.65, 1.05), 1);
|
||||
}
|
||||
// the odd bare barb straight off the spine keeps the trunk prickly
|
||||
if (rng() < 0.55) barb(node, bias + rnd(-0.3, 0.3), rnd(7, 14));
|
||||
}
|
||||
|
||||
const half = parts.join('');
|
||||
const minX = -(maxX + PAD);
|
||||
const vbW = 2 * (maxX + PAD);
|
||||
return (
|
||||
`<svg class="cs-sigil" viewBox="${minX} ${minY} ${vbW} ${vbH}" ` +
|
||||
`<svg class="cs-sigil" viewBox="${n(minX)} ${-PAD} ${n(vbW)} ${H + 2 * PAD}" ` +
|
||||
`preserveAspectRatio="xMidYMid meet" aria-hidden="true" focusable="false" ` +
|
||||
`xmlns="http://www.w3.org/2000/svg">` +
|
||||
spine +
|
||||
`<g class="cs-sig-half">${half}</g>` +
|
||||
`<g class="cs-sig-half" transform="scale(-1 1)">${half}</g>` +
|
||||
`</svg>`
|
||||
|
||||
@@ -837,6 +837,15 @@ code, pre, kbd, samp {
|
||||
text-transform: uppercase;
|
||||
backdrop-filter: blur(3px);
|
||||
}
|
||||
/* Draft/"Sketch" marker — same chip, pinned bottom-left, amber instead of
|
||||
* the neutral catalogue tag. Themed per skin below (no inline colors). */
|
||||
.plate-tag-mini--draft {
|
||||
left: 16px;
|
||||
right: auto;
|
||||
background: color-mix(in srgb, var(--peach) 88%, var(--crust));
|
||||
color: var(--crust);
|
||||
border-color: color-mix(in srgb, var(--peach) 45%, transparent);
|
||||
}
|
||||
/* Breakcore: hard neon catalogue tag — sharp rect, offset shadow, glow.
|
||||
* Matches the layer's hazard-tape / hard-offset chrome language. */
|
||||
.breakcore .plate-tag-mini {
|
||||
@@ -853,6 +862,14 @@ code, pre, kbd, samp {
|
||||
0 0 14px -2px color-mix(in srgb, var(--mauve) 65%, transparent);
|
||||
backdrop-filter: none;
|
||||
}
|
||||
.breakcore .plate-tag-mini--draft {
|
||||
color: var(--peach);
|
||||
border-color: var(--peach);
|
||||
text-shadow: 0 0 6px color-mix(in srgb, var(--peach) 60%, transparent);
|
||||
box-shadow:
|
||||
2px 2px 0 var(--peach),
|
||||
0 0 14px -2px color-mix(in srgb, var(--peach) 65%, transparent);
|
||||
}
|
||||
|
||||
/* Nameplate — the museum-style header used in the site chrome */
|
||||
.nameplate {
|
||||
@@ -2033,8 +2050,7 @@ html.cybersigil body::after {
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
.cybersigil .cs-sigil path,
|
||||
.cybersigil .cs-sigil line {
|
||||
.cybersigil .cs-sigil path {
|
||||
fill: none;
|
||||
stroke: var(--sky);
|
||||
stroke-width: 2;
|
||||
@@ -2044,7 +2060,11 @@ html.cybersigil body::after {
|
||||
stroke-dasharray: 1;
|
||||
stroke-dashoffset: 1;
|
||||
}
|
||||
.cybersigil .cs-sig-spine { opacity: 0.45; stroke-width: 1.4; }
|
||||
/* Stroke-weight tiers — heavy growth, hair filaments, prickly barbs, motifs. */
|
||||
.cybersigil .cs-sigil .cs-sig-main { stroke-width: 2.4; }
|
||||
.cybersigil .cs-sigil .cs-sig-fil { stroke-width: 0.9; opacity: 0.5; }
|
||||
.cybersigil .cs-sigil .cs-sig-barb { stroke-width: 1.3; }
|
||||
.cybersigil .cs-sigil .cs-sig-orn { stroke-width: 1.7; opacity: 0.92; }
|
||||
|
||||
@keyframes cs-carve {
|
||||
to { stroke-dashoffset: 0; }
|
||||
@@ -2167,21 +2187,21 @@ html.cybersigil body::after {
|
||||
0 22px 44px -28px rgba(79, 233, 255, 0.26),
|
||||
0 0 26px -10px color-mix(in srgb, var(--mauve) 34%, transparent);
|
||||
}
|
||||
/* Thin engraved corner brackets. The deliberate sigil motif now comes from
|
||||
* the generated cs-plate-sig, so the old organic leaf masks are dropped. */
|
||||
.cybersigil .plate::before,
|
||||
.cybersigil .plate::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
background-color: color-mix(in srgb, var(--sky) 78%, transparent);
|
||||
-webkit-mask: var(--cs-corner) center / contain no-repeat;
|
||||
mask: var(--cs-corner) center / contain no-repeat;
|
||||
filter: drop-shadow(0 0 3px color-mix(in srgb, var(--sky) 45%, transparent));
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
pointer-events: none;
|
||||
transition: background-color 0.18s ease, filter 0.18s ease;
|
||||
border: 0 solid color-mix(in srgb, var(--sky) 55%, transparent);
|
||||
filter: drop-shadow(0 0 3px color-mix(in srgb, var(--sky) 32%, transparent));
|
||||
transition: border-color 0.18s ease, filter 0.18s ease;
|
||||
}
|
||||
.cybersigil .plate::before { top: 5px; left: 5px; }
|
||||
.cybersigil .plate::after { right: 5px; bottom: 5px; transform: scale(-1); }
|
||||
.cybersigil .plate::before { top: 6px; left: 6px; border-top-width: 2px; border-left-width: 2px; }
|
||||
.cybersigil .plate::after { right: 6px; bottom: 6px; border-right-width: 2px; border-bottom-width: 2px; }
|
||||
.cybersigil .plate:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow:
|
||||
@@ -2192,7 +2212,7 @@ html.cybersigil body::after {
|
||||
}
|
||||
.cybersigil .plate:hover::before,
|
||||
.cybersigil .plate:hover::after {
|
||||
background-color: var(--mauve);
|
||||
border-color: var(--mauve);
|
||||
filter: drop-shadow(0 0 6px color-mix(in srgb, var(--mauve) 60%, transparent));
|
||||
}
|
||||
.cybersigil .plate:focus-visible {
|
||||
@@ -2290,22 +2310,8 @@ html.cybersigil body::after {
|
||||
animation: cs-databend 560ms steps(5, jump-none) 1;
|
||||
}
|
||||
|
||||
/* Prose image framing — thorny growth at opposing corners + cold edge. */
|
||||
.cybersigil .prose figure { position: relative; }
|
||||
.cybersigil .prose figure::before,
|
||||
.cybersigil .prose figure::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-color: color-mix(in srgb, var(--sky) 70%, transparent);
|
||||
-webkit-mask: var(--cs-corner) center / contain no-repeat;
|
||||
mask: var(--cs-corner) center / contain no-repeat;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
.cybersigil .prose figure::before { top: -8px; left: -8px; }
|
||||
.cybersigil .prose figure::after { right: -8px; bottom: -8px; transform: scale(-1); }
|
||||
/* Prose image framing — cold gradient edge only. Old corner leaf masks
|
||||
* removed; figural sigils are no longer pinned to post images. */
|
||||
.cybersigil .prose figure img,
|
||||
.cybersigil .prose img {
|
||||
background:
|
||||
@@ -2694,6 +2700,16 @@ html.cybersigil body::after {
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
/* Sketch marker reads as an unstable/draft signal — amber glyph, split
|
||||
* chromatic shadow, hard offset, matching the corrupted-terminal language. */
|
||||
.cybersigil .plate-tag-mini--draft {
|
||||
color: var(--peach);
|
||||
border-color: var(--peach);
|
||||
box-shadow: 2px 2px 0 color-mix(in srgb, var(--peach) 70%, transparent);
|
||||
text-shadow:
|
||||
-1px 0 0 color-mix(in srgb, var(--peach) 65%, transparent),
|
||||
1px 0 0 color-mix(in srgb, var(--sky) 55%, transparent);
|
||||
}
|
||||
.cybersigil .kbd-tip {
|
||||
background: var(--crust);
|
||||
border-color: var(--mauve);
|
||||
|
||||
Reference in New Issue
Block a user