Build and deploy / test-python (push) Successful in 7m23s
Build and deploy / test-js (push) Successful in 1m24s
Build and deploy / lint-python (push) Successful in 6m29s
Build and deploy / lint-js (push) Failing after 1m31s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Build and deploy / sonarqube (push) Failing after 5m18s
Les vignettes réutilisent le HTML réellement rendu de chaque page (CSS scale trick) et sont centrées dans le conteneur du milieu, au lieu d'une barre pleine largeur. Corrige au passage deux bugs réels trouvés en écrivant les tests : - une page contenant un mini-jeu (bouton Suivant/Recommencer) cassait le parsing HTML car .docPageThumbCard était un <button> englobant un autre <button> ; passage en div role="button" + équivalent clavier, contenu copié rendu inert. - le renommage d'une page par double-clic ne fonctionnait plus du tout (sélecteur .docPageTab oublié lors du renommage des classes). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1992 lines
44 KiB
CSS
1992 lines
44 KiB
CSS
/* document-editor.css — éditeur de support de formation (voir
|
|
docs/plan/PLAN.md). Tout est scopé sous .docEditor3 : aucune règle ici
|
|
ne touche jamais html[data-theme] (figé "dark" pour le reste du site,
|
|
voir templates/base.html) — le thème clair/sombre de CET éditeur est un
|
|
attribut data-theme posé sur .docEditor3 lui-même (voir
|
|
static/document/js/document-editor.js), avec ses propres tokens
|
|
--doc-* ci-dessous plutôt que les --forge-* globaux. */
|
|
|
|
/* Demande explicite : l'éditeur occupe TOUTE la largeur de la fenêtre,
|
|
bord à bord — .content limite normalement à 760px (voir static/style.css)
|
|
et .content-wide à 1600px, tous deux avec un padding fixe. Neutralisé
|
|
ici pour cette page précise seulement (ce fichier n'est chargé QUE par
|
|
templates/document/document_edit.html) : même sélecteur que la règle du
|
|
site (`body.objectEditBody > main.content`, spécificité identique),
|
|
mais chargé après static/style.css dans <head> (voir son
|
|
{% block extra_head %}) donc prioritaire à spécificité égale. */
|
|
body.objectEditBody > main.content {
|
|
max-width: none;
|
|
width: 100%;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
/* La modale (#docScenarioTreeModal) partage ces tokens --doc-* MAIS
|
|
reste volontairement un FRÈRE de .docEditor3 dans le HTML (jamais un
|
|
descendant) : main.content (voir body.objectEditBody dans
|
|
static/style.css) a déjà `overflow:hidden`, qui ROGNE VISUELLEMENT
|
|
tout descendant `position:fixed` à sa propre boîte (même bug déjà
|
|
trouvé le 20/09/2026 pour le bandeau d'outils, voir plus bas) — un
|
|
`position:fixed` doit donc rester un frère de .docEditor3, jamais un
|
|
enfant, pour couvrir le VRAI viewport. D'où la duplication de ce
|
|
sélecteur ici plutôt qu'un simple héritage de variables CSS (qui ne
|
|
franchit pas la frontière DOM entre frères) — voir aussi
|
|
forgeDocApplyTheme (static/document/js/document-editor.js), qui pose
|
|
le même data-theme sur les deux éléments. */
|
|
.docEditor3,
|
|
#docScenarioTreeModal {
|
|
--doc-bg: var(--forge-bg);
|
|
--doc-bg-2: var(--forge-bg-secondary);
|
|
--doc-card: var(--forge-card);
|
|
--doc-border: var(--forge-border);
|
|
--doc-text: var(--forge-text);
|
|
--doc-muted: var(--forge-text-muted);
|
|
--doc-accent: var(--accent);
|
|
--doc-accent-2: var(--accent-2);
|
|
--doc-glow: 0 0 0 1px rgb(255 95 46 / 25%), 0 8px 24px -6px rgb(255 95 46 / 25%);
|
|
--doc-quiz-success-bg: rgb(59 165 93 / 12%);
|
|
--doc-quiz-success-border: #3ba55d;
|
|
--doc-quiz-success-text: #8fe3ab;
|
|
--doc-quiz-danger-bg: rgb(255 95 46 / 12%);
|
|
--doc-quiz-danger-border: #ff5f2e;
|
|
--doc-quiz-danger-text: #ffb3a0;
|
|
}
|
|
|
|
.docEditor3 {
|
|
/* Remplit main.content (voir body.objectEditBody dans static/style.css :
|
|
`main.content` y est déjà `flex:1 1 auto; min-height:0; overflow:hidden;
|
|
display:flex; flex-direction:column;`) — jamais `position:fixed` : cet
|
|
ancêtre a `overflow:hidden`, qui ROGNE VISUELLEMENT tout descendant
|
|
`position:fixed` à sa propre boîte (laquelle commence sous la barre de
|
|
navigation du site, pas au vrai sommet du viewport) — bug réel trouvé
|
|
le 20/09/2026 : le bandeau d'outils (56px) tombait alors entièrement
|
|
dans la zone rognée, invisible, alors que le reste de l'éditeur
|
|
semblait décalé vers le haut à sa place. */
|
|
flex: 1 1 auto;
|
|
min-height: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
background: var(--doc-bg);
|
|
color: var(--doc-text);
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
}
|
|
|
|
.docEditor3[data-theme="light"],
|
|
#docScenarioTreeModal[data-theme="light"] {
|
|
--doc-bg: #f6f5f2;
|
|
--doc-bg-2: #fff;
|
|
--doc-card: #fff;
|
|
--doc-border: #e3e1db;
|
|
--doc-text: #14161c;
|
|
--doc-muted: #6b7280;
|
|
--doc-glow: 0 0 0 1px rgb(255 95 46 / 30%), 0 8px 24px -6px rgb(255 95 46 / 30%);
|
|
--doc-quiz-success-bg: #e4f5ea;
|
|
--doc-quiz-success-border: #2e7d46;
|
|
--doc-quiz-success-text: #1d4c2c;
|
|
--doc-quiz-danger-bg: #fdede8;
|
|
--doc-quiz-danger-border: #c1451f;
|
|
--doc-quiz-danger-text: #7a1418;
|
|
}
|
|
|
|
.docEditor3 *,
|
|
#docScenarioTreeModal * {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.docEditor3 button {
|
|
font-family: inherit;
|
|
}
|
|
|
|
/* ===== TOPBAR ===== */
|
|
.docTopbar {
|
|
height: 56px;
|
|
flex-shrink: 0;
|
|
background: var(--doc-bg-2);
|
|
border-bottom: 1px solid var(--doc-border);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 20px;
|
|
gap: 12px;
|
|
}
|
|
|
|
.docCrumb {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.docCrumbDim {
|
|
color: var(--doc-muted);
|
|
font-weight: 400;
|
|
white-space: nowrap;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.docCrumbCur {
|
|
font-weight: 700;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
color: var(--doc-text);
|
|
}
|
|
|
|
.docTopbarActions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.docIconBtn {
|
|
width: 34px;
|
|
height: 34px;
|
|
border-radius: 10px;
|
|
border: 1px solid var(--doc-border);
|
|
background: transparent;
|
|
color: var(--doc-text);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.docIconBtn:hover:not(:disabled) {
|
|
border-color: var(--doc-muted);
|
|
}
|
|
|
|
.docIconBtn:disabled {
|
|
opacity: 0.35;
|
|
cursor: default;
|
|
}
|
|
|
|
.docDividerV {
|
|
width: 1px;
|
|
height: 22px;
|
|
background: var(--doc-border);
|
|
margin: 0 4px;
|
|
}
|
|
|
|
.docBtnSecondary {
|
|
height: 34px;
|
|
padding: 0 14px;
|
|
border-radius: 10px;
|
|
border: 1px solid var(--doc-border);
|
|
background: transparent;
|
|
color: var(--doc-text);
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 7px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.docBtnSecondary:hover {
|
|
border-color: var(--doc-muted);
|
|
}
|
|
|
|
.docBtnPrimary {
|
|
border-color: var(--doc-accent);
|
|
color: var(--doc-accent);
|
|
}
|
|
|
|
.docBtnPrimary:hover {
|
|
background: rgb(255 95 46 / 8%);
|
|
}
|
|
|
|
.docWidthPicker {
|
|
display: flex;
|
|
gap: 4px;
|
|
background: var(--doc-card);
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 10px;
|
|
padding: 3px;
|
|
}
|
|
|
|
.docWidthBtn {
|
|
border: none;
|
|
background: transparent;
|
|
color: var(--doc-muted);
|
|
font-size: 12.5px;
|
|
font-weight: 600;
|
|
padding: 5px 10px;
|
|
border-radius: 7px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.docWidthBtn.is-active {
|
|
background: rgb(255 95 46 / 12%);
|
|
color: var(--doc-accent);
|
|
}
|
|
|
|
/* ===== BODY LAYOUT ===== */
|
|
.docBodyWrap {
|
|
flex: 1;
|
|
display: flex;
|
|
min-height: 0;
|
|
position: relative;
|
|
}
|
|
|
|
.docSidebar {
|
|
flex-shrink: 0;
|
|
background: var(--doc-bg-2);
|
|
padding: 20px 16px;
|
|
overflow-y: auto;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 22px;
|
|
}
|
|
|
|
.docSidebarLeft {
|
|
width: 248px;
|
|
border-right: 1px solid var(--doc-border);
|
|
}
|
|
|
|
.docSidebarRight {
|
|
width: 300px;
|
|
border-left: 1px solid var(--doc-border);
|
|
}
|
|
|
|
.docSectionLabel {
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.04em;
|
|
color: var(--doc-muted);
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.docShapesGrid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 8px;
|
|
}
|
|
|
|
.docLibItem {
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 10px;
|
|
cursor: grab;
|
|
background: var(--doc-card);
|
|
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
|
}
|
|
|
|
.docLibItem:hover {
|
|
border-color: var(--doc-accent);
|
|
box-shadow: var(--doc-glow);
|
|
}
|
|
|
|
.docLibItem--shape {
|
|
padding: 10px 8px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
|
|
.docLibItem--row {
|
|
padding: 9px 10px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
.docLibItem span:last-child {
|
|
font-size: 12.5px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.docLibList {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
}
|
|
|
|
.docLibIcon {
|
|
width: 18px;
|
|
height: 18px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 14px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.docLibIcon--rectangle::before { content: "▭"; }
|
|
.docLibIcon--cercle::before { content: "○"; }
|
|
.docLibIcon--triangle::before { content: "△"; }
|
|
.docLibIcon--trait::before { content: "―"; }
|
|
.docLibIcon--paragraphe::before { content: "¶"; }
|
|
.docLibIcon--image::before { content: "🖼"; }
|
|
.docLibIcon--bouton::before { content: "⬚"; }
|
|
|
|
.docLibIcon--titre::before {
|
|
content: "T";
|
|
font-weight: 800;
|
|
}
|
|
|
|
.docLibIcon--quiz::before {
|
|
content: "?";
|
|
color: var(--doc-accent);
|
|
font-weight: 800;
|
|
}
|
|
|
|
.docLibIcon--association::before {
|
|
content: "⇄";
|
|
color: var(--doc-accent);
|
|
}
|
|
|
|
.docLibIcon--memory::before {
|
|
content: "▤";
|
|
color: var(--doc-accent);
|
|
}
|
|
|
|
.docLibIcon--mots::before {
|
|
content: "▦";
|
|
color: var(--doc-accent);
|
|
}
|
|
|
|
.docLibIcon--scenario::before {
|
|
content: "◈";
|
|
color: var(--doc-accent);
|
|
}
|
|
|
|
.docLibIcon--zones::before {
|
|
content: "⚠";
|
|
color: var(--doc-accent);
|
|
}
|
|
|
|
/* ---- CANVAS ---- */
|
|
.docCanvasArea {
|
|
flex: 1;
|
|
background: var(--doc-bg);
|
|
padding: 36px 40px;
|
|
overflow: auto;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
position: relative;
|
|
}
|
|
|
|
.docPage {
|
|
width: 760px;
|
|
max-width: 100%;
|
|
height: fit-content;
|
|
background: var(--doc-card);
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 16px;
|
|
box-shadow: 0 1px 3px rgb(0 0 0 / 30%);
|
|
padding: 48px 52px;
|
|
position: relative;
|
|
transform-origin: top center;
|
|
transition: width 0.2s ease;
|
|
}
|
|
|
|
/* Enfant qui reçoit le HTML rendu (voir #docPage vs #docPageContent dans
|
|
document-editor.js — le rafraîchissement du canevas ne remplace QUE ce
|
|
contenu, jamais .docPage elle-même, pour que .docSnapGridOverlay,
|
|
sa voisine, survive à chaque rafraîchissement). */
|
|
.docPageContent {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 18px;
|
|
}
|
|
|
|
.docPage.docPage--fixedWidth {
|
|
width: var(--doc-preview-width, 760px);
|
|
padding: 28px 20px;
|
|
}
|
|
|
|
.docRow {
|
|
position: relative;
|
|
}
|
|
|
|
.docRow > [data-element-id] {
|
|
flex: 1 1 220px;
|
|
min-width: 0;
|
|
}
|
|
|
|
[data-element-id] {
|
|
position: relative;
|
|
border-radius: 8px;
|
|
padding: 6px;
|
|
margin: -6px;
|
|
cursor: pointer;
|
|
transition: background 0.1s ease;
|
|
}
|
|
|
|
[data-kind="row"] > [data-element-id] {
|
|
margin: 0;
|
|
}
|
|
|
|
[data-element-id]:hover {
|
|
background: rgb(255 95 46 / 6%);
|
|
}
|
|
|
|
[data-element-id].docSelected {
|
|
background: rgb(255 95 46 / 10%);
|
|
outline: 1px dashed var(--doc-accent);
|
|
}
|
|
|
|
[data-element-id].docDropTarget--before {
|
|
box-shadow: inset 0 3px 0 0 var(--doc-accent);
|
|
}
|
|
|
|
[data-element-id].docDropTarget--after {
|
|
box-shadow: inset 0 -3px 0 0 var(--doc-accent);
|
|
}
|
|
|
|
[data-element-id].docDropTarget--mergeLeft {
|
|
box-shadow: inset 3px 0 0 0 var(--doc-accent);
|
|
}
|
|
|
|
[data-element-id].docDropTarget--mergeRight {
|
|
box-shadow: inset -3px 0 0 0 var(--doc-accent);
|
|
}
|
|
|
|
.docShape[data-element-id] {
|
|
padding: 0;
|
|
margin: 0;
|
|
border-radius: 0;
|
|
}
|
|
|
|
.docShape[data-element-id].docSelected {
|
|
outline: 2px solid var(--doc-accent);
|
|
background: transparent;
|
|
}
|
|
|
|
.docText {
|
|
outline: none;
|
|
}
|
|
|
|
.docImage.docImagePlaceholder {
|
|
width: 100%;
|
|
height: 190px;
|
|
border-radius: 10px;
|
|
background: var(--doc-bg-2);
|
|
border: 1px dashed var(--doc-border);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
color: var(--doc-muted);
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
img.docImage {
|
|
max-width: 100%;
|
|
border-radius: 10px;
|
|
display: block;
|
|
}
|
|
|
|
.docButton {
|
|
border: none;
|
|
border-radius: 999px;
|
|
padding: 10px 22px;
|
|
font-weight: 700;
|
|
font-size: 14px;
|
|
background: var(--doc-accent);
|
|
color: #14100c;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.docMinigame {
|
|
border: 1px solid var(--doc-accent);
|
|
border-radius: 12px;
|
|
background: rgb(255 95 46 / 8%);
|
|
}
|
|
|
|
.docMinigameBadge {
|
|
padding: 18px 20px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
}
|
|
|
|
.docMinigameLabel {
|
|
font-size: 15px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.docMinigamePlaceholder {
|
|
font-size: 12.5px;
|
|
color: var(--doc-muted);
|
|
}
|
|
|
|
/* ---- Quiz/Association/Memory/Mots mêlés/Scénario : mini-jeux
|
|
interactifs en Mode Aperçu (voir docs/plan/maquettes/
|
|
document-formation-web.html — référence visuelle du Quiz). Masqués en
|
|
édition (seul .docMinigameBadge y est visible, voir plus haut) ;
|
|
jamais les deux visibles en même temps. ---- */
|
|
.docQuizPlayer,
|
|
.docAssocPlayer,
|
|
.docMemoryPlayer,
|
|
.docMotsPlayer,
|
|
.docScenarioPlayer {
|
|
display: none;
|
|
}
|
|
|
|
.docEditor3.docEditor3--preview [data-kind="quiz"] .docMinigameBadge,
|
|
.docEditor3.docEditor3--preview [data-kind="association"] .docMinigameBadge,
|
|
.docEditor3.docEditor3--preview [data-kind="memory"] .docMinigameBadge,
|
|
.docEditor3.docEditor3--preview [data-kind="mots"] .docMinigameBadge,
|
|
.docEditor3.docEditor3--preview [data-kind="scenario"] .docMinigameBadge {
|
|
display: none;
|
|
}
|
|
|
|
.docEditor3.docEditor3--preview [data-kind="quiz"] .docQuizPlayer,
|
|
.docEditor3.docEditor3--preview [data-kind="association"] .docAssocPlayer,
|
|
.docEditor3.docEditor3--preview [data-kind="memory"] .docMemoryPlayer,
|
|
.docEditor3.docEditor3--preview [data-kind="mots"] .docMotsPlayer,
|
|
.docEditor3.docEditor3--preview [data-kind="scenario"] .docScenarioPlayer {
|
|
display: block;
|
|
}
|
|
|
|
.docEditor3.docEditor3--preview [data-kind="quiz"],
|
|
.docEditor3.docEditor3--preview [data-kind="association"],
|
|
.docEditor3.docEditor3--preview [data-kind="memory"],
|
|
.docEditor3.docEditor3--preview [data-kind="mots"],
|
|
.docEditor3.docEditor3--preview [data-kind="scenario"] {
|
|
border: none;
|
|
background: transparent;
|
|
}
|
|
|
|
.docEditor3.docEditor3--preview .docQuizPlayer,
|
|
.docEditor3.docEditor3--preview .docQuizPlayer *,
|
|
.docEditor3.docEditor3--preview .docAssocPlayer,
|
|
.docEditor3.docEditor3--preview .docAssocPlayer *,
|
|
.docEditor3.docEditor3--preview .docMemoryPlayer,
|
|
.docEditor3.docEditor3--preview .docMemoryPlayer *,
|
|
.docEditor3.docEditor3--preview .docMotsPlayer,
|
|
.docEditor3.docEditor3--preview .docMotsPlayer *,
|
|
.docEditor3.docEditor3--preview .docScenarioPlayer,
|
|
.docEditor3.docEditor3--preview .docScenarioPlayer * {
|
|
/* .docEditor3--preview désactive pointer-events sur tout [data-element-id]
|
|
(voir plus bas) pour empêcher la sélection/le déplacement pendant
|
|
l'Aperçu — réactivé ici spécifiquement pour que ces mini-jeux restent
|
|
RÉELLEMENT cliquables/glissables. */
|
|
pointer-events: auto;
|
|
}
|
|
|
|
.docQuizCard,
|
|
.docQuizResultCard {
|
|
background: linear-gradient(180deg, var(--doc-card), var(--doc-bg-2));
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 16px;
|
|
padding: 1.8rem;
|
|
}
|
|
|
|
.docQuizResultCard {
|
|
text-align: center;
|
|
}
|
|
|
|
.docQuizKicker {
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.04em;
|
|
color: var(--doc-accent);
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.docQuizPlayerTitle {
|
|
font-size: 1.15rem;
|
|
font-weight: 700;
|
|
margin: 0 0 20px;
|
|
}
|
|
|
|
.docQuizProgressTrack {
|
|
height: 4px;
|
|
background: var(--doc-border);
|
|
border-radius: 2px;
|
|
overflow: hidden;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.docQuizProgressFill {
|
|
height: 100%;
|
|
width: 0%;
|
|
background: linear-gradient(120deg, var(--doc-accent), var(--doc-accent-2));
|
|
transition: width 0.2s ease;
|
|
}
|
|
|
|
.docQuizMeta {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
font-size: 13px;
|
|
color: var(--doc-muted);
|
|
font-weight: 600;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.docQuizQuestionText {
|
|
font-size: 17px;
|
|
font-weight: 700;
|
|
line-height: 1.3;
|
|
margin: 0 0 18px;
|
|
}
|
|
|
|
.docQuizOptions {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
|
|
.docQuizOption {
|
|
text-align: left;
|
|
padding: 13px 15px;
|
|
border: 1px solid var(--doc-border);
|
|
background: var(--doc-bg-2);
|
|
color: var(--doc-text);
|
|
border-radius: 10px;
|
|
font-size: 15px;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
transition: border-color 0.15s ease;
|
|
}
|
|
|
|
.docQuizOption:hover:not(:disabled) {
|
|
border-color: var(--doc-accent);
|
|
}
|
|
|
|
.docQuizOptionLetter {
|
|
flex-shrink: 0;
|
|
width: 24px;
|
|
height: 24px;
|
|
border-radius: 50%;
|
|
border: 1px solid var(--doc-border);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-weight: 700;
|
|
font-size: 12.5px;
|
|
color: var(--doc-muted);
|
|
}
|
|
|
|
.docQuizOption.is-correct {
|
|
border-color: var(--doc-quiz-success-border);
|
|
background: var(--doc-quiz-success-bg);
|
|
}
|
|
|
|
.docQuizOption.is-correct .docQuizOptionLetter {
|
|
border-color: var(--doc-quiz-success-border);
|
|
background: var(--doc-quiz-success-border);
|
|
color: #0b0e14;
|
|
}
|
|
|
|
.docQuizOption.is-wrong {
|
|
border-color: var(--doc-quiz-danger-border);
|
|
background: var(--doc-quiz-danger-bg);
|
|
}
|
|
|
|
.docQuizOption.is-wrong .docQuizOptionLetter {
|
|
border-color: var(--doc-accent);
|
|
background: var(--doc-accent);
|
|
color: #0b0e14;
|
|
}
|
|
|
|
.docQuizOption:disabled:not(.is-correct, .is-wrong) {
|
|
opacity: 0.45;
|
|
}
|
|
|
|
.docQuizFeedback {
|
|
display: none;
|
|
margin-top: 16px;
|
|
padding: 13px 15px;
|
|
border-radius: 10px;
|
|
font-size: 14px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.docQuizFeedback.is-visible {
|
|
display: block;
|
|
}
|
|
|
|
.docQuizFeedback.is-ok {
|
|
background: var(--doc-quiz-success-bg);
|
|
color: var(--doc-quiz-success-text);
|
|
border: 1px solid var(--doc-quiz-success-border);
|
|
}
|
|
|
|
.docQuizFeedback.is-ko {
|
|
background: var(--doc-quiz-danger-bg);
|
|
color: var(--doc-quiz-danger-text);
|
|
border: 1px solid var(--doc-quiz-danger-border);
|
|
}
|
|
|
|
.docQuizNextBar {
|
|
margin-top: 20px;
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.docQuizNextBtn {
|
|
padding: 0.75rem 1.6rem;
|
|
border-radius: 10px;
|
|
border: none;
|
|
background: linear-gradient(120deg, var(--doc-accent), var(--doc-accent-2));
|
|
color: #14100c;
|
|
font-weight: 700;
|
|
font-size: 14px;
|
|
cursor: pointer;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
transition: opacity 0.2s ease;
|
|
}
|
|
|
|
.docQuizNextBtn.is-visible {
|
|
opacity: 1;
|
|
pointer-events: auto;
|
|
}
|
|
|
|
.docQuizResultBig {
|
|
font-weight: 800;
|
|
font-size: 44px;
|
|
background: linear-gradient(120deg, var(--doc-accent), var(--doc-accent-2));
|
|
background-clip: text;
|
|
color: transparent;
|
|
line-height: 1;
|
|
}
|
|
|
|
.docQuizResultSub {
|
|
margin-top: 6px;
|
|
margin-bottom: 18px;
|
|
font-size: 13px;
|
|
color: var(--doc-muted);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.docQuizRestartBtn {
|
|
padding: 0.7rem 1.5rem;
|
|
border-radius: 10px;
|
|
background: transparent;
|
|
border: 1px solid var(--doc-border);
|
|
color: var(--doc-text);
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
/* ---- Association : plateau de glisser-déposer (voir docs/plan/PLAN.md,
|
|
"relier visuellement deux éléments qui vont ensemble"). Réutilise
|
|
.docQuizFeedback/.docQuizKicker (même rôle visuel) plutôt que de
|
|
dupliquer ces règles. ---- */
|
|
.docAssocCard {
|
|
background: linear-gradient(180deg, var(--doc-card), var(--doc-bg-2));
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 16px;
|
|
padding: 1.8rem;
|
|
}
|
|
|
|
.docAssocTitle {
|
|
font-size: 1.15rem;
|
|
font-weight: 700;
|
|
margin: 0 0 14px;
|
|
}
|
|
|
|
.docAssocMeta {
|
|
font-size: 13px;
|
|
color: var(--doc-muted);
|
|
font-weight: 600;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.docAssocBoard {
|
|
display: flex;
|
|
gap: 24px;
|
|
}
|
|
|
|
.docAssocColumn {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
}
|
|
|
|
.docAssocItem,
|
|
.docAssocSlot {
|
|
padding: 13px 15px;
|
|
border: 1px solid var(--doc-border);
|
|
background: var(--doc-bg-2);
|
|
color: var(--doc-text);
|
|
border-radius: 10px;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
transition: border-color 0.15s ease, opacity 0.15s ease;
|
|
}
|
|
|
|
.docAssocItem {
|
|
cursor: grab;
|
|
}
|
|
|
|
.docAssocItem:hover,
|
|
.docAssocSlot:hover {
|
|
border-color: var(--doc-accent);
|
|
}
|
|
|
|
.docAssocItem.is-selected {
|
|
border-color: var(--doc-accent);
|
|
box-shadow: var(--doc-glow);
|
|
}
|
|
|
|
.docAssocItem.is-matched,
|
|
.docAssocSlot.is-matched {
|
|
border-color: var(--doc-quiz-success-border);
|
|
background: var(--doc-quiz-success-bg);
|
|
color: var(--doc-quiz-success-text);
|
|
cursor: default;
|
|
opacity: 0.85;
|
|
}
|
|
|
|
.docAssocItem.is-wrong,
|
|
.docAssocSlot.is-wrong {
|
|
border-color: var(--doc-quiz-danger-border);
|
|
background: var(--doc-quiz-danger-bg);
|
|
}
|
|
|
|
/* ---- Barre de bouton "Recommencer" partagée par tous les mini-jeux SAUF
|
|
le Quiz (qui garde son propre écran de résultat séparé) : le plateau de
|
|
jeu reste affiché en permanence une fois terminé, seul ce bouton
|
|
apparaît/disparaît (voir retour utilisateur du 20/09/2026). ---- */
|
|
.docMinigameRestartBar {
|
|
display: none;
|
|
margin-top: 20px;
|
|
text-align: center;
|
|
}
|
|
|
|
.docMinigameRestartBar.is-visible {
|
|
display: block;
|
|
}
|
|
|
|
.docMinigameRestartBar button {
|
|
padding: 0.7rem 1.5rem;
|
|
border-radius: 10px;
|
|
background: transparent;
|
|
border: 1px solid var(--doc-border);
|
|
color: var(--doc-text);
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
/* ---- Association : panneau Propriétés — paires reliées visuellement par
|
|
un glyphe entre les deux champs (voir forgeDocRenderAssociationPairHtml,
|
|
static/document/js/document-editor.js). Empilement VERTICAL (pas côte à
|
|
côte) : la colonne du panneau Propriétés est trop étroite pour deux
|
|
champs de texte libre côte à côte, voir retour utilisateur du
|
|
20/09/2026. ---- */
|
|
.docAssocPairRow {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 10px;
|
|
padding: 12px;
|
|
margin-bottom: 10px;
|
|
background: var(--doc-card);
|
|
}
|
|
|
|
.docAssocPairInput {
|
|
width: 100%;
|
|
resize: vertical;
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 8px;
|
|
padding: 8px 10px;
|
|
font-size: 13px;
|
|
font-family: inherit;
|
|
color: var(--doc-text);
|
|
background: var(--doc-bg-2);
|
|
}
|
|
|
|
.docAssocPairLink {
|
|
align-self: center;
|
|
color: var(--doc-accent);
|
|
font-weight: 700;
|
|
font-size: 15px;
|
|
}
|
|
|
|
/* ---- Memory : panneau Propriétés — étiquette au-dessus de chaque champ
|
|
recto/verso (voir forgeDocRenderMemoryCardHtml). Réutilise
|
|
.docAssocPairRow/.docAssocPairInput (même carte, mêmes champs) plutôt
|
|
que de dupliquer leurs règles. ---- */
|
|
.docMemoryFaceLabel {
|
|
font-size: 11.5px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.03em;
|
|
color: var(--doc-muted);
|
|
text-transform: uppercase;
|
|
margin-top: 4px;
|
|
}
|
|
|
|
/* ---- Memory : plateau de jeu (voir docs/plan/PLAN.md, "retourner des
|
|
cartes pour constituer des paires d'images identiques"). Réutilise
|
|
.docAssocTitle/.docAssocMeta/.docMinigameRestartBar (mêmes rôles
|
|
visuels, voir forgeDocRenderMemoryProps ci-dessus). ---- */
|
|
.docMemoryCardWrap {
|
|
background: linear-gradient(180deg, var(--doc-card), var(--doc-bg-2));
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 16px;
|
|
padding: 1.8rem;
|
|
}
|
|
|
|
.docMemoryGrid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
|
|
gap: 12px;
|
|
}
|
|
|
|
.docMemoryCard {
|
|
aspect-ratio: 3 / 4;
|
|
perspective: 700px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.docMemoryCardInner {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 100%;
|
|
transition: transform 0.4s ease;
|
|
transform-style: preserve-3d;
|
|
}
|
|
|
|
.docMemoryCard.is-flipped .docMemoryCardInner {
|
|
transform: rotateY(180deg);
|
|
}
|
|
|
|
.docMemoryCard.is-matched {
|
|
/* Une paire trouvée reste face retournée en PERMANENCE (voir
|
|
forgeDocMemoryFlip, static/document/js/document-editor.js — la
|
|
classe is-flipped n'est jamais retirée pour une carte appariée).
|
|
Pas d'opacité réduite ici : une carte à 55% d'opacité, à côté de
|
|
cartes face cachée pleinement opaques, se lisait comme "repartie
|
|
face cachée" (retour utilisateur du 20/09/2026) alors qu'elle
|
|
restait techniquement retournée — la bordure verte ci-dessous
|
|
suffit à signaler l'état "trouvée", sans jamais assombrir le
|
|
contenu révélé. */
|
|
cursor: default;
|
|
}
|
|
|
|
.docMemoryCardFace {
|
|
position: absolute;
|
|
inset: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 4px;
|
|
padding: 6px;
|
|
border-radius: 10px;
|
|
border: 1px solid var(--doc-border);
|
|
background: var(--doc-bg-2);
|
|
backface-visibility: hidden;
|
|
overflow: hidden;
|
|
text-align: center;
|
|
font-size: 12px;
|
|
color: var(--doc-text);
|
|
}
|
|
|
|
.docMemoryCardFace img {
|
|
max-width: 100%;
|
|
max-height: 60%;
|
|
object-fit: contain;
|
|
}
|
|
|
|
.docMemoryCardFront {
|
|
background: linear-gradient(135deg, var(--doc-accent), var(--doc-accent-2));
|
|
color: #14100c;
|
|
}
|
|
|
|
.docMemoryCardBackGlyph {
|
|
font-size: 26px;
|
|
font-weight: 800;
|
|
}
|
|
|
|
.docMemoryCardBack {
|
|
transform: rotateY(180deg);
|
|
border-color: var(--doc-accent);
|
|
}
|
|
|
|
.docMemoryCard.is-matched .docMemoryCardFace.docMemoryCardBack {
|
|
border-color: var(--doc-quiz-success-border);
|
|
background: var(--doc-quiz-success-bg);
|
|
color: var(--doc-quiz-success-text);
|
|
}
|
|
|
|
/* ---- Mots mêlés : grille interactive (voir docs/plan/PLAN.md, "mots
|
|
cachés horizontalement/verticalement/en diagonale"). Réutilise
|
|
.docAssocCard/.docAssocTitle/.docAssocMeta/.docMinigameRestartBar
|
|
(mêmes rôles visuels que l'Association/Memory ci-dessus). ---- */
|
|
.docMotsBoard {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 24px;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.docMotsGrid {
|
|
display: grid;
|
|
gap: 3px;
|
|
width: min(100%, 420px);
|
|
user-select: none;
|
|
}
|
|
|
|
.docMotsCell {
|
|
aspect-ratio: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border: 1px solid var(--doc-border);
|
|
background: var(--doc-bg-2);
|
|
color: var(--doc-text);
|
|
font-weight: 700;
|
|
font-size: 13px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.docMotsCell.is-selecting {
|
|
border-color: var(--doc-accent);
|
|
background: rgb(255 95 46 / 18%);
|
|
}
|
|
|
|
.docMotsCell.is-found {
|
|
border-color: var(--doc-quiz-success-border);
|
|
background: var(--doc-quiz-success-bg);
|
|
color: var(--doc-quiz-success-text);
|
|
cursor: default;
|
|
}
|
|
|
|
.docMotsWordList {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
min-width: 140px;
|
|
}
|
|
|
|
.docMotsWordChip {
|
|
padding: 8px 12px;
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 8px;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--doc-text);
|
|
background: var(--doc-bg-2);
|
|
}
|
|
|
|
.docMotsWordChip.is-found {
|
|
border-color: var(--doc-quiz-success-border);
|
|
background: var(--doc-quiz-success-bg);
|
|
color: var(--doc-quiz-success-text);
|
|
text-decoration: line-through;
|
|
}
|
|
|
|
.docUnknown {
|
|
font-size: 12.5px;
|
|
color: var(--doc-muted);
|
|
border: 1px dashed var(--doc-border);
|
|
border-radius: 8px;
|
|
padding: 10px;
|
|
}
|
|
|
|
/* ---- grille d'accroche (formes libres) — voir docs/plan/PLAN.md,
|
|
"grille d'accroche réelle" : visible en overlay pendant le déplacement
|
|
d'une forme (voir .docPage--dragging-shape ci-dessous), jamais
|
|
décorative. */
|
|
.docSnapGridOverlay {
|
|
position: absolute;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
z-index: 1;
|
|
opacity: 0;
|
|
transition: opacity 0.1s ease;
|
|
background-image:
|
|
linear-gradient(to right, rgb(255 95 46 / 12%) 1px, transparent 1px),
|
|
linear-gradient(to bottom, rgb(255 95 46 / 12%) 1px, transparent 1px);
|
|
background-size: var(--doc-snap-size, 8px) var(--doc-snap-size, 8px);
|
|
}
|
|
|
|
.docPage.docPage--dragging-shape .docSnapGridOverlay {
|
|
opacity: 1;
|
|
}
|
|
|
|
.docZoomPill {
|
|
position: absolute;
|
|
bottom: 24px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
background: var(--doc-card);
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 999px;
|
|
height: 36px;
|
|
padding: 0 6px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
box-shadow: 0 2px 6px rgb(0 0 0 / 30%);
|
|
}
|
|
|
|
.docZoomPill button {
|
|
width: 26px;
|
|
height: 26px;
|
|
border-radius: 999px;
|
|
border: none;
|
|
background: transparent;
|
|
color: var(--doc-text);
|
|
cursor: pointer;
|
|
font-size: 15px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.docZoomPill button:hover {
|
|
background: var(--doc-bg-2);
|
|
}
|
|
|
|
.docZoomPill span {
|
|
font-size: 12.5px;
|
|
font-weight: 700;
|
|
color: var(--doc-muted);
|
|
width: 40px;
|
|
text-align: center;
|
|
}
|
|
|
|
/* ---- Pilule de navigation entre PAGES en Mode Aperçu (retour
|
|
utilisateur du 21/09/2026 : "il faut implémenter un système de page",
|
|
navigation "page par page avec Suivant/Précédent"). Même position que
|
|
.docZoomPill ci-dessus (jamais affichées en même temps : docZoomPill
|
|
est masquée en Aperçu, voir plus bas) — .is-visible posée par
|
|
forgeDocUpdatePageNavPill (static/document/js/document-editor.js)
|
|
uniquement en Mode Aperçu ET s'il y a plus d'une page, jamais un
|
|
sélecteur CSS seul (une pilule "Page 1 / 1" n'aurait aucun sens). ---- */
|
|
.docPageNavPill {
|
|
display: none;
|
|
position: absolute;
|
|
bottom: 24px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
background: var(--doc-card);
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 999px;
|
|
height: 36px;
|
|
padding: 0 10px;
|
|
align-items: center;
|
|
gap: 10px;
|
|
box-shadow: 0 2px 6px rgb(0 0 0 / 30%);
|
|
}
|
|
|
|
.docPageNavPill.is-visible {
|
|
display: flex;
|
|
}
|
|
|
|
.docPageNavPill button {
|
|
width: 26px;
|
|
height: 26px;
|
|
border-radius: 999px;
|
|
border: none;
|
|
background: transparent;
|
|
color: var(--doc-text);
|
|
cursor: pointer;
|
|
font-size: 16px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.docPageNavPill button:hover:not(:disabled) {
|
|
background: var(--doc-bg-2);
|
|
}
|
|
|
|
.docPageNavPill button:disabled {
|
|
opacity: 0.35;
|
|
cursor: default;
|
|
}
|
|
|
|
.docPageNavPill span {
|
|
font-size: 12.5px;
|
|
font-weight: 700;
|
|
color: var(--doc-muted);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* ---- Bande de VIGNETTES MINIATURES des pages, au-dessus de la page
|
|
ET centrée dans le conteneur du milieu (.docCanvasArea, jamais toute
|
|
la largeur de l'éditeur par-dessus les panneaux latéraux — retour
|
|
utilisateur du 21/09/2026 : "je préfère avoir des miniatures ...
|
|
centré dans le conteneur du milieu"). Chaque vignette affiche un
|
|
VRAI rendu réduit du contenu de sa page (voir .docPageThumbInner plus
|
|
bas, et rendered_pages côté serveur dans routes/document/
|
|
document_edit.py) — jamais une carte générique numéro/titre. Masquée
|
|
en Mode Aperçu (voir la règle .docEditor3--preview plus bas), qui a
|
|
sa propre navigation via .docPageNavPill ci-dessus. ---- */
|
|
.docPageThumbs {
|
|
width: 100%;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: flex-end;
|
|
flex-wrap: wrap;
|
|
gap: 16px;
|
|
padding: 4px 20px 28px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.docPageThumbCard {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 6px;
|
|
flex-shrink: 0;
|
|
border: none;
|
|
background: none;
|
|
padding: 0;
|
|
cursor: pointer;
|
|
font-family: inherit;
|
|
}
|
|
|
|
.docPageThumbCard.is-dragging {
|
|
opacity: 0.4;
|
|
}
|
|
|
|
.docPageThumbCard:focus-visible {
|
|
outline: 2px solid var(--doc-accent);
|
|
outline-offset: 2px;
|
|
border-radius: 10px;
|
|
}
|
|
|
|
.docPageThumbFrame {
|
|
/* <span> (jamais <div>, invalide comme enfant direct d'un <button> —
|
|
voir templates/document/document_edit.html) : display:block requis
|
|
pour que width/aspect-ratio s'appliquent normalement. */
|
|
display: block;
|
|
position: relative;
|
|
width: 132px;
|
|
aspect-ratio: 4 / 3;
|
|
border: 2px solid var(--doc-border);
|
|
border-radius: 10px;
|
|
overflow: hidden;
|
|
background: var(--doc-card);
|
|
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
|
|
transition: border-color 0.15s ease, transform 0.15s ease;
|
|
}
|
|
|
|
.docPageThumbCard:hover .docPageThumbFrame {
|
|
border-color: var(--doc-accent);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.docPageThumbCard.is-active .docPageThumbFrame {
|
|
border-color: var(--doc-accent);
|
|
box-shadow: 0 0 0 2px var(--doc-accent);
|
|
}
|
|
|
|
/* Astuce "scale inverse" pour une vraie vignette : le contenu est rendu
|
|
à sa largeur RÉELLE (voir .docPage, 760px) puis réduit visuellement —
|
|
jamais un second moteur de rendu séparé pour les miniatures, c'est le
|
|
MÊME rendu que le canevas principal, juste redimensionné en CSS. Le
|
|
cadre (.docPageThumbFrame) le rogne verticalement s'il dépasse — un
|
|
support de formation "page" est proche d'un format diapositive, ce
|
|
recadrage reste représentatif. */
|
|
.docPageThumbInner {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 760px;
|
|
transform: scale(0.1737);
|
|
transform-origin: top left;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.docPageThumbDelete {
|
|
position: absolute;
|
|
top: 4px;
|
|
right: 4px;
|
|
display: none;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 18px;
|
|
height: 18px;
|
|
border-radius: 50%;
|
|
border: none;
|
|
background: rgb(11 14 20 / 65%);
|
|
color: #fff;
|
|
font-size: 10px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.docPageThumbCard:hover .docPageThumbDelete {
|
|
display: flex;
|
|
}
|
|
|
|
.docPageThumbDelete:hover {
|
|
background: var(--doc-quiz-danger-border);
|
|
}
|
|
|
|
.docPageThumbs.docPageTabs--single .docPageThumbDelete {
|
|
display: none;
|
|
}
|
|
|
|
.docPageThumbLabel {
|
|
max-width: 132px;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
color: var(--doc-muted);
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.docPageThumbCard.is-active .docPageThumbLabel {
|
|
color: var(--doc-text);
|
|
}
|
|
|
|
.docPageThumbLabel[contenteditable="true"] {
|
|
outline: none;
|
|
border-bottom: 1px solid var(--doc-accent);
|
|
cursor: text;
|
|
user-select: text;
|
|
color: var(--doc-text);
|
|
}
|
|
|
|
.docPageThumbAddCard .docPageThumbFrame {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-style: dashed;
|
|
color: var(--doc-muted);
|
|
font-size: 24px;
|
|
font-weight: 700;
|
|
box-shadow: none;
|
|
}
|
|
|
|
.docPageThumbAddCard:hover .docPageThumbFrame {
|
|
color: var(--doc-text);
|
|
}
|
|
|
|
/* ---- RIGHT SIDEBAR : Propriétés ---- */
|
|
.docField {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
margin-bottom: 14px;
|
|
}
|
|
|
|
.docFieldRow {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 14px;
|
|
}
|
|
|
|
.docField label,
|
|
.docFieldRow .docFieldLabel {
|
|
font-size: 13px;
|
|
color: var(--doc-muted);
|
|
}
|
|
|
|
.docField input,
|
|
.docField textarea,
|
|
.docField select {
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 8px;
|
|
padding: 8px 10px;
|
|
font-size: 13px;
|
|
color: var(--doc-text);
|
|
background: var(--doc-card);
|
|
font-family: inherit;
|
|
width: 100%;
|
|
resize: vertical;
|
|
}
|
|
|
|
.docSwatches {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
|
|
.docSwatch {
|
|
width: 22px;
|
|
height: 22px;
|
|
border-radius: 50%;
|
|
border: 2px solid transparent;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.docSwatch.is-active {
|
|
border-color: var(--doc-text);
|
|
}
|
|
|
|
.docSegmented {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 6px;
|
|
}
|
|
|
|
.docSegBtn {
|
|
padding: 6px 10px;
|
|
border-radius: 8px;
|
|
border: 1px solid var(--doc-border);
|
|
background: var(--doc-card);
|
|
font-size: 12.5px;
|
|
font-weight: 600;
|
|
color: var(--doc-text);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.docSegBtn.is-active {
|
|
background: rgb(255 95 46 / 12%);
|
|
color: var(--doc-accent);
|
|
border-color: var(--doc-accent);
|
|
}
|
|
|
|
.docToolbarRow {
|
|
display: flex;
|
|
gap: 6px;
|
|
margin-bottom: 14px;
|
|
}
|
|
|
|
.docFmtBtn {
|
|
width: 32px;
|
|
height: 32px;
|
|
border-radius: 8px;
|
|
border: 1px solid var(--doc-border);
|
|
background: var(--doc-card);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
font-weight: 800;
|
|
color: var(--doc-text);
|
|
}
|
|
|
|
.docFmtBtn.is-active {
|
|
background: rgb(255 95 46 / 12%);
|
|
border-color: var(--doc-accent);
|
|
color: var(--doc-accent);
|
|
}
|
|
|
|
.docFmtBtn.docFmtBtn--italic {
|
|
font-style: italic;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.docFmtBtn.docFmtBtn--underline {
|
|
text-decoration: underline;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.docDeleteBtn {
|
|
width: 100%;
|
|
height: 38px;
|
|
border-radius: 10px;
|
|
border: 1px solid var(--status-danger, #f04438);
|
|
background: transparent;
|
|
color: var(--status-danger, #f04438);
|
|
font-weight: 700;
|
|
cursor: pointer;
|
|
margin-top: 6px;
|
|
}
|
|
|
|
.docDeleteBtn:hover {
|
|
background: rgb(240 68 56 / 10%);
|
|
}
|
|
|
|
/* ---- Quiz : liste de questions répétable, chacune avec sa propre liste
|
|
de choix répétable (voir forgeDocRenderQuizProps, static/document/js/
|
|
document-editor.js) ---- */
|
|
.docQuizQuestionsLabel {
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.docQuizQuestion {
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 10px;
|
|
padding: 12px;
|
|
margin-bottom: 10px;
|
|
background: var(--doc-card);
|
|
}
|
|
|
|
.docQuizQuestionHeader {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.docQuizRemoveBtn {
|
|
border: none;
|
|
background: transparent;
|
|
color: var(--doc-muted);
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.docQuizRemoveBtn:hover {
|
|
color: var(--status-danger, #f04438);
|
|
}
|
|
|
|
.docQuizChoicesList {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
margin: 8px 0;
|
|
}
|
|
|
|
.docQuizChoiceRow {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.docQuizChoiceRow input[type="text"] {
|
|
flex: 1;
|
|
}
|
|
|
|
/* ---- Scénario : panneau Propriétés — chaque choix garde sa case "bon
|
|
choix"/son texte (.docQuizChoiceRow, réutilisé tel quel) ET, en plus,
|
|
sa conséquence en dessous (voir forgeDocRenderScenarioChoiceHtml,
|
|
static/document/js/document-editor.js) — un bloc de plus que le Quiz,
|
|
d'où ce conteneur dédié. ---- */
|
|
.docScenarioChoiceBlock {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
}
|
|
|
|
.docQuizAddBtn {
|
|
width: 100%;
|
|
justify-content: center;
|
|
margin-top: 4px;
|
|
}
|
|
|
|
.docEmptyState {
|
|
font-size: 13px;
|
|
color: var(--doc-muted);
|
|
text-align: center;
|
|
padding: 30px 10px;
|
|
}
|
|
|
|
/* ===== BOTTOM NAV (mobile) ===== */
|
|
.docBottomNav {
|
|
display: none;
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 58px;
|
|
background: var(--doc-bg-2);
|
|
border-top: 1px solid var(--doc-border);
|
|
z-index: 40;
|
|
align-items: stretch;
|
|
justify-content: space-around;
|
|
}
|
|
|
|
.docBottomNav button {
|
|
flex: 1;
|
|
background: transparent;
|
|
border: none;
|
|
color: var(--doc-muted);
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.docBottomNav button.is-active {
|
|
color: var(--doc-accent);
|
|
}
|
|
|
|
.docSidebarClose {
|
|
display: none;
|
|
}
|
|
|
|
/* ===== MODE APERÇU : masque tout le chrome d'édition ===== */
|
|
.docEditor3.docEditor3--preview .docSidebarLeft,
|
|
.docEditor3.docEditor3--preview .docSidebarRight,
|
|
.docEditor3.docEditor3--preview .docBottomNav,
|
|
.docEditor3.docEditor3--preview .docZoomPill,
|
|
.docEditor3.docEditor3--preview .docPageThumbs {
|
|
display: none;
|
|
}
|
|
|
|
.docEditor3.docEditor3--preview [data-element-id] {
|
|
cursor: default;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.docEditor3.docEditor3--preview [data-element-id]:hover,
|
|
.docEditor3.docEditor3--preview [data-element-id].docSelected {
|
|
background: transparent;
|
|
outline: none;
|
|
}
|
|
|
|
.docEditor3.docEditor3--preview .docCanvasArea {
|
|
padding: 40px;
|
|
}
|
|
|
|
/* ---- Modale générique (utilisée aujourd'hui par l'éditeur d'arbre du
|
|
mini-jeu Scénario, voir forgeDocOpenScenarioTreeModal) — overlay +
|
|
boîte de dialogue centrée, indépendante de tout framework externe
|
|
(aucune dépendance partagée avec game_engine, voir CLAUDE.md). ---- */
|
|
.docModalBackdrop {
|
|
display: none;
|
|
position: fixed;
|
|
inset: 0;
|
|
background: rgb(11 14 20 / 65%);
|
|
z-index: 200;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 24px;
|
|
color: var(--doc-text);
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
}
|
|
|
|
.docModalBackdrop.is-open {
|
|
display: flex;
|
|
}
|
|
|
|
.docModalDialog {
|
|
background: var(--doc-bg-2);
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 16px;
|
|
width: min(640px, 100%);
|
|
max-height: min(80vh, 720px);
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.docModalHeader {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 16px 20px;
|
|
border-bottom: 1px solid var(--doc-border);
|
|
}
|
|
|
|
.docModalTitle {
|
|
font-size: 15px;
|
|
font-weight: 700;
|
|
color: var(--doc-text);
|
|
}
|
|
|
|
.docModalCloseBtn {
|
|
border: none;
|
|
background: transparent;
|
|
color: var(--doc-muted);
|
|
font-size: 16px;
|
|
cursor: pointer;
|
|
padding: 4px;
|
|
}
|
|
|
|
.docModalCloseBtn:hover {
|
|
color: var(--doc-text);
|
|
}
|
|
|
|
.docModalBody {
|
|
padding: 20px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
/* ---- Scénario : panneau Propriétés — une carte par scénario (titre +
|
|
nombre de nœuds + bouton d'ouverture de la modale), l'édition de
|
|
l'arbre lui-même se fait entièrement dans la modale (voir
|
|
forgeDocRenderScenarioTreeModal). ---- */
|
|
.docScenarioCard .docBtnSecondary {
|
|
width: 100%;
|
|
justify-content: center;
|
|
}
|
|
|
|
/* ---- Scénario : éditeur d'arbre en GRAPHE VISUEL (retour utilisateur
|
|
du 21/09/2026 : "passons à un véritable graphe visuel", remplace la
|
|
première version en liste de nœuds + menus déroulants). Dialogue bien
|
|
plus grand que la modale générique par défaut : un graphe a besoin de
|
|
place pour être lisible. ---- */
|
|
#docScenarioTreeModal .docModalDialog {
|
|
width: min(1180px, 95vw);
|
|
max-height: min(88vh, 820px);
|
|
}
|
|
|
|
#docScenarioTreeModal .docModalBody {
|
|
padding: 0;
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
min-height: 0;
|
|
}
|
|
|
|
.docScenarioGraphTitleRow {
|
|
padding: 16px 20px;
|
|
border-bottom: 1px solid var(--doc-border);
|
|
}
|
|
|
|
.docScenarioGraphTitleRow input {
|
|
width: 100%;
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 8px;
|
|
padding: 8px 10px;
|
|
font-size: 14px;
|
|
font-weight: 700;
|
|
color: var(--doc-text);
|
|
background: var(--doc-bg-2);
|
|
}
|
|
|
|
.docScenarioGraphToolbar {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 10px 20px;
|
|
border-bottom: 1px solid var(--doc-border);
|
|
}
|
|
|
|
.docScenarioGraphHint {
|
|
font-size: 12.5px;
|
|
color: var(--doc-muted);
|
|
}
|
|
|
|
.docScenarioGraphHint.is-connecting {
|
|
color: var(--doc-accent);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.docScenarioGraphSplit {
|
|
flex: 1 1 auto;
|
|
min-height: 0;
|
|
display: flex;
|
|
}
|
|
|
|
.docScenarioGraphCanvas {
|
|
position: relative;
|
|
flex: 1 1 auto;
|
|
min-width: 0;
|
|
overflow: auto;
|
|
background:
|
|
radial-gradient(circle, var(--doc-border) 1px, transparent 1px) 0 0 / 24px 24px,
|
|
var(--doc-bg-2);
|
|
cursor: default;
|
|
}
|
|
|
|
.docScenarioGraphCanvas.is-connecting {
|
|
cursor: crosshair;
|
|
}
|
|
|
|
.docScenarioGraphCanvas.is-connecting .docScenarioGraphNode {
|
|
cursor: crosshair;
|
|
}
|
|
|
|
.docScenarioGraphInner {
|
|
position: relative;
|
|
width: 2200px;
|
|
height: 1600px;
|
|
}
|
|
|
|
.docScenarioGraphSvg {
|
|
position: absolute;
|
|
inset: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.docScenarioGraphEdge {
|
|
fill: none;
|
|
stroke: var(--doc-muted);
|
|
stroke-width: 2;
|
|
}
|
|
|
|
.docScenarioGraphEdgeLabel {
|
|
position: absolute;
|
|
transform: translate(-50%, -50%);
|
|
background: var(--doc-bg-2);
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 6px;
|
|
padding: 2px 7px;
|
|
font-size: 11px;
|
|
font-weight: 600;
|
|
color: var(--doc-muted);
|
|
white-space: nowrap;
|
|
max-width: 140px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.docScenarioGraphNode {
|
|
position: absolute;
|
|
width: 180px;
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 10px;
|
|
background: var(--doc-card);
|
|
padding: 10px 12px;
|
|
cursor: grab;
|
|
user-select: none;
|
|
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
|
|
}
|
|
|
|
.docScenarioGraphNode:active {
|
|
cursor: grabbing;
|
|
}
|
|
|
|
.docScenarioGraphNode.is-root {
|
|
border-color: var(--doc-accent);
|
|
}
|
|
|
|
.docScenarioGraphNode.is-selected {
|
|
border-color: var(--doc-accent);
|
|
box-shadow: 0 0 0 2px var(--doc-accent);
|
|
}
|
|
|
|
.docScenarioGraphNode.is-connect-target:hover {
|
|
border-color: var(--doc-quiz-success-border);
|
|
box-shadow: 0 0 0 2px var(--doc-quiz-success-border);
|
|
}
|
|
|
|
.docScenarioGraphNodeLabel {
|
|
font-size: 10.5px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.03em;
|
|
text-transform: uppercase;
|
|
color: var(--doc-accent);
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.docScenarioGraphNodeText {
|
|
font-size: 12.5px;
|
|
line-height: 1.35;
|
|
color: var(--doc-text);
|
|
display: -webkit-box;
|
|
-webkit-box-orient: vertical;
|
|
-webkit-line-clamp: 4;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.docScenarioGraphInspector {
|
|
flex: 0 0 300px;
|
|
border-left: 1px solid var(--doc-border);
|
|
padding: 16px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.docScenarioGraphInspectorEmpty {
|
|
font-size: 13px;
|
|
color: var(--doc-muted);
|
|
text-align: center;
|
|
padding: 30px 10px;
|
|
}
|
|
|
|
.docScenarioChoiceTargetRow {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
margin-top: 4px;
|
|
}
|
|
|
|
.docScenarioChoiceTargetLabel {
|
|
flex: 1;
|
|
min-width: 0;
|
|
font-size: 12px;
|
|
color: var(--doc-muted);
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.docScenarioChoiceLinkBtn {
|
|
border: 1px solid var(--doc-border);
|
|
background: transparent;
|
|
color: var(--doc-text);
|
|
border-radius: 6px;
|
|
padding: 3px 8px;
|
|
font-size: 11.5px;
|
|
cursor: pointer;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.docScenarioChoiceLinkBtn:hover {
|
|
border-color: var(--doc-accent);
|
|
}
|
|
|
|
.docScenarioChoiceLinkBtn.is-active {
|
|
border-color: var(--doc-accent);
|
|
background: var(--doc-accent);
|
|
color: #14100c;
|
|
}
|
|
|
|
/* ===== RESPONSIVE (chrome de l'éditeur lui-même, <900px) ===== */
|
|
@media (width <= 900px) {
|
|
.docCrumbDim {
|
|
display: none;
|
|
}
|
|
|
|
.docTopbar {
|
|
padding: 0 14px;
|
|
}
|
|
|
|
.docBtnLabel {
|
|
display: none;
|
|
}
|
|
|
|
.docBtnSecondary {
|
|
padding: 0 10px;
|
|
}
|
|
|
|
.docWidthPicker {
|
|
display: none;
|
|
}
|
|
|
|
.docCanvasArea {
|
|
padding: 18px 14px 84px;
|
|
}
|
|
|
|
.docPage {
|
|
padding: 28px 20px;
|
|
}
|
|
|
|
.docSidebar {
|
|
/* position:absolute relative à .docBodyWrap (position:relative, voir
|
|
plus haut) plutôt que position:fixed relative au viewport : ce
|
|
dernier subirait le même rognage par overflow:hidden que .docEditor3
|
|
(voir son commentaire) — inutile de toute façon ici, .docBodyWrap
|
|
exclut déjà .docTopbar/.docBottomNav (ses frères, jamais ses
|
|
parents), inset:0 suffit sans aucun calcul de hauteur de bandeau. */
|
|
position: absolute;
|
|
inset: 0;
|
|
width: 100%;
|
|
z-index: 30;
|
|
transition: transform 0.25s ease;
|
|
}
|
|
|
|
.docSidebarLeft {
|
|
transform: translateX(-100%);
|
|
}
|
|
|
|
.docSidebarRight {
|
|
transform: translateX(100%);
|
|
}
|
|
|
|
.docSidebar.is-open {
|
|
transform: translateX(0);
|
|
}
|
|
|
|
.docSidebarClose {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--doc-muted);
|
|
margin-bottom: 16px;
|
|
cursor: pointer;
|
|
padding: 0 16px;
|
|
}
|
|
|
|
.docBottomNav {
|
|
display: flex;
|
|
}
|
|
|
|
.docAssocBoard {
|
|
flex-direction: column;
|
|
}
|
|
}
|