- ContextMapper : 4 questions contexte → méthode de décision optimale (advice process Laloux, vote inertiel WoT, consentement sociocratique, Smith…) - SocioElection : guide élection sociocratique 6 étapes + advice process + clarté de rôle - WorkflowMilestones : 11 jalons de protocole (7 essentiels), durées recommandées, principes Ostrom - WorkspaceSelector : sélecteur de collectif multi-site dans le header - SectionLayout : toolbox en USlideover droit sur mobile, sidebar sticky desktop - Décisions : ContextMapper intégré + guide consentement - Mandats : SocioElection intégré + cycle de mandat - Documents : guide inertie 4 niveaux + structure + IPFS - Protocoles : WorkflowMilestones + protocole élection sociocratique ajouté - Renommage projet Glibredecision → libreDecision (dossier + sources) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
660 lines
18 KiB
Vue
660 lines
18 KiB
Vue
<script setup lang="ts">
|
||
/**
|
||
* ContextMapper — Recommandeur de méthode de décision.
|
||
* 4 questions de contexte → méthode optimale + justification.
|
||
* Basé sur : Smith (WoT G1), Laloux (advice process), sociocracie.
|
||
*/
|
||
|
||
interface Option { value: string; label: string; icon: string }
|
||
interface Question { id: string; question: string; hint?: string; options: Option[] }
|
||
|
||
interface MethodRec {
|
||
name: string
|
||
icon: string
|
||
tag: string
|
||
tagColor: string
|
||
description: string
|
||
formula?: string
|
||
when: string
|
||
pros: string[]
|
||
cons: string[]
|
||
}
|
||
|
||
const questions: Question[] = [
|
||
{
|
||
id: 'urgency',
|
||
question: 'Quelle est l\'urgence ?',
|
||
hint: 'Le délai disponible avant que la décision soit nécessaire',
|
||
options: [
|
||
{ value: 'immediate', label: 'Immédiate', icon: 'i-lucide-zap' },
|
||
{ value: 'short', label: '< 48h', icon: 'i-lucide-clock' },
|
||
{ value: 'normal', label: 'Planifiable', icon: 'i-lucide-calendar' },
|
||
],
|
||
},
|
||
{
|
||
id: 'stakes',
|
||
question: 'Quel est l\'enjeu ?',
|
||
hint: 'L\'impact et la réversibilité de la décision',
|
||
options: [
|
||
{ value: 'irreversible', label: 'Irréversible', icon: 'i-lucide-lock' },
|
||
{ value: 'major', label: 'Majeur', icon: 'i-lucide-alert-triangle' },
|
||
{ value: 'moderate', label: 'Modéré', icon: 'i-lucide-minus-circle' },
|
||
{ value: 'minor', label: 'Mineur', icon: 'i-lucide-info' },
|
||
],
|
||
},
|
||
{
|
||
id: 'groupSize',
|
||
question: 'Taille du groupe ?',
|
||
hint: 'Nombre de personnes concernées ou habilitées à voter',
|
||
options: [
|
||
{ value: 'small', label: '< 10', icon: 'i-lucide-user' },
|
||
{ value: 'medium', label: '10 – 100', icon: 'i-lucide-users' },
|
||
{ value: 'large', label: '100+', icon: 'i-lucide-globe' },
|
||
],
|
||
},
|
||
{
|
||
id: 'nature',
|
||
question: 'Nature de la décision ?',
|
||
hint: 'Le type de compétence principalement sollicité',
|
||
options: [
|
||
{ value: 'technical', label: 'Technique', icon: 'i-lucide-cpu' },
|
||
{ value: 'political', label: 'Politique', icon: 'i-lucide-landmark' },
|
||
{ value: 'operational', label: 'Opérationnelle', icon: 'i-lucide-settings' },
|
||
],
|
||
},
|
||
]
|
||
|
||
const answers = ref<Record<string, string>>({})
|
||
const step = ref(0)
|
||
const animating = ref(false)
|
||
|
||
const currentQuestion = computed(() => questions[step.value])
|
||
const isComplete = computed(() => Object.keys(answers.value).length === questions.length)
|
||
const progress = computed(() => (step.value / questions.length) * 100)
|
||
|
||
function selectAnswer(questionId: string, value: string) {
|
||
answers.value = { ...answers.value, [questionId]: value }
|
||
if (step.value < questions.length - 1) {
|
||
animating.value = true
|
||
setTimeout(() => {
|
||
step.value++
|
||
animating.value = false
|
||
}, 160)
|
||
}
|
||
}
|
||
|
||
function goBack() {
|
||
if (step.value > 0) step.value--
|
||
}
|
||
|
||
function reset() {
|
||
answers.value = {}
|
||
step.value = 0
|
||
}
|
||
|
||
const recommendation = computed((): MethodRec | null => {
|
||
if (!isComplete.value) return null
|
||
const { urgency, stakes, groupSize, nature } = answers.value
|
||
|
||
// Immediate → Advice process (Laloux)
|
||
if (urgency === 'immediate') {
|
||
return {
|
||
name: 'Processus de sollicitation d\'avis',
|
||
icon: 'i-lucide-message-circle',
|
||
tag: 'Laloux / Teal',
|
||
tagColor: 'teal',
|
||
description: 'Le décideur identifié consulte les personnes expertes et impactées, puis décide seul et en rend compte. Rapide, non-bloquant, responsabilisant.',
|
||
formula: 'Pas de vote — consultation libre → décision documentée → compte-rendu',
|
||
when: 'Urgence opérationnelle, décision réversible, responsable clairement identifié.',
|
||
pros: ['Rapide (< 2h)', 'Non-bloquant', 'Responsabilise le décideur'],
|
||
cons: ['Requiert confiance dans le décideur', 'Pas de validation collective'],
|
||
}
|
||
}
|
||
|
||
// Technical + medium/large → Smith WoT
|
||
if (nature === 'technical' && groupSize !== 'small') {
|
||
return {
|
||
name: 'Vote inertiel WoT + critère Smith',
|
||
icon: 'i-lucide-network',
|
||
tag: 'G1 standard',
|
||
tagColor: 'accent',
|
||
description: 'Vote communautaire avec seuil adaptatif à la participation. Le critère Smith garantit que la décision reflète l\'expertise des validateurs.',
|
||
formula: 'R = C + B^W + (M + (1−M)·(1−(T/W)^G))·max(0,T−C)\nSeuil Smith : ⌈SmithWoT^S⌉',
|
||
when: 'Décision technique nécessitant validation par les experts WoT (forgerons, CoTec).',
|
||
pros: ['Validé par expertise', 'Adaptatif à la participation', 'Tracé on-chain'],
|
||
cons: ['Durée minimum 7-30j', 'Complexité de la formule'],
|
||
}
|
||
}
|
||
|
||
// Irreversible + large → High threshold WoT
|
||
if (stakes === 'irreversible' && groupSize === 'large') {
|
||
return {
|
||
name: 'Vote inertiel WoT (inertie forte)',
|
||
icon: 'i-lucide-shield',
|
||
tag: 'G1 renforcé',
|
||
tagColor: 'secondary',
|
||
description: 'Pour les décisions irréversibles à fort impact : seuil de quasi-unanimité si faible participation, majorité qualifiée avec forte participation.',
|
||
formula: 'R = C + B^W + (M + (1−M)·(1−(T/W)^G))·max(0,T−C)\nParamètres : M=67%, G=0.3 (inertie forte)',
|
||
when: 'Textes fondateurs, modifications structurelles, décisions irréversibles pour 100+ membres.',
|
||
pros: ['Protection maximale', 'Légitimité forte', 'Résistant aux minorités actives'],
|
||
cons: ['Durée longue (30+ jours)', 'Peut bloquer les évolutions nécessaires'],
|
||
}
|
||
}
|
||
|
||
// Small group → Sociocratic consent
|
||
if (groupSize === 'small') {
|
||
return {
|
||
name: 'Consentement sociocratique',
|
||
icon: 'i-lucide-check-circle-2',
|
||
tag: 'Sociocracie',
|
||
tagColor: 'tertiary',
|
||
description: 'Adoption si aucune objection grave n\'est soulevée. Une objection grave = la décision nuit à la mission commune, pas juste une préférence personnelle.',
|
||
formula: 'Adoptée si : aucune objection grave parmi les membres du cercle',
|
||
when: 'Cercle de travail (< 10 membres), enjeu modéré, décision réversible.',
|
||
pros: ['Rapide', 'Inclusif', 'Distingue objection grave et préférence'],
|
||
cons: ['Ne convient pas aux grands groupes', 'Risque de pression sociale'],
|
||
}
|
||
}
|
||
|
||
// Political + medium → WoT majority
|
||
if (nature === 'political') {
|
||
return {
|
||
name: 'Vote majoritaire WoT',
|
||
icon: 'i-lucide-vote',
|
||
tag: 'G1 standard',
|
||
tagColor: 'accent',
|
||
description: 'Vote binaire (Pour/Contre) avec seuil adaptatif à la participation WoT. Standard pour les décisions politiques de la communauté.',
|
||
formula: 'R = C + B^W + (M + (1−M)·(1−(T/W)^G))·max(0,T−C)',
|
||
when: 'Décision politique communautaire, participation variable, groupe >10.',
|
||
pros: ['Standard WoT', 'Adaptatif', 'Tracé on-chain'],
|
||
cons: ['Durée 7-30j', 'Participation faible possible'],
|
||
}
|
||
}
|
||
|
||
// Default: minor/operational
|
||
return {
|
||
name: 'Advice process + validation légère',
|
||
icon: 'i-lucide-thumbs-up',
|
||
tag: 'Léger',
|
||
tagColor: 'teal',
|
||
description: 'Pour les décisions mineures ou opérationnelles : consultation des parties concernées, décision par le responsable désigné, notification de la communauté.',
|
||
formula: 'Consultation → Décision → Notification (sans vote formel)',
|
||
when: 'Décision opérationnelle de faible impact, facilement réversible.',
|
||
pros: ['Très rapide', 'Non-bloquant', 'Adapté à l\'opérationnel'],
|
||
cons: ['Légitimité limitée', 'Ne convient pas aux enjeux majeurs'],
|
||
}
|
||
})
|
||
|
||
const emit = defineEmits<{ use: [name: string] }>()
|
||
</script>
|
||
|
||
<template>
|
||
<div class="cmap">
|
||
<!-- Header -->
|
||
<div class="cmap__head">
|
||
<UIcon name="i-lucide-compass" class="cmap__head-icon" />
|
||
<div>
|
||
<h3 class="cmap__title">Choisir une méthode</h3>
|
||
<p class="cmap__subtitle">4 questions pour la méthode adaptée</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Result -->
|
||
<Transition name="fade-up" mode="out-in">
|
||
<div v-if="isComplete" key="result" class="cmap__result">
|
||
<div class="cmap__result-header">
|
||
<div class="cmap__result-icon">
|
||
<UIcon :name="recommendation!.icon" />
|
||
</div>
|
||
<div class="cmap__result-info">
|
||
<span class="cmap__result-tag" :class="`cmap__result-tag--${recommendation!.tagColor}`">
|
||
{{ recommendation!.tag }}
|
||
</span>
|
||
<h4 class="cmap__result-name">{{ recommendation!.name }}</h4>
|
||
</div>
|
||
</div>
|
||
|
||
<p class="cmap__result-desc">{{ recommendation!.description }}</p>
|
||
|
||
<div v-if="recommendation!.formula" class="cmap__formula">
|
||
<span class="cmap__formula-label">Formule</span>
|
||
<pre class="cmap__formula-code">{{ recommendation!.formula }}</pre>
|
||
</div>
|
||
|
||
<div class="cmap__pros-cons">
|
||
<div>
|
||
<span class="cmap__pros-label">Pour</span>
|
||
<ul class="cmap__list cmap__list--pro">
|
||
<li v-for="p in recommendation!.pros" :key="p">{{ p }}</li>
|
||
</ul>
|
||
</div>
|
||
<div>
|
||
<span class="cmap__cons-label">Contre</span>
|
||
<ul class="cmap__list cmap__list--con">
|
||
<li v-for="c in recommendation!.cons" :key="c">{{ c }}</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<p class="cmap__when">
|
||
<UIcon name="i-lucide-lightbulb" />
|
||
{{ recommendation!.when }}
|
||
</p>
|
||
|
||
<div class="cmap__result-actions">
|
||
<button class="cmap__btn-reset" @click="reset">
|
||
<UIcon name="i-lucide-refresh-cw" />
|
||
Recommencer
|
||
</button>
|
||
<button class="cmap__btn-use" @click="emit('use', recommendation!.name)">
|
||
<UIcon name="i-lucide-play" />
|
||
Utiliser cette méthode
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Quiz -->
|
||
<div v-else key="quiz" class="cmap__quiz">
|
||
<!-- Progress -->
|
||
<div class="cmap__progress">
|
||
<div class="cmap__progress-bar" :style="{ width: `${progress}%` }" />
|
||
</div>
|
||
<span class="cmap__step-label">{{ step + 1 }} / {{ questions.length }}</span>
|
||
|
||
<!-- Question -->
|
||
<Transition name="slide-right" mode="out-in">
|
||
<div :key="step" class="cmap__question-block">
|
||
<p class="cmap__question">{{ currentQuestion.question }}</p>
|
||
<p v-if="currentQuestion.hint" class="cmap__hint">{{ currentQuestion.hint }}</p>
|
||
|
||
<div class="cmap__options">
|
||
<button
|
||
v-for="opt in currentQuestion.options"
|
||
:key="opt.value"
|
||
class="cmap__option"
|
||
:class="{ 'cmap__option--selected': answers[currentQuestion.id] === opt.value }"
|
||
@click="selectAnswer(currentQuestion.id, opt.value)"
|
||
>
|
||
<div class="cmap__option-icon">
|
||
<UIcon :name="opt.icon" />
|
||
</div>
|
||
<span class="cmap__option-label">{{ opt.label }}</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</Transition>
|
||
|
||
<button v-if="step > 0" class="cmap__back" @click="goBack">
|
||
<UIcon name="i-lucide-chevron-left" />
|
||
Retour
|
||
</button>
|
||
</div>
|
||
</Transition>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.cmap {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 1rem;
|
||
}
|
||
|
||
.cmap__head {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.cmap__head-icon {
|
||
font-size: 1.375rem;
|
||
color: var(--mood-accent);
|
||
flex-shrink: 0;
|
||
margin-top: 0.125rem;
|
||
}
|
||
|
||
.cmap__title {
|
||
font-size: 1rem;
|
||
font-weight: 800;
|
||
color: var(--mood-text);
|
||
margin: 0;
|
||
}
|
||
|
||
.cmap__subtitle {
|
||
font-size: 0.8125rem;
|
||
color: var(--mood-text-muted);
|
||
margin: 0;
|
||
}
|
||
|
||
/* Progress */
|
||
.cmap__progress {
|
||
height: 4px;
|
||
background: var(--mood-accent-soft);
|
||
border-radius: 4px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.cmap__progress-bar {
|
||
height: 100%;
|
||
background: var(--mood-accent);
|
||
border-radius: 4px;
|
||
transition: width 0.3s ease;
|
||
}
|
||
|
||
.cmap__step-label {
|
||
font-size: 0.6875rem;
|
||
font-weight: 700;
|
||
color: var(--mood-text-muted);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.06em;
|
||
}
|
||
|
||
/* Question */
|
||
.cmap__question-block {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.cmap__question {
|
||
font-size: 0.9375rem;
|
||
font-weight: 700;
|
||
color: var(--mood-text);
|
||
margin: 0;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.cmap__hint {
|
||
font-size: 0.75rem;
|
||
color: var(--mood-text-muted);
|
||
margin: 0;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.cmap__options {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.cmap__option {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
padding: 0.75rem 1rem;
|
||
background: var(--mood-accent-soft);
|
||
border-radius: 12px;
|
||
cursor: pointer;
|
||
transition: transform 0.1s ease, box-shadow 0.1s ease, background 0.1s ease;
|
||
text-align: left;
|
||
min-height: 2.75rem;
|
||
}
|
||
|
||
.cmap__option:hover {
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 3px 10px var(--mood-shadow);
|
||
}
|
||
|
||
.cmap__option:active { transform: translateY(0); }
|
||
|
||
.cmap__option--selected {
|
||
background: var(--mood-accent);
|
||
}
|
||
|
||
.cmap__option--selected .cmap__option-icon,
|
||
.cmap__option--selected .cmap__option-label {
|
||
color: var(--mood-accent-text);
|
||
}
|
||
|
||
.cmap__option-icon {
|
||
width: 1.75rem;
|
||
height: 1.75rem;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 8px;
|
||
background: var(--mood-surface);
|
||
color: var(--mood-accent);
|
||
flex-shrink: 0;
|
||
font-size: 0.875rem;
|
||
}
|
||
|
||
.cmap__option--selected .cmap__option-icon {
|
||
background: rgba(255,255,255,0.2);
|
||
color: var(--mood-accent-text);
|
||
}
|
||
|
||
.cmap__option-label {
|
||
font-size: 0.875rem;
|
||
font-weight: 600;
|
||
color: var(--mood-text);
|
||
}
|
||
|
||
.cmap__back {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.375rem;
|
||
font-size: 0.8125rem;
|
||
font-weight: 600;
|
||
color: var(--mood-text-muted);
|
||
background: none;
|
||
cursor: pointer;
|
||
padding: 0.375rem 0;
|
||
transition: color 0.1s ease;
|
||
}
|
||
.cmap__back:hover { color: var(--mood-text); }
|
||
|
||
/* Result */
|
||
.cmap__result {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.875rem;
|
||
}
|
||
|
||
.cmap__result-header {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.cmap__result-icon {
|
||
width: 2.5rem;
|
||
height: 2.5rem;
|
||
flex-shrink: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 12px;
|
||
background: var(--mood-accent-soft);
|
||
color: var(--mood-accent);
|
||
font-size: 1.125rem;
|
||
}
|
||
|
||
.cmap__result-info {
|
||
flex: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.25rem;
|
||
}
|
||
|
||
.cmap__result-tag {
|
||
font-size: 0.625rem;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.06em;
|
||
padding: 2px 8px;
|
||
border-radius: 20px;
|
||
width: fit-content;
|
||
}
|
||
.cmap__result-tag--accent {
|
||
background: var(--mood-accent-soft);
|
||
color: var(--mood-accent);
|
||
}
|
||
.cmap__result-tag--teal {
|
||
background: color-mix(in srgb, var(--mood-success) 15%, transparent);
|
||
color: var(--mood-success);
|
||
}
|
||
.cmap__result-tag--secondary {
|
||
background: color-mix(in srgb, var(--mood-secondary, var(--mood-accent)) 15%, transparent);
|
||
color: var(--mood-secondary, var(--mood-accent));
|
||
}
|
||
.cmap__result-tag--tertiary {
|
||
background: color-mix(in srgb, var(--mood-tertiary, var(--mood-accent)) 15%, transparent);
|
||
color: var(--mood-tertiary, var(--mood-accent));
|
||
}
|
||
|
||
.cmap__result-name {
|
||
font-size: 0.9375rem;
|
||
font-weight: 800;
|
||
color: var(--mood-text);
|
||
margin: 0;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.cmap__result-desc {
|
||
font-size: 0.8125rem;
|
||
color: var(--mood-text-muted);
|
||
line-height: 1.6;
|
||
margin: 0;
|
||
}
|
||
|
||
.cmap__formula {
|
||
background: var(--mood-accent-soft);
|
||
border-radius: 10px;
|
||
padding: 0.625rem 0.875rem;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.25rem;
|
||
}
|
||
|
||
.cmap__formula-label {
|
||
font-size: 0.6875rem;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.06em;
|
||
color: var(--mood-accent);
|
||
}
|
||
|
||
.cmap__formula-code {
|
||
font-family: ui-monospace, SFMono-Regular, monospace;
|
||
font-size: 0.75rem;
|
||
color: var(--mood-text);
|
||
margin: 0;
|
||
white-space: pre-wrap;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.cmap__pros-cons {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.cmap__pros-label,
|
||
.cmap__cons-label {
|
||
display: block;
|
||
font-size: 0.625rem;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.06em;
|
||
margin-bottom: 0.25rem;
|
||
}
|
||
.cmap__pros-label { color: var(--mood-success); }
|
||
.cmap__cons-label { color: var(--mood-error); }
|
||
|
||
.cmap__list {
|
||
margin: 0;
|
||
padding: 0;
|
||
list-style: none;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.25rem;
|
||
}
|
||
|
||
.cmap__list li {
|
||
font-size: 0.6875rem;
|
||
color: var(--mood-text-muted);
|
||
padding-left: 0.875rem;
|
||
position: relative;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.cmap__list--pro li::before {
|
||
content: '✓';
|
||
position: absolute;
|
||
left: 0;
|
||
color: var(--mood-success);
|
||
font-weight: 700;
|
||
font-size: 0.5rem;
|
||
top: 0.2em;
|
||
}
|
||
|
||
.cmap__list--con li::before {
|
||
content: '·';
|
||
position: absolute;
|
||
left: 0;
|
||
color: var(--mood-error);
|
||
font-weight: 700;
|
||
}
|
||
|
||
.cmap__when {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 0.5rem;
|
||
font-size: 0.75rem;
|
||
color: var(--mood-text-muted);
|
||
margin: 0;
|
||
line-height: 1.5;
|
||
font-style: italic;
|
||
}
|
||
|
||
.cmap__result-actions {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.cmap__btn-reset {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.375rem;
|
||
padding: 0.5rem 0.875rem;
|
||
font-size: 0.8125rem;
|
||
font-weight: 600;
|
||
color: var(--mood-text-muted);
|
||
background: var(--mood-accent-soft);
|
||
border-radius: 20px;
|
||
cursor: pointer;
|
||
transition: transform 0.1s ease;
|
||
}
|
||
.cmap__btn-reset:hover { transform: translateY(-1px); color: var(--mood-text); }
|
||
|
||
.cmap__btn-use {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.375rem;
|
||
padding: 0.5rem 1.125rem;
|
||
font-size: 0.8125rem;
|
||
font-weight: 700;
|
||
color: var(--mood-accent-text);
|
||
background: var(--mood-accent);
|
||
border-radius: 20px;
|
||
cursor: pointer;
|
||
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
||
}
|
||
.cmap__btn-use:hover {
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 4px 12px var(--mood-shadow);
|
||
}
|
||
|
||
/* Transitions */
|
||
.fade-up-enter-active, .fade-up-leave-active { transition: all 0.2s ease; }
|
||
.fade-up-enter-from { opacity: 0; transform: translateY(8px); }
|
||
.fade-up-leave-to { opacity: 0; transform: translateY(-4px); }
|
||
|
||
.slide-right-enter-active, .slide-right-leave-active { transition: all 0.16s ease; }
|
||
.slide-right-enter-from { opacity: 0; transform: translateX(12px); }
|
||
.slide-right-leave-to { opacity: 0; transform: translateX(-8px); }
|
||
</style>
|