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: {
url: 'https://gratewizard.ml',
url: import.meta.dev ? 'http://localhost:3009' : 'https://gratewizard.ml',
popup: {
width: 420,
height: 720,

View File

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

View File

@@ -60,10 +60,10 @@
@keyframes glow-pulse {
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% {
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 */
.font-display {
font-family: 'Syne', system-ui, sans-serif;
font-family: 'Outfit', system-ui, sans-serif;
}
.font-sans {
font-family: 'Space Grotesk', system-ui, sans-serif;
font-family: 'Inter', system-ui, sans-serif;
}
.font-mono {

View File

@@ -3,20 +3,20 @@
@import './typography.css';
:root {
--color-primary: 12 76% 48%;
--color-accent: 36 80% 52%;
--color-bg: 20 8% 3.5%;
--color-surface: 20 8% 8%;
--color-surface-light: 20 8% 13%;
--color-primary: 18 80% 45%;
--color-accent: 32 85% 50%;
--color-bg: 16 12% 4%;
--color-surface: 16 12% 9%;
--color-surface-light: 16 10% 14%;
--color-text: 0 0% 100%;
--color-text-muted: 0 0% 100% / 0.6;
--color-text-muted: 0 0% 60%;
--header-height: 4rem;
--player-height: 0rem;
--sidebar-width: 280px;
--font-display: 'Syne', sans-serif;
--font-sans: 'Space Grotesk', sans-serif;
--font-display: 'Outfit', sans-serif;
--font-sans: 'Inter', sans-serif;
--font-mono: 'JetBrains Mono', monospace;
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
@@ -43,9 +43,22 @@ body {
min-height: 100dvh;
}
button {
border: none;
background: none;
cursor: pointer;
font: inherit;
color: inherit;
}
a {
text-decoration: none;
color: inherit;
}
::selection {
background-color: hsl(var(--color-primary) / 0.3);
color: white;
color: hsl(var(--color-text));
}
:focus-visible {
@@ -72,6 +85,78 @@ body {
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-enter-active,
.page-leave-active {

View File

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

View File

@@ -50,7 +50,7 @@
<template v-if="isScrollMode">{{ scrollPercent }}%</template>
<template v-else>{{ currentPage + 1 }}<span class="op-40">/</span>{{ totalPages }}</template>
</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" />
</button>
</div>
@@ -633,6 +633,14 @@ onUnmounted(() => {
color: white;
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 {
flex: 1;
font-family: var(--font-display, 'Syne', sans-serif);
@@ -744,7 +752,7 @@ onUnmounted(() => {
position: relative;
flex: 1;
width: 100%;
overflow: hidden;
overflow: hidden auto;
border-radius: 0.75rem;
background: hsl(20 8% 5% / 0.4);
backdrop-filter: blur(16px);
@@ -796,12 +804,16 @@ onUnmounted(() => {
.reader-columns :deep(h3) {
break-after: avoid;
}
.reader-columns :deep(p),
.reader-columns :deep(blockquote),
.reader-columns :deep(ul),
.reader-columns :deep(ol) {
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 */
.reader-shadow {

View File

@@ -57,6 +57,6 @@ function playSong(song: Song) {
.chapter-title {
font-size: clamp(2rem, 5vw, 2.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>

View File

@@ -1,5 +1,34 @@
<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="grid items-center gap-12 md:grid-cols-2">
<!-- Book cover -->
@@ -63,10 +92,10 @@ const { data: content } = await usePageContent('home')
aspect-ratio: 3 / 4;
border-radius: 0.75rem;
overflow: hidden;
border: 1px solid hsl(20 8% 18%);
border: 1px solid hsl(var(--color-text) / 0.1);
box-shadow:
0 12px 40px hsl(0 0% 0% / 0.5),
0 0 0 1px hsl(20 8% 15%);
0 12px 40px hsl(var(--color-text) / 0.15),
0 0 0 1px hsl(var(--color-text) / 0.08);
transition: transform 0.5s cubic-bezier(0.645, 0.045, 0.355, 1),
box-shadow 0.5s ease;
max-width: 360px;
@@ -75,8 +104,8 @@ const { data: content } = await usePageContent('home')
.book-cover-3d:hover {
transform: rotateY(-8deg) rotateX(3deg) scale(1.02);
box-shadow:
12px 16px 48px hsl(0 0% 0% / 0.6),
0 0 0 1px hsl(12 76% 48% / 0.2);
12px 16px 48px hsl(var(--color-text) / 0.2),
0 0 0 1px hsl(var(--color-primary) / 0.2);
}
.book-cover-img {
@@ -89,4 +118,24 @@ const { data: content } = await usePageContent('home')
.heading-section {
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>

View File

@@ -4,7 +4,24 @@
<div class="grid items-center gap-12 md:grid-cols-2">
<!-- Book cover -->
<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">
<img
:src="content?.book.coverImage"
@@ -68,10 +85,10 @@ const { data: content } = await usePageContent('home')
aspect-ratio: 3 / 4;
border-radius: 0.75rem;
overflow: hidden;
border: 1px solid hsl(20 8% 18%);
border: 1px solid hsl(var(--color-text) / 0.1);
box-shadow:
0 12px 40px hsl(0 0% 0% / 0.5),
0 0 0 1px hsl(20 8% 15%);
0 12px 40px hsl(var(--color-text) / 0.15),
0 0 0 1px hsl(var(--color-text) / 0.08);
transition: transform 0.5s cubic-bezier(0.645, 0.045, 0.355, 1),
box-shadow 0.5s ease;
max-width: 360px;
@@ -80,8 +97,8 @@ const { data: content } = await usePageContent('home')
.book-cover-3d:hover {
transform: rotateY(-8deg) rotateX(3deg) scale(1.02);
box-shadow:
12px 16px 48px hsl(0 0% 0% / 0.6),
0 0 0 1px hsl(12 76% 48% / 0.2);
12px 16px 48px hsl(var(--color-text) / 0.2),
0 0 0 1px hsl(var(--color-primary) / 0.2);
}
.book-cover-img {
@@ -94,4 +111,25 @@ const { data: content } = await usePageContent('home')
.heading-section {
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>

View File

@@ -1,5 +1,37 @@
<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="mx-auto max-w-3xl text-center">
<UiScrollReveal>
@@ -41,4 +73,24 @@ const { data: content } = await usePageContent('home')
.heading-section {
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>

View File

@@ -2,8 +2,20 @@
<section class="section-padding">
<div class="container-content">
<UiScrollReveal>
<div class="gw-card">
<div class="flex flex-col items-center text-center gap-4 md:flex-row md:text-left md:gap-8">
<div class="gw-card relative overflow-hidden">
<!-- 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 -->
<div class="gw-icon-wrapper">
<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);
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>

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-[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 -->
<div class="container-content relative z-10 px-4">
<div class="mx-auto max-w-3xl text-center">
@@ -54,4 +73,25 @@ const { data: content } = await usePageContent('home')
.hero-title {
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>

View File

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

View File

@@ -1,5 +1,37 @@
<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">
<UiScrollReveal>
<div class="text-center mb-12">
@@ -48,4 +80,24 @@ const featuredSongs = computed(() => bookData.getSongs().slice(0, 6))
.heading-section {
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>

View File

@@ -1,6 +1,45 @@
<template>
<footer class="border-t border-white/8 bg-surface-600">
<div class="container-content px-4 py-8">
<footer class="footer-wrap border-t border-white/8 bg-surface-600">
<!-- 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">
<!-- Credits -->
<p class="text-sm text-white/40">
@@ -26,3 +65,21 @@
<script setup lang="ts">
const { data: site } = await useSiteContent()
</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">
<div class="container-content flex h-[var(--header-height)] items-center justify-between px-4">
<!-- 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" />
<span class="text-gradient">{{ site?.identity.name }}</span>
<span class="font-calligraphy font-bold text-gradient text-xl italic">{{ site?.identity.name }}</span>
</NuxtLink>
<!-- Desktop navigation -->
@@ -18,6 +18,7 @@
>
{{ item.label }}
</NuxtLink>
<UiPaletteSelector />
</nav>
<!-- Mobile menu button -->

View File

@@ -99,13 +99,18 @@
<div :class="store.isPlaying ? 'i-lucide-pause' : 'i-lucide-play'" class="h-4 w-4" />
</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 -->
<button
class="pill-expand"
:aria-label="isExpanded ? 'Réduire' : 'Développer'"
@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>
</div>
</div>
@@ -116,7 +121,7 @@
import { onClickOutside } from '@vueuse/core'
const store = usePlayerStore()
const { setVolume, togglePlayPause } = useAudioPlayer()
const { setVolume, togglePlayPause, playNext } = useAudioPlayer()
useMediaSession()
@@ -235,22 +240,40 @@ onClickOutside(widgetRef, () => {
.pill-play:hover { transform: scale(1.08); }
.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 */
.pill-expand {
display: flex;
align-items: center;
justify-content: center;
width: 1.25rem;
height: 1.25rem;
width: 1.75rem;
height: 1.75rem;
border-radius: 50%;
background: transparent;
background: hsl(0 0% 100% / 0.08);
border: none;
color: hsl(0 0% 100% / 0.3);
color: hsl(0 0% 100% / 0.5);
cursor: pointer;
transition: color 0.2s;
transition: all 0.2s;
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

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);
height: calc(100dvh - var(--header-height));
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;
}

View File

@@ -1,5 +1,34 @@
<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">
<ContentRenderer v-if="page" :value="page" class="prose" />
</div>
@@ -19,3 +48,25 @@ const { data: page } = await useAsyncData('about', () =>
queryCollection('pages').path('/pages/about').first(),
)
</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>
<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">
<header class="mb-12 text-center">
<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 {
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>

View File

@@ -1,6 +1,48 @@
<template>
<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">
<!-- Back link -->
<UiScrollReveal>
@@ -77,14 +119,14 @@ const { url, launch } = useGrateWizard()
.gw-feature-card {
padding: 1.5rem;
border-radius: 0.75rem;
border: 1px solid hsl(20 8% 18%);
background: hsl(20 8% 8% / 0.5);
border: 1px solid hsl(var(--color-text) / 0.1);
background: hsl(var(--color-surface) / 0.5);
transition: border-color 0.3s ease, background 0.3s ease;
}
.gw-feature-card:hover {
border-color: hsl(40 80% 50% / 0.25);
background: hsl(20 8% 10% / 0.5);
background: hsl(var(--color-surface-light) / 0.5);
}
code {
@@ -94,4 +136,24 @@ code {
border-radius: 0.25em;
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>

View File

@@ -1,5 +1,54 @@
<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">
<header class="mb-12 text-center">
<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 {
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>

View File

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

View File

@@ -7,12 +7,6 @@ readingTime: "15 min"
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,
son économie,
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)
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.
(Choir: La mesure...)
Pour ne plus obliger. Pour ne plus devoir.
Je donne à mon économie, j'alimente le réservoir.
\[break\]
Si je t'aime, j'y mets mon affect.
Si je ne t'aime pas, du moins je te respecte.
Je compte sur les autres, sur mon économie,
Pour y trouver ma pleine mesure.
La solidarité organique, pas de paniK
La solidarité organique, pas de panique
Elle franchit les limites.
(Choir: Organique...)
\[Verse 1\]
"Je ne veux rien en retour", c'est ce que tu dis.
Mais tu m'obliges.
Tu crées une dette non dite,
@@ -47,7 +37,6 @@ La solidarité mécanique est complexe
Elle a ses limites.
(Whisper: C'est horrible pour les mites)
\[Le rythme devient plus sec, plus percussif. Flow rapide\]
Alors j'opère un retournement.
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.
(...)
Je rentre du marché, nouveau vocabulaire :
\[Male\]- "Hey - j'ai reçu une semaine de cour-ss."
\[Female\]- "Wow, t'as mis gratitude max à la source ?"
(Male) - "Hey - j'ai reçu une semaine de cours."
(Female) - "Wow, t'as mis gratitude max à la source ?"
\[Bridge - Duet Call & Response\]
(Male) Je ne paye plus.
(Female) Je mesure. J'estime...
(Male) Je négocie détendu. (c'est un virage)
@@ -77,16 +65,13 @@ Je rentre du marché, nouveau vocabulaire :
(Female) De la masse.
(Both) Ou une température.
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.
\[Chorus - Ensemble\]
(Groove s'intensifie, la batterie claque)
Le D.U, c'est une mesure.
(Choir: La mesure...)
Pour ne plus obliger. Pour ne plus devoir.
Je donne à mon économie, j'alimente le réservoir.
\[break\]
Si je t'aime, j'y mets mon affect.
Si je ne t'aime pas, du moins je te respecte.
Je compte sur les autres, sur mon économie,
@@ -95,8 +80,6 @@ La solidarité organique, pas de panique
Elle franchit les limites.
(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 ?
Pas le temps des discours, philosopher sur la canette ?
(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 entres dans la danse.
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.
(...)
Au delà d'un simple théorème
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 construit avec les autres.
(Fade out on the warm bass line)
\[Female sexy\]
Construction culturelle.
\[break smooth\]
J'évalue mon degré de gratitude
Pour que ça devienne une habitude.

View File

@@ -7,12 +7,6 @@ readingTime: "25 min"
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,
son économie,
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)
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.
(Choir: La mesure...)
Pour ne plus obliger. Pour ne plus devoir.
Je donne à mon économie, j'alimente le réservoir.
\[break\]
Si je t'aime, j'y mets mon affect.
Si je ne t'aime pas, du moins je te respecte.
Je compte sur les autres, sur mon économie,
Pour y trouver ma pleine mesure.
La solidarité organique, pas de paniK
La solidarité organique, pas de panique
Elle franchit les limites.
(Choir: Organique...)
\[Verse 1\]
"Je ne veux rien en retour", c'est ce que tu dis.
Mais tu m'obliges.
Tu crées une dette non dite,
@@ -47,7 +37,6 @@ La solidarité mécanique est complexe
Elle a ses limites.
(Whisper: C'est horrible pour les mites)
\[Le rythme devient plus sec, plus percussif. Flow rapide\]
Alors j'opère un retournement.
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.
(...)
Je rentre du marché, nouveau vocabulaire :
\[Male\]- "Hey - j'ai reçu une semaine de cour-ss."
\[Female\]- "Wow, t'as mis gratitude max à la source ?"
(Male) - "Hey - j'ai reçu une semaine de cours."
(Female) - "Wow, t'as mis gratitude max à la source ?"
\[Bridge - Duet Call & Response\]
(Male) Je ne paye plus.
(Female) Je mesure. J'estime...
(Male) Je négocie détendu. (c'est un virage)
@@ -77,16 +65,13 @@ Je rentre du marché, nouveau vocabulaire :
(Female) De la masse.
(Both) Ou une température.
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.
\[Chorus - Ensemble\]
(Groove s'intensifie, la batterie claque)
Le D.U, c'est une mesure.
(Choir: La mesure...)
Pour ne plus obliger. Pour ne plus devoir.
Je donne à mon économie, j'alimente le réservoir.
\[break\]
Si je t'aime, j'y mets mon affect.
Si je ne t'aime pas, du moins je te respecte.
Je compte sur les autres, sur mon économie,
@@ -95,8 +80,6 @@ La solidarité organique, pas de panique
Elle franchit les limites.
(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 ?
Pas le temps des discours, philosopher sur la canette ?
(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 entres dans la danse.
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.
(...)
Au delà d'un simple théorème
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 construit avec les autres.
(Fade out on the warm bass line)
\[Female sexy\]
Construction culturelle.
\[break smooth\]
J'évalue mon degré de gratitude
Pour que ça devienne une habitude.

View File

@@ -7,9 +7,6 @@ readingTime: "20 min"
Les asymétries
\[Intro\]
(Cello and Bowed Bass: low, scraping texture)
(Piano: Single discordant note repeated)
(Male: Voix très posée, grave, proche)
Entre soi... Connivence fait loi.
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.
(Females: Pseudo-isolés... Pseudo-isolés...)
\[Verse 1\]
(Drums enter: intricate brushwork, soft but fast)
J'ai vu des collectifs, plus ou moins dissruptifs.
Parfois prônant le don, par exemple Solariss.
J'ai vu des collectifs, plus ou moins disruptifs.
Parfois prônant le don, par exemple Solaris.
J'y ai vu de l'usure, le sentiment d'abus.
Des abandons moribons, ...
Des abandons moribonds, ...
Ha bon ?
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
Mais ! Est-ce là une bonne raison ?
\[Bridge 1\]
(Rhythm becomes jagged, syncopated stops)
Rien n'est symétrique, ce n'est pas magique.
(Females: ) Rien.
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 à genoux sur un toit...ho ! balaise
(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 ?
on légifère ? On écrit des lois ?
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.
(suspension) Ne pas tranchez les sorts, à leur insu.
\[Verse 2\]
(Bass is now plucked, heavy groove)
Pour limiter le ressentiment,
la lassitude, l'envenime-ment.
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 ?
Qui réellement, récompense et compense ? sans dépense ni dispense ?
\[Bridge 2\]
Une communauté ne peut pas tout écrire.
La loi, ne peut pas tout régler.
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, ...
C'est le pire.
\[Chorus\]
(Music swells slightly)
Alors que faire ? On désespère, on baisse les bras ?
on légifère ? On écrit des lois ?
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 tranchez pas mon sort, à mon insu.
\[Bridge 1\]
(Rhythm becomes jagged, syncopated stops)
Rien n'est symétrique. Ce n'est pas magique.
(Females:) Rien.
Pour se rétablir, retomber sur nos pieds
Il existe un outil qui s'appelle, ... "la monnaie".
\[Verse 3\]
C'est elle en permanence qui résout le "schmilblick".
Même sans qu'on y pense, faut avouer, c'est pratique
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.
Ne t'y méprends pas, son pouvoir est immense.
\[Outro\]
(Piano flowing arpeggios, fading)
Résoudre le problème des asymétries.
Réduire le besoin de légiférer.
Pour une simple coloc... ou pour un monde entier.

View File

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

View File

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

View File

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

View File

@@ -5,17 +5,12 @@ order: 3
readingTime: "8 min"
---
\[Intro\]
(Piano Rhodes : accords jazzy)
(Basse : Ligne ronde et enveloppante)
Une économie du don ?
Mais de quel don nous parlons ?
Ce mariage fait peur. Il claque.
Un oxymore, on l'apprend juste après l'bac.
Une contradiction pour l'esprit.
\[Verse 1\]
On évacue tout de suite le spirituel.
Désolé pour le karma, désolé pour le ciel.
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,
À une autre forme de construction.
\[Chorus\]
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.
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...
Ça ne pardonne pas.
\[Verse 2\]
J'entends les rêves de brûler la monnaie.
"Le troc", "la gratuité", "Mocica", le grand projet.
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.
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.
C'est quand tu donnes ton temps, ton énergie, ta sueur,
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 le début d'un accord.
\[Chorus 2\]
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.
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.
\[Outro\]
De quel don nous parlons ?
...Celui qui construit.
Celui qui nous relie.

View File

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

View File

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

View File

@@ -7,14 +7,9 @@ readingTime: "20 min"
Ainsi soit-il
\[Intro\]
\[Vinyl Crackle Sound\]
\[Minimalist Piano Loop\]
Fiat...
Fiat Lux… Fiat Euro.
\[Verse 1\]
\[Spoken Word, Calm and Clear\]
Fiat. Ce n'est pas une marque.
C'est du latin.
Ça veut dire : "Que cela soit".
@@ -25,15 +20,11 @@ Un monopole déclaré.
Une clé de voûte qui tient tout l'édifice.
Si la clé casse... tout s'écroule.
\[Chorus\]
\[Melodic Hook, Softly Sung\]
Mais la monnaie n'est pas la richesse.
C'est juste le mètre... pas le tissu.
C'est le baromètre... pas le climat.
Ne confondons pas la carte et le territoire.
\[Verse 2\]
\[Rhythmic Spoken, Slightly Faster\]
Message aux pionniers :
Faire tourner la monnaie en rond, ce n'est pas créer.
Se faire des virements autour d'une table...
@@ -45,19 +36,12 @@ L'économie, c'est "passer la seconde".
C'est produire. Transformer.
Le reste ? C'est de la comptabilité.
\[Bridge\]
\[Bass Line Drops\]
\[Pause\]
Notre monnaie-dette a un code génétique.
Elle programme le manque. Elle programme la course.
Mais le DU...
Le DU change le code source.
\[Outro\]
\[Fading Music\]
\[Whispered\]
Même accès pour tous.
Même pouvoir de création.
Ce n'est plus "Que la dette 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({
provider: 'bunny',
fonts: {
display: 'Syne:400,500,600,700,800',
sans: 'Space Grotesk:300,400,500,600,700',
display: 'Outfit:400,500,600,700,800',
sans: 'Inter:300,400,500,600,700',
mono: 'JetBrains Mono:400,500,700',
calligraphy: 'Playfair Display:400,700',
},
}),
],
@@ -33,47 +34,47 @@ export default defineConfig({
theme: {
colors: {
primary: {
DEFAULT: 'hsl(12, 76%, 48%)',
50: 'hsl(12, 76%, 95%)',
100: 'hsl(12, 76%, 88%)',
200: 'hsl(12, 76%, 76%)',
300: 'hsl(12, 76%, 64%)',
400: 'hsl(12, 76%, 56%)',
500: 'hsl(12, 76%, 48%)',
600: 'hsl(12, 76%, 40%)',
700: 'hsl(12, 76%, 33%)',
800: 'hsl(12, 76%, 26%)',
900: 'hsl(12, 76%, 18%)',
DEFAULT: 'hsl(var(--color-primary))',
50: 'hsl(18, 80%, 95%)',
100: 'hsl(18, 80%, 88%)',
200: 'hsl(18, 80%, 76%)',
300: 'hsl(18, 80%, 64%)',
400: 'hsl(18, 80%, 56%)',
500: 'hsl(var(--color-primary))',
600: 'hsl(18, 80%, 38%)',
700: 'hsl(18, 80%, 30%)',
800: 'hsl(18, 80%, 24%)',
900: 'hsl(18, 80%, 16%)',
},
accent: {
DEFAULT: 'hsl(36, 80%, 52%)',
50: 'hsl(36, 80%, 95%)',
100: 'hsl(36, 80%, 88%)',
200: 'hsl(36, 80%, 76%)',
300: 'hsl(36, 80%, 66%)',
400: 'hsl(36, 80%, 58%)',
500: 'hsl(36, 80%, 52%)',
600: 'hsl(36, 80%, 44%)',
700: 'hsl(36, 80%, 36%)',
800: 'hsl(36, 80%, 28%)',
900: 'hsl(36, 80%, 20%)',
DEFAULT: 'hsl(var(--color-accent))',
50: 'hsl(32, 85%, 95%)',
100: 'hsl(32, 85%, 88%)',
200: 'hsl(32, 85%, 76%)',
300: 'hsl(32, 85%, 66%)',
400: 'hsl(32, 85%, 58%)',
500: 'hsl(var(--color-accent))',
600: 'hsl(32, 85%, 42%)',
700: 'hsl(32, 85%, 34%)',
800: 'hsl(32, 85%, 26%)',
900: 'hsl(32, 85%, 18%)',
},
surface: {
DEFAULT: 'hsl(20, 8%, 8%)',
50: 'hsl(20, 8%, 22%)',
100: 'hsl(20, 8%, 18%)',
200: 'hsl(20, 8%, 13%)',
300: 'hsl(20, 8%, 10%)',
400: 'hsl(20, 8%, 8%)',
500: 'hsl(20, 8%, 6%)',
600: 'hsl(20, 8%, 4%)',
bg: 'hsl(20, 8%, 3.5%)',
DEFAULT: 'hsl(var(--color-surface))',
50: 'hsl(16, 12%, 22%)',
100: 'hsl(16, 12%, 18%)',
200: 'hsl(16, 12%, 13%)',
300: 'hsl(16, 12%, 10%)',
400: 'hsl(var(--color-surface))',
500: 'hsl(16, 12%, 6%)',
600: 'hsl(16, 12%, 4%)',
bg: 'hsl(var(--color-bg))',
},
},
},
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-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-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 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',
'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',