diff --git a/app/app.config.ts b/app/app.config.ts index 77b52a4..907808d 100644 --- a/app/app.config.ts +++ b/app/app.config.ts @@ -16,8 +16,8 @@ export default defineAppConfig({ gratewizard: { url: import.meta.dev ? 'http://localhost:3001' : 'https://gratewizard.axiom-team.fr', popup: { - width: 420, - height: 720, + width: 480, + height: 860, }, }, libredecision: { diff --git a/app/assets/css/fonts.css b/app/assets/css/fonts.css index b0bf479..6bad2cc 100644 --- a/app/assets/css/fonts.css +++ b/app/assets/css/fonts.css @@ -2,11 +2,11 @@ /* This file provides fallback and utility classes */ .font-display { - font-family: 'Outfit', system-ui, sans-serif; + font-family: 'Syne', system-ui, sans-serif; } .font-sans { - font-family: 'Inter', system-ui, sans-serif; + font-family: 'Space Grotesk', system-ui, sans-serif; } .font-mono { diff --git a/app/assets/css/main.css b/app/assets/css/main.css index a5e5974..40811ae 100644 --- a/app/assets/css/main.css +++ b/app/assets/css/main.css @@ -5,9 +5,9 @@ :root { --color-primary: 18 80% 45%; --color-accent: 32 85% 50%; - --color-bg: 220 12% 15%; - --color-surface: 220 10% 19%; - --color-surface-light: 220 8% 24%; + --color-bg: 215 8% 22%; + --color-surface: 213 7% 27%; + --color-surface-light: 210 6% 32%; --color-text: 0 0% 100%; --color-text-muted: 0 0% 65%; @@ -15,8 +15,8 @@ --player-height: 0rem; --sidebar-width: 280px; - --font-display: 'Outfit', sans-serif; - --font-sans: 'Inter', sans-serif; + --font-display: 'Syne', sans-serif; + --font-sans: 'Space Grotesk', sans-serif; --font-mono: 'JetBrains Mono', monospace; --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1); diff --git a/app/components/home/AxisBlock.vue b/app/components/home/AxisBlock.vue index c878792..caf2b91 100644 --- a/app/components/home/AxisBlock.vue +++ b/app/components/home/AxisBlock.vue @@ -24,7 +24,8 @@ >
-
+ Ğ1 +

@@ -40,15 +41,31 @@
- + +
+ +
+ +
+ +

@@ -60,6 +77,8 @@ interface AxisAction { id: string label: string icon: string + highlight?: boolean + secondary?: boolean } interface AxisItem { @@ -86,6 +105,14 @@ const emit = defineEmits<{ 'launch-gratewizard': [] }>() +function primaryActions(actions: AxisAction[]) { + return actions.filter(a => !a.secondary) +} + +function secondaryActions(actions: AxisAction[]) { + return actions.filter(a => a.secondary) +} + function handleAction(id: string) { if (id === 'open-player') emit('open-player') else if (id === 'open-pdf') emit('open-pdf') @@ -179,13 +206,22 @@ function itemAttrs(item: AxisItem) { } .axis-item-icon--primary { - background: hsl(var(--color-primary) / 0.1); + background: hsl(var(--color-primary) / 0.18); color: hsl(var(--color-primary)); + box-shadow: 0 0 14px hsl(var(--color-primary) / 0.15); } .axis-item-icon--accent { - background: hsl(var(--color-accent) / 0.1); + background: hsl(var(--color-accent) / 0.18); color: hsl(var(--color-accent)); + box-shadow: 0 0 14px hsl(var(--color-accent) / 0.15); +} + +.axis-item-icon-g1 { + font-family: var(--font-display); + font-weight: 700; + font-size: 1.1rem; + line-height: 1; } .gestation-badge { @@ -204,12 +240,26 @@ function itemAttrs(item: AxisItem) { } .axis-actions { + display: flex; + flex-direction: column; + gap: 0; + border-top: 1px solid hsl(var(--color-text) / 0.06); + background: hsl(var(--color-bg) / 0.4); +} + +.axis-actions-row { display: flex; flex-wrap: wrap; gap: 0.375rem; padding: 0.75rem 1.25rem; - border-top: 1px solid hsl(var(--color-text) / 0.06); - background: hsl(var(--color-bg) / 0.4); +} + +.axis-actions-secondary { + display: flex; + flex-wrap: wrap; + gap: 0.375rem; + padding: 0.5rem 1.25rem 0.75rem; + border-top: 1px solid hsl(var(--color-text) / 0.04); } .axis-action-btn { @@ -232,4 +282,28 @@ function itemAttrs(item: AxisItem) { background: hsl(var(--color-primary) / 0.12); border-color: hsl(var(--color-primary) / 0.3); } + +.axis-action-btn--highlight { + color: hsl(var(--color-primary)); + background: hsl(var(--color-primary) / 0.12); + border-color: hsl(var(--color-primary) / 0.25); +} + +.axis-action-btn--highlight:hover { + background: hsl(var(--color-primary) / 0.2); + border-color: hsl(var(--color-primary) / 0.4); +} + +.axis-action-btn--secondary { + color: hsl(var(--color-text) / 0.45); + background: transparent; + border-color: hsl(var(--color-text) / 0.06); + font-size: 0.75rem; +} + +.axis-action-btn--secondary:hover { + color: hsl(var(--color-accent)); + background: hsl(var(--color-accent) / 0.08); + border-color: hsl(var(--color-accent) / 0.2); +} diff --git a/app/components/home/BookSection.vue b/app/components/home/BookSection.vue new file mode 100644 index 0000000..5249837 --- /dev/null +++ b/app/components/home/BookSection.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/app/components/home/HeroSection.vue b/app/components/home/HeroSection.vue index 3961021..33d38b7 100644 --- a/app/components/home/HeroSection.vue +++ b/app/components/home/HeroSection.vue @@ -2,7 +2,7 @@
-
+
@@ -55,19 +55,17 @@ diff --git a/app/components/home/TypewriterText.vue b/app/components/home/TypewriterText.vue index 1be2c19..191b9a7 100644 --- a/app/components/home/TypewriterText.vue +++ b/app/components/home/TypewriterText.vue @@ -1,171 +1,260 @@ diff --git a/app/components/layout/TheHeader.vue b/app/components/layout/TheHeader.vue index 311374b..77585e2 100644 --- a/app/components/layout/TheHeader.vue +++ b/app/components/layout/TheHeader.vue @@ -16,8 +16,10 @@ @@ -39,13 +54,17 @@
- + diff --git a/app/composables/useTypewriter.ts b/app/composables/useTypewriter.ts deleted file mode 100644 index 8f8b06e..0000000 --- a/app/composables/useTypewriter.ts +++ /dev/null @@ -1,109 +0,0 @@ -export interface TypewriterSentence { - text: string - style?: 'title' | 'citation' | 'text' - stays?: boolean - separator?: boolean -} - -interface SequenceOptions { - fadeMs?: number - holdMs?: number - gapMs?: number -} - -export function useTypewriter(sentences: TypewriterSentence[], options: SequenceOptions = {}) { - const { - fadeMs = 1000, - holdMs = 2800, - gapMs = 300, - } = options - - const currentText = ref('') - const currentStyle = ref('title') - const isVisible = ref(false) - const lockedSentences = ref([]) - const isComplete = ref(false) - - let currentIdx = -1 - let timer: ReturnType | null = null - - function clearTimer() { - if (timer) { - clearTimeout(timer) - timer = null - } - } - - function next() { - currentIdx++ - if (currentIdx >= sentences.length) { - currentText.value = '' - isComplete.value = true - return - } - - const sentence = sentences[currentIdx] - - if (sentence.separator) { - lockedSentences.value = [...lockedSentences.value, { text: '', separator: true }] - } - - // Set text while invisible - currentText.value = sentence.text - currentStyle.value = sentence.style || 'title' - - // Fade in on next frame - requestAnimationFrame(() => { - isVisible.value = true - }) - - // After fade-in + hold → fade out - timer = setTimeout(() => { - isVisible.value = false - - // After fade-out completes → lock if stays, then next - timer = setTimeout(() => { - if (sentence.stays) { - lockedSentences.value = [...lockedSentences.value, { ...sentence }] - } - timer = setTimeout(next, gapMs) - }, fadeMs) - }, fadeMs + holdMs) - } - - function start() { - next() - } - - function skipToEnd() { - clearTimer() - isVisible.value = false - currentText.value = '' - - const locked: TypewriterSentence[] = [] - for (const sentence of sentences) { - if (sentence.separator) { - locked.push({ text: '', separator: true }) - } - if (sentence.stays) { - locked.push({ ...sentence }) - } - } - - lockedSentences.value = locked - currentIdx = sentences.length - 1 - isComplete.value = true - } - - onUnmounted(clearTimer) - - return { - currentText: readonly(currentText), - currentStyle: readonly(currentStyle), - isVisible, - lockedSentences: readonly(lockedSentences), - isComplete: readonly(isComplete), - start, - skipToEnd, - } -} diff --git a/app/layouts/default.vue b/app/layouts/default.vue index f3e7b5b..4bdb27f 100644 --- a/app/layouts/default.vue +++ b/app/layouts/default.vue @@ -1,8 +1,27 @@ @@ -135,6 +145,9 @@ useHead({ const { data: chapters } = await useAsyncData('book-toc', () => queryCollection('book').order('order', 'ASC').all(), ) + +const showBookPlayer = ref(false) +const showPdfReader = ref(false)