Embed EpiphanyGraph biosphere viewer
This commit is contained in:
parent
bb7255205b
commit
f23e4891f7
17 changed files with 2223 additions and 529 deletions
|
|
@ -5,7 +5,9 @@ societies. It is a nested web of memory-bearing contracts. A cell, wound, herd,
|
|||
route, reef, breeding ground, and nation can all be political at their own scale
|
||||
without becoming the same kind of mind.
|
||||
|
||||
<div class="zyphos-biosphere-graph"></div>
|
||||
<link rel="stylesheet" href="/static/epiphany-graph/assets/viewer.css" />
|
||||
<div class="zyphos-epiphany-graph-root"></div>
|
||||
<script type="module" src="/static/epiphany-graph/assets/viewer.js"></script>
|
||||
|
||||
## Scale Bands
|
||||
|
||||
|
|
|
|||
1
Eusocial Interbeing/static/epiphany-graph/assets/elk.js
Normal file
1
Eusocial Interbeing/static/epiphany-graph/assets/elk.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
function r(o){throw new Error('Could not dynamically require "'+o+'". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.')}export{r as c};
|
||||
1
Eusocial Interbeing/static/epiphany-graph/assets/react.js
vendored
Normal file
1
Eusocial Interbeing/static/epiphany-graph/assets/react.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1 @@
|
|||
html,body,#root{min-height:100%;margin:0}body{background:radial-gradient(circle at 72% 12%,rgba(255,174,88,.16),transparent 24%),radial-gradient(circle at 10% 18%,rgba(142,223,176,.16),transparent 28%),linear-gradient(180deg,#03070d,#07110f 52%,#091712);color:#effcf8;font-family:Ubuntu,Segoe UI,sans-serif}.zyphos-epiphany-graph-app{box-sizing:border-box;min-height:100vh;padding:16px}.zyphos-epiphany-graph-app *{box-sizing:border-box}.zyphos-graph-error{white-space:pre-wrap;color:#fecaca;background:#450a0a99;border:1px solid rgba(248,113,113,.45);border-radius:16px;padding:16px}
|
||||
33
Eusocial Interbeing/static/epiphany-graph/assets/viewer.js
Normal file
33
Eusocial Interbeing/static/epiphany-graph/assets/viewer.js
Normal file
File diff suppressed because one or more lines are too long
15
Eusocial Interbeing/static/epiphany-graph/viewer.html
Normal file
15
Eusocial Interbeing/static/epiphany-graph/viewer.html
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Zyphos Fractal Eusocial Web</title>
|
||||
<script type="module" crossorigin src="./assets/viewer.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="./assets/react.js">
|
||||
<link rel="modulepreload" crossorigin href="./assets/elk.js">
|
||||
<link rel="stylesheet" crossorigin href="./assets/viewer.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="zyphos-epiphany-graph-root"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -13,3 +13,19 @@ The site follows the same deployment pattern as `AetheriaLore`:
|
|||
- custom domain: `zyphos.gamecult.org`
|
||||
|
||||
DNS should point `zyphos.gamecult.org` at GitHub Pages, not Yggdrasil.
|
||||
|
||||
## EpiphanyGraph Embed
|
||||
|
||||
The fractal biosphere graph uses a small React/Vite embed around the neighboring
|
||||
`EpiphanyGraph` viewer source. Quartz does not build this bundle during Pages
|
||||
deploys, so rebuild the static assets before committing graph embed changes:
|
||||
|
||||
```powershell
|
||||
cd "E:\Projects\Eusocial Interbeing\site\epiphany-graph-embed"
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
The build writes deployable assets into
|
||||
`Eusocial Interbeing/static/epiphany-graph/`, which Quartz then copies into the
|
||||
site output.
|
||||
|
|
|
|||
1
site/epiphany-graph-embed/.gitignore
vendored
Normal file
1
site/epiphany-graph-embed/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
node_modules/
|
||||
1724
site/epiphany-graph-embed/package-lock.json
generated
Normal file
1724
site/epiphany-graph-embed/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
18
site/epiphany-graph-embed/package.json
Normal file
18
site/epiphany-graph-embed/package.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "@gamecult/zyphos-epiphany-graph-embed",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "vite build --base ./"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vitejs/plugin-react": "^5.1.1",
|
||||
"elkjs": "^0.11.0",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.2.4"
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
||||
318
site/epiphany-graph-embed/src/main.tsx
Normal file
318
site/epiphany-graph-embed/src/main.tsx
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
import React from "react"
|
||||
import { flushSync } from "react-dom"
|
||||
import ReactDOM from "react-dom/client"
|
||||
import { EpiphanyGraphViewer } from "../../../../EpiphanyGraph/web/epiphany-graph-viewer/src/lib/EpiphanyGraphViewer"
|
||||
import type { EpiphanyGraphsState } from "../../../../EpiphanyGraph/web/epiphany-graph-viewer/src/lib/types"
|
||||
import "./styles.css"
|
||||
|
||||
type GraphErrorBoundaryState = {
|
||||
error: Error | null
|
||||
}
|
||||
|
||||
class GraphErrorBoundary extends React.Component<React.PropsWithChildren, GraphErrorBoundaryState> {
|
||||
state: GraphErrorBoundaryState = { error: null }
|
||||
|
||||
static getDerivedStateFromError(error: Error) {
|
||||
return { error }
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.error) {
|
||||
return (
|
||||
<pre className="zyphos-graph-error">
|
||||
{this.state.error.message}
|
||||
{"\n"}
|
||||
{this.state.error.stack}
|
||||
</pre>
|
||||
)
|
||||
}
|
||||
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
||||
const zyphosGraphState: EpiphanyGraphsState = {
|
||||
architecture: {
|
||||
nodes: [
|
||||
{
|
||||
id: "memorioplast",
|
||||
title: "Memorioplast Substrate",
|
||||
status: "verified",
|
||||
purpose: "Mutable memory endosymbionts make memory exchange cellular before it becomes culture.",
|
||||
mechanism: "Cells store, edit, and transmit local state through symbiotic memory organelles.",
|
||||
metaphor: "The biosphere's smallest treaty office.",
|
||||
},
|
||||
{
|
||||
id: "tissue-boundaries",
|
||||
title: "Tissue Boundary Politics",
|
||||
status: "active",
|
||||
purpose: "Immune and repair systems decide what counts as self enough to enter a body.",
|
||||
mechanism: "Wounds, grafts, gut traffic, and root sheaths meter memory compatibility by channel.",
|
||||
metaphor: "Customs inspection conducted by flesh.",
|
||||
},
|
||||
{
|
||||
id: "organism-contracts",
|
||||
title: "Organism Contracts",
|
||||
status: "active",
|
||||
purpose: "Mobile and rooted beings remain semi-independent while serving larger memory economies.",
|
||||
mechanism: "Prismwake mats, threadwings, glassbacks, burden flowers, and lantern trees exchange food, route memory, warning, and sanction.",
|
||||
metaphor: "Individuals with several employers and no clean resignation path.",
|
||||
},
|
||||
{
|
||||
id: "colony-routes",
|
||||
title: "Colony and Route Bodies",
|
||||
status: "active",
|
||||
purpose: "Colonies and logistics corridors become stable organs of larger ecological memory.",
|
||||
mechanism: "Lattice ants, candle fungal roads, and choir reefs read local history and sell or gift transit.",
|
||||
metaphor: "Roads that remember your manners.",
|
||||
},
|
||||
{
|
||||
id: "archive-commons",
|
||||
title: "Archives and Commons",
|
||||
status: "active",
|
||||
purpose: "Long-lived archives and reproductive commons hold continuity beyond mobile lifetimes.",
|
||||
mechanism: "Mother trees guard deep memory while breeding grounds bind caretakers, infants, symbionts, and law.",
|
||||
metaphor: "Library, nursery, court, and immune organ pretending to be a place.",
|
||||
},
|
||||
{
|
||||
id: "native-nations",
|
||||
title: "Native Nation Scale Interbeings",
|
||||
status: "verified",
|
||||
purpose: "Multispecies territorial bodies manage boundary permeability as foreign policy.",
|
||||
mechanism: "Archival, migratory, fungal-road, reef, and wetland polities admit or reject traffic by channel.",
|
||||
metaphor: "A border that is alive enough to be offended.",
|
||||
},
|
||||
{
|
||||
id: "imperial-clausiform",
|
||||
title: "Imperium airavense clausum",
|
||||
status: "verified",
|
||||
purpose: "Artificial quarter-continent superorganism with strict boundaries and tuned autonomy.",
|
||||
mechanism: "The Biological Memetic Engine authors an impermeable topology where all permitted variation serves imperial goals.",
|
||||
metaphor: "A utility function wearing ancestral skin.",
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: "cell-to-tissue",
|
||||
source_id: "memorioplast",
|
||||
target_id: "tissue-boundaries",
|
||||
kind: "scales",
|
||||
label: "scales",
|
||||
mechanism: "Cellular memory compatibility becomes immune and repair policy.",
|
||||
},
|
||||
{
|
||||
id: "tissue-to-organism",
|
||||
source_id: "tissue-boundaries",
|
||||
target_id: "organism-contracts",
|
||||
kind: "bounds",
|
||||
label: "bounds",
|
||||
mechanism: "Bodies inherit boundary habits from tissue-scale memory law.",
|
||||
},
|
||||
{
|
||||
id: "organism-to-route",
|
||||
source_id: "organism-contracts",
|
||||
target_id: "colony-routes",
|
||||
kind: "routes",
|
||||
label: "routes",
|
||||
mechanism: "Repeated exchanges stabilize into trails, colonies, and diagnostic circuits.",
|
||||
},
|
||||
{
|
||||
id: "route-to-archive",
|
||||
source_id: "colony-routes",
|
||||
target_id: "archive-commons",
|
||||
kind: "connects",
|
||||
label: "connects",
|
||||
mechanism: "Routes connect breeding grounds, mother archives, shelters, licks, and corpse gardens.",
|
||||
},
|
||||
{
|
||||
id: "archive-to-nation",
|
||||
source_id: "archive-commons",
|
||||
target_id: "native-nations",
|
||||
kind: "scales",
|
||||
label: "scales",
|
||||
mechanism: "Archives and commons become durable territorial agency.",
|
||||
},
|
||||
{
|
||||
id: "empire-attacks-native",
|
||||
source_id: "imperial-clausiform",
|
||||
target_id: "native-nations",
|
||||
kind: "invades",
|
||||
label: "invades",
|
||||
mechanism: "Imperial topology tries to overwrite negotiated permeability with uniform control.",
|
||||
},
|
||||
],
|
||||
},
|
||||
dataflow: {
|
||||
nodes: [
|
||||
{
|
||||
id: "memory-state",
|
||||
title: "Mutable Memory State",
|
||||
status: "verified",
|
||||
purpose: "Local experience becomes transferable biological state.",
|
||||
mechanism: "Endosymbionts store route, injury, trust, hunger, and taboo emphasis.",
|
||||
metaphor: "Evidence that can breed.",
|
||||
},
|
||||
{
|
||||
id: "compatibility-filter",
|
||||
title: "Compatibility Filter",
|
||||
status: "active",
|
||||
purpose: "Organisms decide which traffic may cross which boundary.",
|
||||
mechanism: "Permeability differs for nutrient, symbiont, memory, reproductive, and military channels.",
|
||||
metaphor: "Diplomacy as membrane behavior.",
|
||||
},
|
||||
{
|
||||
id: "contract-ledger",
|
||||
title: "Contract Ledger",
|
||||
status: "active",
|
||||
purpose: "Repeated exchange accumulates into debt, trust, warning, and sanction.",
|
||||
mechanism: "Mats, roads, reefs, flowers, and herds remember who fed, cheated, healed, or lied.",
|
||||
metaphor: "Ecology keeping receipts.",
|
||||
},
|
||||
{
|
||||
id: "route-authority",
|
||||
title: "Route Authority",
|
||||
status: "active",
|
||||
purpose: "Transit becomes governance when places open, close, tax, or reroute movement.",
|
||||
mechanism: "Threadwings, fungal roads, lattice ants, and seasonal corridors carry permission.",
|
||||
metaphor: "Infrastructure with opinions.",
|
||||
},
|
||||
{
|
||||
id: "sovereign-permeability",
|
||||
title: "Sovereign Permeability",
|
||||
status: "verified",
|
||||
purpose: "At nation scale, boundary policy becomes foreign policy.",
|
||||
mechanism: "Native nations apply different rules to each traffic channel instead of one flat border.",
|
||||
metaphor: "A treaty made of valves.",
|
||||
},
|
||||
{
|
||||
id: "imperial-uniformity",
|
||||
title: "Imperial Uniformity",
|
||||
status: "verified",
|
||||
purpose: "The Empire replaces negotiated ecology with controlled memetic structure.",
|
||||
mechanism: "It allows exactly the autonomy needed for productivity and rejects uncontrolled memory traffic.",
|
||||
metaphor: "Harmony with a knife behind it.",
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
id: "state-filter",
|
||||
source_id: "memory-state",
|
||||
target_id: "compatibility-filter",
|
||||
kind: "filtered_by",
|
||||
label: "filtered by",
|
||||
mechanism: "Memory is useful only if a boundary admits or rewrites it.",
|
||||
},
|
||||
{
|
||||
id: "filter-ledger",
|
||||
source_id: "compatibility-filter",
|
||||
target_id: "contract-ledger",
|
||||
kind: "records",
|
||||
label: "records",
|
||||
mechanism: "Accepted or rejected traffic becomes remembered precedent.",
|
||||
},
|
||||
{
|
||||
id: "ledger-route",
|
||||
source_id: "contract-ledger",
|
||||
target_id: "route-authority",
|
||||
kind: "authorizes",
|
||||
label: "authorizes",
|
||||
mechanism: "Debts and trust decide which roads, herds, and reefs open.",
|
||||
},
|
||||
{
|
||||
id: "route-sovereignty",
|
||||
source_id: "route-authority",
|
||||
target_id: "sovereign-permeability",
|
||||
kind: "scales",
|
||||
label: "scales",
|
||||
mechanism: "Route decisions aggregate into nation-scale boundary regimes.",
|
||||
},
|
||||
{
|
||||
id: "uniformity-overwrites",
|
||||
source_id: "imperial-uniformity",
|
||||
target_id: "sovereign-permeability",
|
||||
kind: "overwrites",
|
||||
label: "overwrites",
|
||||
mechanism: "The Empire treats local permeability as disorder to be corrected.",
|
||||
},
|
||||
],
|
||||
},
|
||||
links: [
|
||||
{
|
||||
architecture_node_id: "memorioplast",
|
||||
dataflow_node_id: "memory-state",
|
||||
relationship: "memory substrate",
|
||||
},
|
||||
{
|
||||
architecture_node_id: "tissue-boundaries",
|
||||
dataflow_node_id: "compatibility-filter",
|
||||
relationship: "boundary policy",
|
||||
},
|
||||
{
|
||||
architecture_node_id: "organism-contracts",
|
||||
dataflow_node_id: "contract-ledger",
|
||||
relationship: "organism-scale accounting",
|
||||
},
|
||||
{
|
||||
architecture_node_id: "colony-routes",
|
||||
dataflow_node_id: "route-authority",
|
||||
relationship: "transit governance",
|
||||
},
|
||||
{
|
||||
architecture_node_id: "native-nations",
|
||||
dataflow_node_id: "sovereign-permeability",
|
||||
relationship: "foreign policy",
|
||||
},
|
||||
{
|
||||
architecture_node_id: "imperial-clausiform",
|
||||
dataflow_node_id: "imperial-uniformity",
|
||||
relationship: "artificial topology",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<main className="zyphos-epiphany-graph-app">
|
||||
<GraphErrorBoundary>
|
||||
<EpiphanyGraphViewer
|
||||
state={zyphosGraphState}
|
||||
title="Zyphos Fractal Eusocial Web"
|
||||
/>
|
||||
</GraphErrorBoundary>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
|
||||
function mountZyphosGraph() {
|
||||
document.querySelectorAll(".zyphos-epiphany-graph-root").forEach((element) => {
|
||||
if (!(element instanceof HTMLElement)) {
|
||||
return
|
||||
}
|
||||
|
||||
if (element.dataset.ready === "true" && element.childElementCount > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const root = ReactDOM.createRoot(element)
|
||||
flushSync(() => {
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
)
|
||||
})
|
||||
element.dataset.ready = "true"
|
||||
})
|
||||
}
|
||||
|
||||
function scheduleZyphosGraphMount() {
|
||||
mountZyphosGraph()
|
||||
window.setTimeout(mountZyphosGraph, 50)
|
||||
window.setTimeout(mountZyphosGraph, 300)
|
||||
window.setTimeout(mountZyphosGraph, 1000)
|
||||
window.setTimeout(mountZyphosGraph, 2000)
|
||||
}
|
||||
|
||||
document.addEventListener("nav", scheduleZyphosGraphMount)
|
||||
document.addEventListener("DOMContentLoaded", scheduleZyphosGraphMount)
|
||||
scheduleZyphosGraphMount()
|
||||
34
site/epiphany-graph-embed/src/styles.css
Normal file
34
site/epiphany-graph-embed/src/styles.css
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
html,
|
||||
body,
|
||||
#root {
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background:
|
||||
radial-gradient(circle at 72% 12%, rgba(255, 174, 88, 0.16), transparent 24%),
|
||||
radial-gradient(circle at 10% 18%, rgba(142, 223, 176, 0.16), transparent 28%),
|
||||
linear-gradient(180deg, #03070d 0%, #07110f 52%, #091712 100%);
|
||||
color: #effcf8;
|
||||
font-family: "Ubuntu", "Segoe UI", sans-serif;
|
||||
}
|
||||
|
||||
.zyphos-epiphany-graph-app {
|
||||
box-sizing: border-box;
|
||||
min-height: 100vh;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.zyphos-epiphany-graph-app * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.zyphos-graph-error {
|
||||
white-space: pre-wrap;
|
||||
color: #fecaca;
|
||||
background: rgba(69, 10, 10, 0.6);
|
||||
border: 1px solid rgba(248, 113, 113, 0.45);
|
||||
border-radius: 16px;
|
||||
padding: 16px;
|
||||
}
|
||||
12
site/epiphany-graph-embed/viewer.html
Normal file
12
site/epiphany-graph-embed/viewer.html
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Zyphos Fractal Eusocial Web</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="zyphos-epiphany-graph-root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
39
site/epiphany-graph-embed/vite.config.ts
Normal file
39
site/epiphany-graph-embed/vite.config.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import react from "@vitejs/plugin-react"
|
||||
import { defineConfig } from "vite"
|
||||
import { fileURLToPath } from "node:url"
|
||||
|
||||
const viewerEntry = fileURLToPath(new URL("./viewer.html", import.meta.url))
|
||||
const reactPath = fileURLToPath(new URL("./node_modules/react", import.meta.url))
|
||||
const reactDomPath = fileURLToPath(new URL("./node_modules/react-dom", import.meta.url))
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
react: reactPath,
|
||||
"react-dom": reactDomPath,
|
||||
},
|
||||
},
|
||||
server: {
|
||||
host: "127.0.0.1",
|
||||
port: 4182,
|
||||
},
|
||||
build: {
|
||||
outDir: "../../Eusocial Interbeing/static/epiphany-graph",
|
||||
emptyOutDir: true,
|
||||
rollupOptions: {
|
||||
input: {
|
||||
viewer: viewerEntry,
|
||||
},
|
||||
output: {
|
||||
manualChunks: {
|
||||
react: ["react", "react-dom"],
|
||||
elk: ["elkjs/lib/elk.bundled.js"],
|
||||
},
|
||||
entryFileNames: "assets/[name].js",
|
||||
chunkFileNames: "assets/[name].js",
|
||||
assetFileNames: "assets/[name][extname]",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
@ -3,7 +3,6 @@ import * as Plugin from "./quartz/plugins"
|
|||
import { QuartzEmitterPlugin } from "./quartz/plugins/types"
|
||||
import { Fragment, h } from "preact"
|
||||
import { zyphosHeroScript } from "./quartz/components/ZyphosHeroScript"
|
||||
import { zyphosBiosphereGraphScript } from "./quartz/components/ZyphosBiosphereGraphScript"
|
||||
|
||||
const ZyphosFavicons: QuartzEmitterPlugin = () => ({
|
||||
name: "ZyphosFavicons",
|
||||
|
|
@ -37,22 +36,6 @@ const ZyphosHeroResources: QuartzEmitterPlugin = () => ({
|
|||
}),
|
||||
})
|
||||
|
||||
const ZyphosBiosphereGraphResources: QuartzEmitterPlugin = () => ({
|
||||
name: "ZyphosBiosphereGraphResources",
|
||||
async *emit() {},
|
||||
async *partialEmit() {},
|
||||
externalResources: () => ({
|
||||
js: [
|
||||
{
|
||||
script: zyphosBiosphereGraphScript,
|
||||
loadTime: "afterDOMReady",
|
||||
contentType: "inline",
|
||||
spaPreserve: true,
|
||||
},
|
||||
],
|
||||
}),
|
||||
})
|
||||
|
||||
const config: QuartzConfig = {
|
||||
configuration: {
|
||||
pageTitle: "Zyphos",
|
||||
|
|
@ -149,7 +132,6 @@ const config: QuartzConfig = {
|
|||
Plugin.Static(),
|
||||
ZyphosFavicons(),
|
||||
ZyphosHeroResources(),
|
||||
ZyphosBiosphereGraphResources(),
|
||||
Plugin.NotFoundPage(),
|
||||
Plugin.CustomOgImages(),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -1,312 +0,0 @@
|
|||
import { QuartzComponent, QuartzComponentConstructor } from "./types"
|
||||
|
||||
export const zyphosBiosphereGraphScript = String.raw`
|
||||
function bootZyphosBiosphereGraph() {
|
||||
const mounts = document.querySelectorAll(".zyphos-biosphere-graph")
|
||||
if (!mounts.length) return
|
||||
|
||||
const graph = {
|
||||
nodes: [
|
||||
{ id: "memorioplast", title: "Memorioplast", layer: "cellular", x: 0, y: 0, size: 30, purpose: "Mutable memory endosymbiont. Stores, edits, and transmits local cellular experience." },
|
||||
{ id: "cell_congress", title: "Cell Congress", layer: "cellular", x: -170, y: -70, size: 24, purpose: "Cell clusters negotiate repair, immunity, hunger, and inherited response." },
|
||||
{ id: "mirror_amoebae", title: "Mirror Amoebae", layer: "microbial", x: 170, y: -70, size: 22, purpose: "Copy and replay nearby memory states in wounds, ponds, stomachs, and root sheaths." },
|
||||
{ id: "immune_liturgy", title: "Immune Liturgy", layer: "tissue", x: 0, y: -190, size: 24, purpose: "Boundary rituals inside flesh. Decides which memories count as self enough." },
|
||||
{ id: "prismwake_mats", title: "Prismwake Mats", layer: "organism", x: -330, y: -170, size: 27, purpose: "Wetland sheets that pay gentle grazers with sugar and punish overfeeding." },
|
||||
{ id: "lantern_trees", title: "Lantern Trees", layer: "organism", x: 330, y: -170, size: 28, purpose: "Umbros-facing canopies that light eclipse routes and tax passage through pulses." },
|
||||
{ id: "threadwings", title: "Threadwing Couriers", layer: "organism", x: -380, y: 20, size: 24, purpose: "Bird-analogues moving pollen, gut symbionts, route memory, and grudges." },
|
||||
{ id: "glassbacks", title: "Glassback Grazers", layer: "organism", x: -260, y: 170, size: 26, purpose: "Heat-sharing herd bodies whose translucent plates leak social and immune truth." },
|
||||
{ id: "lattice_ants", title: "Lattice Ants", layer: "colony", x: 30, y: 190, size: 23, purpose: "Temporary body-bridges and ground-reading memory circuits." },
|
||||
{ id: "candle_roads", title: "Candle Fungal Roads", layer: "colony", x: 300, y: 120, size: 27, purpose: "Dim mycelial routes that connect shelters, groves, licks, and breeding commons." },
|
||||
{ id: "choir_reefs", title: "Choir Reefs", layer: "colony", x: 430, y: -20, size: 26, purpose: "Aquatic colonies that negotiate through current, pressure, shelter, and song." },
|
||||
{ id: "burden_flowers", title: "Burden Flowers", layer: "organism", x: -120, y: 300, size: 22, purpose: "Mobile plant-analogues that carry immune and emotional rumor on traveler hides." },
|
||||
{ id: "mother_archives", title: "Mother Archives", layer: "archive", x: 230, y: -330, size: 31, purpose: "Long-lived tree networks that hold ancestry, precedent, debt, disaster, and law." },
|
||||
{ id: "breeding_grounds", title: "Breeding Grounds", layer: "commons", x: -230, y: 360, size: 31, purpose: "Sacred reproductive commons where infants, caretakers, infirm adults, and ecological partners hold continuity." },
|
||||
{ id: "viator_nations", title: "Viator Nations", layer: "nation", x: -520, y: 280, size: 34, purpose: "Migratory route interbeings whose territory travels through herds, bloom corridors, and debt." },
|
||||
{ id: "archival_nations", title: "Archival Nations", layer: "nation", x: 120, y: -500, size: 35, purpose: "Mother-tree centered native polities with porous food borders and guarded deep memory." },
|
||||
{ id: "fungal_nations", title: "Fungal Road Nations", layer: "nation", x: 540, y: 260, size: 34, purpose: "Logistics organisms whose hospitality is accounting with teeth." },
|
||||
{ id: "reef_nations", title: "Reef Nations", layer: "nation", x: 620, y: -190, size: 33, purpose: "Waterway polities whose borders follow larvae, current, mineral taste, and storm memory." },
|
||||
{ id: "native_patchwork", title: "Native Patchwork", layer: "continent", x: -210, y: -540, size: 40, purpose: "Three quarters of the Airawa continent: many sovereign interbeings grown by local negotiation." },
|
||||
{ id: "imperium", title: "Imperium airavense clausum", layer: "empire", x: 520, y: -500, size: 42, purpose: "Artificial quarter-continent clausiform being with impermeable borders and tuned autonomy." },
|
||||
{ id: "memetic_engine", title: "Biological Memetic Engine", layer: "technology", x: 470, y: -650, size: 29, purpose: "Engineered organisms, rituals, and signal payloads that industrialize coercion through trusted memory channels." },
|
||||
{ id: "saaueia_mesh", title: "Sa'auei'a Reciprocal Mesh", layer: "civilization", x: -570, y: 470, size: 36, purpose: "Mobile family units moving through remembered ecologies where cooperation must be earned repeatedly." },
|
||||
],
|
||||
edges: [
|
||||
["memorioplast", "cell_congress", "stores"],
|
||||
["memorioplast", "mirror_amoebae", "copies"],
|
||||
["cell_congress", "immune_liturgy", "regulates"],
|
||||
["mirror_amoebae", "immune_liturgy", "confuses"],
|
||||
["immune_liturgy", "prismwake_mats", "bounds"],
|
||||
["immune_liturgy", "lantern_trees", "bounds"],
|
||||
["prismwake_mats", "glassbacks", "feeds"],
|
||||
["glassbacks", "burden_flowers", "carries"],
|
||||
["burden_flowers", "breeding_grounds", "reports"],
|
||||
["threadwings", "prismwake_mats", "pollinates"],
|
||||
["threadwings", "lantern_trees", "routes"],
|
||||
["threadwings", "mother_archives", "carries"],
|
||||
["lattice_ants", "candle_roads", "diagnoses"],
|
||||
["lattice_ants", "glassbacks", "reads"],
|
||||
["candle_roads", "breeding_grounds", "connects"],
|
||||
["candle_roads", "fungal_nations", "scales"],
|
||||
["choir_reefs", "reef_nations", "scales"],
|
||||
["mother_archives", "archival_nations", "scales"],
|
||||
["glassbacks", "viator_nations", "scales"],
|
||||
["breeding_grounds", "saaueia_mesh", "anchors"],
|
||||
["viator_nations", "saaueia_mesh", "treaties"],
|
||||
["archival_nations", "native_patchwork", "member"],
|
||||
["viator_nations", "native_patchwork", "member"],
|
||||
["fungal_nations", "native_patchwork", "member"],
|
||||
["reef_nations", "native_patchwork", "member"],
|
||||
["memetic_engine", "imperium", "authors"],
|
||||
["imperium", "native_patchwork", "invades"],
|
||||
["imperium", "archival_nations", "denies"],
|
||||
["imperium", "fungal_nations", "seals"],
|
||||
["imperium", "saaueia_mesh", "misreads"],
|
||||
["native_patchwork", "saaueia_mesh", "possible envoys"],
|
||||
],
|
||||
}
|
||||
|
||||
const layerColors = {
|
||||
cellular: "#8edfb0",
|
||||
microbial: "#b9f6ca",
|
||||
tissue: "#73c6e3",
|
||||
organism: "#ffae58",
|
||||
colony: "#f7d06d",
|
||||
archive: "#a7f3d0",
|
||||
commons: "#f9a8d4",
|
||||
nation: "#c4b5fd",
|
||||
continent: "#efefff",
|
||||
empire: "#ff6b6b",
|
||||
technology: "#f97316",
|
||||
civilization: "#7dd3fc",
|
||||
}
|
||||
|
||||
mounts.forEach((mount) => {
|
||||
if (mount.dataset.ready === "true") return
|
||||
mount.dataset.ready = "true"
|
||||
|
||||
const shell = document.createElement("div")
|
||||
shell.className = "zyphos-graph-shell"
|
||||
|
||||
const toolbar = document.createElement("div")
|
||||
toolbar.className = "zyphos-graph-toolbar"
|
||||
|
||||
const title = document.createElement("div")
|
||||
title.className = "zyphos-graph-title"
|
||||
title.innerHTML = "<strong>Fractal Eusocial Web</strong><span>Memory, matter, boundary, and sovereignty channels.</span>"
|
||||
|
||||
const controls = document.createElement("div")
|
||||
controls.className = "zyphos-graph-controls"
|
||||
const zoomIn = graphButton("+", "Zoom in")
|
||||
const zoomOut = graphButton("-", "Zoom out")
|
||||
const reset = graphButton("Reset", "Reset view")
|
||||
controls.append(zoomIn, zoomOut, reset)
|
||||
toolbar.append(title, controls)
|
||||
|
||||
const body = document.createElement("div")
|
||||
body.className = "zyphos-graph-body"
|
||||
|
||||
const viewport = document.createElement("div")
|
||||
viewport.className = "zyphos-graph-viewport"
|
||||
|
||||
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg")
|
||||
svg.setAttribute("role", "img")
|
||||
svg.setAttribute("aria-label", "Interactive graph of Zyphos biosphere relationships")
|
||||
viewport.append(svg)
|
||||
|
||||
const detail = document.createElement("aside")
|
||||
detail.className = "zyphos-graph-detail"
|
||||
body.append(viewport, detail)
|
||||
shell.append(toolbar, body)
|
||||
mount.replaceChildren(shell)
|
||||
|
||||
const edgeLayer = document.createElementNS("http://www.w3.org/2000/svg", "g")
|
||||
const nodeLayer = document.createElementNS("http://www.w3.org/2000/svg", "g")
|
||||
svg.append(edgeLayer, nodeLayer)
|
||||
|
||||
let selected = { kind: "node", id: "memorioplast" }
|
||||
let transform = { x: 0, y: 0, scale: 0.78 }
|
||||
let dragging = null
|
||||
|
||||
function worldToScreen(point) {
|
||||
return {
|
||||
x: point.x * transform.scale + transform.x,
|
||||
y: point.y * transform.scale + transform.y,
|
||||
}
|
||||
}
|
||||
|
||||
function setTransform(next) {
|
||||
transform = next
|
||||
nodeLayer.setAttribute("transform", "translate(" + transform.x + " " + transform.y + ") scale(" + transform.scale + ")")
|
||||
edgeLayer.setAttribute("transform", "translate(" + transform.x + " " + transform.y + ") scale(" + transform.scale + ")")
|
||||
}
|
||||
|
||||
function fit() {
|
||||
const rect = viewport.getBoundingClientRect()
|
||||
const xs = graph.nodes.map((node) => node.x)
|
||||
const ys = graph.nodes.map((node) => node.y)
|
||||
const minX = Math.min.apply(null, xs) - 130
|
||||
const maxX = Math.max.apply(null, xs) + 130
|
||||
const minY = Math.min.apply(null, ys) - 130
|
||||
const maxY = Math.max.apply(null, ys) + 130
|
||||
const scale = Math.max(0.42, Math.min(1.08, Math.min(rect.width / (maxX - minX), rect.height / (maxY - minY))))
|
||||
setTransform({
|
||||
scale,
|
||||
x: (rect.width - (minX + maxX) * scale) / 2,
|
||||
y: (rect.height - (minY + maxY) * scale) / 2,
|
||||
})
|
||||
}
|
||||
|
||||
function render() {
|
||||
edgeLayer.replaceChildren()
|
||||
nodeLayer.replaceChildren()
|
||||
|
||||
const lookup = new Map(graph.nodes.map((node) => [node.id, node]))
|
||||
graph.edges.forEach((edge, index) => {
|
||||
const source = lookup.get(edge[0])
|
||||
const target = lookup.get(edge[1])
|
||||
if (!source || !target) return
|
||||
|
||||
const line = document.createElementNS("http://www.w3.org/2000/svg", "line")
|
||||
line.setAttribute("x1", source.x)
|
||||
line.setAttribute("y1", source.y)
|
||||
line.setAttribute("x2", target.x)
|
||||
line.setAttribute("y2", target.y)
|
||||
line.setAttribute("class", selected.kind === "edge" && selected.id === index ? "selected" : "")
|
||||
line.dataset.edgeIndex = String(index)
|
||||
line.addEventListener("click", (event) => {
|
||||
event.stopPropagation()
|
||||
selected = { kind: "edge", id: index }
|
||||
render()
|
||||
})
|
||||
edgeLayer.append(line)
|
||||
|
||||
const label = document.createElementNS("http://www.w3.org/2000/svg", "text")
|
||||
label.setAttribute("x", (source.x + target.x) / 2)
|
||||
label.setAttribute("y", (source.y + target.y) / 2 - 8)
|
||||
label.setAttribute("class", "zyphos-graph-edge-label")
|
||||
label.textContent = edge[2]
|
||||
edgeLayer.append(label)
|
||||
})
|
||||
|
||||
graph.nodes.forEach((node) => {
|
||||
const group = document.createElementNS("http://www.w3.org/2000/svg", "g")
|
||||
group.setAttribute("transform", "translate(" + node.x + " " + node.y + ")")
|
||||
group.setAttribute("class", selected.kind === "node" && selected.id === node.id ? "zyphos-graph-node selected" : "zyphos-graph-node")
|
||||
group.addEventListener("click", (event) => {
|
||||
event.stopPropagation()
|
||||
selected = { kind: "node", id: node.id }
|
||||
render()
|
||||
})
|
||||
|
||||
const aura = document.createElementNS("http://www.w3.org/2000/svg", "circle")
|
||||
aura.setAttribute("r", node.size + 12)
|
||||
aura.setAttribute("fill", layerColors[node.layer] + "24")
|
||||
group.append(aura)
|
||||
|
||||
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle")
|
||||
circle.setAttribute("r", node.size)
|
||||
circle.setAttribute("fill", layerColors[node.layer] || "#8edfb0")
|
||||
group.append(circle)
|
||||
|
||||
const text = document.createElementNS("http://www.w3.org/2000/svg", "text")
|
||||
text.setAttribute("y", node.size + 22)
|
||||
text.setAttribute("text-anchor", "middle")
|
||||
text.textContent = node.title
|
||||
group.append(text)
|
||||
nodeLayer.append(group)
|
||||
})
|
||||
|
||||
updateDetail()
|
||||
setTransform(transform)
|
||||
}
|
||||
|
||||
function updateDetail() {
|
||||
if (selected.kind === "edge") {
|
||||
const edge = graph.edges[selected.id]
|
||||
const source = graph.nodes.find((node) => node.id === edge[0])
|
||||
const target = graph.nodes.find((node) => node.id === edge[1])
|
||||
detail.innerHTML = "<p class='zyphos-graph-kicker'>Selected relation</p><h2>" + edge[2] + "</h2><p><strong>" + source.title + "</strong> to <strong>" + target.title + "</strong></p><p>This edge marks one channel of memory, matter, permission, coercion, or obligation. It is a live ecological contract, not a decorative arrow.</p>"
|
||||
return
|
||||
}
|
||||
|
||||
const node = graph.nodes.find((item) => item.id === selected.id) || graph.nodes[0]
|
||||
const incoming = graph.edges.filter((edge) => edge[1] === node.id).length
|
||||
const outgoing = graph.edges.filter((edge) => edge[0] === node.id).length
|
||||
detail.innerHTML = "<p class='zyphos-graph-kicker'>" + node.layer + "</p><h2>" + node.title + "</h2><p>" + node.purpose + "</p><dl><dt>Incoming</dt><dd>" + incoming + "</dd><dt>Outgoing</dt><dd>" + outgoing + "</dd></dl>"
|
||||
}
|
||||
|
||||
function zoomAt(factor, clientX, clientY) {
|
||||
const rect = viewport.getBoundingClientRect()
|
||||
const x = clientX == null ? rect.width / 2 : clientX - rect.left
|
||||
const y = clientY == null ? rect.height / 2 : clientY - rect.top
|
||||
const nextScale = Math.max(0.24, Math.min(2.6, transform.scale * factor))
|
||||
const worldX = (x - transform.x) / transform.scale
|
||||
const worldY = (y - transform.y) / transform.scale
|
||||
setTransform({
|
||||
scale: nextScale,
|
||||
x: x - worldX * nextScale,
|
||||
y: y - worldY * nextScale,
|
||||
})
|
||||
}
|
||||
|
||||
svg.addEventListener("wheel", (event) => {
|
||||
event.preventDefault()
|
||||
zoomAt(event.deltaY < 0 ? 1.12 : 1 / 1.12, event.clientX, event.clientY)
|
||||
}, { passive: false })
|
||||
|
||||
svg.addEventListener("pointerdown", (event) => {
|
||||
if (event.target !== svg) return
|
||||
dragging = { x: event.clientX, y: event.clientY, tx: transform.x, ty: transform.y }
|
||||
svg.setPointerCapture(event.pointerId)
|
||||
})
|
||||
|
||||
svg.addEventListener("pointermove", (event) => {
|
||||
if (!dragging) return
|
||||
setTransform({
|
||||
...transform,
|
||||
x: dragging.tx + event.clientX - dragging.x,
|
||||
y: dragging.ty + event.clientY - dragging.y,
|
||||
})
|
||||
})
|
||||
|
||||
svg.addEventListener("pointerup", () => {
|
||||
dragging = null
|
||||
})
|
||||
|
||||
svg.addEventListener("click", (event) => {
|
||||
if (event.target === svg) {
|
||||
selected = { kind: "node", id: "memorioplast" }
|
||||
render()
|
||||
}
|
||||
})
|
||||
|
||||
zoomIn.addEventListener("click", () => zoomAt(1.18))
|
||||
zoomOut.addEventListener("click", () => zoomAt(1 / 1.18))
|
||||
reset.addEventListener("click", fit)
|
||||
window.addEventListener("resize", fit)
|
||||
|
||||
render()
|
||||
fit()
|
||||
})
|
||||
}
|
||||
|
||||
function graphButton(text, label) {
|
||||
const button = document.createElement("button")
|
||||
button.type = "button"
|
||||
button.textContent = text
|
||||
button.setAttribute("aria-label", label)
|
||||
return button
|
||||
}
|
||||
|
||||
document.addEventListener("nav", bootZyphosBiosphereGraph)
|
||||
document.addEventListener("DOMContentLoaded", bootZyphosBiosphereGraph)
|
||||
bootZyphosBiosphereGraph()
|
||||
`
|
||||
|
||||
export default (() => {
|
||||
const ZyphosBiosphereGraphScript: QuartzComponent = () => (
|
||||
<script dangerouslySetInnerHTML={{ __html: zyphosBiosphereGraphScript }} />
|
||||
)
|
||||
|
||||
return ZyphosBiosphereGraphScript
|
||||
}) satisfies QuartzComponentConstructor
|
||||
|
|
@ -257,200 +257,14 @@ article th {
|
|||
border-color: rgba(142, 223, 176, 0.12);
|
||||
}
|
||||
|
||||
.zyphos-biosphere-graph {
|
||||
margin: 1.25rem 0 1.6rem;
|
||||
}
|
||||
|
||||
.zyphos-graph-shell {
|
||||
display: grid;
|
||||
gap: 0.85rem;
|
||||
}
|
||||
|
||||
.zyphos-graph-toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem;
|
||||
padding: 0.85rem 1rem;
|
||||
border: 1px solid rgba(142, 223, 176, 0.14);
|
||||
border-radius: 18px;
|
||||
background: linear-gradient(135deg, rgba(7, 17, 15, 0.94), rgba(13, 20, 31, 0.9));
|
||||
}
|
||||
|
||||
.zyphos-graph-title {
|
||||
display: grid;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.zyphos-graph-title strong {
|
||||
color: #f2fff8;
|
||||
font-family: var(--titleFont);
|
||||
font-size: 1rem;
|
||||
font-weight: 300;
|
||||
letter-spacing: 0.06em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.zyphos-graph-title span {
|
||||
color: rgba(189, 212, 202, 0.76);
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
|
||||
.zyphos-graph-controls {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.45rem;
|
||||
}
|
||||
|
||||
.zyphos-graph-controls button {
|
||||
min-height: 2.1rem;
|
||||
border: 1px solid rgba(142, 223, 176, 0.18);
|
||||
border-radius: 999px;
|
||||
padding: 0.25rem 0.75rem;
|
||||
background: rgba(142, 223, 176, 0.07);
|
||||
color: #e2f6ee;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.zyphos-graph-controls button:hover {
|
||||
border-color: rgba(255, 174, 88, 0.42);
|
||||
background: rgba(255, 174, 88, 0.14);
|
||||
}
|
||||
|
||||
.zyphos-graph-body {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: 0.9rem;
|
||||
min-height: 620px;
|
||||
}
|
||||
|
||||
.zyphos-graph-viewport,
|
||||
.zyphos-graph-detail {
|
||||
border: 1px solid rgba(142, 223, 176, 0.12);
|
||||
border-radius: 20px;
|
||||
background:
|
||||
radial-gradient(circle at 70% 14%, rgba(255, 174, 88, 0.14), transparent 22%),
|
||||
linear-gradient(180deg, rgba(4, 9, 14, 0.96), rgba(7, 17, 15, 0.94));
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);
|
||||
}
|
||||
|
||||
.zyphos-graph-viewport {
|
||||
min-height: 620px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.zyphos-graph-viewport svg {
|
||||
.zyphos-epiphany-graph-root {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 620px;
|
||||
cursor: grab;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.zyphos-graph-viewport svg:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.zyphos-graph-viewport line {
|
||||
stroke: rgba(142, 223, 176, 0.34);
|
||||
stroke-width: 2.3;
|
||||
vector-effect: non-scaling-stroke;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.zyphos-graph-viewport line.selected {
|
||||
stroke: #ffae58;
|
||||
stroke-width: 4;
|
||||
}
|
||||
|
||||
.zyphos-graph-edge-label {
|
||||
fill: rgba(239, 252, 248, 0.62);
|
||||
font-size: 12px;
|
||||
font-family: var(--codeFont);
|
||||
paint-order: stroke;
|
||||
stroke: rgba(4, 9, 14, 0.86);
|
||||
stroke-width: 5px;
|
||||
text-anchor: middle;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.zyphos-graph-node {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.zyphos-graph-node circle:first-child {
|
||||
opacity: 0.28;
|
||||
}
|
||||
|
||||
.zyphos-graph-node circle:nth-child(2) {
|
||||
stroke: rgba(239, 252, 248, 0.7);
|
||||
stroke-width: 1.4;
|
||||
vector-effect: non-scaling-stroke;
|
||||
}
|
||||
|
||||
.zyphos-graph-node.selected circle:nth-child(2) {
|
||||
stroke: #fff3e8;
|
||||
stroke-width: 3;
|
||||
}
|
||||
|
||||
.zyphos-graph-node text {
|
||||
fill: #effcf8;
|
||||
font-family: var(--titleFont);
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
paint-order: stroke;
|
||||
pointer-events: none;
|
||||
stroke: rgba(4, 9, 14, 0.92);
|
||||
stroke-width: 5px;
|
||||
}
|
||||
|
||||
.zyphos-graph-detail {
|
||||
display: grid;
|
||||
align-content: start;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.zyphos-graph-detail h2 {
|
||||
margin: 0;
|
||||
color: #f2fff8;
|
||||
font-family: var(--titleFont);
|
||||
font-size: 1.35rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.08;
|
||||
}
|
||||
|
||||
.zyphos-graph-detail p {
|
||||
margin: 0;
|
||||
color: rgba(189, 212, 202, 0.84);
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.zyphos-graph-kicker {
|
||||
color: var(--secondary) !important;
|
||||
font-family: var(--titleFont);
|
||||
font-size: 0.76rem;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.zyphos-graph-detail dl {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, auto 1fr);
|
||||
gap: 0.35rem 0.7rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.zyphos-graph-detail dt {
|
||||
color: rgba(189, 212, 202, 0.62);
|
||||
}
|
||||
|
||||
.zyphos-graph-detail dd {
|
||||
margin: 0;
|
||||
color: #effcf8;
|
||||
min-height: 820px;
|
||||
margin: 1.25rem 0 1.6rem;
|
||||
border: 1px solid rgba(142, 223, 176, 0.14);
|
||||
border-radius: 20px;
|
||||
background: #03070d;
|
||||
}
|
||||
|
||||
@media all and not ($desktop) {
|
||||
|
|
@ -468,12 +282,7 @@ article th {
|
|||
margin-inline: -1rem;
|
||||
}
|
||||
|
||||
.zyphos-graph-viewport,
|
||||
.zyphos-graph-viewport svg {
|
||||
min-height: 520px;
|
||||
}
|
||||
|
||||
.zyphos-graph-detail dl {
|
||||
grid-template-columns: auto 1fr;
|
||||
.zyphos-epiphany-graph-root {
|
||||
min-height: 780px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue