v2 : les 14 écrans — le tour complet de la démocratie d'exercice

- Aujourd'hui (Fil 13 sections + capture sticky) + Le chemin (tunnel 2 gestes,
  Q0 inline, 3 chips, dérogation asymétrique, alternatives réglage/consignation)
- Registre + fiche décision (timeline, périmètre premier/second lieu auditable,
  affluence non-ignorable, S'instruire condensé, éléments + cartographie de
  clôture, épreuve du réel, Remettre en question, PV A4, gravure)
- Salle de vote 5 modalités (consentement, nuancé+histogramme, binaire hérité
  avec jauge inertielle, Réglage collectif complet — faisceau, médiane basse,
  Pour moi, Explorer, cristallisation-geste —, élection à départage humain)
- Textes (Pacte en clair, document vivant, diff, vue projetée, Atelier des
  formules porté du v1 sur le moteur unique) + Mandats (faits comptés, feux
  de la rampe, wizard 3 étapes) + Observatoire (consigner→observer→protocoliser)
- Onboarding 7 gabarits + Données locales (export/import, attributs, atelier)
- voting→framing gardé (Reformuler d'un réglage figé non cristallisé)
- Pages v1 supprimées (login, documents, mandates, protocols, sanctuary,
  tools, decisions/new) — 342 tests verts, build zéro erreur

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-08-11 13:47:20 +02:00
co-authored by Claude Fable 5
parent d886302b59
commit e164b5f6c1
96 changed files with 14097 additions and 11777 deletions
@@ -0,0 +1,174 @@
<script setup lang="ts">
// <!-- ld-v2 --> Carte d'un document de la bibliothèque : titre, rôle,
// nombre de clauses, mini-jauges des sessions en cours sur ses clauses,
// provenance résumée (version, vote d'origine), badge 井 si gravures.
import type { TextDoc } from '~/types/domain'
import { PACT_BADGE } from '~/lexicon'
import type { ClauseSessionGauge } from './textsModel'
import { formatInt } from './textsModel'
const props = withDefaults(defineProps<{
doc: TextDoc
clauseCount: number
gauges?: ClauseSessionGauge[]
engraved?: number
/** Carte épinglée du Pacte — mise en avant. */
pinned?: boolean
}>(), { gauges: () => [], engraved: 0, pinned: false })
const roleLabel = computed(() => (props.doc.role === 'pact' ? PACT_BADGE : 'document de référence'))
const version = computed(() =>
props.doc.provenance?.sources.find(s => s.version)?.version)
const voteSummary = computed(() => {
const r = props.doc.provenance?.voteRecord?.result
if (!r) return null
return `${formatInt(r.for)} pour · ${formatInt(r.against)} contre — toile de ${formatInt(r.wotSize)}`
})
const visibleGauges = computed(() => props.gauges.slice(0, 3))
</script>
<template>
<!-- ld-v2 -->
<NuxtLink
:to="`/textes/${doc.slug}`"
class="doc-card ld-card ld-card--hover"
:class="{ 'doc-card--pinned': pinned }"
>
<header class="doc-card__head">
<span class="doc-card__role" :class="{ 'doc-card__role--pact': doc.role === 'pact' }">
<UIcon :name="doc.role === 'pact' ? 'i-lucide-scroll' : 'i-lucide-book-open'" />
{{ roleLabel }}
</span>
<span v-if="engraved > 0" class="doc-card__engraved" title="Décisions gravées sur ce texte">
{{ engraved }}
</span>
</header>
<h3 class="doc-card__title">{{ doc.title }}</h3>
<p class="doc-card__desc">{{ doc.description }}</p>
<div class="doc-card__meta">
<span class="doc-card__chip">
<UIcon name="i-lucide-list" />
{{ clauseCount }} clause{{ clauseCount > 1 ? 's' : '' }}
</span>
<span v-if="version" class="doc-card__chip doc-card__chip--mono">v{{ version }}</span>
<span v-if="voteSummary" class="doc-card__chip doc-card__chip--vote">
<UIcon name="i-lucide-vote" />
{{ voteSummary }}
</span>
</div>
<div v-if="visibleGauges.length" class="doc-card__sessions">
<p class="doc-card__sessions-heading">
{{ gauges.length > 1 ? `${gauges.length} sessions en cours` : 'Une session en cours' }}
</p>
<div class="doc-card__gauges">
<SessionMiniGauge
v-for="gauge in visibleGauges"
:key="gauge.decisionId"
:gauge="gauge"
@click.stop
/>
</div>
</div>
</NuxtLink>
</template>
<style scoped>
.doc-card {
display: flex;
flex-direction: column;
gap: 0.6rem;
padding: clamp(1rem, 3vw, 1.35rem);
text-decoration: none;
color: inherit;
}
.doc-card--pinned {
background:
linear-gradient(140deg, var(--mood-accent-soft), transparent 55%),
var(--mood-surface);
}
.doc-card__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
}
.doc-card__role {
display: inline-flex;
align-items: center;
gap: 0.35rem;
font-size: 0.6875rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.06em;
padding: 3px 10px;
border-radius: var(--r-pill);
background: color-mix(in srgb, var(--mood-text) 7%, transparent);
color: var(--mood-text-muted);
}
.doc-card__role--pact { background: var(--mood-accent-soft); color: var(--mood-accent); }
.doc-card__engraved {
font-size: 0.8125rem;
font-weight: 800;
color: var(--mood-status-vigueur);
}
.doc-card__title {
font-size: clamp(1rem, 2.5vw, 1.15rem);
font-weight: 800;
color: var(--mood-text);
letter-spacing: -0.01em;
line-height: 1.25;
}
.doc-card__desc {
font-size: 0.8125rem;
color: var(--mood-text-muted);
line-height: 1.55;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.doc-card__meta { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.15rem; }
.doc-card__chip {
display: inline-flex;
align-items: center;
gap: 0.3rem;
font-size: 0.71875rem;
font-weight: 700;
padding: 3px 9px;
border-radius: var(--r-pill);
background: color-mix(in srgb, var(--mood-text) 6%, transparent);
color: var(--mood-text-muted);
}
.doc-card__chip--mono { font-family: monospace; }
.doc-card__chip--vote {
background: var(--mood-status-vigueur-bg);
color: var(--mood-status-vigueur);
}
.doc-card__sessions {
margin-top: 0.35rem;
padding-top: 0.6rem;
box-shadow: 0 -1px 0 color-mix(in srgb, var(--mood-text) 7%, transparent);
display: flex;
flex-direction: column;
gap: 0.35rem;
}
.doc-card__sessions-heading {
font-size: 0.6875rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--mood-status-vote);
}
.doc-card__gauges { display: flex; flex-direction: column; gap: 0.3rem; }
</style>