Refonte UI complète : palettes saisonnières, typo moderne, paroles nettoyées, Shadoks

- Nettoyage paroles : suppression instructions Suno AI, corrections prononciation (11 fichiers)
- 4 palettes saisonnières (Automne/Hiver dark, Printemps/Été light) avec sélecteur
- Typographie modernisée : Outfit (display) + Inter (sans) remplacent Syne + Space Grotesk
- Styles adaptatifs : CSS vars pour couleurs, overrides light mode complets
- Mini-player : bouton Next ajouté, flèche expand plus visible
- BookPlayer : fix scroll mode paginé, croix de fermeture visible
- Illustrations Shadoks inline SVG dans 11 composants/pages
- Suppression soulignés navigation, reset boutons, bordures propres

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-02-23 03:35:45 +01:00
parent 6f422a7369
commit ac4aff4985
43 changed files with 1362 additions and 302 deletions

View File

@@ -20,7 +20,7 @@ export default defineAppConfig({
], ],
}, },
gratewizard: { gratewizard: {
url: 'https://gratewizard.ml', url: import.meta.dev ? 'http://localhost:3009' : 'https://gratewizard.ml',
popup: { popup: {
width: 420, width: 420,
height: 720, height: 720,

View File

@@ -14,6 +14,9 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
const paletteStore = usePaletteStore()
onMounted(() => paletteStore.applyToDOM())
useHead({ useHead({
titleTemplate: (title) => { titleTemplate: (title) => {
return title ? `${title} — Le Librodrome` : 'Le librodrome' return title ? `${title} — Le Librodrome` : 'Le librodrome'

View File

@@ -60,10 +60,10 @@
@keyframes glow-pulse { @keyframes glow-pulse {
0%, 100% { 0%, 100% {
box-shadow: 0 0 8px hsl(12 76% 48% / 0.3); box-shadow: 0 0 8px hsl(var(--color-primary) / 0.3);
} }
50% { 50% {
box-shadow: 0 0 24px hsl(12 76% 48% / 0.6); box-shadow: 0 0 24px hsl(var(--color-primary) / 0.6);
} }
} }

View File

@@ -2,11 +2,11 @@
/* This file provides fallback and utility classes */ /* This file provides fallback and utility classes */
.font-display { .font-display {
font-family: 'Syne', system-ui, sans-serif; font-family: 'Outfit', system-ui, sans-serif;
} }
.font-sans { .font-sans {
font-family: 'Space Grotesk', system-ui, sans-serif; font-family: 'Inter', system-ui, sans-serif;
} }
.font-mono { .font-mono {

View File

@@ -3,20 +3,20 @@
@import './typography.css'; @import './typography.css';
:root { :root {
--color-primary: 12 76% 48%; --color-primary: 18 80% 45%;
--color-accent: 36 80% 52%; --color-accent: 32 85% 50%;
--color-bg: 20 8% 3.5%; --color-bg: 16 12% 4%;
--color-surface: 20 8% 8%; --color-surface: 16 12% 9%;
--color-surface-light: 20 8% 13%; --color-surface-light: 16 10% 14%;
--color-text: 0 0% 100%; --color-text: 0 0% 100%;
--color-text-muted: 0 0% 100% / 0.6; --color-text-muted: 0 0% 60%;
--header-height: 4rem; --header-height: 4rem;
--player-height: 0rem; --player-height: 0rem;
--sidebar-width: 280px; --sidebar-width: 280px;
--font-display: 'Syne', sans-serif; --font-display: 'Outfit', sans-serif;
--font-sans: 'Space Grotesk', sans-serif; --font-sans: 'Inter', sans-serif;
--font-mono: 'JetBrains Mono', monospace; --font-mono: 'JetBrains Mono', monospace;
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
@@ -43,9 +43,22 @@ body {
min-height: 100dvh; min-height: 100dvh;
} }
button {
border: none;
background: none;
cursor: pointer;
font: inherit;
color: inherit;
}
a {
text-decoration: none;
color: inherit;
}
::selection { ::selection {
background-color: hsl(var(--color-primary) / 0.3); background-color: hsl(var(--color-primary) / 0.3);
color: white; color: hsl(var(--color-text));
} }
:focus-visible { :focus-visible {
@@ -72,6 +85,78 @@ body {
background: hsl(var(--color-text) / 0.25); background: hsl(var(--color-text) / 0.25);
} }
/* ═══ Light mode overrides ═══ */
.palette-light {
color-scheme: light;
}
.palette-light,
.palette-light .text-white {
color: hsl(var(--color-text));
}
/* white with opacity → dark text with same opacity */
.palette-light .text-white\/20 { color: hsl(var(--color-text) / 0.2); }
.palette-light .text-white\/30 { color: hsl(var(--color-text) / 0.3); }
.palette-light .text-white\/40 { color: hsl(var(--color-text) / 0.4); }
.palette-light .text-white\/45 { color: hsl(var(--color-text) / 0.45); }
.palette-light .text-white\/50 { color: hsl(var(--color-text) / 0.5); }
.palette-light .text-white\/60 { color: hsl(var(--color-text) / 0.6); }
.palette-light .text-white\/70 { color: hsl(var(--color-text) / 0.7); }
.palette-light .text-white\/80 { color: hsl(var(--color-text) / 0.8); }
.palette-light .text-white\/85 { color: hsl(var(--color-text) / 0.85); }
/* white backgrounds → surface tones */
.palette-light .bg-white\/5 { background-color: hsl(var(--color-text) / 0.04); }
.palette-light .bg-white\/8 { background-color: hsl(var(--color-text) / 0.06); }
.palette-light .bg-white\/10 { background-color: hsl(var(--color-text) / 0.07); }
/* borders */
.palette-light .border-white\/8 { border-color: hsl(var(--color-text) / 0.1); }
/* hover overrides */
.palette-light .hover\:text-white:hover,
.palette-light .hover\:text-white\/70:hover,
.palette-light .hover\:text-white\/80:hover {
color: hsl(var(--color-text));
}
.palette-light .hover\:text-white\/60:hover {
color: hsl(var(--color-text) / 0.6);
}
.palette-light .hover\:bg-white\/5:hover {
background-color: hsl(var(--color-text) / 0.04);
}
.palette-light .hover\:bg-white\/10:hover {
background-color: hsl(var(--color-text) / 0.07);
}
/* placeholder overrides */
.palette-light .placeholder\:text-white\/30::placeholder {
color: hsl(var(--color-text) / 0.3);
}
/* Prose/content in light mode */
.palette-light .prose { color: hsl(var(--color-text)); }
.palette-light .prose :where(h1,h2,h3,h4,h5,h6) { color: hsl(var(--color-text)); }
/* Active states */
.palette-light .text-white\! { color: hsl(var(--color-text)) !important; }
.palette-light .active-class .text-white\! { color: hsl(var(--color-text)) !important; }
/* text-gradient in light mode */
.palette-light .text-gradient {
background-image: linear-gradient(to right, hsl(var(--color-primary)), hsl(var(--color-accent)));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
/* card surfaces */
.palette-light .card-surface {
background: hsl(var(--color-surface));
border-color: hsl(var(--color-text) / 0.1);
}
/* Page transitions */ /* Page transitions */
.page-enter-active, .page-enter-active,
.page-leave-active { .page-leave-active {

View File

@@ -3,7 +3,7 @@
font-family: var(--font-sans); font-family: var(--font-sans);
font-size: 1.125rem; font-size: 1.125rem;
line-height: 1.8; line-height: 1.8;
color: hsl(0 0% 100% / 0.90); color: hsl(var(--color-text) / 0.90);
max-width: 65ch; max-width: 65ch;
} }
@@ -13,11 +13,11 @@
font-weight: 800; font-weight: 800;
line-height: 1.25; line-height: 1.25;
letter-spacing: -0.02em; letter-spacing: -0.02em;
color: white; color: hsl(var(--color-text));
margin-top: 0; margin-top: 0;
margin-bottom: 1.5rem; margin-bottom: 1.5rem;
padding-bottom: 0.75rem; padding-bottom: 0.75rem;
border-bottom: 2px solid hsl(12 76% 48% / 0.4); border-bottom: 2px solid hsl(var(--color-primary) / 0.4);
} }
.prose h2 { .prose h2 {
@@ -26,11 +26,11 @@
font-weight: 700; font-weight: 700;
line-height: 1.3; line-height: 1.3;
letter-spacing: -0.01em; letter-spacing: -0.01em;
color: white; color: hsl(var(--color-text));
margin-top: 3.5rem; margin-top: 3.5rem;
margin-bottom: 1rem; margin-bottom: 1rem;
padding-left: 0.75rem; padding-left: 0.75rem;
border-left: 3px solid hsl(12 76% 48% / 0.5); border-left: 3px solid hsl(var(--color-primary) / 0.5);
} }
.prose h3 { .prose h3 {
@@ -38,7 +38,7 @@
font-size: clamp(1.25rem, 3vw, 1.625rem); font-size: clamp(1.25rem, 3vw, 1.625rem);
font-weight: 600; font-weight: 600;
line-height: 1.4; line-height: 1.4;
color: hsl(0 0% 100% / 0.92); color: hsl(var(--color-text) / 0.92);
margin-top: 3rem; margin-top: 3rem;
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
} }
@@ -49,7 +49,7 @@
width: 0.5rem; width: 0.5rem;
height: 0.5rem; height: 0.5rem;
border-radius: 50%; border-radius: 50%;
background: hsl(36 80% 52%); background: hsl(var(--color-accent));
margin-right: 0.625rem; margin-right: 0.625rem;
vertical-align: middle; vertical-align: middle;
position: relative; position: relative;
@@ -61,7 +61,7 @@
font-size: clamp(1.065rem, 2.5vw, 1.25rem); font-size: clamp(1.065rem, 2.5vw, 1.25rem);
font-weight: 600; font-weight: 600;
line-height: 1.45; line-height: 1.45;
color: hsl(0 0% 100% / 0.85); color: hsl(var(--color-text) / 0.85);
margin-top: 2.5rem; margin-top: 2.5rem;
margin-bottom: 0.625rem; margin-bottom: 0.625rem;
} }
@@ -69,7 +69,7 @@
.prose h4::before { .prose h4::before {
content: '//'; content: '//';
font-family: var(--font-mono); font-family: var(--font-mono);
color: hsl(36 80% 52%); color: hsl(var(--color-accent));
margin-right: 0.5rem; margin-right: 0.5rem;
font-weight: 500; font-weight: 500;
} }
@@ -78,7 +78,7 @@
.prose h2 + p, .prose h2 + p,
.prose h3 + p { .prose h3 + p {
font-size: 1.175rem; font-size: 1.175rem;
color: hsl(0 0% 100% / 0.75); color: hsl(var(--color-text) / 0.75);
line-height: 1.85; line-height: 1.85;
} }
@@ -88,25 +88,25 @@
} }
.prose a { .prose a {
color: hsl(12 76% 68%); color: hsl(var(--color-primary) / 0.85);
text-decoration: underline; text-decoration: underline;
text-decoration-color: hsl(12 76% 58% / 0.3); text-decoration-color: hsl(var(--color-primary) / 0.3);
text-underline-offset: 3px; text-underline-offset: 3px;
transition: text-decoration-color 0.2s; transition: text-decoration-color 0.2s;
} }
.prose a:hover { .prose a:hover {
text-decoration-color: hsl(12 76% 58%); text-decoration-color: hsl(var(--color-primary));
} }
.prose blockquote { .prose blockquote {
margin: 2rem 0; margin: 2rem 0;
padding: 1rem 1.5rem; padding: 1rem 1.5rem;
border-left: 3px solid hsl(12 76% 58%); border-left: 3px solid hsl(var(--color-primary));
background: hsl(240 10% 8%); background: hsl(var(--color-surface));
border-radius: 0 0.5rem 0.5rem 0; border-radius: 0 0.5rem 0.5rem 0;
font-style: italic; font-style: italic;
color: hsl(0 0% 100% / 0.75); color: hsl(var(--color-text) / 0.75);
} }
.prose blockquote p:last-child { .prose blockquote p:last-child {
@@ -116,17 +116,17 @@
.prose code { .prose code {
font-family: var(--font-mono); font-family: var(--font-mono);
font-size: 0.875em; font-size: 0.875em;
background: hsl(240 10% 12%); background: hsl(var(--color-surface-light));
padding: 0.2em 0.4em; padding: 0.2em 0.4em;
border-radius: 0.25rem; border-radius: 0.25rem;
color: hsl(31 97% 66%); color: hsl(var(--color-accent));
} }
.prose pre { .prose pre {
margin: 2rem 0; margin: 2rem 0;
padding: 1.5rem; padding: 1.5rem;
background: hsl(240 10% 6%); background: hsl(var(--color-bg));
border: 1px solid hsl(0 0% 100% / 0.08); border: 1px solid hsl(var(--color-text) / 0.08);
border-radius: 0.75rem; border-radius: 0.75rem;
overflow-x: auto; overflow-x: auto;
} }
@@ -134,7 +134,7 @@
.prose pre code { .prose pre code {
background: none; background: none;
padding: 0; padding: 0;
color: hsl(0 0% 100% / 0.87); color: hsl(var(--color-text) / 0.87);
} }
.prose ul, .prose ul,
@@ -149,22 +149,22 @@
} }
.prose li::marker { .prose li::marker {
color: hsl(12 76% 58%); color: hsl(var(--color-primary));
} }
.prose hr { .prose hr {
margin: 3rem 0; margin: 3rem 0;
border: none; border: none;
border-top: 1px solid hsl(0 0% 100% / 0.1); border-top: 1px solid hsl(var(--color-text) / 0.1);
} }
.prose strong { .prose strong {
color: white; color: hsl(var(--color-text));
font-weight: 600; font-weight: 600;
} }
.prose em { .prose em {
color: hsl(0 0% 100% / 0.9); color: hsl(var(--color-text) / 0.9);
} }
.prose img { .prose img {

View File

@@ -50,7 +50,7 @@
<template v-if="isScrollMode">{{ scrollPercent }}%</template> <template v-if="isScrollMode">{{ scrollPercent }}%</template>
<template v-else>{{ currentPage + 1 }}<span class="op-40">/</span>{{ totalPages }}</template> <template v-else>{{ currentPage + 1 }}<span class="op-40">/</span>{{ totalPages }}</template>
</span> </span>
<button class="reader-bar-btn" @click="close" aria-label="Fermer"> <button class="reader-bar-btn reader-bar-close" @click="close" aria-label="Fermer">
<div class="i-lucide-x h-5 w-5" /> <div class="i-lucide-x h-5 w-5" />
</button> </button>
</div> </div>
@@ -633,6 +633,14 @@ onUnmounted(() => {
color: white; color: white;
background: hsl(0 0% 100% / 0.06); background: hsl(0 0% 100% / 0.06);
} }
.reader-bar-close {
color: hsl(0 0% 100% / 0.7);
background: hsl(0 0% 100% / 0.08);
}
.reader-bar-close:hover {
color: white;
background: hsl(0 70% 55% / 0.3);
}
.reader-bar-title { .reader-bar-title {
flex: 1; flex: 1;
font-family: var(--font-display, 'Syne', sans-serif); font-family: var(--font-display, 'Syne', sans-serif);
@@ -744,7 +752,7 @@ onUnmounted(() => {
position: relative; position: relative;
flex: 1; flex: 1;
width: 100%; width: 100%;
overflow: hidden; overflow: hidden auto;
border-radius: 0.75rem; border-radius: 0.75rem;
background: hsl(20 8% 5% / 0.4); background: hsl(20 8% 5% / 0.4);
backdrop-filter: blur(16px); backdrop-filter: blur(16px);
@@ -796,12 +804,16 @@ onUnmounted(() => {
.reader-columns :deep(h3) { .reader-columns :deep(h3) {
break-after: avoid; break-after: avoid;
} }
.reader-columns :deep(p),
.reader-columns :deep(blockquote), .reader-columns :deep(blockquote),
.reader-columns :deep(ul), .reader-columns :deep(ul),
.reader-columns :deep(ol) { .reader-columns :deep(ol) {
break-inside: avoid; break-inside: avoid;
} }
/* p with pre-line (lyrics) can be taller than a column — allow break */
.reader-columns :deep(p) {
break-inside: auto;
overflow-y: auto;
}
/* Page-turn shadow overlay */ /* Page-turn shadow overlay */
.reader-shadow { .reader-shadow {

View File

@@ -57,6 +57,6 @@ function playSong(song: Song) {
.chapter-title { .chapter-title {
font-size: clamp(2rem, 5vw, 2.75rem); font-size: clamp(2rem, 5vw, 2.75rem);
padding-bottom: 0.75rem; padding-bottom: 0.75rem;
border-bottom: 2px solid hsl(12 76% 48% / 0.4); border-bottom: 2px solid hsl(var(--color-primary) / 0.4);
} }
</style> </style>

View File

@@ -1,5 +1,34 @@
<template> <template>
<section class="section-padding"> <section class="relative overflow-hidden section-padding">
<!-- Shadok thinker: ovoid character sitting, hand on chin, thinking bubble -->
<svg class="shadok-thinker" viewBox="0 0 220 280" fill="none" aria-hidden="true">
<!-- Body (seated, leaning forward) -->
<ellipse cx="100" cy="160" rx="42" ry="50" fill="currentColor" opacity="0.85"/>
<!-- Head (tilted) -->
<ellipse cx="110" cy="95" rx="25" ry="24" fill="currentColor" opacity="0.8"/>
<!-- Neck -->
<path d="M100 118 Q105 110 108 105" stroke="currentColor" stroke-width="6" stroke-linecap="round" opacity="0.6" fill="none"/>
<!-- Eyes (contemplative) -->
<circle cx="103" cy="90" r="4.5" fill="currentColor" opacity="0.2"/>
<circle cx="120" cy="90" r="4.5" fill="currentColor" opacity="0.2"/>
<circle cx="104" cy="89" r="1.8" fill="currentColor" opacity="0.5"/>
<circle cx="121" cy="89" r="1.8" fill="currentColor" opacity="0.5"/>
<!-- Arm to chin -->
<line x1="140" y1="145" x2="130" y2="108" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
<!-- Hand on chin -->
<circle cx="130" cy="105" r="5" fill="currentColor" opacity="0.45"/>
<!-- Seated legs (crossed/bent) -->
<path d="M75 205 Q60 230 50 240" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6" fill="none"/>
<path d="M120 205 Q140 220 145 240" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6" fill="none"/>
<!-- Feet -->
<path d="M50 240 L38 243 M50 240 L45 246" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.5"/>
<path d="M145 240 L133 243 M145 240 L140 246" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.5"/>
<!-- Thinking bubbles -->
<circle cx="150" cy="78" r="5" fill="currentColor" opacity="0.3"/>
<circle cx="165" cy="62" r="8" fill="currentColor" opacity="0.25"/>
<circle cx="185" cy="42" r="12" fill="currentColor" opacity="0.2"/>
</svg>
<div class="container-content"> <div class="container-content">
<div class="grid items-center gap-12 md:grid-cols-2"> <div class="grid items-center gap-12 md:grid-cols-2">
<!-- Book cover --> <!-- Book cover -->
@@ -63,10 +92,10 @@ const { data: content } = await usePageContent('home')
aspect-ratio: 3 / 4; aspect-ratio: 3 / 4;
border-radius: 0.75rem; border-radius: 0.75rem;
overflow: hidden; overflow: hidden;
border: 1px solid hsl(20 8% 18%); border: 1px solid hsl(var(--color-text) / 0.1);
box-shadow: box-shadow:
0 12px 40px hsl(0 0% 0% / 0.5), 0 12px 40px hsl(var(--color-text) / 0.15),
0 0 0 1px hsl(20 8% 15%); 0 0 0 1px hsl(var(--color-text) / 0.08);
transition: transform 0.5s cubic-bezier(0.645, 0.045, 0.355, 1), transition: transform 0.5s cubic-bezier(0.645, 0.045, 0.355, 1),
box-shadow 0.5s ease; box-shadow 0.5s ease;
max-width: 360px; max-width: 360px;
@@ -75,8 +104,8 @@ const { data: content } = await usePageContent('home')
.book-cover-3d:hover { .book-cover-3d:hover {
transform: rotateY(-8deg) rotateX(3deg) scale(1.02); transform: rotateY(-8deg) rotateX(3deg) scale(1.02);
box-shadow: box-shadow:
12px 16px 48px hsl(0 0% 0% / 0.6), 12px 16px 48px hsl(var(--color-text) / 0.2),
0 0 0 1px hsl(12 76% 48% / 0.2); 0 0 0 1px hsl(var(--color-primary) / 0.2);
} }
.book-cover-img { .book-cover-img {
@@ -89,4 +118,24 @@ const { data: content } = await usePageContent('home')
.heading-section { .heading-section {
font-size: clamp(1.625rem, 4vw, 2.125rem); font-size: clamp(1.625rem, 4vw, 2.125rem);
} }
.shadok-thinker {
position: absolute;
right: 3%;
bottom: 8%;
width: clamp(90px, 12vw, 170px);
opacity: 0.1;
pointer-events: none;
color: hsl(var(--color-accent));
animation: shadok-float-thinker 10s ease-in-out infinite;
}
@keyframes shadok-float-thinker {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-8px); }
}
@media (max-width: 768px) {
.shadok-thinker { display: none; }
}
</style> </style>

View File

@@ -4,7 +4,24 @@
<div class="grid items-center gap-12 md:grid-cols-2"> <div class="grid items-center gap-12 md:grid-cols-2">
<!-- Book cover --> <!-- Book cover -->
<UiScrollReveal> <UiScrollReveal>
<div class="book-cover-wrapper"> <div class="book-cover-wrapper relative">
<!-- Shadok pumper -->
<svg class="shadok-pumper" viewBox="0 0 200 240" fill="none" aria-hidden="true">
<ellipse cx="100" cy="130" rx="55" ry="65" fill="currentColor" opacity="0.9"/>
<ellipse cx="100" cy="60" rx="30" ry="28" fill="currentColor" opacity="0.85"/>
<circle cx="88" cy="54" r="6" fill="currentColor" opacity="0.2"/>
<circle cx="112" cy="54" r="6" fill="currentColor" opacity="0.2"/>
<circle cx="90" cy="53" r="2.5" fill="currentColor" opacity="0.5"/>
<circle cx="114" cy="53" r="2.5" fill="currentColor" opacity="0.5"/>
<polygon points="100,68 115,78 85,78" fill="currentColor" opacity="0.6"/>
<line x1="80" y1="192" x2="70" y2="230" stroke="currentColor" stroke-width="4" stroke-linecap="round" opacity="0.7"/>
<line x1="120" y1="192" x2="130" y2="230" stroke="currentColor" stroke-width="4" stroke-linecap="round" opacity="0.7"/>
<line x1="70" y1="230" x2="55" y2="232" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.5"/>
<line x1="130" y1="230" x2="145" y2="232" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.5"/>
<line x1="155" y1="110" x2="190" y2="90" stroke="currentColor" stroke-width="4" stroke-linecap="round" opacity="0.6"/>
<line x1="190" y1="90" x2="190" y2="120" stroke="currentColor" stroke-width="4" stroke-linecap="round" opacity="0.6"/>
<rect x="180" y="118" width="18" height="40" rx="3" fill="currentColor" opacity="0.4"/>
</svg>
<div class="book-cover-3d"> <div class="book-cover-3d">
<img <img
:src="content?.book.coverImage" :src="content?.book.coverImage"
@@ -68,10 +85,10 @@ const { data: content } = await usePageContent('home')
aspect-ratio: 3 / 4; aspect-ratio: 3 / 4;
border-radius: 0.75rem; border-radius: 0.75rem;
overflow: hidden; overflow: hidden;
border: 1px solid hsl(20 8% 18%); border: 1px solid hsl(var(--color-text) / 0.1);
box-shadow: box-shadow:
0 12px 40px hsl(0 0% 0% / 0.5), 0 12px 40px hsl(var(--color-text) / 0.15),
0 0 0 1px hsl(20 8% 15%); 0 0 0 1px hsl(var(--color-text) / 0.08);
transition: transform 0.5s cubic-bezier(0.645, 0.045, 0.355, 1), transition: transform 0.5s cubic-bezier(0.645, 0.045, 0.355, 1),
box-shadow 0.5s ease; box-shadow 0.5s ease;
max-width: 360px; max-width: 360px;
@@ -80,8 +97,8 @@ const { data: content } = await usePageContent('home')
.book-cover-3d:hover { .book-cover-3d:hover {
transform: rotateY(-8deg) rotateX(3deg) scale(1.02); transform: rotateY(-8deg) rotateX(3deg) scale(1.02);
box-shadow: box-shadow:
12px 16px 48px hsl(0 0% 0% / 0.6), 12px 16px 48px hsl(var(--color-text) / 0.2),
0 0 0 1px hsl(12 76% 48% / 0.2); 0 0 0 1px hsl(var(--color-primary) / 0.2);
} }
.book-cover-img { .book-cover-img {
@@ -94,4 +111,25 @@ const { data: content } = await usePageContent('home')
.heading-section { .heading-section {
font-size: clamp(1.625rem, 4vw, 2.125rem); font-size: clamp(1.625rem, 4vw, 2.125rem);
} }
.shadok-pumper {
position: absolute;
right: -15%;
bottom: 5%;
width: clamp(60px, 10vw, 120px);
opacity: 0.12;
pointer-events: none;
color: hsl(var(--color-primary));
animation: shadok-float 10s ease-in-out infinite;
z-index: 1;
}
@keyframes shadok-float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
@media (max-width: 768px) {
.shadok-pumper { display: none; }
}
</style> </style>

View File

@@ -1,5 +1,37 @@
<template> <template>
<section class="section-padding"> <section class="relative overflow-hidden section-padding">
<!-- Shadok scale: balance with absurd objects -->
<svg class="shadok-scale" viewBox="0 0 260 280" fill="none" aria-hidden="true">
<!-- Vertical pole -->
<line x1="130" y1="40" x2="130" y2="240" stroke="currentColor" stroke-width="4" stroke-linecap="round" opacity="0.8"/>
<!-- Base triangle -->
<polygon points="100,240 160,240 130,220" fill="currentColor" opacity="0.5"/>
<!-- Horizontal beam -->
<line x1="40" y1="80" x2="220" y2="60" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.7"/>
<!-- Pivot circle -->
<circle cx="130" cy="70" r="8" fill="currentColor" opacity="0.6"/>
<!-- Left pan (chain lines) -->
<line x1="40" y1="80" x2="30" y2="120" stroke="currentColor" stroke-width="2" stroke-linecap="round" opacity="0.5"/>
<line x1="40" y1="80" x2="70" y2="120" stroke="currentColor" stroke-width="2" stroke-linecap="round" opacity="0.5"/>
<!-- Left pan dish -->
<path d="M20 120 Q50 135 80 120" stroke="currentColor" stroke-width="2.5" fill="currentColor" opacity="0.35"/>
<!-- Absurd object on left: a snail -->
<ellipse cx="50" cy="112" rx="14" ry="8" fill="currentColor" opacity="0.5"/>
<path d="M60 108 Q68 95 58 92 Q48 90 52 100" stroke="currentColor" stroke-width="2" fill="none" opacity="0.4"/>
<!-- Right pan (chain lines) -->
<line x1="220" y1="60" x2="210" y2="100" stroke="currentColor" stroke-width="2" stroke-linecap="round" opacity="0.5"/>
<line x1="220" y1="60" x2="250" y2="100" stroke="currentColor" stroke-width="2" stroke-linecap="round" opacity="0.5"/>
<!-- Right pan dish -->
<path d="M200 100 Q230 115 260 100" stroke="currentColor" stroke-width="2.5" fill="currentColor" opacity="0.35"/>
<!-- Absurd object on right: a star/coin -->
<circle cx="230" cy="92" r="10" fill="currentColor" opacity="0.4"/>
<circle cx="230" cy="92" r="5" fill="currentColor" opacity="0.2"/>
<!-- Tiny Shadok perched on top -->
<ellipse cx="130" cy="35" rx="12" ry="10" fill="currentColor" opacity="0.6"/>
<circle cx="130" cy="22" r="7" fill="currentColor" opacity="0.55"/>
<circle cx="133" cy="20" r="2" fill="currentColor" opacity="0.3"/>
</svg>
<div class="container-content"> <div class="container-content">
<div class="mx-auto max-w-3xl text-center"> <div class="mx-auto max-w-3xl text-center">
<UiScrollReveal> <UiScrollReveal>
@@ -41,4 +73,24 @@ const { data: content } = await usePageContent('home')
.heading-section { .heading-section {
font-size: clamp(1.625rem, 4vw, 2.125rem); font-size: clamp(1.625rem, 4vw, 2.125rem);
} }
.shadok-scale {
position: absolute;
left: 2%;
top: 10%;
width: clamp(100px, 14vw, 200px);
opacity: 0.1;
pointer-events: none;
color: hsl(var(--color-primary));
animation: shadok-float-scale 9s ease-in-out infinite;
}
@keyframes shadok-float-scale {
0%, 100% { transform: translateY(0) rotate(0deg); }
50% { transform: translateY(-10px) rotate(2deg); }
}
@media (max-width: 768px) {
.shadok-scale { display: none; }
}
</style> </style>

View File

@@ -2,8 +2,20 @@
<section class="section-padding"> <section class="section-padding">
<div class="container-content"> <div class="container-content">
<UiScrollReveal> <UiScrollReveal>
<div class="gw-card"> <div class="gw-card relative overflow-hidden">
<div class="flex flex-col items-center text-center gap-4 md:flex-row md:text-left md:gap-8"> <!-- Shadok blob -->
<svg class="shadok-blob" viewBox="0 0 200 180" fill="none" aria-hidden="true">
<path d="M60 90 Q30 50 70 30 Q110 10 140 40 Q180 60 170 100 Q165 140 130 155 Q90 170 55 145 Q25 125 60 90Z" fill="currentColor" opacity="0.12"/>
<path d="M60 90 Q30 50 70 30 Q110 10 140 40 Q180 60 170 100 Q165 140 130 155 Q90 170 55 145 Q25 125 60 90Z" stroke="currentColor" stroke-width="1.5" opacity="0.2"/>
<circle cx="100" cy="80" r="8" fill="currentColor" opacity="0.08"/>
<circle cx="120" cy="110" r="6" fill="currentColor" opacity="0.06"/>
<circle cx="80" cy="105" r="5" fill="currentColor" opacity="0.07"/>
<circle cx="95" cy="72" r="3" fill="currentColor" opacity="0.3"/>
<circle cx="108" cy="70" r="3" fill="currentColor" opacity="0.3"/>
<circle cx="96" cy="71" r="1.2" fill="currentColor" opacity="0.5"/>
<circle cx="109" cy="69" r="1.2" fill="currentColor" opacity="0.5"/>
</svg>
<div class="flex flex-col items-center text-center gap-4 md:flex-row md:text-left md:gap-8 relative z-1">
<!-- Icon --> <!-- Icon -->
<div class="gw-icon-wrapper"> <div class="gw-icon-wrapper">
<div class="i-lucide-sparkles h-8 w-8 text-amber-400" /> <div class="i-lucide-sparkles h-8 w-8 text-amber-400" />
@@ -75,4 +87,24 @@ const { data: content } = await usePageContent('home')
border: 1px solid hsl(40 80% 50% / 0.15); border: 1px solid hsl(40 80% 50% / 0.15);
flex-shrink: 0; flex-shrink: 0;
} }
.shadok-blob {
position: absolute;
right: -2%;
top: -20%;
width: clamp(100px, 15vw, 180px);
opacity: 0.15;
pointer-events: none;
color: hsl(var(--color-accent));
animation: shadok-drift 12s ease-in-out infinite;
}
@keyframes shadok-drift {
0%, 100% { transform: translateY(0) rotate(0deg); }
50% { transform: translateY(-8px) rotate(3deg); }
}
@media (max-width: 768px) {
.shadok-blob { display: none; }
}
</style> </style>

View File

@@ -4,6 +4,25 @@
<div class="absolute inset-0 bg-gradient-to-b from-primary/10 via-transparent to-surface-bg" /> <div class="absolute inset-0 bg-gradient-to-b from-primary/10 via-transparent to-surface-bg" />
<div class="absolute inset-0 bg-[radial-gradient(ellipse_at_top,hsl(12_76%_48%/0.15),transparent_70%)]" /> <div class="absolute inset-0 bg-[radial-gradient(ellipse_at_top,hsl(12_76%_48%/0.15),transparent_70%)]" />
<!-- Shadok bird decoration -->
<svg class="shadok-bird" viewBox="0 0 180 260" fill="none" aria-hidden="true">
<ellipse cx="90" cy="100" rx="45" ry="40" fill="currentColor" opacity="0.85"/>
<circle cx="130" cy="60" r="22" fill="currentColor" opacity="0.8"/>
<path d="M110 85 Q125 70 128 63" stroke="currentColor" stroke-width="8" stroke-linecap="round" opacity="0.7" fill="none"/>
<circle cx="136" cy="55" r="5" fill="currentColor" opacity="0.3"/>
<circle cx="137" cy="54" r="2" fill="currentColor" opacity="0.6"/>
<polygon points="150,58 175,50 152,65" fill="currentColor" opacity="0.6"/>
<line x1="75" y1="138" x2="60" y2="230" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
<line x1="105" y1="138" x2="115" y2="230" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
<circle cx="66" cy="190" r="4" fill="currentColor" opacity="0.4"/>
<circle cx="111" cy="190" r="4" fill="currentColor" opacity="0.4"/>
<path d="M60 230 L45 233 M60 230 L55 236 M60 230 L65 235" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.5"/>
<path d="M115 230 L100 233 M115 230 L110 236 M115 230 L120 235" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.5"/>
<path d="M48 95 Q20 80 15 65" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.5" fill="none"/>
<path d="M48 100 Q22 92 10 85" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.4" fill="none"/>
<path d="M48 105 Q25 102 12 100" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.3" fill="none"/>
</svg>
<!-- Content --> <!-- Content -->
<div class="container-content relative z-10 px-4"> <div class="container-content relative z-10 px-4">
<div class="mx-auto max-w-3xl text-center"> <div class="mx-auto max-w-3xl text-center">
@@ -54,4 +73,25 @@ const { data: content } = await usePageContent('home')
.hero-title { .hero-title {
font-size: clamp(2.25rem, 7vw, 4rem); font-size: clamp(2.25rem, 7vw, 4rem);
} }
.shadok-bird {
position: absolute;
right: 5%;
top: 15%;
width: clamp(80px, 12vw, 160px);
opacity: 0.12;
pointer-events: none;
color: hsl(var(--color-primary));
animation: shadok-float 8s ease-in-out infinite;
z-index: 1;
}
@keyframes shadok-float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-12px); }
}
@media (max-width: 768px) {
.shadok-bird { display: none; }
}
</style> </style>

View File

@@ -106,8 +106,8 @@ function formatDate(iso: string) {
<style scoped> <style scoped>
.message-form-card { .message-form-card {
background: hsl(20 8% 6%); background: hsl(var(--color-surface));
border: 1px solid hsl(20 8% 14%); border: 1px solid hsl(var(--color-text) / 0.1);
border-radius: 0.75rem; border-radius: 0.75rem;
padding: 1.5rem; padding: 1.5rem;
} }
@@ -116,25 +116,25 @@ function formatDate(iso: string) {
width: 100%; width: 100%;
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
border-radius: 0.5rem; border-radius: 0.5rem;
border: 1px solid hsl(20 8% 18%); border: 1px solid hsl(var(--color-text) / 0.12);
background: hsl(20 8% 4%); background: hsl(var(--color-bg));
color: white; color: hsl(var(--color-text));
font-size: 0.875rem; font-size: 0.875rem;
transition: border-color 0.2s; transition: border-color 0.2s;
} }
.msg-input::placeholder { .msg-input::placeholder {
color: hsl(20 8% 40%); color: hsl(var(--color-text) / 0.35);
} }
.msg-input:focus { .msg-input:focus {
outline: none; outline: none;
border-color: hsl(12 76% 48% / 0.5); border-color: hsl(var(--color-primary) / 0.5);
} }
.message-card { .message-card {
background: hsl(20 8% 6%); background: hsl(var(--color-surface));
border: 1px solid hsl(20 8% 14%); border: 1px solid hsl(var(--color-text) / 0.1);
border-radius: 0.75rem; border-radius: 0.75rem;
padding: 1rem 1.25rem; padding: 1rem 1.25rem;
} }

View File

@@ -1,5 +1,37 @@
<template> <template>
<section class="section-padding bg-surface-600/50"> <section class="relative overflow-hidden section-padding bg-surface-600/50">
<!-- Shadok musician: round character playing a trumpet -->
<svg class="shadok-musician" viewBox="0 0 220 280" fill="none" aria-hidden="true">
<!-- Body (ovoid) -->
<ellipse cx="100" cy="150" rx="45" ry="55" fill="currentColor" opacity="0.85"/>
<!-- Head -->
<circle cx="100" cy="80" r="28" fill="currentColor" opacity="0.8"/>
<!-- Eyes -->
<circle cx="90" cy="74" r="5" fill="currentColor" opacity="0.2"/>
<circle cx="110" cy="74" r="5" fill="currentColor" opacity="0.2"/>
<circle cx="91" cy="73" r="2" fill="currentColor" opacity="0.5"/>
<circle cx="111" cy="73" r="2" fill="currentColor" opacity="0.5"/>
<!-- Mouth (blowing) -->
<circle cx="125" cy="86" r="4" fill="currentColor" opacity="0.3"/>
<!-- Trumpet -->
<line x1="128" y1="86" x2="185" y2="78" stroke="currentColor" stroke-width="5" stroke-linecap="round" opacity="0.7"/>
<path d="M185 68 Q200 78 185 88" stroke="currentColor" stroke-width="3" fill="currentColor" opacity="0.45"/>
<!-- Trumpet valves -->
<circle cx="155" cy="80" r="3" fill="currentColor" opacity="0.3"/>
<circle cx="165" cy="79" r="3" fill="currentColor" opacity="0.3"/>
<!-- Music notes floating -->
<circle cx="205" cy="60" r="4" fill="currentColor" opacity="0.4"/>
<line x1="209" y1="60" x2="209" y2="42" stroke="currentColor" stroke-width="1.5" opacity="0.4"/>
<circle cx="195" cy="45" r="3" fill="currentColor" opacity="0.3"/>
<line x1="198" y1="45" x2="198" y2="30" stroke="currentColor" stroke-width="1.5" opacity="0.3"/>
<!-- Legs -->
<line x1="82" y1="202" x2="72" y2="255" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
<line x1="118" y1="202" x2="128" y2="255" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
<!-- Feet -->
<path d="M72 255 L58 258 M72 255 L66 261" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.5"/>
<path d="M128 255 L114 258 M128 255 L122 261" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.5"/>
</svg>
<div class="container-content"> <div class="container-content">
<UiScrollReveal> <UiScrollReveal>
<div class="text-center mb-12"> <div class="text-center mb-12">
@@ -48,4 +80,24 @@ const featuredSongs = computed(() => bookData.getSongs().slice(0, 6))
.heading-section { .heading-section {
font-size: clamp(1.625rem, 4vw, 2.125rem); font-size: clamp(1.625rem, 4vw, 2.125rem);
} }
.shadok-musician {
position: absolute;
right: 3%;
top: 5%;
width: clamp(90px, 12vw, 170px);
opacity: 0.1;
pointer-events: none;
color: hsl(var(--color-primary));
animation: shadok-float-musician 8s ease-in-out infinite;
}
@keyframes shadok-float-musician {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
@media (max-width: 768px) {
.shadok-musician { display: none; }
}
</style> </style>

View File

@@ -1,6 +1,45 @@
<template> <template>
<footer class="border-t border-white/8 bg-surface-600"> <footer class="footer-wrap border-t border-white/8 bg-surface-600">
<div class="container-content px-4 py-8"> <!-- Shadok pattern -->
<svg class="footer-shadok-pattern" viewBox="0 0 400 80" fill="none" aria-hidden="true">
<g transform="translate(20,10)">
<ellipse cx="15" cy="25" rx="12" ry="14" fill="currentColor" opacity="0.08"/>
<circle cx="15" cy="10" r="7" fill="currentColor" opacity="0.06"/>
<line x1="10" y1="38" x2="8" y2="55" stroke="currentColor" stroke-width="1.5" opacity="0.06"/>
<line x1="20" y1="38" x2="22" y2="55" stroke="currentColor" stroke-width="1.5" opacity="0.06"/>
</g>
<g transform="translate(80,15)">
<ellipse cx="15" cy="22" rx="10" ry="12" fill="currentColor" opacity="0.06"/>
<circle cx="15" cy="8" r="6" fill="currentColor" opacity="0.05"/>
<line x1="10" y1="33" x2="8" y2="48" stroke="currentColor" stroke-width="1.5" opacity="0.05"/>
<line x1="20" y1="33" x2="22" y2="48" stroke="currentColor" stroke-width="1.5" opacity="0.05"/>
</g>
<g transform="translate(140,8)">
<ellipse cx="15" cy="25" rx="11" ry="13" fill="currentColor" opacity="0.07"/>
<circle cx="15" cy="10" r="6.5" fill="currentColor" opacity="0.06"/>
<line x1="10" y1="37" x2="7" y2="54" stroke="currentColor" stroke-width="1.5" opacity="0.06"/>
<line x1="20" y1="37" x2="23" y2="54" stroke="currentColor" stroke-width="1.5" opacity="0.06"/>
</g>
<g transform="translate(210,18)">
<ellipse cx="15" cy="20" rx="10" ry="11" fill="currentColor" opacity="0.05"/>
<circle cx="15" cy="7" r="5.5" fill="currentColor" opacity="0.04"/>
<line x1="10" y1="30" x2="9" y2="44" stroke="currentColor" stroke-width="1.5" opacity="0.04"/>
<line x1="20" y1="30" x2="21" y2="44" stroke="currentColor" stroke-width="1.5" opacity="0.04"/>
</g>
<g transform="translate(270,12)">
<ellipse cx="15" cy="24" rx="12" ry="14" fill="currentColor" opacity="0.07"/>
<circle cx="15" cy="9" r="7" fill="currentColor" opacity="0.06"/>
<line x1="10" y1="37" x2="7" y2="55" stroke="currentColor" stroke-width="1.5" opacity="0.06"/>
<line x1="20" y1="37" x2="23" y2="55" stroke="currentColor" stroke-width="1.5" opacity="0.06"/>
</g>
<g transform="translate(340,16)">
<ellipse cx="15" cy="22" rx="10" ry="12" fill="currentColor" opacity="0.06"/>
<circle cx="15" cy="8" r="6" fill="currentColor" opacity="0.05"/>
<line x1="10" y1="33" x2="8" y2="48" stroke="currentColor" stroke-width="1.5" opacity="0.05"/>
<line x1="20" y1="33" x2="22" y2="48" stroke="currentColor" stroke-width="1.5" opacity="0.05"/>
</g>
</svg>
<div class="container-content px-4 py-8 relative z-1">
<div class="flex flex-col items-center gap-4 md:flex-row md:justify-between"> <div class="flex flex-col items-center gap-4 md:flex-row md:justify-between">
<!-- Credits --> <!-- Credits -->
<p class="text-sm text-white/40"> <p class="text-sm text-white/40">
@@ -26,3 +65,21 @@
<script setup lang="ts"> <script setup lang="ts">
const { data: site } = await useSiteContent() const { data: site } = await useSiteContent()
</script> </script>
<style scoped>
.footer-wrap {
position: relative;
overflow: hidden;
}
.footer-shadok-pattern {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: auto;
opacity: 0.6;
pointer-events: none;
color: hsl(var(--color-primary));
}
</style>

View File

@@ -2,9 +2,9 @@
<header class="sticky top-0 z-40 border-b border-white/8 bg-surface-bg/80 backdrop-blur-xl"> <header class="sticky top-0 z-40 border-b border-white/8 bg-surface-bg/80 backdrop-blur-xl">
<div class="container-content flex h-[var(--header-height)] items-center justify-between px-4"> <div class="container-content flex h-[var(--header-height)] items-center justify-between px-4">
<!-- Logo --> <!-- Logo -->
<NuxtLink to="/" class="flex items-center gap-2 font-display text-lg font-bold tracking-tight"> <NuxtLink to="/" class="flex items-center gap-2 text-lg tracking-tight">
<div class="i-lucide-book-open h-6 w-6 text-primary" /> <div class="i-lucide-book-open h-6 w-6 text-primary" />
<span class="text-gradient">{{ site?.identity.name }}</span> <span class="font-calligraphy font-bold text-gradient text-xl italic">{{ site?.identity.name }}</span>
</NuxtLink> </NuxtLink>
<!-- Desktop navigation --> <!-- Desktop navigation -->
@@ -18,6 +18,7 @@
> >
{{ item.label }} {{ item.label }}
</NuxtLink> </NuxtLink>
<UiPaletteSelector />
</nav> </nav>
<!-- Mobile menu button --> <!-- Mobile menu button -->

View File

@@ -99,13 +99,18 @@
<div :class="store.isPlaying ? 'i-lucide-pause' : 'i-lucide-play'" class="h-4 w-4" /> <div :class="store.isPlaying ? 'i-lucide-pause' : 'i-lucide-play'" class="h-4 w-4" />
</button> </button>
<!-- Next -->
<button class="pill-next" aria-label="Suivant" @click.stop="playNext" :disabled="!store.hasNext">
<div class="i-lucide-skip-forward h-3.5 w-3.5" />
</button>
<!-- Expand --> <!-- Expand -->
<button <button
class="pill-expand" class="pill-expand"
:aria-label="isExpanded ? 'Réduire' : 'Développer'" :aria-label="isExpanded ? 'Réduire' : 'Développer'"
@click.stop="toggleExpanded" @click.stop="toggleExpanded"
> >
<div :class="isExpanded ? 'i-lucide-chevron-down' : 'i-lucide-chevron-up'" class="h-3.5 w-3.5" /> <div :class="isExpanded ? 'i-lucide-chevron-down' : 'i-lucide-chevron-up'" class="h-4 w-4" />
</button> </button>
</div> </div>
</div> </div>
@@ -116,7 +121,7 @@
import { onClickOutside } from '@vueuse/core' import { onClickOutside } from '@vueuse/core'
const store = usePlayerStore() const store = usePlayerStore()
const { setVolume, togglePlayPause } = useAudioPlayer() const { setVolume, togglePlayPause, playNext } = useAudioPlayer()
useMediaSession() useMediaSession()
@@ -235,22 +240,40 @@ onClickOutside(widgetRef, () => {
.pill-play:hover { transform: scale(1.08); } .pill-play:hover { transform: scale(1.08); }
.pill-play:active { transform: scale(0.94); } .pill-play:active { transform: scale(0.94); }
/* Next */
.pill-next {
display: flex;
align-items: center;
justify-content: center;
width: 1.5rem;
height: 1.5rem;
border-radius: 50%;
background: transparent;
border: none;
color: hsl(0 0% 100% / 0.6);
cursor: pointer;
transition: all 0.15s;
flex-shrink: 0;
}
.pill-next:hover { color: white; }
.pill-next:disabled { opacity: 0.3; cursor: default; }
/* Expand chevron */ /* Expand chevron */
.pill-expand { .pill-expand {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 1.25rem; width: 1.75rem;
height: 1.25rem; height: 1.75rem;
border-radius: 50%; border-radius: 50%;
background: transparent; background: hsl(0 0% 100% / 0.08);
border: none; border: none;
color: hsl(0 0% 100% / 0.3); color: hsl(0 0% 100% / 0.5);
cursor: pointer; cursor: pointer;
transition: color 0.2s; transition: all 0.2s;
flex-shrink: 0; flex-shrink: 0;
} }
.pill-expand:hover { color: hsl(0 0% 100% / 0.7); } .pill-expand:hover { color: hsl(0 0% 100% / 0.9); background: hsl(0 0% 100% / 0.15); }
/* ═══════════════════════════════════════ /* ═══════════════════════════════════════
PANEL PANEL

View File

@@ -0,0 +1,261 @@
<template>
<div class="settings-selector" ref="selectorRef">
<button
class="settings-trigger"
aria-label="Réglages d'affichage"
@click="isOpen = !isOpen"
>
<div class="i-lucide-settings h-5 w-5" />
</button>
<Transition name="settings-dropdown">
<div v-if="isOpen" class="settings-dropdown">
<h4 class="settings-title">Affichage</h4>
<!-- Palette grid : 4 saisons -->
<div class="settings-section">
<span class="settings-label">Ambiance</span>
<div class="settings-palette-grid">
<button
v-for="name in paletteNames"
:key="name"
class="settings-palette-btn"
:class="{
'settings-palette-btn--active': paletteStore.currentPalette === name,
'settings-palette-btn--light': paletteStore.palettes[name].isLight,
}"
:title="paletteStore.palettes[name].label"
@click="paletteStore.setPalette(name)"
>
<span class="settings-palette-preview">
<span class="settings-palette-dot" :style="{ background: `hsl(${paletteStore.palettes[name].primary})` }" />
<span class="settings-palette-dot" :style="{ background: `hsl(${paletteStore.palettes[name].accent})` }" />
</span>
<span class="settings-palette-info">
<span class="settings-palette-name">{{ paletteStore.palettes[name].label }}</span>
<span class="settings-palette-mode">{{ paletteStore.palettes[name].isLight ? 'Clair' : 'Sombre' }}</span>
</span>
</button>
</div>
</div>
<!-- Font size -->
<div class="settings-section">
<span class="settings-label">Taille texte</span>
<div class="settings-toggle-group">
<button
v-for="size in fontSizes"
:key="size.value"
class="settings-toggle"
:class="{ 'settings-toggle--active': currentFontSize === size.value }"
@click="setFontSize(size.value)"
>
{{ size.label }}
</button>
</div>
</div>
</div>
</Transition>
</div>
</template>
<script setup lang="ts">
import { onClickOutside } from '@vueuse/core'
import type { PaletteName } from '~/stores/palette'
const paletteStore = usePaletteStore()
const selectorRef = ref<HTMLElement>()
const isOpen = ref(false)
const paletteNames: PaletteName[] = ['automne', 'hiver', 'printemps', 'ete']
const currentFontSize = ref(
(import.meta.client && localStorage.getItem('fontSize')) || 'normal',
)
const fontSizes = [
{ label: 'A-', value: 'small' },
{ label: 'A', value: 'normal' },
{ label: 'A+', value: 'large' },
]
function setFontSize(size: string) {
currentFontSize.value = size
if (import.meta.client) {
localStorage.setItem('fontSize', size)
const root = document.documentElement
const map: Record<string, string> = { small: '14px', normal: '16px', large: '18px' }
root.style.fontSize = map[size] || '16px'
}
}
// Apply font size on mount
onMounted(() => {
const saved = localStorage.getItem('fontSize')
if (saved) setFontSize(saved)
})
onClickOutside(selectorRef, () => { isOpen.value = false })
</script>
<style scoped>
.settings-selector {
position: relative;
}
.settings-trigger {
display: flex;
align-items: center;
justify-content: center;
width: 2.25rem;
height: 2.25rem;
border-radius: 0.5rem;
color: hsl(var(--color-text) / 0.7);
transition: all 0.2s;
}
.settings-trigger:hover {
color: hsl(var(--color-text));
background: hsl(var(--color-text) / 0.1);
}
.settings-dropdown {
position: absolute;
top: calc(100% + 0.5rem);
right: 0;
width: 260px;
padding: 0.75rem;
border-radius: 0.75rem;
background: hsl(var(--color-surface));
backdrop-filter: blur(16px);
border: 1px solid hsl(var(--color-text) / 0.08);
box-shadow: 0 8px 32px hsl(0 0% 0% / 0.3);
display: flex;
flex-direction: column;
gap: 0.75rem;
z-index: 50;
}
.settings-title {
font-family: var(--font-display);
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
color: hsl(var(--color-text) / 0.4);
margin: 0;
}
.settings-section {
display: flex;
flex-direction: column;
gap: 0.375rem;
}
.settings-label {
font-size: 0.7rem;
font-weight: 600;
color: hsl(var(--color-text) / 0.5);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.settings-toggle-group {
display: flex;
gap: 0.25rem;
background: hsl(var(--color-text) / 0.04);
border-radius: 0.5rem;
padding: 0.125rem;
}
.settings-toggle {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: 0.375rem;
padding: 0.375rem 0.5rem;
border-radius: 0.375rem;
font-size: 0.75rem;
font-weight: 500;
color: hsl(var(--color-text) / 0.5);
transition: all 0.15s;
}
.settings-toggle:hover {
color: hsl(var(--color-text) / 0.8);
}
.settings-toggle--active {
background: hsl(var(--color-primary));
color: white;
box-shadow: 0 1px 4px hsl(var(--color-primary) / 0.3);
}
.settings-palette-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 0.375rem;
}
.settings-palette-btn {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem;
border-radius: 0.5rem;
transition: all 0.2s;
color: hsl(var(--color-text) / 0.6);
background: hsl(var(--color-text) / 0.02);
}
.settings-palette-btn:hover {
background: hsl(var(--color-text) / 0.06);
color: hsl(var(--color-text) / 0.9);
}
.settings-palette-btn--active {
background: hsl(var(--color-text) / 0.1);
color: hsl(var(--color-text));
box-shadow: inset 0 0 0 1.5px hsl(var(--color-primary) / 0.5);
}
.settings-palette-preview {
display: flex;
gap: 0.125rem;
flex-shrink: 0;
}
.settings-palette-dot {
width: 0.75rem;
height: 0.75rem;
border-radius: 50%;
box-shadow: 0 1px 3px hsl(0 0% 0% / 0.25);
}
.settings-palette-info {
display: flex;
flex-direction: column;
gap: 0;
min-width: 0;
}
.settings-palette-name {
font-size: 0.75rem;
font-weight: 600;
line-height: 1.2;
}
.settings-palette-mode {
font-size: 0.6rem;
opacity: 0.5;
line-height: 1.2;
}
.settings-dropdown-enter-active {
transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
.settings-dropdown-leave-active {
transition: all 0.15s ease;
}
.settings-dropdown-enter-from,
.settings-dropdown-leave-to {
opacity: 0;
transform: translateY(-4px) scale(0.96);
}
</style>

View File

@@ -35,7 +35,7 @@
top: var(--header-height); top: var(--header-height);
height: calc(100dvh - var(--header-height)); height: calc(100dvh - var(--header-height));
overflow-y: auto; overflow-y: auto;
border-right: 1px solid hsl(0 0% 100% / 0.08); border-right: 1px solid hsl(var(--color-text) / 0.08);
padding: 1.5rem; padding: 1.5rem;
} }

View File

@@ -1,5 +1,34 @@
<template> <template>
<div class="section-padding"> <div class="relative overflow-hidden section-padding">
<!-- Shadok philosopher: character sitting cross-legged, floating -->
<svg class="shadok-philosopher" viewBox="0 0 220 280" fill="none" aria-hidden="true">
<!-- Body (round, serene) -->
<ellipse cx="110" cy="150" rx="48" ry="55" fill="currentColor" opacity="0.85"/>
<!-- Head -->
<ellipse cx="110" cy="82" rx="26" ry="25" fill="currentColor" opacity="0.8"/>
<!-- Closed eyes (meditating) -->
<path d="M96 80 Q100 84 105 80" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none" opacity="0.4"/>
<path d="M115 80 Q119 84 124 80" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none" opacity="0.4"/>
<!-- Serene smile -->
<path d="M102 93 Q110 98 118 93" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.3"/>
<!-- Arms resting on knees -->
<path d="M64 155 Q55 180 70 200" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" fill="none" opacity="0.6"/>
<path d="M156 155 Q165 180 150 200" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" fill="none" opacity="0.6"/>
<!-- Hands on knees -->
<circle cx="70" cy="200" r="5" fill="currentColor" opacity="0.4"/>
<circle cx="150" cy="200" r="5" fill="currentColor" opacity="0.4"/>
<!-- Crossed legs -->
<path d="M80 205 Q90 230 120 235" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" fill="none" opacity="0.6"/>
<path d="M140 205 Q130 230 100 235" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" fill="none" opacity="0.6"/>
<!-- Floating aura lines -->
<path d="M50 245 Q110 260 170 245" stroke="currentColor" stroke-width="1.5" stroke-dasharray="4 4" fill="none" opacity="0.25"/>
<path d="M60 255 Q110 268 160 255" stroke="currentColor" stroke-width="1" stroke-dasharray="3 5" fill="none" opacity="0.18"/>
<!-- Small sparkles around -->
<circle cx="55" cy="110" r="2.5" fill="currentColor" opacity="0.2"/>
<circle cx="170" cy="95" r="2" fill="currentColor" opacity="0.18"/>
<circle cx="160" cy="130" r="3" fill="currentColor" opacity="0.15"/>
</svg>
<div class="container-content mx-auto max-w-3xl"> <div class="container-content mx-auto max-w-3xl">
<ContentRenderer v-if="page" :value="page" class="prose" /> <ContentRenderer v-if="page" :value="page" class="prose" />
</div> </div>
@@ -19,3 +48,25 @@ const { data: page } = await useAsyncData('about', () =>
queryCollection('pages').path('/pages/about').first(), queryCollection('pages').path('/pages/about').first(),
) )
</script> </script>
<style scoped>
.shadok-philosopher {
position: absolute;
right: 3%;
bottom: 8%;
width: clamp(90px, 13vw, 180px);
opacity: 0.1;
pointer-events: none;
color: hsl(var(--color-primary));
animation: shadok-float-philosopher 11s ease-in-out infinite;
}
@keyframes shadok-float-philosopher {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-12px); }
}
@media (max-width: 768px) {
.shadok-philosopher { display: none; }
}
</style>

View File

@@ -1,5 +1,41 @@
<template> <template>
<div class="section-padding"> <div class="relative overflow-hidden section-padding">
<!-- Shadok DJ: character with headphones behind a turntable -->
<svg class="shadok-dj" viewBox="0 0 260 300" fill="none" aria-hidden="true">
<!-- Body -->
<ellipse cx="130" cy="155" rx="42" ry="50" fill="currentColor" opacity="0.85"/>
<!-- Head -->
<circle cx="130" cy="88" r="26" fill="currentColor" opacity="0.8"/>
<!-- Headphones band -->
<path d="M104 78 Q130 55 156 78" stroke="currentColor" stroke-width="4" stroke-linecap="round" fill="none" opacity="0.6"/>
<!-- Headphone ear pads -->
<ellipse cx="102" cy="85" rx="8" ry="12" fill="currentColor" opacity="0.5"/>
<ellipse cx="158" cy="85" rx="8" ry="12" fill="currentColor" opacity="0.5"/>
<!-- Eyes (cool, half-lidded) -->
<ellipse cx="120" cy="85" rx="5" ry="3" fill="currentColor" opacity="0.25"/>
<ellipse cx="140" cy="85" rx="5" ry="3" fill="currentColor" opacity="0.25"/>
<circle cx="121" cy="86" r="1.8" fill="currentColor" opacity="0.5"/>
<circle cx="141" cy="86" r="1.8" fill="currentColor" opacity="0.5"/>
<!-- Mouth (grin) -->
<path d="M122 98 Q130 104 138 98" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none" opacity="0.35"/>
<!-- Arms reaching to turntable -->
<line x1="90" y1="150" x2="55" y2="195" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
<line x1="170" y1="150" x2="205" y2="195" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
<!-- Turntable body -->
<rect x="30" y="200" width="200" height="18" rx="4" fill="currentColor" opacity="0.4"/>
<!-- Turntable platter (ellipse for perspective) -->
<ellipse cx="130" cy="200" rx="55" ry="15" fill="currentColor" opacity="0.25"/>
<ellipse cx="130" cy="200" rx="55" ry="15" stroke="currentColor" stroke-width="1.5" fill="none" opacity="0.35"/>
<!-- Record center -->
<circle cx="130" cy="200" r="5" fill="currentColor" opacity="0.4"/>
<!-- Tone arm -->
<line x1="195" y1="188" x2="150" y2="195" stroke="currentColor" stroke-width="2" stroke-linecap="round" opacity="0.5"/>
<circle cx="195" cy="188" r="3" fill="currentColor" opacity="0.35"/>
<!-- Legs -->
<line x1="115" y1="202" x2="105" y2="260" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
<line x1="145" y1="202" x2="155" y2="260" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
</svg>
<div class="container-content"> <div class="container-content">
<header class="mb-12 text-center"> <header class="mb-12 text-center">
<p class="mb-2 font-mono text-sm tracking-widest text-accent uppercase">{{ content?.kicker }}</p> <p class="mb-2 font-mono text-sm tracking-widest text-accent uppercase">{{ content?.kicker }}</p>
@@ -107,4 +143,24 @@ const filteredSongs = computed(() => {
.page-title { .page-title {
font-size: clamp(2rem, 5vw, 2.75rem); font-size: clamp(2rem, 5vw, 2.75rem);
} }
.shadok-dj {
position: absolute;
right: 2%;
top: 3%;
width: clamp(100px, 14vw, 190px);
opacity: 0.1;
pointer-events: none;
color: hsl(var(--color-accent));
animation: shadok-float-dj 8s ease-in-out infinite;
}
@keyframes shadok-float-dj {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
@media (max-width: 768px) {
.shadok-dj { display: none; }
}
</style> </style>

View File

@@ -1,6 +1,48 @@
<template> <template>
<NuxtLayout> <NuxtLayout>
<div class="section-padding"> <div class="relative overflow-hidden section-padding">
<!-- Shadok alchemist: character stirring a cauldron with sparkles -->
<svg class="shadok-alchemist" viewBox="0 0 240 320" fill="none" aria-hidden="true">
<!-- Body -->
<ellipse cx="120" cy="145" rx="40" ry="48" fill="currentColor" opacity="0.85"/>
<!-- Head -->
<circle cx="120" cy="82" r="24" fill="currentColor" opacity="0.8"/>
<!-- Wizard hat -->
<polygon points="120,30 100,80 140,80" fill="currentColor" opacity="0.5"/>
<line x1="100" y1="80" x2="140" y2="80" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.45"/>
<!-- Hat star -->
<circle cx="118" cy="55" r="3" fill="currentColor" opacity="0.25"/>
<!-- Eyes (mischievous) -->
<circle cx="111" cy="78" r="4.5" fill="currentColor" opacity="0.2"/>
<circle cx="129" cy="78" r="4.5" fill="currentColor" opacity="0.2"/>
<circle cx="112" cy="77" r="2" fill="currentColor" opacity="0.5"/>
<circle cx="130" cy="77" r="2" fill="currentColor" opacity="0.5"/>
<!-- Grin -->
<path d="M112 92 Q120 98 128 92" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none" opacity="0.35"/>
<!-- Arm holding spoon/stick -->
<line x1="158" y1="140" x2="175" y2="210" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
<!-- Other arm -->
<path d="M82 145 Q65 165 70 185" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" fill="none" opacity="0.6"/>
<!-- Cauldron -->
<path d="M60 220 Q60 260 120 260 Q180 260 180 220" fill="currentColor" opacity="0.4"/>
<ellipse cx="120" cy="220" rx="60" ry="15" fill="currentColor" opacity="0.3"/>
<ellipse cx="120" cy="220" rx="60" ry="15" stroke="currentColor" stroke-width="2" fill="none" opacity="0.4"/>
<!-- Cauldron legs -->
<line x1="80" y1="258" x2="75" y2="280" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.5"/>
<line x1="160" y1="258" x2="165" y2="280" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.5"/>
<!-- Bubbles from cauldron -->
<circle cx="100" cy="210" r="5" fill="currentColor" opacity="0.2"/>
<circle cx="135" cy="205" r="4" fill="currentColor" opacity="0.18"/>
<circle cx="115" cy="198" r="3" fill="currentColor" opacity="0.15"/>
<!-- Sparkles -->
<circle cx="90" cy="190" r="2" fill="currentColor" opacity="0.25"/>
<circle cx="150" cy="195" r="2.5" fill="currentColor" opacity="0.2"/>
<circle cx="105" cy="185" r="1.5" fill="currentColor" opacity="0.2"/>
<!-- Character legs -->
<line x1="105" y1="190" x2="95" y2="225" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.5"/>
<line x1="135" y1="190" x2="145" y2="225" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.5"/>
</svg>
<div class="container-content max-w-3xl mx-auto"> <div class="container-content max-w-3xl mx-auto">
<!-- Back link --> <!-- Back link -->
<UiScrollReveal> <UiScrollReveal>
@@ -77,14 +119,14 @@ const { url, launch } = useGrateWizard()
.gw-feature-card { .gw-feature-card {
padding: 1.5rem; padding: 1.5rem;
border-radius: 0.75rem; border-radius: 0.75rem;
border: 1px solid hsl(20 8% 18%); border: 1px solid hsl(var(--color-text) / 0.1);
background: hsl(20 8% 8% / 0.5); background: hsl(var(--color-surface) / 0.5);
transition: border-color 0.3s ease, background 0.3s ease; transition: border-color 0.3s ease, background 0.3s ease;
} }
.gw-feature-card:hover { .gw-feature-card:hover {
border-color: hsl(40 80% 50% / 0.25); border-color: hsl(40 80% 50% / 0.25);
background: hsl(20 8% 10% / 0.5); background: hsl(var(--color-surface-light) / 0.5);
} }
code { code {
@@ -94,4 +136,24 @@ code {
border-radius: 0.25em; border-radius: 0.25em;
background: hsl(40 80% 50% / 0.1); background: hsl(40 80% 50% / 0.1);
} }
.shadok-alchemist {
position: absolute;
right: 2%;
top: 15%;
width: clamp(100px, 14vw, 190px);
opacity: 0.1;
pointer-events: none;
color: hsl(var(--color-accent));
animation: shadok-float-alchemist 10s ease-in-out infinite;
}
@keyframes shadok-float-alchemist {
0%, 100% { transform: translateY(0) rotate(0deg); }
50% { transform: translateY(-10px) rotate(1deg); }
}
@media (max-width: 768px) {
.shadok-alchemist { display: none; }
}
</style> </style>

View File

@@ -1,5 +1,54 @@
<template> <template>
<div class="section-padding"> <div class="relative overflow-hidden section-padding">
<!-- Shadok reader: character with big glasses reading a book -->
<svg class="shadok-reader" viewBox="0 0 240 300" fill="none" aria-hidden="true">
<!-- Body -->
<ellipse cx="120" cy="170" rx="45" ry="55" fill="currentColor" opacity="0.85"/>
<!-- Head -->
<circle cx="120" cy="100" r="28" fill="currentColor" opacity="0.8"/>
<!-- Big round glasses -->
<circle cx="107" cy="94" r="11" stroke="currentColor" stroke-width="2.5" fill="none" opacity="0.5"/>
<circle cx="133" cy="94" r="11" stroke="currentColor" stroke-width="2.5" fill="none" opacity="0.5"/>
<line x1="118" y1="94" x2="122" y2="94" stroke="currentColor" stroke-width="2" opacity="0.5"/>
<!-- Eyes behind glasses -->
<circle cx="108" cy="93" r="2.5" fill="currentColor" opacity="0.5"/>
<circle cx="134" cy="93" r="2.5" fill="currentColor" opacity="0.5"/>
<!-- Arms holding book -->
<line x1="78" y1="155" x2="60" y2="180" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
<line x1="162" y1="155" x2="180" y2="180" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
<!-- Book (open) -->
<rect x="55" y="175" width="55" height="40" rx="2" fill="currentColor" opacity="0.35"/>
<rect x="110" y="175" width="55" height="40" rx="2" fill="currentColor" opacity="0.3"/>
<line x1="110" y1="175" x2="110" y2="215" stroke="currentColor" stroke-width="2" opacity="0.5"/>
<!-- Book lines (text) -->
<line x1="65" y1="188" x2="100" y2="188" stroke="currentColor" stroke-width="1" opacity="0.2"/>
<line x1="65" y1="195" x2="95" y2="195" stroke="currentColor" stroke-width="1" opacity="0.2"/>
<line x1="65" y1="202" x2="98" y2="202" stroke="currentColor" stroke-width="1" opacity="0.2"/>
<!-- Legs -->
<line x1="105" y1="222" x2="95" y2="270" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
<line x1="135" y1="222" x2="145" y2="270" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
</svg>
<!-- Shadok stack: pile of books tilting -->
<svg class="shadok-stack" viewBox="0 0 160 220" fill="none" aria-hidden="true">
<!-- Bottom book -->
<rect x="20" y="170" width="120" height="22" rx="3" fill="currentColor" opacity="0.5" transform="rotate(-2 80 181)"/>
<!-- Second book -->
<rect x="30" y="145" width="100" height="20" rx="3" fill="currentColor" opacity="0.45" transform="rotate(3 80 155)"/>
<!-- Third book -->
<rect x="25" y="120" width="110" height="18" rx="3" fill="currentColor" opacity="0.4" transform="rotate(-4 80 129)"/>
<!-- Fourth book -->
<rect x="35" y="97" width="90" height="18" rx="3" fill="currentColor" opacity="0.35" transform="rotate(5 80 106)"/>
<!-- Fifth book (tilting more) -->
<rect x="40" y="74" width="80" height="17" rx="3" fill="currentColor" opacity="0.3" transform="rotate(-7 80 82)"/>
<!-- Top book (really tilting) -->
<rect x="45" y="52" width="70" height="16" rx="3" fill="currentColor" opacity="0.25" transform="rotate(10 80 60)"/>
<!-- Tiny Shadok sitting on top -->
<ellipse cx="85" cy="42" rx="10" ry="8" fill="currentColor" opacity="0.5"/>
<circle cx="85" cy="30" r="6" fill="currentColor" opacity="0.45"/>
<circle cx="87" cy="29" r="1.5" fill="currentColor" opacity="0.3"/>
</svg>
<div class="container-content"> <div class="container-content">
<header class="mb-12 text-center"> <header class="mb-12 text-center">
<p class="mb-2 font-mono text-sm tracking-widest text-primary uppercase">{{ content?.kicker }}</p> <p class="mb-2 font-mono text-sm tracking-widest text-primary uppercase">{{ content?.kicker }}</p>
@@ -68,4 +117,41 @@ const { data: chapters } = await useAsyncData('book-toc', () =>
.page-title { .page-title {
font-size: clamp(2rem, 5vw, 2.75rem); font-size: clamp(2rem, 5vw, 2.75rem);
} }
.shadok-reader {
position: absolute;
right: 2%;
top: 5%;
width: clamp(90px, 13vw, 180px);
opacity: 0.1;
pointer-events: none;
color: hsl(var(--color-primary));
animation: shadok-float-reader 9s ease-in-out infinite;
}
.shadok-stack {
position: absolute;
left: 2%;
bottom: 5%;
width: clamp(80px, 11vw, 150px);
opacity: 0.1;
pointer-events: none;
color: hsl(var(--color-accent));
animation: shadok-float-stack 11s ease-in-out infinite;
}
@keyframes shadok-float-reader {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
@keyframes shadok-float-stack {
0%, 100% { transform: translateY(0) rotate(0deg); }
50% { transform: translateY(-8px) rotate(-2deg); }
}
@media (max-width: 768px) {
.shadok-reader { display: none; }
.shadok-stack { display: none; }
}
</style> </style>

View File

@@ -42,8 +42,8 @@ function formatDate(iso: string) {
<style scoped> <style scoped>
.message-card { .message-card {
background: hsl(20 8% 6%); background: hsl(var(--color-surface));
border: 1px solid hsl(20 8% 14%); border: 1px solid hsl(var(--color-text) / 0.1);
border-radius: 0.75rem; border-radius: 0.75rem;
padding: 1.25rem 1.5rem; padding: 1.25rem 1.5rem;
} }

118
app/stores/palette.ts Normal file
View File

@@ -0,0 +1,118 @@
export type PaletteName = 'automne' | 'hiver' | 'printemps' | 'ete'
interface PaletteColors {
primary: string
accent: string
surface: string
bg: string
surfaceLight: string
text: string
textMuted: string
isLight: boolean
label: string
icon: string
}
const palettes: Record<PaletteName, PaletteColors> = {
// ══════ DARK THEMES ══════
// Automne : cuivre chaud, feuilles mortes, terre brûlée
automne: {
primary: '18 80% 45%', // cuivre profond
accent: '32 85% 50%', // ambre doré
surface: '16 12% 9%', // écorce sombre
bg: '16 12% 4%', // terre noire
surfaceLight: '16 10% 14%', // bois fumé
text: '0 0% 100%',
textMuted: '0 0% 60%',
isLight: false,
label: 'Automne',
icon: 'i-lucide-leaf',
},
// Hiver : bleu nuit, givre, argent lunaire
hiver: {
primary: '215 55% 52%', // bleu nuit étoilé
accent: '195 40% 65%', // givre argenté
surface: '220 15% 10%', // ciel de minuit
bg: '225 18% 5%', // nuit polaire
surfaceLight: '220 12% 15%', // brume nocturne
text: '0 0% 100%',
textMuted: '210 10% 60%',
isLight: false,
label: 'Hiver',
icon: 'i-lucide-snowflake',
},
// ══════ LIGHT THEMES ══════
// Printemps : vert tendre, rose cerisier, lumière fraîche
printemps: {
primary: '145 50% 38%', // vert bourgeon
accent: '340 65% 55%', // rose cerisier
surface: '120 12% 93%', // rosée du matin
bg: '100 15% 96%', // clarté verte
surfaceLight: '120 8% 87%', // feuille pâle
text: '150 15% 12%', // vert profond
textMuted: '140 8% 42%', // mousse
isLight: true,
label: 'Printemps',
icon: 'i-lucide-flower-2',
},
// Été : doré solaire, turquoise mer, lumineux chaleureux
ete: {
primary: '25 85% 52%', // soleil couchant
accent: '175 55% 42%', // turquoise marin
surface: '40 25% 92%', // sable clair
bg: '42 30% 96%', // lumière dorée
surfaceLight: '38 18% 86%', // dune
text: '30 20% 12%', // terre chaude
textMuted: '30 10% 40%', // ombre estivale
isLight: true,
label: 'Été',
icon: 'i-lucide-sun',
},
}
export const usePaletteStore = defineStore('palette', () => {
const currentPalette = ref<PaletteName>(
(import.meta.client && localStorage.getItem('palette') as PaletteName) || 'automne',
)
const colors = computed(() => palettes[currentPalette.value])
const isLight = computed(() => colors.value.isLight)
function applyToDOM() {
if (!import.meta.client) return
const c = colors.value
const root = document.documentElement
const s = root.style
s.setProperty('--color-primary', c.primary)
s.setProperty('--color-accent', c.accent)
s.setProperty('--color-surface', c.surface)
s.setProperty('--color-bg', c.bg)
s.setProperty('--color-surface-light', c.surfaceLight)
s.setProperty('--color-text', c.text)
s.setProperty('--color-text-muted', c.textMuted)
// Toggle light/dark class for CSS overrides
root.classList.toggle('palette-light', c.isLight)
root.classList.toggle('palette-dark', !c.isLight)
s.setProperty('color-scheme', c.isLight ? 'light' : 'dark')
}
function setPalette(name: PaletteName) {
currentPalette.value = name
if (import.meta.client) localStorage.setItem('palette', name)
applyToDOM()
}
return {
currentPalette,
colors,
palettes,
isLight,
setPalette,
applyToDOM,
}
})

View File

@@ -7,18 +7,16 @@ readingTime: "25 min"
Coder un rêve Coder un rêve
\[Verse\]
Dans l'ombre des géants big tek Dans l'ombre des géants big tek
Des lignes poussent discrètes Des lignes poussent discrètes
Tourné vers la grande ourse Tourné vers la grande ourse
Je coule open source Je coule open source
\[Prechorus\] Balance ton Json
Balance ton Jiz Onne
mon shell résonne mon shell résonne
Coup de dés Py - thon Runtime Upgrade, ninja blayde Coup de dés Python Runtime Upgrade, ninja blade
Ça bilt, Docker compose. Devant l'écran je pose. Ca biilt. Ça build, Docker compose. Devant l'écran je pose. Ça build.
shhhhhhh... it com paille ...llss, shhhhhhh... it compiles ...llss,
tapis dans une typo sans serif, je check les certif tapis dans une typo sans serif, je check les certif
merde ça lag, merde ça lag,
mate mes log, mate mes log,
@@ -26,14 +24,13 @@ j'ai la langue qui bog.
Mon café est tout froid Mon café est tout froid
Je ne perds pas la foi. Je ne perds pas la foi.
\[Chorus\] Codeurs de rêve
Codeurs de rêv
Rime pour les dèvs Rime pour les dèvs
dans les réseaux, où que j'aille dans les réseaux, où que j'aille
vous êtes mes sudo, vous êtes mes sudo,
mes samouraï mes samouraï
Hackers, Admin, Hackers, Admin,
Dèvop, développ Devop, develop
Vous décentralisez Vous décentralisez
Vous open sourcez Vous open sourcez
Un monde moins obscur Un monde moins obscur
@@ -41,7 +38,6 @@ Duniterre bien sûr.
Notre toil fiduciaire Notre toil fiduciaire
Nous pouvez être fiers Nous pouvez être fiers
\[Verse 2\]
Appel aux ressources Appel aux ressources
Donner vie au code source Donner vie au code source
Léger besoin de finance Léger besoin de finance
@@ -53,7 +49,6 @@ Du fuel pour les applis
tous vos dons ... les mettent à l'abri tous vos dons ... les mettent à l'abri
Merci Merci
\[Bridge\]
Crash à minuit Crash à minuit
Je reste éveillé Je reste éveillé
Le bug fatal Le bug fatal
@@ -63,14 +58,13 @@ Je vais le basher
Le café est-il prêt ? Le café est-il prêt ?
C'est bientôt aujourd'hui C'est bientôt aujourd'hui
\[Chorus\] Codeurs de rêve
Codeurs de rêv
Rime pour les dèvs Rime pour les dèvs
dans les réseaux, où que j'aille dans les réseaux, où que j'aille
vous êtes mes sudo, vous êtes mes sudo,
mes samouraï mes samouraï
Hackers, Admin, Hackers, Admin,
Dèvop, développ Devop, develop
Vous décentralisez Vous décentralisez
Vous open sourcez Vous open sourcez
Un monde moins obscur Un monde moins obscur

View File

@@ -7,12 +7,6 @@ readingTime: "15 min"
Inverser les flux Inverser les flux
\[Intro\]
\[Guitar vibraphone : Accords riches et harmoniques\]
\[Basse : Une ligne très ronde qui glisse\]
\[Cut Brass swell\]
\[Articulte, french\]
Tu choisis ... ta monnaie, Tu choisis ... ta monnaie,
son économie, son économie,
Tu passes de l'une à l'autre Tu passes de l'une à l'autre
@@ -23,22 +17,18 @@ Tu peux aussi ne plus l'appeler monnaie du tout.
Changer de lunettes, (mais pas les rose) Changer de lunettes, (mais pas les rose)
c'est un ruban-mètre, posé sur la planète. c'est un ruban-mètre, posé sur la planète.
\[Chorus\]
(Groove s'intensifie, la batterie claque)
Le D.U, c'est une mesure. Le D.U, c'est une mesure.
(Choir: La mesure...) (Choir: La mesure...)
Pour ne plus obliger. Pour ne plus devoir. Pour ne plus obliger. Pour ne plus devoir.
Je donne à mon économie, j'alimente le réservoir. Je donne à mon économie, j'alimente le réservoir.
\[break\]
Si je t'aime, j'y mets mon affect. Si je t'aime, j'y mets mon affect.
Si je ne t'aime pas, du moins je te respecte. Si je ne t'aime pas, du moins je te respecte.
Je compte sur les autres, sur mon économie, Je compte sur les autres, sur mon économie,
Pour y trouver ma pleine mesure. Pour y trouver ma pleine mesure.
La solidarité organique, pas de paniK La solidarité organique, pas de panique
Elle franchit les limites. Elle franchit les limites.
(Choir: Organique...) (Choir: Organique...)
\[Verse 1\]
"Je ne veux rien en retour", c'est ce que tu dis. "Je ne veux rien en retour", c'est ce que tu dis.
Mais tu m'obliges. Mais tu m'obliges.
Tu crées une dette non dite, Tu crées une dette non dite,
@@ -47,7 +37,6 @@ La solidarité mécanique est complexe
Elle a ses limites. Elle a ses limites.
(Whisper: C'est horrible pour les mites) (Whisper: C'est horrible pour les mites)
\[Le rythme devient plus sec, plus percussif. Flow rapide\]
Alors j'opère un retournement. Alors j'opère un retournement.
Je choisis mes mots, c'est un glissement. Je choisis mes mots, c'est un glissement.
(...) (...)
@@ -65,10 +54,9 @@ La "dépossession monétaire" n'a plus lieu d'être.
Ce n'est plus mortifère, ni délétère. Ce n'est plus mortifère, ni délétère.
(...) (...)
Je rentre du marché, nouveau vocabulaire : Je rentre du marché, nouveau vocabulaire :
\[Male\]- "Hey - j'ai reçu une semaine de cour-ss." (Male) - "Hey - j'ai reçu une semaine de cours."
\[Female\]- "Wow, t'as mis gratitude max à la source ?" (Female) - "Wow, t'as mis gratitude max à la source ?"
\[Bridge - Duet Call & Response\]
(Male) Je ne paye plus. (Male) Je ne paye plus.
(Female) Je mesure. J'estime... (Female) Je mesure. J'estime...
(Male) Je négocie détendu. (c'est un virage) (Male) Je négocie détendu. (c'est un virage)
@@ -77,16 +65,13 @@ Je rentre du marché, nouveau vocabulaire :
(Female) De la masse. (Female) De la masse.
(Both) Ou une température. (Both) Ou une température.
L'économie, c'est de l'énergie, de la chaleur c'est sûr. L'économie, c'est de l'énergie, de la chaleur c'est sûr.
\[break\] Je grave ma gratitude dans la chaîne. Je grave ma gratitude dans la chaîne.
C'est une trans-action. Au sens noble du terme. C'est une trans-action. Au sens noble du terme.
\[Chorus - Ensemble\]
(Groove s'intensifie, la batterie claque)
Le D.U, c'est une mesure. Le D.U, c'est une mesure.
(Choir: La mesure...) (Choir: La mesure...)
Pour ne plus obliger. Pour ne plus devoir. Pour ne plus obliger. Pour ne plus devoir.
Je donne à mon économie, j'alimente le réservoir. Je donne à mon économie, j'alimente le réservoir.
\[break\]
Si je t'aime, j'y mets mon affect. Si je t'aime, j'y mets mon affect.
Si je ne t'aime pas, du moins je te respecte. Si je ne t'aime pas, du moins je te respecte.
Je compte sur les autres, sur mon économie, Je compte sur les autres, sur mon économie,
@@ -95,8 +80,6 @@ La solidarité organique, pas de panique
Elle franchit les limites. Elle franchit les limites.
(Choir: Organique...) (Choir: Organique...)
\[Verse 3 - Male Lead\]
\[Musique s'épure, Basse et Claquements de doigts. Question tone\]
Dis,... et quand c'est la course ? Si c'est une buvette ? Dis,... et quand c'est la course ? Si c'est une buvette ?
Pas le temps des discours, philosopher sur la canette ? Pas le temps des discours, philosopher sur la canette ?
(Female : Faut qu'ça dépote !) (Female : Faut qu'ça dépote !)
@@ -107,21 +90,14 @@ Ils ont posé leurs références.
Tu prends ou pas, tu vois si c'est bon, Tu prends ou pas, tu vois si c'est bon,
tu entres dans la danse. tu entres dans la danse.
Tu peux gratifier plus, si le cœur t'en dit. Tu peux gratifier plus, si le cœur t'en dit.
Plaider un co-eff. relatif aussi... Plaider un coeff. relatif aussi...
Mais la mesure est là, autour d'un bel invariant, on sait où on va. Mais la mesure est là, autour d'un bel invariant, on sait où on va.
(...) (...)
Au delà d'un simple théorème Au delà d'un simple théorème
C'est le cadeau de la T.R.M. C'est le cadeau de la T.R.M.
\[Outro\]
(Female ad-libs: Équilibre... Invariant...)
\[Rhodes solo, jazzy and improvised\]
\[Male spoken sexy\]
On frotte nos échelles. On frotte nos échelles.
On construit avec les autres. On construit avec les autres.
(Fade out on the warm bass line)
\[Female sexy\]
Construction culturelle. Construction culturelle.
\[break smooth\]
J'évalue mon degré de gratitude J'évalue mon degré de gratitude
Pour que ça devienne une habitude. Pour que ça devienne une habitude.

View File

@@ -7,12 +7,6 @@ readingTime: "25 min"
Inverser les flux Inverser les flux
\[Intro\]
\[Guitar vibraphone : Accords riches et harmoniques\]
\[Basse : Une ligne très ronde qui glisse\]
\[Cut Brass swell\]
\[Articulte, french\]
Tu choisis ... ta monnaie, Tu choisis ... ta monnaie,
son économie, son économie,
Tu passes de l'une à l'autre Tu passes de l'une à l'autre
@@ -23,22 +17,18 @@ Tu peux aussi ne plus l'appeler monnaie du tout.
Changer de lunettes, (mais pas les rose) Changer de lunettes, (mais pas les rose)
c'est un ruban-mètre, posé sur la planète. c'est un ruban-mètre, posé sur la planète.
\[Chorus\]
(Groove s'intensifie, la batterie claque)
Le D.U, c'est une mesure. Le D.U, c'est une mesure.
(Choir: La mesure...) (Choir: La mesure...)
Pour ne plus obliger. Pour ne plus devoir. Pour ne plus obliger. Pour ne plus devoir.
Je donne à mon économie, j'alimente le réservoir. Je donne à mon économie, j'alimente le réservoir.
\[break\]
Si je t'aime, j'y mets mon affect. Si je t'aime, j'y mets mon affect.
Si je ne t'aime pas, du moins je te respecte. Si je ne t'aime pas, du moins je te respecte.
Je compte sur les autres, sur mon économie, Je compte sur les autres, sur mon économie,
Pour y trouver ma pleine mesure. Pour y trouver ma pleine mesure.
La solidarité organique, pas de paniK La solidarité organique, pas de panique
Elle franchit les limites. Elle franchit les limites.
(Choir: Organique...) (Choir: Organique...)
\[Verse 1\]
"Je ne veux rien en retour", c'est ce que tu dis. "Je ne veux rien en retour", c'est ce que tu dis.
Mais tu m'obliges. Mais tu m'obliges.
Tu crées une dette non dite, Tu crées une dette non dite,
@@ -47,7 +37,6 @@ La solidarité mécanique est complexe
Elle a ses limites. Elle a ses limites.
(Whisper: C'est horrible pour les mites) (Whisper: C'est horrible pour les mites)
\[Le rythme devient plus sec, plus percussif. Flow rapide\]
Alors j'opère un retournement. Alors j'opère un retournement.
Je choisis mes mots, c'est un glissement. Je choisis mes mots, c'est un glissement.
(...) (...)
@@ -65,10 +54,9 @@ La "dépossession monétaire" n'a plus lieu d'être.
Ce n'est plus mortifère, ni délétère. Ce n'est plus mortifère, ni délétère.
(...) (...)
Je rentre du marché, nouveau vocabulaire : Je rentre du marché, nouveau vocabulaire :
\[Male\]- "Hey - j'ai reçu une semaine de cour-ss." (Male) - "Hey - j'ai reçu une semaine de cours."
\[Female\]- "Wow, t'as mis gratitude max à la source ?" (Female) - "Wow, t'as mis gratitude max à la source ?"
\[Bridge - Duet Call & Response\]
(Male) Je ne paye plus. (Male) Je ne paye plus.
(Female) Je mesure. J'estime... (Female) Je mesure. J'estime...
(Male) Je négocie détendu. (c'est un virage) (Male) Je négocie détendu. (c'est un virage)
@@ -77,16 +65,13 @@ Je rentre du marché, nouveau vocabulaire :
(Female) De la masse. (Female) De la masse.
(Both) Ou une température. (Both) Ou une température.
L'économie, c'est de l'énergie, de la chaleur c'est sûr. L'économie, c'est de l'énergie, de la chaleur c'est sûr.
\[break\] Je grave ma gratitude dans la chaîne. Je grave ma gratitude dans la chaîne.
C'est une trans-action. Au sens noble du terme. C'est une trans-action. Au sens noble du terme.
\[Chorus - Ensemble\]
(Groove s'intensifie, la batterie claque)
Le D.U, c'est une mesure. Le D.U, c'est une mesure.
(Choir: La mesure...) (Choir: La mesure...)
Pour ne plus obliger. Pour ne plus devoir. Pour ne plus obliger. Pour ne plus devoir.
Je donne à mon économie, j'alimente le réservoir. Je donne à mon économie, j'alimente le réservoir.
\[break\]
Si je t'aime, j'y mets mon affect. Si je t'aime, j'y mets mon affect.
Si je ne t'aime pas, du moins je te respecte. Si je ne t'aime pas, du moins je te respecte.
Je compte sur les autres, sur mon économie, Je compte sur les autres, sur mon économie,
@@ -95,8 +80,6 @@ La solidarité organique, pas de panique
Elle franchit les limites. Elle franchit les limites.
(Choir: Organique...) (Choir: Organique...)
\[Verse 3 - Male Lead\]
\[Musique s'épure, Basse et Claquements de doigts. Question tone\]
Dis,... et quand c'est la course ? Si c'est une buvette ? Dis,... et quand c'est la course ? Si c'est une buvette ?
Pas le temps des discours, philosopher sur la canette ? Pas le temps des discours, philosopher sur la canette ?
(Female : Faut qu'ça dépote !) (Female : Faut qu'ça dépote !)
@@ -107,21 +90,14 @@ Ils ont posé leurs références.
Tu prends ou pas, tu vois si c'est bon, Tu prends ou pas, tu vois si c'est bon,
tu entres dans la danse. tu entres dans la danse.
Tu peux gratifier plus, si le cœur t'en dit. Tu peux gratifier plus, si le cœur t'en dit.
Plaider un co-eff. relatif aussi... Plaider un coeff. relatif aussi...
Mais la mesure est là, autour d'un bel invariant, on sait où on va. Mais la mesure est là, autour d'un bel invariant, on sait où on va.
(...) (...)
Au delà d'un simple théorème Au delà d'un simple théorème
C'est le cadeau de la T.R.M. C'est le cadeau de la T.R.M.
\[Outro\]
(Female ad-libs: Équilibre... Invariant...)
\[Rhodes solo, jazzy and improvised\]
\[Male spoken sexy\]
On frotte nos échelles. On frotte nos échelles.
On construit avec les autres. On construit avec les autres.
(Fade out on the warm bass line)
\[Female sexy\]
Construction culturelle. Construction culturelle.
\[break smooth\]
J'évalue mon degré de gratitude J'évalue mon degré de gratitude
Pour que ça devienne une habitude. Pour que ça devienne une habitude.

View File

@@ -7,9 +7,6 @@ readingTime: "20 min"
Les asymétries Les asymétries
\[Intro\]
(Cello and Bowed Bass: low, scraping texture)
(Piano: Single discordant note repeated)
(Male: Voix très posée, grave, proche) (Male: Voix très posée, grave, proche)
Entre soi... Connivence fait loi. Entre soi... Connivence fait loi.
On est entre nous On se rassure. On est entre nous On se rassure.
@@ -19,12 +16,10 @@ Nous sommes trop convaincus.
Cela nous endort. Ce que je crois peut-être à tord ... tue. Cela nous endort. Ce que je crois peut-être à tord ... tue.
(Females: Pseudo-isolés... Pseudo-isolés...) (Females: Pseudo-isolés... Pseudo-isolés...)
\[Verse 1\] J'ai vu des collectifs, plus ou moins disruptifs.
(Drums enter: intricate brushwork, soft but fast) Parfois prônant le don, par exemple Solaris.
J'ai vu des collectifs, plus ou moins dissruptifs.
Parfois prônant le don, par exemple Solariss.
J'y ai vu de l'usure, le sentiment d'abus. J'y ai vu de l'usure, le sentiment d'abus.
Des abandons moribons, ... Des abandons moribonds, ...
Ha bon ? Ha bon ?
Malgré quelques notifs, et les esprits attentifs Malgré quelques notifs, et les esprits attentifs
@@ -32,8 +27,6 @@ Parfois nous le savons, dans les violons on pisse
J'ai vu aussi bien sûr, quelques trous du cul J'ai vu aussi bien sûr, quelques trous du cul
Mais ! Est-ce là une bonne raison ? Mais ! Est-ce là une bonne raison ?
\[Bridge 1\]
(Rhythm becomes jagged, syncopated stops)
Rien n'est symétrique, ce n'est pas magique. Rien n'est symétrique, ce n'est pas magique.
(Females: ) Rien. (Females: ) Rien.
Une pomme aujourd'hui, n'est pas la même demain. Une pomme aujourd'hui, n'est pas la même demain.
@@ -42,10 +35,7 @@ Et s'il y a une cagette, ce n'est pas cinq palettes.
Six heures assis à parler bien à l'aise... Six heures assis à parler bien à l'aise...
Six heures à genoux sur un toit...ho ! balaise Six heures à genoux sur un toit...ho ! balaise
(Females:) Est-ce le même geste, ou une ascèse ? mmmhh. (Females:) Est-ce le même geste, ou une ascèse ? mmmhh.
(Silence - 1 second)
\[Chorus\]
(Music swells slightly, singing questions)
Alors que faire ? On désespère, on baisse les bras ? Alors que faire ? On désespère, on baisse les bras ?
on légifère ? On écrit des lois ? on légifère ? On écrit des lois ?
On réglemente, on décide de l'issue ? On réglemente, on décide de l'issue ?
@@ -54,8 +44,6 @@ Mieux vaut peut-être un protocole avec quelques bémol.
Une façon de traiter, d'éviter de juger, préfigurer. Une façon de traiter, d'éviter de juger, préfigurer.
(suspension) Ne pas tranchez les sorts, à leur insu. (suspension) Ne pas tranchez les sorts, à leur insu.
\[Verse 2\]
(Bass is now plucked, heavy groove)
Pour limiter le ressentiment, Pour limiter le ressentiment,
la lassitude, l'envenime-ment. la lassitude, l'envenime-ment.
Il suffit d'une mesure. Il suffit d'une mesure.
@@ -68,7 +56,6 @@ Qui célèbre le mérite ? - Dans l'ombre du monde ;
Pas celui des héritages ? - Ce s'rait possible ça ? Pas celui des héritages ? - Ce s'rait possible ça ?
Qui réellement, récompense et compense ? sans dépense ni dispense ? Qui réellement, récompense et compense ? sans dépense ni dispense ?
\[Bridge 2\]
Une communauté ne peut pas tout écrire. Une communauté ne peut pas tout écrire.
La loi, ne peut pas tout régler. La loi, ne peut pas tout régler.
même si c'est toi qui la fait même si c'est toi qui la fait
@@ -77,8 +64,6 @@ Une belle et grande morale ? - c'est bancal :
Il est utile de prévenir, se souvenir, ... Il est utile de prévenir, se souvenir, ...
C'est le pire. C'est le pire.
\[Chorus\]
(Music swells slightly)
Alors que faire ? On désespère, on baisse les bras ? Alors que faire ? On désespère, on baisse les bras ?
on légifère ? On écrit des lois ? on légifère ? On écrit des lois ?
On réglemente, on décide de l'issue ? On réglemente, on décide de l'issue ?
@@ -88,23 +73,18 @@ Une façon de traiter, d'éviter de juger, préfigurer.
(suspension) Ne pas tranchez les sorts, à leur insu. (suspension) Ne pas tranchez les sorts, à leur insu.
(suspension) Ne tranchez pas mon sort, à mon insu. (suspension) Ne tranchez pas mon sort, à mon insu.
\[Bridge 1\]
(Rhythm becomes jagged, syncopated stops)
Rien n'est symétrique. Ce n'est pas magique. Rien n'est symétrique. Ce n'est pas magique.
(Females:) Rien. (Females:) Rien.
Pour se rétablir, retomber sur nos pieds Pour se rétablir, retomber sur nos pieds
Il existe un outil qui s'appelle, ... "la monnaie". Il existe un outil qui s'appelle, ... "la monnaie".
\[Verse 3\]
C'est elle en permanence qui résout le "schmilblick". C'est elle en permanence qui résout le "schmilblick".
Même sans qu'on y pense, faut avouer, c'est pratique Même sans qu'on y pense, faut avouer, c'est pratique
C'est elle qui compense, récompense ou dispense C'est elle qui compense, récompense ou dispense
Mais attention, délit de pleine flagrance ! (drum, suspension) Mais attention, délit de pleine flagrance !
Chaque monnaie programme sa propre engence. Chaque monnaie programme sa propre engence.
Ne t'y méprends pas, son pouvoir est immense. Ne t'y méprends pas, son pouvoir est immense.
\[Outro\]
(Piano flowing arpeggios, fading)
Résoudre le problème des asymétries. Résoudre le problème des asymétries.
Réduire le besoin de légiférer. Réduire le besoin de légiférer.
Pour une simple coloc... ou pour un monde entier. Pour une simple coloc... ou pour un monde entier.

View File

@@ -7,7 +7,6 @@ readingTime: "18 min"
Désir des arts Désir des arts
\[Intro\]
La matière est peu docile La matière est peu docile
L'esprit est agile L'esprit est agile
Il désire l'art Il désire l'art
@@ -15,19 +14,16 @@ Les mains sont habiles
précieuses et volubiles précieuses et volubiles
Elles façonnent des amarres Elles façonnent des amarres
\[Pont\]
C'est reparti pour un tour C'est reparti pour un tour
Pour le désir des arts Pour le désir des arts
Nous voilà de retour Nous voilà de retour
Vous nous avez manqué Vous nous avez manqué
\[Refrain\]
Les mains habiles Les mains habiles
L'esprit agile L'esprit agile
La matière plus docile La matière plus docile
Sous le geste de l'émotion Sous le geste de l'émotion
\[Couplet 1\]
Les structures sont solides Les structures sont solides
Les intentions fluides Les intentions fluides
Notre équipe est unie Notre équipe est unie
@@ -35,13 +31,11 @@ Prête pour l'inédit
Nous serons à vos côtés Nous serons à vos côtés
En ce lieu de toute beauté En ce lieu de toute beauté
\[Refrain\]
Les mains habiles Les mains habiles
L'esprit agile L'esprit agile
La matière plus docile La matière plus docile
Sous le geste de l'émotion Sous le geste de l'émotion
\[Couplet 2\]
Qu'ils soient cent Qu'ils soient cent
Qu'ils soient mille Qu'ils soient mille
Curieux ou passionnés Curieux ou passionnés
@@ -51,13 +45,11 @@ Partout est l'attrait
Touchez sans les gants Touchez sans les gants
Ici le désir vrille Ici le désir vrille
\[Refrain\]
Les mains habiles Les mains habiles
L'esprit agile L'esprit agile
La matière plus docile La matière plus docile
Sous le geste de l'émotion Sous le geste de l'émotion
\[Outro\]
L'Art est de retour L'Art est de retour
Il célèbre l'amour Il célèbre l'amour
Demain un autre jour Demain un autre jour

View File

@@ -7,14 +7,9 @@ readingTime: "8 min"
Ainsi soit-il Ainsi soit-il
\[Intro\]
\[Vinyl Crackle Sound\]
\[Minimalist Piano Loop\]
Fiat... Fiat...
Fiat Lux… Fiat Euro. Fiat Lux… Fiat Euro.
\[Verse 1\]
\[Spoken Word, Calm and Clear\]
Fiat. Ce n'est pas une marque. Fiat. Ce n'est pas une marque.
C'est du latin. C'est du latin.
Ça veut dire : "Que cela soit". Ça veut dire : "Que cela soit".
@@ -25,15 +20,11 @@ Un monopole déclaré.
Une clé de voûte qui tient tout l'édifice. Une clé de voûte qui tient tout l'édifice.
Si la clé casse... tout s'écroule. Si la clé casse... tout s'écroule.
\[Chorus\]
\[Melodic Hook, Softly Sung\]
Mais la monnaie n'est pas la richesse. Mais la monnaie n'est pas la richesse.
C'est juste le mètre... pas le tissu. C'est juste le mètre... pas le tissu.
C'est le baromètre... pas le climat. C'est le baromètre... pas le climat.
Ne confondons pas la carte et le territoire. Ne confondons pas la carte et le territoire.
\[Verse 2\]
\[Rhythmic Spoken, Slightly Faster\]
Message aux pionniers : Message aux pionniers :
Faire tourner la monnaie en rond, ce n'est pas créer. Faire tourner la monnaie en rond, ce n'est pas créer.
Se faire des virements autour d'une table... Se faire des virements autour d'une table...
@@ -45,19 +36,12 @@ L'économie, c'est "passer la seconde".
C'est produire. Transformer. C'est produire. Transformer.
Le reste ? C'est de la comptabilité. Le reste ? C'est de la comptabilité.
\[Bridge\]
\[Bass Line Drops\]
\[Pause\]
Notre monnaie-dette a un code génétique. Notre monnaie-dette a un code génétique.
Elle programme le manque. Elle programme la course. Elle programme le manque. Elle programme la course.
Mais le DU... Mais le DU...
Le DU change le code source. Le DU change le code source.
\[Outro\]
\[Fading Music\]
\[Whispered\]
Même accès pour tous. Même accès pour tous.
Même pouvoir de création. Même pouvoir de création.
Ce n'est plus "Que la dette soit". Ce n'est plus "Que la dette soit".
C'est "Que l'équilibre soit". C'est "Que l'équilibre soit".
\[Silence\]

View File

@@ -5,7 +5,6 @@ order: 1
readingTime: "15 min" readingTime: "15 min"
--- ---
\[Intro\]
Ce livre est un essai. Ce livre est un essai.
Une proposition. Une proposition.
Une intention. Une intention.
@@ -13,16 +12,14 @@ Une invitation.
(...) (...)
Une façon. Une façon.
\[bridge\] Deux mille vingt-quatre j'écris tout l'été, ...
Deux mille vingt'-quatre j'écris tout l'été, ... Deux mille vingt-cinq je mûris toute l'année, ...
Deux mille vingt'-cinq je mûris toute l'année, ... Deux mille vingt-six la sortie... mmmmhh,
Deux mille vingt'-siss la sortie... mmmmhh,
Le temps pass Le temps pass
Est-ce une menace ? Est-ce une menace ?
\[Verse 1\]
Pour éviter tout quiproquo : Pour éviter tout quiproquo :
Pour mieux zaimer le propos Pour mieux aimer le propos
Ce n'est pas une théorie. Ce n'est pas une théorie.
Pas d'u-niversalité. Pas d'u-niversalité.
Loin s'en faut. Loin s'en faut.
@@ -34,20 +31,17 @@ Civile et artistique
Mais si ça reste à ton échelle... c'est symbolique. Mais si ça reste à ton échelle... c'est symbolique.
Viser mon bassin de vie ? ça se complique. Viser mon bassin de vie ? ça se complique.
\[Chorus\] Ce livre n'est pas un guide,
Ce livre n'est pas un guiDe,
davantage un guit'. davantage un guit'.
Ce n'est pas un Kit. Ce n'est pas un kit.
Ça ne dit pas quoi faire lundi. Ça ne dit pas quoi faire lundi.
\[Bridge\]
Tu veux une baguette magique ? Tu veux une baguette magique ?
Supprimer la pression, l'oppression ? - Ce s'rait pas con... Supprimer la pression, l'oppression ? - Ce s'rait pas con...
Si je ne résous pas mes problèmes d'aujourd'hui... à quoi bon ? Si je ne résous pas mes problèmes d'aujourd'hui... à quoi bon ?
Naviguer dans le ciel des idées... Naviguer dans le ciel des idées...
C'est fini ! C'est fini !
\[Verse 2\]
Créer une économie ? Créer une économie ?
On en a déjà une. Tu veux décrocher la lune ? On en a déjà une. Tu veux décrocher la lune ?
Elle couvre mes besoins vitaux. De facto. Elle couvre mes besoins vitaux. De facto.
@@ -62,33 +56,29 @@ restez là sans mot dire,
"restez des enfants !"... "restez des enfants !"...
mmh, suspect et sans avenir. mmh, suspect et sans avenir.
\[Bridge\]
Deux mille vingt-six. L'année des défis. Deux mille vingt-six. L'année des défis.
Ne plus subir les agendas. Créer les nôtr'. Ne plus subir les agendas. Créer les nôtr'.
On manque de repères ?... Entre autres, ... On manque de repères ?... Entre autres, ...
Faut les trouver,... Faut les trouver,...
En produisant, les inventer. En produisant, les inventer.
\[Chorus\] Ce livre n'est pas un guide,
Ce livre n'est pas un guiDe,
davantage un guit'. davantage un guit'.
Ce n'est pas un Kit. Ce n'est pas un kit.
Ça ne dit pas quoi faire lundi. Ça ne dit pas quoi faire lundi.
\[Bridge 2\]
C'est un os à ronger. C'est un os à ronger.
Une cartographie. Quelques boussoles. Une cartographie. Quelques boussoles.
Dans une jungle à défricher. Dans une jungle à défricher.
Ce n'est pas encore l'heure... Ce n'est pas encore l'heure...
Du prêtà-porter. Du prêt-à-porter.
A nous de tailler. A nous de tailler.
\[outro\]
On tourne une page pour voir ? On tourne une page pour voir ?
Décline le rôle de la bonne poire... Décline le rôle de la bonne poire...
Je n'ai pas que l'espoir Je n'ai pas que l'espoir
J'ai un pouvoir J'ai un pouvoir
Tu as bien mieux que l'espoir, Tu as bien mieux que l'espoir,
Nous - zavons un grand pouvoir. Nous avons un grand pouvoir.
(whisper :) hey, on tourne une page ? (whisper :) hey, on tourne une page ?

View File

@@ -5,17 +5,12 @@ order: 3
readingTime: "8 min" readingTime: "8 min"
--- ---
\[Intro\]
(Piano Rhodes : accords jazzy)
(Basse : Ligne ronde et enveloppante)
Une économie du don ? Une économie du don ?
Mais de quel don nous parlons ? Mais de quel don nous parlons ?
Ce mariage fait peur. Il claque. Ce mariage fait peur. Il claque.
Un oxymore, on l'apprend juste après l'bac. Un oxymore, on l'apprend juste après l'bac.
Une contradiction pour l'esprit. Une contradiction pour l'esprit.
\[Verse 1\]
On évacue tout de suite le spirituel. On évacue tout de suite le spirituel.
Désolé pour le karma, désolé pour le ciel. Désolé pour le karma, désolé pour le ciel.
Je ne parle pas du "centuple divin". Je ne parle pas du "centuple divin".
@@ -25,7 +20,6 @@ Ici, c'est un geste. Juste un geste.
Qui sert de base, qui sert de fondation, Qui sert de base, qui sert de fondation,
À une autre forme de construction. À une autre forme de construction.
\[Chorus\]
Ce n'est pas l'image d'Épinal, le don gratuit, le don idéal. Ce n'est pas l'image d'Épinal, le don gratuit, le don idéal.
Marcel Mauss nous l'a dit, dans son essai radical. Marcel Mauss nous l'a dit, dans son essai radical.
Ce n'est pas un cadeau, c'est un cycle vital. Ce n'est pas un cadeau, c'est un cycle vital.
@@ -35,7 +29,6 @@ C'est un pacte, une tension, parfois même un combat.
Si tu ne redonnes pas, si tu enfreins le protocole... Si tu ne redonnes pas, si tu enfreins le protocole...
Ça ne pardonne pas. Ça ne pardonne pas.
\[Verse 2\]
J'entends les rêves de brûler la monnaie. J'entends les rêves de brûler la monnaie.
"Le troc", "la gratuité", "Mocica", le grand projet. "Le troc", "la gratuité", "Mocica", le grand projet.
La monnaie serait le vice, la corruption mentale. La monnaie serait le vice, la corruption mentale.
@@ -46,8 +39,6 @@ La monnaie-dette, celle qui nous tient, celle qui nous guette.
Mais si la monnaie est libre ? Elle permet les équilibres. Mais si la monnaie est libre ? Elle permet les équilibres.
Si elle devient notre outil ? Elle change le récit. Si elle devient notre outil ? Elle change le récit.
\[Bridge\]
(Music strips down. Just Bass and Snare rimshots)
Le don qui se mesure, donne la mesure. Le don qui se mesure, donne la mesure.
C'est quand tu donnes ton temps, ton énergie, ta sueur, C'est quand tu donnes ton temps, ton énergie, ta sueur,
Que tu crées ton propre étalon de valeur. Que tu crées ton propre étalon de valeur.
@@ -56,7 +47,6 @@ Puis silence. Le geste pose un nouveau décor.
Le don — c'est pas une perte. Le don — c'est pas une perte.
Le don — c'est le début d'un accord. Le don — c'est le début d'un accord.
\[Chorus 2\]
Ce n'est pas l'image d'Épinal, le don gratuit, le don idéal. Ce n'est pas l'image d'Épinal, le don gratuit, le don idéal.
Marcel Mauss nous l'a dit, dans son essai radical. Marcel Mauss nous l'a dit, dans son essai radical.
Ce n'est pas un cadeau, c'est un cycle vital. Ce n'est pas un cadeau, c'est un cycle vital.
@@ -64,7 +54,6 @@ Ce n'est pas un cadeau, c'est un cycle vital.
Le don qui se mesure, donne la mesure. Le don qui se mesure, donne la mesure.
Le don qui se mesure, donne la mesure. Le don qui se mesure, donne la mesure.
\[Outro\]
De quel don nous parlons ? De quel don nous parlons ?
...Celui qui construit. ...Celui qui construit.
Celui qui nous relie. Celui qui nous relie.

View File

@@ -7,7 +7,6 @@ readingTime: "35 min"
Hymne à la monnaie libre Hymne à la monnaie libre
\[Verse\]
Des cercles qui se croisent Des cercles qui se croisent
Sans les regards qui toisent Sans les regards qui toisent
Des poings qui se détendent Des poings qui se détendent
@@ -18,7 +17,6 @@ Un futur qui s'écrit
Un souffle tout petit Un souffle tout petit
Pas de chaînes pour les pensées Pas de chaînes pour les pensées
\[Chorus\]
Construire des vies Construire des vies
Nos cœurs qui grossissent Nos cœurs qui grossissent
Couvrir nos besoins Couvrir nos besoins
@@ -34,7 +32,6 @@ Y consacrer nos vies
Si tu en as la fibre Si tu en as la fibre
C'est l'hymne à la monnaie libre C'est l'hymne à la monnaie libre
\[Verse 2\]
Les jours se lèvent sur des rêves partagés Les jours se lèvent sur des rêves partagés
Quelques champs pour les possibles Quelques champs pour les possibles
Des ponts à imaginer Des ponts à imaginer
@@ -43,7 +40,6 @@ Pas de trône
Juste l'humanité Juste l'humanité
Est-elle si pénible ? Est-elle si pénible ?
\[Bridge\]
Ni maîtres ni esclaves Ni maîtres ni esclaves
Juste un écho Juste un écho
Des esprits qui dansent Des esprits qui dansent
@@ -51,7 +47,6 @@ Dans des corps qui pensent
Un chant nouveau Un chant nouveau
Surmontent les entraves Surmontent les entraves
\[Chorus\]
Construire des vies Construire des vies
Nos cœurs qui grossissent Nos cœurs qui grossissent
Couvrir nos besoins Couvrir nos besoins

View File

@@ -7,7 +7,6 @@ readingTime: "30 min"
Hymne à la monnaie libre Hymne à la monnaie libre
\[Verse\]
Des cercles qui se croisent Des cercles qui se croisent
Sans les regards qui toisent Sans les regards qui toisent
Des poings qui se détendent Des poings qui se détendent
@@ -18,7 +17,6 @@ Un futur qui s'écrit
Un souffle tout petit Un souffle tout petit
Pas de chaînes pour les pensées Pas de chaînes pour les pensées
\[Chorus\]
Construire des vies Construire des vies
Nos cœurs qui grossissent Nos cœurs qui grossissent
Couvrir nos besoins Couvrir nos besoins
@@ -34,7 +32,6 @@ Y consacrer nos vies
Si tu en as la fibre Si tu en as la fibre
C'est l'hymne à la monnaie libre C'est l'hymne à la monnaie libre
\[Verse 2\]
Les jours se lèvent sur des rêves partagés Les jours se lèvent sur des rêves partagés
Quelques champs pour les possibles Quelques champs pour les possibles
Des ponts à imaginer Des ponts à imaginer
@@ -43,7 +40,6 @@ Pas de trône
Juste l'humanité Juste l'humanité
Est-elle si pénible ? Est-elle si pénible ?
\[Bridge\]
Ni maîtres ni esclaves Ni maîtres ni esclaves
Juste un écho Juste un écho
Des esprits qui dansent Des esprits qui dansent
@@ -51,7 +47,6 @@ Dans des corps qui pensent
Un chant nouveau Un chant nouveau
Surmontent les entraves Surmontent les entraves
\[Chorus\]
Construire des vies Construire des vies
Nos cœurs qui grossissent Nos cœurs qui grossissent
Couvrir nos besoins Couvrir nos besoins

View File

@@ -7,14 +7,9 @@ readingTime: "20 min"
Ainsi soit-il Ainsi soit-il
\[Intro\]
\[Vinyl Crackle Sound\]
\[Minimalist Piano Loop\]
Fiat... Fiat...
Fiat Lux… Fiat Euro. Fiat Lux… Fiat Euro.
\[Verse 1\]
\[Spoken Word, Calm and Clear\]
Fiat. Ce n'est pas une marque. Fiat. Ce n'est pas une marque.
C'est du latin. C'est du latin.
Ça veut dire : "Que cela soit". Ça veut dire : "Que cela soit".
@@ -25,15 +20,11 @@ Un monopole déclaré.
Une clé de voûte qui tient tout l'édifice. Une clé de voûte qui tient tout l'édifice.
Si la clé casse... tout s'écroule. Si la clé casse... tout s'écroule.
\[Chorus\]
\[Melodic Hook, Softly Sung\]
Mais la monnaie n'est pas la richesse. Mais la monnaie n'est pas la richesse.
C'est juste le mètre... pas le tissu. C'est juste le mètre... pas le tissu.
C'est le baromètre... pas le climat. C'est le baromètre... pas le climat.
Ne confondons pas la carte et le territoire. Ne confondons pas la carte et le territoire.
\[Verse 2\]
\[Rhythmic Spoken, Slightly Faster\]
Message aux pionniers : Message aux pionniers :
Faire tourner la monnaie en rond, ce n'est pas créer. Faire tourner la monnaie en rond, ce n'est pas créer.
Se faire des virements autour d'une table... Se faire des virements autour d'une table...
@@ -45,19 +36,12 @@ L'économie, c'est "passer la seconde".
C'est produire. Transformer. C'est produire. Transformer.
Le reste ? C'est de la comptabilité. Le reste ? C'est de la comptabilité.
\[Bridge\]
\[Bass Line Drops\]
\[Pause\]
Notre monnaie-dette a un code génétique. Notre monnaie-dette a un code génétique.
Elle programme le manque. Elle programme la course. Elle programme le manque. Elle programme la course.
Mais le DU... Mais le DU...
Le DU change le code source. Le DU change le code source.
\[Outro\]
\[Fading Music\]
\[Whispered\]
Même accès pour tous. Même accès pour tous.
Même pouvoir de création. Même pouvoir de création.
Ce n'est plus "Que la dette soit". Ce n'est plus "Que la dette soit".
C'est "Que l'équilibre soit". C'est "Que l'équilibre soit".
\[Silence\]

View File

@@ -0,0 +1,26 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 180 260" fill="none">
<!-- Body: round absurd bird -->
<ellipse cx="90" cy="100" rx="45" ry="40" fill="currentColor" opacity="0.85"/>
<!-- Head: tilted -->
<circle cx="130" cy="60" r="22" fill="currentColor" opacity="0.8"/>
<!-- Neck -->
<path d="M110 85 Q125 70 128 63" stroke="currentColor" stroke-width="8" stroke-linecap="round" opacity="0.7" fill="none"/>
<!-- Eye -->
<circle cx="136" cy="55" r="5" fill="currentColor" opacity="0.2"/>
<circle cx="137" cy="54" r="2" fill="currentColor" opacity="0.5"/>
<!-- Beak: long, absurd -->
<polygon points="150,58 175,50 152,65" fill="currentColor" opacity="0.6"/>
<!-- Long legs -->
<line x1="75" y1="138" x2="60" y2="230" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
<line x1="105" y1="138" x2="115" y2="230" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
<!-- Knees (knobby) -->
<circle cx="66" cy="190" r="4" fill="currentColor" opacity="0.4"/>
<circle cx="111" cy="190" r="4" fill="currentColor" opacity="0.4"/>
<!-- Feet -->
<path d="M60 230 L45 233 M60 230 L55 236 M60 230 L65 235" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.5"/>
<path d="M115 230 L100 233 M115 230 L110 236 M115 230 L120 235" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.5"/>
<!-- Tail feathers -->
<path d="M48 95 Q20 80 15 65" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.5" fill="none"/>
<path d="M48 100 Q22 92 10 85" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.4" fill="none"/>
<path d="M48 105 Q25 102 12 100" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.3" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,14 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 180" fill="none">
<!-- Amorphous blob shape -->
<path d="M60 90 Q30 50 70 30 Q110 10 140 40 Q180 60 170 100 Q165 140 130 155 Q90 170 55 145 Q25 125 60 90Z" fill="currentColor" opacity="0.12"/>
<path d="M60 90 Q30 50 70 30 Q110 10 140 40 Q180 60 170 100 Q165 140 130 155 Q90 170 55 145 Q25 125 60 90Z" stroke="currentColor" stroke-width="1.5" opacity="0.2"/>
<!-- Inner texture -->
<circle cx="100" cy="80" r="8" fill="currentColor" opacity="0.08"/>
<circle cx="120" cy="110" r="6" fill="currentColor" opacity="0.06"/>
<circle cx="80" cy="105" r="5" fill="currentColor" opacity="0.07"/>
<!-- Tiny eyes (personality) -->
<circle cx="95" cy="72" r="3" fill="currentColor" opacity="0.3"/>
<circle cx="108" cy="70" r="3" fill="currentColor" opacity="0.3"/>
<circle cx="96" cy="71" r="1.2" fill="currentColor" opacity="0.5"/>
<circle cx="109" cy="69" r="1.2" fill="currentColor" opacity="0.5"/>
</svg>

After

Width:  |  Height:  |  Size: 977 B

View File

@@ -0,0 +1,45 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 80" fill="none">
<!-- Repeating mini-shadok pattern -->
<!-- Shadok 1 -->
<g transform="translate(20,10)">
<ellipse cx="15" cy="25" rx="12" ry="14" fill="currentColor" opacity="0.08"/>
<circle cx="15" cy="10" r="7" fill="currentColor" opacity="0.06"/>
<line x1="10" y1="38" x2="8" y2="55" stroke="currentColor" stroke-width="1.5" opacity="0.06"/>
<line x1="20" y1="38" x2="22" y2="55" stroke="currentColor" stroke-width="1.5" opacity="0.06"/>
</g>
<!-- Shadok 2 -->
<g transform="translate(80,15)">
<ellipse cx="15" cy="22" rx="10" ry="12" fill="currentColor" opacity="0.06"/>
<circle cx="15" cy="8" r="6" fill="currentColor" opacity="0.05"/>
<line x1="10" y1="33" x2="8" y2="48" stroke="currentColor" stroke-width="1.5" opacity="0.05"/>
<line x1="20" y1="33" x2="22" y2="48" stroke="currentColor" stroke-width="1.5" opacity="0.05"/>
</g>
<!-- Shadok 3 -->
<g transform="translate(140,8)">
<ellipse cx="15" cy="25" rx="11" ry="13" fill="currentColor" opacity="0.07"/>
<circle cx="15" cy="10" r="6.5" fill="currentColor" opacity="0.06"/>
<line x1="10" y1="37" x2="7" y2="54" stroke="currentColor" stroke-width="1.5" opacity="0.06"/>
<line x1="20" y1="37" x2="23" y2="54" stroke="currentColor" stroke-width="1.5" opacity="0.06"/>
</g>
<!-- Shadok 4 -->
<g transform="translate(210,18)">
<ellipse cx="15" cy="20" rx="10" ry="11" fill="currentColor" opacity="0.05"/>
<circle cx="15" cy="7" r="5.5" fill="currentColor" opacity="0.04"/>
<line x1="10" y1="30" x2="9" y2="44" stroke="currentColor" stroke-width="1.5" opacity="0.04"/>
<line x1="20" y1="30" x2="21" y2="44" stroke="currentColor" stroke-width="1.5" opacity="0.04"/>
</g>
<!-- Shadok 5 -->
<g transform="translate(270,12)">
<ellipse cx="15" cy="24" rx="12" ry="14" fill="currentColor" opacity="0.07"/>
<circle cx="15" cy="9" r="7" fill="currentColor" opacity="0.06"/>
<line x1="10" y1="37" x2="7" y2="55" stroke="currentColor" stroke-width="1.5" opacity="0.06"/>
<line x1="20" y1="37" x2="23" y2="55" stroke="currentColor" stroke-width="1.5" opacity="0.06"/>
</g>
<!-- Shadok 6 -->
<g transform="translate(340,16)">
<ellipse cx="15" cy="22" rx="10" ry="12" fill="currentColor" opacity="0.06"/>
<circle cx="15" cy="8" r="6" fill="currentColor" opacity="0.05"/>
<line x1="10" y1="33" x2="8" y2="48" stroke="currentColor" stroke-width="1.5" opacity="0.05"/>
<line x1="20" y1="33" x2="22" y2="48" stroke="currentColor" stroke-width="1.5" opacity="0.05"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="none">
<!-- Planet body -->
<circle cx="100" cy="100" r="70" fill="currentColor" opacity="0.15"/>
<circle cx="100" cy="100" r="70" stroke="currentColor" stroke-width="2" opacity="0.3"/>
<!-- Craters -->
<circle cx="80" cy="75" r="15" fill="currentColor" opacity="0.08"/>
<circle cx="120" cy="110" r="20" fill="currentColor" opacity="0.06"/>
<circle cx="90" cy="130" r="10" fill="currentColor" opacity="0.1"/>
<!-- Ring / orbit -->
<ellipse cx="100" cy="100" rx="95" ry="25" stroke="currentColor" stroke-width="1.5" opacity="0.2" transform="rotate(-20 100 100)"/>
<!-- Small moons -->
<circle cx="30" cy="50" r="8" fill="currentColor" opacity="0.25"/>
<circle cx="175" cy="140" r="5" fill="currentColor" opacity="0.2"/>
<!-- Surface features -->
<path d="M65 90 Q75 85 85 90" stroke="currentColor" stroke-width="1.5" opacity="0.15" fill="none"/>
<path d="M105 120 Q115 115 125 118" stroke="currentColor" stroke-width="1.5" opacity="0.12" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1,24 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 240" fill="none">
<!-- Body: ovoid shape -->
<ellipse cx="100" cy="130" rx="55" ry="65" fill="currentColor" opacity="0.9"/>
<!-- Head: smaller oval on top -->
<ellipse cx="100" cy="60" rx="30" ry="28" fill="currentColor" opacity="0.85"/>
<!-- Eyes -->
<circle cx="88" cy="54" r="6" fill="currentColor" opacity="0.2"/>
<circle cx="112" cy="54" r="6" fill="currentColor" opacity="0.2"/>
<circle cx="90" cy="53" r="2.5" fill="currentColor" opacity="0.5"/>
<circle cx="114" cy="53" r="2.5" fill="currentColor" opacity="0.5"/>
<!-- Beak -->
<polygon points="100,68 115,78 85,78" fill="currentColor" opacity="0.6"/>
<!-- Legs -->
<line x1="80" y1="192" x2="70" y2="230" stroke="currentColor" stroke-width="4" stroke-linecap="round" opacity="0.7"/>
<line x1="120" y1="192" x2="130" y2="230" stroke="currentColor" stroke-width="4" stroke-linecap="round" opacity="0.7"/>
<!-- Feet -->
<line x1="70" y1="230" x2="55" y2="232" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.5"/>
<line x1="130" y1="230" x2="145" y2="232" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.5"/>
<!-- Pump handle -->
<line x1="155" y1="110" x2="190" y2="90" stroke="currentColor" stroke-width="4" stroke-linecap="round" opacity="0.6"/>
<line x1="190" y1="90" x2="190" y2="120" stroke="currentColor" stroke-width="4" stroke-linecap="round" opacity="0.6"/>
<!-- Pump body -->
<rect x="180" y="118" width="18" height="40" rx="3" fill="currentColor" opacity="0.4"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -20,9 +20,10 @@ export default defineConfig({
presetWebFonts({ presetWebFonts({
provider: 'bunny', provider: 'bunny',
fonts: { fonts: {
display: 'Syne:400,500,600,700,800', display: 'Outfit:400,500,600,700,800',
sans: 'Space Grotesk:300,400,500,600,700', sans: 'Inter:300,400,500,600,700',
mono: 'JetBrains Mono:400,500,700', mono: 'JetBrains Mono:400,500,700',
calligraphy: 'Playfair Display:400,700',
}, },
}), }),
], ],
@@ -33,47 +34,47 @@ export default defineConfig({
theme: { theme: {
colors: { colors: {
primary: { primary: {
DEFAULT: 'hsl(12, 76%, 48%)', DEFAULT: 'hsl(var(--color-primary))',
50: 'hsl(12, 76%, 95%)', 50: 'hsl(18, 80%, 95%)',
100: 'hsl(12, 76%, 88%)', 100: 'hsl(18, 80%, 88%)',
200: 'hsl(12, 76%, 76%)', 200: 'hsl(18, 80%, 76%)',
300: 'hsl(12, 76%, 64%)', 300: 'hsl(18, 80%, 64%)',
400: 'hsl(12, 76%, 56%)', 400: 'hsl(18, 80%, 56%)',
500: 'hsl(12, 76%, 48%)', 500: 'hsl(var(--color-primary))',
600: 'hsl(12, 76%, 40%)', 600: 'hsl(18, 80%, 38%)',
700: 'hsl(12, 76%, 33%)', 700: 'hsl(18, 80%, 30%)',
800: 'hsl(12, 76%, 26%)', 800: 'hsl(18, 80%, 24%)',
900: 'hsl(12, 76%, 18%)', 900: 'hsl(18, 80%, 16%)',
}, },
accent: { accent: {
DEFAULT: 'hsl(36, 80%, 52%)', DEFAULT: 'hsl(var(--color-accent))',
50: 'hsl(36, 80%, 95%)', 50: 'hsl(32, 85%, 95%)',
100: 'hsl(36, 80%, 88%)', 100: 'hsl(32, 85%, 88%)',
200: 'hsl(36, 80%, 76%)', 200: 'hsl(32, 85%, 76%)',
300: 'hsl(36, 80%, 66%)', 300: 'hsl(32, 85%, 66%)',
400: 'hsl(36, 80%, 58%)', 400: 'hsl(32, 85%, 58%)',
500: 'hsl(36, 80%, 52%)', 500: 'hsl(var(--color-accent))',
600: 'hsl(36, 80%, 44%)', 600: 'hsl(32, 85%, 42%)',
700: 'hsl(36, 80%, 36%)', 700: 'hsl(32, 85%, 34%)',
800: 'hsl(36, 80%, 28%)', 800: 'hsl(32, 85%, 26%)',
900: 'hsl(36, 80%, 20%)', 900: 'hsl(32, 85%, 18%)',
}, },
surface: { surface: {
DEFAULT: 'hsl(20, 8%, 8%)', DEFAULT: 'hsl(var(--color-surface))',
50: 'hsl(20, 8%, 22%)', 50: 'hsl(16, 12%, 22%)',
100: 'hsl(20, 8%, 18%)', 100: 'hsl(16, 12%, 18%)',
200: 'hsl(20, 8%, 13%)', 200: 'hsl(16, 12%, 13%)',
300: 'hsl(20, 8%, 10%)', 300: 'hsl(16, 12%, 10%)',
400: 'hsl(20, 8%, 8%)', 400: 'hsl(var(--color-surface))',
500: 'hsl(20, 8%, 6%)', 500: 'hsl(16, 12%, 6%)',
600: 'hsl(20, 8%, 4%)', 600: 'hsl(16, 12%, 4%)',
bg: 'hsl(20, 8%, 3.5%)', bg: 'hsl(var(--color-bg))',
}, },
}, },
}, },
shortcuts: { shortcuts: {
'btn-primary': 'inline-flex items-center justify-center px-6 py-3 rounded-lg bg-primary text-white font-display font-semibold tracking-wide transition-all duration-200 hover:bg-primary-600 hover:scale-105 active:scale-95', 'btn-primary': 'inline-flex items-center justify-center px-6 py-3 rounded-lg bg-primary text-white font-display font-semibold tracking-wide border-none transition-all duration-200 hover:bg-primary-600 hover:scale-105 active:scale-95',
'btn-accent': 'inline-flex items-center justify-center px-6 py-3 rounded-lg bg-accent text-surface-bg font-display font-semibold tracking-wide transition-all duration-200 hover:bg-accent-600 hover:scale-105 active:scale-95', 'btn-accent': 'inline-flex items-center justify-center px-6 py-3 rounded-lg bg-accent text-surface-bg font-display font-semibold tracking-wide border-none transition-all duration-200 hover:bg-accent-600 hover:scale-105 active:scale-95',
'btn-ghost': 'inline-flex items-center justify-center px-4 py-2 rounded-lg border-none text-white/70 font-sans transition-all duration-200 hover:bg-white/10 hover:text-white', 'btn-ghost': 'inline-flex items-center justify-center px-4 py-2 rounded-lg border-none text-white/70 font-sans transition-all duration-200 hover:bg-white/10 hover:text-white',
'card-surface': 'rounded-xl bg-surface border border-white/8 p-6 transition-all duration-300 hover:border-primary/30 hover:shadow-lg hover:shadow-primary/5', 'card-surface': 'rounded-xl bg-surface border border-white/8 p-6 transition-all duration-300 hover:border-primary/30 hover:shadow-lg hover:shadow-primary/5',
'text-gradient': 'bg-gradient-to-r from-primary-300 to-accent bg-clip-text text-transparent', 'text-gradient': 'bg-gradient-to-r from-primary-300 to-accent bg-clip-text text-transparent',