diff --git a/tests/helpers/data-validation.mjs b/tests/helpers/data-validation.mjs index e3e49d5..3ef7ba1 100644 --- a/tests/helpers/data-validation.mjs +++ b/tests/helpers/data-validation.mjs @@ -99,12 +99,18 @@ export function validateSiteContent(site) { "about.eyebrow", "about.title", "about.description", + "about.sectionsEyebrow", + "about.sectionsTitle", "about.toolsTitle", + "about.toolsIntro", "about.toolsLibraryLink", "about.limitsTitle", "about.contribute.eyebrow", "about.contribute.title", + "about.contribute.image", + "about.contribute.imageAlt", "about.contribute.description", + "about.contribute.exampleIntro", "about.contribute.example", "about.contribute.promptTitle", "about.contribute.promptIntro", @@ -521,17 +527,62 @@ export function validateSiteContent(site) { assert.ok(Array.isArray(aboutSections), "about.sections must be an array"); assert.ok(aboutSections.length >= 2, "about.sections must contain at least 2 items"); aboutSections.forEach((section) => { + assertNonEmptyString({ section }, "section.icon"); + assertNonEmptyString({ section }, "section.image"); assertNonEmptyString({ section }, "section.title"); assertNonEmptyString({ section }, "section.text"); + if (section.afterText) { + assertNonEmptyString({ section }, "section.afterText"); + } + if (section.warning) { + assertNonEmptyString({ section }, "section.warning"); + } + if (section.bullets) { + assert.ok(Array.isArray(section.bullets), "section.bullets must be an array"); + assert.ok(section.bullets.length >= 1, "section.bullets must not be empty"); + if (section.bulletsColumns) { + assert.ok([1, 2].includes(section.bulletsColumns), "section.bulletsColumns must be 1 or 2"); + } + section.bullets.forEach((bullet, bulletIndex) => { + assert.equal(typeof bullet, "string", `section.bullets[${bulletIndex}] must be a string`); + assert.ok(bullet.trim(), `section.bullets[${bulletIndex}] must not be empty`); + }); + } }); - const aboutTools = valueAt(site, "about.tools"); - assert.ok(Array.isArray(aboutTools), "about.tools must be an array"); - assert.ok(aboutTools.length >= 1, "about.tools must contain at least 1 item"); - aboutTools.forEach((tool) => { - assertNonEmptyString({ tool }, "tool.icon"); - assertNonEmptyString({ tool }, "tool.name"); - assertNonEmptyString({ tool }, "tool.description"); + const aboutToolGroups = valueAt(site, "about.toolGroups"); + assert.ok(Array.isArray(aboutToolGroups), "about.toolGroups must be an array"); + assert.ok(aboutToolGroups.length >= 3, "about.toolGroups must contain at least 3 items"); + aboutToolGroups.forEach((group, index) => { + assertNonEmptyString({ group }, "group.icon"); + assertNonEmptyString({ group }, "group.title"); + assertNonEmptyString({ group }, "group.text"); + assert.ok(Array.isArray(group.examples), `about.toolGroups[${index}].examples must be an array`); + assert.ok(group.examples.length >= 1, `about.toolGroups[${index}].examples must not be empty`); + group.examples.forEach((example, exampleIndex) => { + assert.equal(typeof example, "string", `about.toolGroups[${index}].examples[${exampleIndex}] must be a string`); + assert.ok(example.trim(), `about.toolGroups[${index}].examples[${exampleIndex}] must not be empty`); + }); + }); + + const contributeBlocks = valueAt(site, "about.contribute.blocks"); + assert.ok(Array.isArray(contributeBlocks), "about.contribute.blocks must be an array"); + assert.equal(contributeBlocks.length, 3, "about.contribute.blocks must contain 3 items"); + contributeBlocks.forEach((block, index) => { + assertNonEmptyString({ block }, "block.icon"); + assertNonEmptyString({ block }, "block.title"); + assertNonEmptyString({ block }, "block.text"); + if (block.note) { + assertNonEmptyString({ block }, "block.note"); + } + if (block.bullets) { + assert.ok(Array.isArray(block.bullets), `about.contribute.blocks[${index}].bullets must be an array`); + assert.ok(block.bullets.length >= 1, `about.contribute.blocks[${index}].bullets must not be empty`); + block.bullets.forEach((bullet, bulletIndex) => { + assert.equal(typeof bullet, "string", `about.contribute.blocks[${index}].bullets[${bulletIndex}] must be a string`); + assert.ok(bullet.trim(), `about.contribute.blocks[${index}].bullets[${bulletIndex}] must not be empty`); + }); + } }); const onboardingCards = valueAt(site, "home.onboarding.cards"); diff --git a/tests/static-app.test.mjs b/tests/static-app.test.mjs index 6755cbe..9796ff5 100644 --- a/tests/static-app.test.mjs +++ b/tests/static-app.test.mjs @@ -72,7 +72,8 @@ test("vite entrypoint and app shell are wired", async () => { assert.match(aboutPage, /export function AboutPage/); assert.match(aboutPage, /HomeOnboardingSection/); assert.match(aboutPage, /siteContent\.home\.onboarding/); - assert.match(aboutPage, /module-icon-\$\{tool\.icon \|\| "notepad"\}/); + assert.match(aboutPage, /module-icon-\$\{group\.icon \|\| "notepad"\}/); + assert.match(aboutPage, /about-tool-examples/); assert.match(libraryPage, /export function LibraryPage/); assert.match(libraryPage, /LIBRARY_TOOLBOX_ID/); assert.match(libraryPage, /TaskPlannerModule/); @@ -133,9 +134,15 @@ test("legacy icons and primary images are available", async () => { await readFile("website/public/static/icons/rubber.svg", "utf8"); await readFile("website/public/static/icons/trashcan.svg", "utf8"); await readFile("website/public/static/icons/zoom.svg", "utf8"); + await readFile("website/public/static/icons/warning.svg", "utf8"); await readFile("website/public/favicon.ico"); await readFile("website/public/static/img/dragon-home.png"); await readFile("website/public/static/img/dragon-sidebar.png"); + await readFile("website/public/static/img/dragon-dual-monitor.png"); + await readFile("website/public/static/img/dragon-session-tools.png"); + await readFile("website/public/static/img/dragon-game-guides.png"); + await readFile("website/public/static/img/dragon-local-data.png"); + await readFile("website/public/static/img/dragon-contribution-data.png"); await readFile("website/public/static/img/dragon-gameguide.png"); await readFile("website/public/static/img/dragon-toolbox.png"); await readFile("website/public/static/img/onboarding/toolbox-overview.png"); diff --git a/website/public/data/site.json b/website/public/data/site.json index 4fa93c4..36e2e4a 100644 --- a/website/public/data/site.json +++ b/website/public/data/site.json @@ -1299,98 +1299,100 @@ "about": { "eyebrow": "À propos", "title": "C'est quoi Sokko G ?", - "description": "Un espace second écran pour garder vos outils, repères et infos de jeu sous la main pendant une session.", + "description": "Sokko G est un espace local pour organiser vos notes, outils et repères de jeu. Il est pensé pour rester ouvert à côté de votre partie, sans compte et sans synchronisation obligatoire.", + "sectionsEyebrow": "Concept", + "sectionsTitle": "Faciliter vos sessions gaming", "sections": [ { - "title": "Le but du site", - "text": "Sokko G sert à regrouper les outils utiles pendant une session : notes, suivis, images, liens et repères de jeu. L’objectif est de garder les informations importantes accessibles rapidement sur un second écran." - }, - { - "title": "Fonctionnement", - "text": "Chaque toolbox est composée d’outils que vous ajoutez selon vos besoins. Elle peut rester indépendante ou être associée à une page jeu pour passer rapidement du guide à vos notes personnelles." - }, - { - "title": "Stockage local", - "text": "Aucun compte, aucune synchronisation cloud et aucune donnée utilisateur envoyée au serveur. Les toolboxes sont enregistrées localement dans le navigateur que vous utilisez." - }, - { - "title": "IndexedDB simplement", - "text": "IndexedDB est un espace de stockage intégré au navigateur. Il permet de conserver des données locales plus riches, comme des images ou des annotations. Ces données restent liées au navigateur et au profil utilisé : un nettoyage, un changement d’appareil ou une réinitialisation peut les supprimer." - } - ], - "toolsTitle": "Outils disponibles", - "toolsLibraryLink": "Voir la librairie d'outils", - "contribute": { - "eyebrow": "Contribuer", - "title": "Proposer une liste de jeu", - "description": "Les listes servent à transformer des données de jeu en checklists prêtes à l’emploi : équipements à fabriquer, créatures à capturer, monstres à battre, objets à suivre, etc. Une bonne liste est structurée par catégories, avec des noms clairs, une courte description utile et une quantité uniquement quand elle a du sens.\n\nExemple :", - "example": "{\n \"titre\": \"Équipements\",\n \"armures\": [\n {\n \"nom\": \"Set d'armure sombre\",\n \"description\": \"Obtenir en quête\",\n \"quantite\": 1\n }\n ]\n}", - "promptTitle": "Prompt utile", - "promptIntro": "Ajoutez cette précision à votre demande pour obtenir un JSON directement exploitable :", - "promptText": "Génère uniquement un JSON valide.\nLe JSON doit contenir une propriété racine \"titre\", puis une ou plusieurs catégories.\nChaque catégorie doit être un tableau d’objets.\nChaque objet doit contenir la propriété \"nom\".\nLes propriétés \"description\" et \"quantite\" sont facultatives.\nRegroupe les éléments de manière logique.\nNe renvoie aucun texte, aucune explication et aucun Markdown." - }, - "tools": [ - { - "icon": "notepad", - "name": "Bloc notes", - "description": "Rédigez des notes riches avec titres, listes, mise en forme et repères rapides pour garder vos infos de session au même endroit." - }, - { - "icon": "checklist", - "name": "Checklist", - "description": "Suivez des objectifs en catégories ou hors catégorie, avec cases, quantités et descriptions utiles pour crafts, ressources ou préparatifs." + "icon": "controller", + "image": "/static/img/dragon-session-tools.png", + "title": "Un support pour jouer plus efficacement", + "text": "Sokko G garde vos informations utiles à portée de main :", + "bullets": ["Objectifs", "Notes", "Images", "Timers", "Liens", "Calculs", "Suivis"], + "afterText": "L’idée est simple : limiter les allers-retours et retrouver rapidement ce dont vous avez besoin." }, { "icon": "picture", - "name": "Images", - "description": "Regroupez des captures, cartes ou références visuelles ajoutées par fichier ou collage, consultables sans quitter la toolbox." + "image": "/static/img/dragon-dual-monitor.png", + "title": "Pensé pour le second écran", + "text": "De plus en plus de joueurs utilisent deux écrans.\nSokko G profite de cet espace pour garder l’essentiel visible pendant que le jeu reste au premier plan : un guide, une checklist, une carte annotée ou un timer.\nVous consultez vos repères d’un coup d’œil, sans interrompre la partie." }, { - "icon": "map", - "name": "Annotation d'images", - "description": "Ajoutez marqueurs et dessins sur une image unique pour préparer une carte, un plan de route ou une référence importante." + "icon": "toolbox", + "image": "/static/img/dragon-game-guides.png", + "title": "Guides de jeux et toolboxes", + "text": "Le site propose des pages de jeux qui servent de guides ou de références.\nVos toolboxes restent personnelles, mais elles peuvent être associées à un jeu.\nCela permet de garder les informations générales d’un côté, et vos notes, outils ou suivis de session juste à côté." }, { - "icon": "link", - "name": "Liens", - "description": "Gardez vos raccourcis de session vers guides, builds, cartes interactives ou ressources externes, avec titres lisibles." + "icon": "export", + "image": "/static/img/dragon-local-data.png", + "title": "Stockage local : avantages et limites", + "text": "Vos toolboxes restent dans le navigateur utilisé :", + "bullets": ["Pas de compte", "Pas de configuration serveur", "Pas de synchronisation imposée"], + "bulletsColumns": 1, + "warning": "En échange, vous risquez de perdre vos données si vous videz le navigateur, changez de profil, changez de machine ou réinstallez le système. Pensez à exporter une sauvegarde avant ces actions." + } + ], + "toolsTitle": "Outils disponibles", + "toolsIntro": "Les outils sont là pour gérer différents types d’informations. Vous choisissez seulement les groupes utiles à votre session, puis la librairie détaille chaque outil disponible.", + "toolsLibraryLink": "Voir la librairie d'outils", + "toolGroups": [ + { + "icon": "notepad", + "title": "Notes et suivi", + "text": "Écrire des rappels, suivre des objectifs, organiser une routine ou préparer une liste de choses à faire.", + "examples": ["Notes", "Checklists", "Tâches"] }, { - "icon": "abacus", - "name": "Compteurs", - "description": "Créez des compteurs ajustables pour suivre essais, ressources, scores, victoires ou stocks sans passer par un tableau." - }, - { - "icon": "controller", - "name": "Combos", - "description": "Documentez des séquences de touches par catégorie, avec supports manette ou clavier/souris et étapes successives lisibles." - }, - { - "icon": "calculator", - "name": "Calculateur", - "description": "Calculez des valeurs, enregistrez des résultats et construisez une arborescence pour comparer coûts, crafts ou conversions." + "icon": "picture", + "title": "Médias et repères visuels", + "text": "Conserver des captures, cartes, images de référence ou annotations visibles pendant la session.", + "examples": ["Images", "Annotations", "Cartes"] }, { "icon": "table", - "name": "Tableau", - "description": "Structurez des données en grille avec formules, lignes/colonnes ajustables et copie TSV pour comparer besoins ou valeurs." + "title": "Données et calculs", + "text": "Comparer des valeurs, compter des ressources, préparer des tableaux ou calculer des besoins de craft.", + "examples": ["Compteurs", "Tableaux", "Calculs"] }, { "icon": "clock", - "name": "Timer", - "description": "Préparez chronomètres, étapes, comptes à rebours, alertes et intervalles récurrents pour suivre les temps importants." - }, - { - "icon": "tasklist", - "name": "Planificateur de tâches", - "description": "Organisez des tâches ponctuelles, quotidiennes ou hebdomadaires avec catégories, sous-tâches et dépendances parent/enfant." - }, - { - "icon": "chest-armor", - "name": "Planificateur d'équipements", - "description": "Planifiez un build par types d'équipement, avec pièces actives, statistiques, matériaux de craft, bonus et sertissages." + "title": "Temps et actions", + "text": "Gérer des timers, routines, rotations, commandes ou séquences à reproduire sans perdre le fil.", + "examples": ["Timers", "Combos", "Routines"] } ], + "contribute": { + "eyebrow": "Contribuer", + "title": "Proposer une liste de données", + "image": "/static/img/dragon-contribution-data.png", + "imageAlt": "Mascotte dragon préparant une liste de données structurée", + "description": "Les contributions attendues sont des listes de données, pas des guides complets. Une liste bien préparée permet de mettre à disposition une checklist complète, prête à être utilisée dans une toolbox.", + "blocks": [ + { + "icon": "checklist", + "title": "Pourquoi contribuer ?", + "text": "Une bonne liste fait gagner du temps aux joueurs : ils peuvent importer une checklist déjà structurée au lieu de tout recopier à la main." + }, + { + "icon": "tasklist", + "title": "Quels éléments proposer ?", + "text": "Tout ce qui se suit facilement en checklist : armures, matériaux, monstres, objets à récupérer, routes de farm ou objectifs de jeu.", + "bullets": ["Catégories", "Quantités à compléter", "Noms clairs"], + "note": "La propriété description peut servir de renseignement supplémentaire sur une page de liste, mais elle n’est pas affichée dans la checklist importée." + }, + { + "icon": "export", + "title": "Comment contribuer ?", + "text": "Vous pouvez préparer la liste vous-même ou demander à une IA de générer le fichier. Le format final est un JSON : c’est ce qui permet à Sokko G de créer la checklist automatiquement." + } + ], + "exampleIntro": "Exemple de liste exploitable :", + "example": "{\n \"titre\": \"Équipements\",\n \"armures\": [\n {\n \"nom\": \"Set d'armure sombre\",\n \"description\": \"Obtenir en quête\",\n \"quantite\": 1\n }\n ]\n}", + "promptTitle": "Se faire aider par une IA", + "promptIntro": "Si vous utilisez une IA pour préparer la liste, vous pouvez lui donner cette consigne afin d’obtenir un fichier JSON directement exploitable :", + "promptText": "Génère uniquement un JSON valide.\nLe JSON doit contenir une propriété racine \"titre\", puis une ou plusieurs catégories.\nChaque catégorie doit être un tableau d’objets.\nChaque objet doit contenir la propriété \"nom\".\nLes propriétés \"description\" et \"quantite\" sont facultatives.\nRegroupe les éléments de manière logique.\nNe renvoie aucun texte, aucune explication et aucun Markdown." + }, "limitsTitle": "À retenir", "limits": [ { diff --git a/website/public/static/icons/warning.svg b/website/public/static/icons/warning.svg new file mode 100644 index 0000000..5a7b412 --- /dev/null +++ b/website/public/static/icons/warning.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/website/public/static/img/dragon-contribution-data.png b/website/public/static/img/dragon-contribution-data.png new file mode 100644 index 0000000..3ed5131 Binary files /dev/null and b/website/public/static/img/dragon-contribution-data.png differ diff --git a/website/public/static/img/dragon-dual-monitor.png b/website/public/static/img/dragon-dual-monitor.png new file mode 100644 index 0000000..c169f3b Binary files /dev/null and b/website/public/static/img/dragon-dual-monitor.png differ diff --git a/website/public/static/img/dragon-game-guides.png b/website/public/static/img/dragon-game-guides.png new file mode 100644 index 0000000..eaa8a63 Binary files /dev/null and b/website/public/static/img/dragon-game-guides.png differ diff --git a/website/public/static/img/dragon-gameguide.png b/website/public/static/img/dragon-gameguide.png index edc1e9c..8d5c387 100644 Binary files a/website/public/static/img/dragon-gameguide.png and b/website/public/static/img/dragon-gameguide.png differ diff --git a/website/public/static/img/dragon-local-data.png b/website/public/static/img/dragon-local-data.png new file mode 100644 index 0000000..b3be47e Binary files /dev/null and b/website/public/static/img/dragon-local-data.png differ diff --git a/website/public/static/img/dragon-session-tools.png b/website/public/static/img/dragon-session-tools.png new file mode 100644 index 0000000..15b2013 Binary files /dev/null and b/website/public/static/img/dragon-session-tools.png differ diff --git a/website/public/static/img/dragon-toolbox.png b/website/public/static/img/dragon-toolbox.png index bc5c361..63c2749 100644 Binary files a/website/public/static/img/dragon-toolbox.png and b/website/public/static/img/dragon-toolbox.png differ diff --git a/website/src/pages/AboutPage.jsx b/website/src/pages/AboutPage.jsx index a885e1c..caf0c1f 100644 --- a/website/src/pages/AboutPage.jsx +++ b/website/src/pages/AboutPage.jsx @@ -7,7 +7,8 @@ import { HomeOnboardingSection } from "./HomeOnboardingSection.jsx"; export function AboutPage({ siteContent }) { const content = siteContent.about; const limits = Array.isArray(content.limits) ? content.limits : []; - const storageSectionIndex = content.sections.findIndex((section) => section.title.toLowerCase().includes("stockage")); + const toolGroups = Array.isArray(content.toolGroups) ? content.toolGroups : []; + const contributeBlocks = Array.isArray(content.contribute?.blocks) ? content.contribute.blocks : []; const normalizeReminder = (item, index) => typeof item === "string" ? { title: `Point ${index + 1}`, text: item, icon: "toolbox" } : item; useEffect(() => { if (!location.hash.includes("#contribuer")) return; @@ -25,41 +26,64 @@ export function AboutPage({ siteContent }) {
-

Principes

-

Comment ça fonctionne

+

{content.sectionsEyebrow}

+

{content.sectionsTitle}

{content.sections.map((section, index) => (
- {String(index + 1).padStart(2, "0")} -
-

{section.title}

-

- {index === storageSectionIndex && limits.length > 0 && ( -
-

{content.limitsTitle}

-
    - {limits.map((item, itemIndex) => { - const reminder = normalizeReminder(item, itemIndex); - return ( -
  • -
  • - ); - })} -
-
+

+

+
+ {section.image && ( + )} +
+

+ {Array.isArray(section.bullets) && section.bullets.length > 0 && ( +
    + {section.bullets.map((bullet) =>
  • {bullet}
  • )} +
+ )} + {section.afterText &&

} + {section.warning && ( +

+ + +

+ )} +
))}
+ {limits.length > 0 && ( +
+

{content.limitsTitle}

+ +
+ )}
@@ -67,36 +91,71 @@ export function AboutPage({ siteContent }) {

Toolbox

{content.toolsTitle}

+ {content.toolsIntro &&

{content.toolsIntro}

}
- {content.toolsLibraryLink}
- {content.tools.map((tool) => ( -
+ {toolGroups.map((group) => ( +
))}
+ {content.toolsLibraryLink}
{content.contribute && (

{content.contribute.eyebrow}

{content.contribute.title}

+ {content.contribute.image && ( + {content.contribute.imageAlt + )}

-
{content.contribute.example}
-
-

{content.contribute.promptTitle}

-

-
{content.contribute.promptText}
+ {contributeBlocks.length > 0 && ( +
+ {contributeBlocks.map((block) => ( +
+
+ ))} +
+ )} +
+
+ {content.contribute.exampleIntro &&

} +
{content.contribute.example}
+
+
+

{content.contribute.promptTitle}

+

+
{content.contribute.promptText}
+
diff --git a/website/src/styles/_home.scss b/website/src/styles/_home.scss index 70054df..d741947 100644 --- a/website/src/styles/_home.scss +++ b/website/src/styles/_home.scss @@ -1242,12 +1242,16 @@ span { .about-story-section { display: grid; - grid-template-columns: minmax(210px, 260px) minmax(0, 1fr); - gap: var(--space-6); + gap: var(--space-5); align-items: start; padding: var(--space-6); } +.about-story-section .about-section-heading { + position: static; + max-width: 860px; +} + .about-section-heading { position: sticky; top: var(--space-5); @@ -1257,46 +1261,145 @@ span { margin-bottom: 0; } +.about-section-heading .about-contribute-illustration { + margin-top: var(--space-6); +} + .about-story-list { display: grid; - gap: 0; + grid-template-columns: repeat(2, minmax(0, 1fr)); + justify-content: center; + width: 100%; + gap: var(--space-4); + margin-inline: auto; } .about-story-item { display: grid; - grid-template-columns: 42px minmax(0, 1fr); + grid-template-rows: auto 1fr; + gap: var(--space-3); + align-items: stretch; + min-width: 0; + overflow: hidden; + padding: var(--space-4); + border: 1px solid rgba(150, 165, 205, 0.12); + border-radius: var(--radius-md); + background: + linear-gradient(135deg, rgba(139, 92, 246, 0.1), transparent 42%), + rgba(16, 20, 38, 0.36); +} + +.about-story-body { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-4); - padding: var(--space-4) 0; - border-bottom: 1px solid rgba(165, 180, 252, 0.1); + align-items: center; + min-width: 0; } -.about-story-item:first-child { - padding-top: 0; -} - -.about-story-item:last-child { - border-bottom: 0; -} - -.about-story-item > span { - color: var(--color-accent-gold); - font-size: var(--font-size-xs); - font-weight: 900; - letter-spacing: 0.08em; - line-height: 1.8; +.about-story-body > div { + display: grid; + align-content: center; + gap: 10px; + min-height: 210px; + padding-block: var(--space-4); } .about-story-item h3 { - margin: 0 0 6px; + display: flex; + align-items: center; + gap: 10px; + min-width: 0; + margin: 0; + padding: 0 0 var(--space-3); + border-bottom: 1px solid rgba(246, 196, 83, 0.12); color: var(--color-text-primary); - font-size: var(--font-size-lg); + font-size: var(--font-size-base); } -.about-story-item > div > p { - max-width: 108ch; +.about-story-item h3 .module-icon { + width: 30px; + height: 30px; + flex: 0 0 30px; + border: 1px solid rgba(246, 196, 83, 0.24); + border-radius: var(--radius-sm); + background: rgba(246, 196, 83, 0.09); +} + +.about-story-item h3 .module-icon-svg { + width: 16px; + height: 16px; +} + +.about-story-body p { margin: 0; color: var(--color-text-secondary); - line-height: 1.62; + font-size: var(--font-size-sm); + line-height: 1.58; +} + +.about-story-bullets { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 4px var(--space-4); + margin: 0; + padding-left: 1.15rem; + color: var(--color-text-secondary); + font-size: var(--font-size-sm); + line-height: 1.5; +} + +.about-story-bullets li { + padding-left: 2px; +} + +.about-story-bullets li::marker { + color: var(--color-accent-gold); +} + +.about-story-bullets.single-column { + grid-template-columns: 1fr; +} + +.about-story-warning { + display: flex; + align-items: flex-start; + gap: 7px; + color: var(--color-warning); + font-size: var(--font-size-xs); + font-weight: 500; + line-height: 1.5; +} + +.about-story-warning .ui-icon { + width: 15px; + min-width: 15px; + height: 15px; + margin-top: 1px; + color: var(--color-warning); + opacity: 0.88; +} + +.about-story-visual { + display: grid; + min-height: 230px; + place-items: center; + overflow: hidden; + border: 1px solid rgba(246, 196, 83, 0.12); + border-radius: var(--radius-md); + background: + radial-gradient(circle at center, rgba(139, 92, 246, 0.28), transparent 60%), + radial-gradient(circle at 24% 18%, rgba(246, 196, 83, 0.1), transparent 36%), + rgba(5, 7, 17, 0.18); +} + +.about-story-illustration { + display: block; + width: min(96%, 340px); + max-height: 250px; + object-fit: contain; + opacity: 0.94; + filter: saturate(1.02) brightness(1.02) drop-shadow(0 18px 26px rgba(0, 0, 0, 0.32)); } .about-tools-section { @@ -1305,14 +1408,28 @@ span { padding: var(--space-6); } +.about-tools-section .page-heading p:not(.eyebrow) { + max-width: 780px; + margin: 8px 0 0; + color: var(--color-text-secondary); + line-height: 1.56; +} + .about-contribute-section { display: grid; grid-template-columns: minmax(210px, 260px) minmax(0, 1fr); gap: var(--space-6); + align-items: stretch; scroll-margin-top: var(--space-6); padding: var(--space-6); } +.about-contribute-section .about-section-heading { + display: grid; + grid-template-rows: auto 1fr; + align-self: stretch; +} + .about-contribute-content { display: grid; gap: var(--space-4); @@ -1325,8 +1442,69 @@ span { line-height: 1.62; } +.about-contribute-blocks { + display: grid; + gap: var(--space-4); +} + +.about-contribute-block { + display: grid; + grid-template-columns: 30px minmax(0, 1fr); + gap: 12px; + align-items: start; + min-width: 0; +} + +.about-contribute-block .module-icon { + width: 28px; + height: 28px; + border: 1px solid rgba(246, 196, 83, 0.22); + border-radius: var(--radius-sm); + background: rgba(246, 196, 83, 0.09); +} + +.about-contribute-block .module-icon-svg { + width: 15px; + height: 15px; +} + +.about-contribute-block h3 { + margin: 0 0 6px; + color: var(--color-text-primary); + font-size: var(--font-size-sm); +} + +.about-contribute-block p { + margin: 0; + color: var(--color-text-secondary); + font-size: var(--font-size-sm); + line-height: 1.5; +} + +.about-contribute-block ul { + display: grid; + gap: 4px; + margin: 10px 0 0; + padding-left: 1.15rem; + color: var(--color-text-secondary); + font-size: var(--font-size-sm); + line-height: 1.45; +} + +.about-contribute-block li::marker { + color: var(--color-accent-gold); +} + +.about-contribute-block .about-contribute-note { + margin-top: var(--space-3); + color: var(--color-text-muted); + font-size: var(--font-size-xs); + line-height: 1.45; +} + .about-contribute-content pre, -.about-prompt-box { +.about-prompt-box, +.about-code-example { margin: 0; border: 1px solid rgba(165, 180, 252, 0.12); border-radius: var(--radius-md); @@ -1340,6 +1518,58 @@ span { overflow-wrap: anywhere; } +.about-contribute-formats { + display: grid; + grid-template-columns: minmax(260px, 0.64fr) minmax(0, 1.36fr); + gap: var(--space-4); + align-items: stretch; + margin-top: var(--space-3); +} + +.about-code-example { + display: grid; + grid-template-rows: auto 1fr; + overflow: hidden; + background: + linear-gradient(135deg, rgba(139, 92, 246, 0.08), transparent 45%), + rgba(5, 7, 17, 0.32); +} + +.about-code-example > p { + margin: 0; + padding: 8px var(--space-3); + border-bottom: 1px solid rgba(246, 196, 83, 0.14); + color: var(--color-text-secondary); + font-size: var(--font-size-sm); + font-weight: 800; + line-height: 1.4; +} + +.about-code-example pre { + border: 0; + border-radius: 0; + background: transparent; + padding: var(--space-3); +} + +.about-contribute-illustration { + display: block; + width: min(100%, 260px); + max-height: 420px; + object-fit: contain; + filter: saturate(1.05) brightness(1.04) drop-shadow(0 26px 38px rgba(0, 0, 0, 0.36)); +} + +.about-section-heading .about-contribute-illustration { + width: min(100%, 300px); + max-height: 520px; + align-self: center; + margin-top: var(--space-6); + object-fit: contain; + opacity: 0.94; + filter: saturate(1.02) brightness(0.99) drop-shadow(0 22px 30px rgba(0, 0, 0, 0.32)); +} + .about-contribute-content code { color: var(--color-text-primary); font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace; @@ -1349,6 +1579,8 @@ span { } .about-prompt-box { + display: grid; + align-content: start; padding: var(--space-4); } @@ -1412,11 +1644,43 @@ span { line-height: 1.5; } +.about-tool-examples { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin: 10px 0 0; + padding: 0; + list-style: none; +} + +.about-tool-examples li { + padding: 4px 8px; + border: 1px solid rgba(246, 196, 83, 0.18); + border-radius: var(--radius-pill); + background: rgba(246, 196, 83, 0.08); + color: var(--color-accent-gold); + font-size: var(--font-size-xs); + font-weight: 800; +} + +.about-tools-library-button { + display: flex; + width: fit-content; + min-width: min(100%, 300px); + align-items: center; + justify-content: center; + justify-self: center; + margin-top: var(--space-2); +} + .about-storage-reminders { display: grid; - gap: 12px; - margin-top: var(--space-4); - padding-left: 44px; + gap: var(--space-3); + margin-top: var(--space-2); + padding: var(--space-5); + border: 1px solid rgba(246, 196, 83, 0.16); + border-radius: var(--radius-md); + background: rgba(5, 7, 17, 0.24); } .about-storage-reminders .eyebrow { @@ -1426,52 +1690,53 @@ span { .about-reminders-list { display: grid; - gap: 0; + grid-template-columns: repeat(4, minmax(0, 260px)); + justify-content: center; + gap: var(--space-4); margin: 0; - padding: 2px 0 2px var(--space-5); + padding: 0; list-style: none; - border-left: 1px solid rgba(246, 196, 83, 0.22); } .about-reminder-item { display: grid; - grid-template-columns: 38px minmax(0, 1fr); - gap: 12px; - align-items: start; - padding: 12px 0; - border-bottom: 1px solid rgba(165, 180, 252, 0.1); + gap: 10px; + min-width: 0; + padding: var(--space-3); + border: 1px solid rgba(150, 165, 205, 0.1); + border-radius: var(--radius-sm); + background: rgba(16, 20, 38, 0.34); } -.about-reminder-item:first-child { - padding-top: 0; -} - -.about-reminder-item:last-child { - padding-bottom: 0; - border-bottom: 0; +.about-reminder-heading { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; } .about-reminder-item .module-icon { - width: 34px; - height: 34px; - flex: 0 0 34px; + width: 30px; + height: 30px; + flex: 0 0 30px; border: 1px solid rgba(246, 196, 83, 0.26); - border-radius: var(--radius-md); + border-radius: var(--radius-sm); background: rgba(246, 196, 83, 0.1); } .about-reminder-item h4 { - margin: 0 0 6px; + margin: 0; color: var(--color-text-primary); - font-size: var(--font-size-base); + font-size: var(--font-size-xs); + line-height: 1.25; } .about-reminder-item p { max-width: none; margin: 0; color: var(--color-text-secondary); - font-size: var(--font-size-sm); - line-height: 1.48; + font-size: var(--font-size-xs); + line-height: 1.45; } .home-stats { diff --git a/website/src/styles/_icons.scss b/website/src/styles/_icons.scss index 32c14a1..8ba4455 100644 --- a/website/src/styles/_icons.scss +++ b/website/src/styles/_icons.scss @@ -42,6 +42,11 @@ -webkit-mask-image: url("/static/icons/info.svg"); } +.ui-icon-warning { + mask-image: url("/static/icons/warning.svg"); + -webkit-mask-image: url("/static/icons/warning.svg"); +} + .ui-icon-export { mask-image: url("/static/icons/export.svg"); -webkit-mask-image: url("/static/icons/export.svg"); diff --git a/website/src/styles/_responsive.scss b/website/src/styles/_responsive.scss index 42ea965..846b3c7 100644 --- a/website/src/styles/_responsive.scss +++ b/website/src/styles/_responsive.scss @@ -41,6 +41,10 @@ .about-section-heading { position: static; } + + .about-reminders-list { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } } @media (max-height: 760px) and (min-width: 1025px) { @@ -86,7 +90,11 @@ .home-onboarding-layout, .home-onboarding-cards, .about-story-section, + .about-story-list, + .about-contribute-blocks, .about-contribute-section, + .about-contribute-formats, + .about-reminders-list, .about-tools-list, .section-grid, .info-grid { @@ -101,8 +109,42 @@ border-bottom: 0; } - .about-storage-reminders { - padding-left: 0; + .about-story-list { + width: 100%; + } + + .about-story-body { + grid-template-columns: 1fr; + } + + .about-story-visual { + min-height: 176px; + } + + .about-story-illustration { + width: min(86%, 220px); + } + + .about-story-bullets { + grid-template-columns: 1fr; + } + + .about-contribute-illustration { + width: min(100%, 220px); + max-height: 260px; + justify-self: center; + } + + .about-contribute-section .about-section-heading { + grid-template-rows: auto; + } + + .about-section-heading .about-contribute-illustration { + margin-top: var(--space-5); + } + + .about-code-example pre { + padding-left: var(--space-4); } .hero,