Nouveau bouton dans le topbar de l'éditeur, à côté d'Aperçu, qui ouvre une modale listant les thèmes du catalogue (document_engine/themes/). Cliquer un thème charge un VRAI aperçu (rendu serveur réel dans un iframe, jamais une resucée CSS côté client) avec le choix de garder le contenu actuel ou de le remplacer par le contenu de démonstration du modèle. Architecture pensée pour une centaine de thèmes futurs : chaque thème est une feuille de style externe (static/document/themes/<id>.css) qui habille les classes fixes du moteur, jamais du code qui en changerait la structure. Premier thème implémenté pour valider le mécanisme : "Sécurité Incendie" (6 pages de contenu réel, quiz inclus). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2398 lines
58 KiB
CSS
2398 lines
58 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;
|
|
}
|
|
|
|
.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--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--paragraphe::before { content: "¶"; }
|
|
.docLibIcon--image::before { content: "🖼"; }
|
|
.docLibIcon--bouton::before { content: "⬚"; }
|
|
.docLibIcon--liste_puces::before { content: "☰"; }
|
|
.docLibIcon--liste_numerotee::before { content: "①"; }
|
|
.docLibIcon--badge::before { content: "◉"; }
|
|
.docLibIcon--carte::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;
|
|
}
|
|
|
|
/* Page au format A4 PAYSAGE, taille FIXE comme une vraie diapositive
|
|
(retour utilisateur du 23/09/2026 : "une page [...] dois etre un
|
|
format a4 orienter horizontalement comme une slide [...] le contenu
|
|
ne defile pas, si ya plus de place les contenu va sur une autre
|
|
page") — ratio 297:210 (A4 paysage) fixé par aspect-ratio, jamais
|
|
height:fit-content comme avant (la page ne grandit plus avec son
|
|
contenu). overflow:hidden : le contenu qui dépasse est clipsé, PAS
|
|
de défilement interne — au créateur de répartir sur plusieurs pages,
|
|
comme un vrai logiciel de diaporama ne reflow jamais automatiquement
|
|
une diapositive trop pleine. */
|
|
.docPage {
|
|
/* --doc-page-width : SOURCE UNIQUE utilisée à la fois par width et par
|
|
le calcul de max-height ci-dessous (jamais deux nombres séparés à
|
|
tenir synchronisés) — .docPage--fixedWidth et le point de rupture
|
|
mobile la redéfinissent, max-height suit automatiquement. */
|
|
--doc-page-width: 960px;
|
|
|
|
width: var(--doc-page-width);
|
|
max-width: 100%;
|
|
aspect-ratio: 297 / 210;
|
|
|
|
/* Filet de sécurité en plus d'aspect-ratio (retour utilisateur du
|
|
23/09/2026 : "le contenu ne dois jamais s'adapter verticalement" —
|
|
bug réel constaté : un <div> flex (voir .docCanvasArea) applique
|
|
une "taille minimale automatique" à ses enfants qui peut agrandir
|
|
la page au-delà de la hauteur dérivée d'aspect-ratio pour éviter de
|
|
rogner leur contenu ; max-height fixe un plafond dur qu'aucun
|
|
contenu ne peut dépasser, quelle que soit cette mécanique). */
|
|
max-height: calc(var(--doc-page-width) * 210 / 297);
|
|
min-height: 0;
|
|
|
|
/* Blanche FIXE, en édition ET en Aperçu (retour utilisateur du
|
|
24/09/2026 : "la page [...] soi blanche comme dans tous éditeur
|
|
normale") — jamais var(--doc-card), qui suit le thème clair/sombre
|
|
de L'ÉDITEUR (chrome autour), pas de la PAGE elle-même. */
|
|
background: #fff;
|
|
|
|
/* Le contenu écrit color:var(--forge-text) en style inline par défaut
|
|
(voir document_engine/rendering/render_document_element.py) — ce
|
|
token vaut #e8ecf4 (quasi blanc) partout ailleurs dans le site,
|
|
illisible sur une page blanche fixe. Redéfini ICI seulement (pas
|
|
globalement, le reste du site reste en thème sombre figé) — même
|
|
principe déjà appliqué aux vignettes de page (session du 21/09/2026,
|
|
depuis supprimées, mais le même besoin persiste ici). */
|
|
--forge-text: #14161c;
|
|
--forge-text-muted: #6b7280;
|
|
|
|
/* Les mini-jeux (cartes Quiz/Association/Memory/Mots mêlés/Scénario,
|
|
leurs options/cartes/boutons) s'appuient sur les tokens --doc-*
|
|
(--doc-card, --doc-bg-2, --doc-text, --doc-border, ceux de succès
|
|
et d'échec du Quiz) — sombres par défaut, assortis au CHROME de
|
|
l'éditeur, pas à la page (retour utilisateur
|
|
du 24/09/2026 : "le style des mini jeux dois etre revue pour etre
|
|
sur fond blanc"). Réutilise TELLES QUELLES les valeurs déjà
|
|
définies pour .docEditor3[data-theme="light"] (palette claire déjà
|
|
conçue et éprouvée dans ce fichier) plutôt que d'en inventer une
|
|
troisième version : la page étant DÉSORMAIS TOUJOURS blanche (voir
|
|
background ci-dessus), son contenu doit toujours utiliser la
|
|
palette claire, peu importe le thème choisi pour le chrome autour. */
|
|
--doc-bg: #f6f5f2;
|
|
--doc-bg-2: #fff;
|
|
--doc-card: #fff;
|
|
--doc-border: #e3e1db;
|
|
--doc-text: #14161c;
|
|
--doc-muted: #6b7280;
|
|
--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;
|
|
|
|
/* Bug réel constaté le 24/09/2026 (capture à l'appui) : redéfinir la
|
|
custom property --doc-text ci-dessus NE SUFFIT PAS pour les textes
|
|
de mini-jeu SANS déclaration `color` propre (ex. .docQuizPlayerTitle,
|
|
.docQuizQuestionText) — `color` est une propriété HÉRITÉE, et sans
|
|
déclaration explicite ici, ces éléments héritent la couleur déjà
|
|
CALCULÉE plus haut sur .docEditor3 (color:var(--doc-text) résolu
|
|
LÀ-BAS avec la palette sombre de l'éditeur, quasi blanche), jamais
|
|
une réévaluation de --doc-text à CE niveau. Poser `color` ICI casse
|
|
cette chaîne d'héritage et la relance avec la bonne valeur locale. */
|
|
color: var(--doc-text);
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 16px;
|
|
box-shadow: 0 1px 3px rgb(0 0 0 / 30%);
|
|
padding: 48px 52px;
|
|
position: relative;
|
|
overflow: hidden;
|
|
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). */
|
|
.docPageContent {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 18px;
|
|
height: 100%;
|
|
}
|
|
|
|
/* Bug réel constaté le 23/09/2026 (retour utilisateur, capture à
|
|
l'appui) : flex-shrink vaut 1 par défaut pour tout enfant flex —
|
|
une fois .docPage plafonnée en hauteur (voir plus haut), les
|
|
éléments top-niveau se COMPRESSAIENT tous pour continuer à tenir
|
|
(ex. .docImagePlaceholder, pourtant height:190px, rétrécissait
|
|
visiblement) au lieu de déborder. Sans ce débordement RÉEL,
|
|
forgeDocCheckPageOverflow (scrollHeight vs clientHeight) ne
|
|
détectait jamais rien à paginer. flex-shrink:0 empêche toute
|
|
compression : le contenu garde sa taille naturelle et déborde
|
|
franchement, ce qui déclenche la pagination automatique. */
|
|
.docPageContent > [data-element-id] {
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.docPage.docPage--fixedWidth {
|
|
--doc-page-width: var(--doc-preview-width, 760px);
|
|
|
|
width: var(--doc-page-width);
|
|
padding: 28px 20px;
|
|
}
|
|
|
|
/* Un mini-jeu occupe TOUTE la page, à lui seul (retour utilisateur du
|
|
23/09/2026 : "un mini jeu dois occupper toute une page" — confirmé :
|
|
"une page avec mini-jeu = uniquement ce mini-jeu"). Garantie posée
|
|
côté serveur (routes/document/document_element_add.py : impossible
|
|
d'ajouter un mini-jeu sur une page non vide, ni d'ajouter autre chose
|
|
sur une page qui en contient déjà un) — :only-child ici n'est donc
|
|
jamais un cas ambigu, juste la mise en forme plein-cadre de cette
|
|
garantie. Le padding de .docPage disparaît (plein bord) et le badge
|
|
mini-jeu (nom/"2 paires"/etc.) disparaît ENTIÈREMENT (retour
|
|
utilisateur du 23/09/2026 : "ces éléments [...] doivent disparaitre
|
|
et le composant mini jeu pren toute la place") — plus une simple
|
|
réduction d'espace, le joueur seul occupe toute la hauteur. */
|
|
.docPage:has(> .docPageContent > .docMinigame:only-child) {
|
|
padding: 0;
|
|
}
|
|
|
|
.docPageContent > .docMinigame:only-child {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
border-radius: 0;
|
|
border-width: 0;
|
|
}
|
|
|
|
.docPageContent > .docMinigame:only-child .docMinigameBadge {
|
|
display: none;
|
|
}
|
|
|
|
.docPageContent > .docMinigame:only-child > [class$="Player"] {
|
|
flex: 1;
|
|
min-height: 0;
|
|
}
|
|
|
|
.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);
|
|
}
|
|
|
|
.docText {
|
|
outline: none;
|
|
|
|
/* Un mot sans espace plus long que la largeur du bloc doit se couper
|
|
plutôt que déborder hors de la page (retour utilisateur du
|
|
23/09/2026 : "les élément paragraphe ne vont pas à la ligne") —
|
|
overflow-wrap (jamais word-break:break-all, qui casserait même les
|
|
mots normaux au milieu sans raison) ne coupe QUE si le mot ne tient
|
|
vraiment pas sur une ligne. */
|
|
overflow-wrap: break-word;
|
|
}
|
|
|
|
.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;
|
|
}
|
|
|
|
.docList {
|
|
margin: 0;
|
|
padding-left: 1.4em;
|
|
font-size: 15px;
|
|
line-height: 1.6;
|
|
color: var(--forge-text);
|
|
}
|
|
|
|
.docList li + li {
|
|
margin-top: 4px;
|
|
}
|
|
|
|
.docMinigame {
|
|
border: 1px solid var(--doc-accent);
|
|
|
|
/* Bords carrés, jamais arrondis (retour utilisateur du 24/09/2026 :
|
|
"les bord des mini jeu ne doivent plus etre arrondie") — même
|
|
règle pour toutes les cartes/boutons/cases du joueur ci-dessous. */
|
|
border-radius: 0;
|
|
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 : le joueur RÉEL est
|
|
désormais VISIBLE en permanence, édition ET Aperçu (retour
|
|
utilisateur du 23/09/2026 : "les mini jeux doive etre visible meme
|
|
en edition mais jouable dans l'apercus") — seule l'INTERACTIVITÉ
|
|
(répondre/glisser/retourner une carte) reste réservée au Mode
|
|
Aperçu, via pointer-events ci-dessous (jamais via display:none,
|
|
sinon rien ne serait visible en édition). ---- */
|
|
|
|
/* Le joueur remplit TOUTE la hauteur/largeur disponible (voir
|
|
[class$="Player"] plus haut, flex:1 dans .docMinigame:only-child) ET
|
|
sa CARTE interne (.docQuizCard, .docAssocCard, .docMemoryCardWrap...)
|
|
doit elle aussi occuper toute cette place, pas juste une carte
|
|
centrée avec une largeur confortable (retour utilisateur du
|
|
23/09/2026 : "le mini jeu dois prendre toute la hauteur et la
|
|
largeur du document" — une carte à largeur limitée, même centrée,
|
|
ne suffisait pas). align-items:stretch (valeur par défaut, posée
|
|
explicitement pour être claire) étire la carte sur tout l'axe
|
|
croisé ; sa largeur 100% (voir chaque carte plus bas, sans
|
|
max-width) fait le reste. */
|
|
.docQuizPlayer,
|
|
.docAssocPlayer,
|
|
.docMemoryPlayer,
|
|
.docMotsPlayer,
|
|
.docScenarioPlayer {
|
|
pointer-events: none;
|
|
display: flex;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.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 * {
|
|
/* Deux raisons cumulées de désactiver pointer-events ailleurs, toutes
|
|
les deux réactivées ICI en Mode Aperçu : (1) .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 ; (2) le
|
|
joueur lui-même est pointer-events:none PAR DÉFAUT (voir plus haut)
|
|
pour rester visible mais non "jouable" hors Aperçu (retour
|
|
utilisateur du 23/09/2026). Cette règle est donc le SEUL endroit où
|
|
un mini-jeu redevient réellement cliquable/glissable. */
|
|
pointer-events: auto;
|
|
}
|
|
|
|
.docQuizCard,
|
|
.docQuizResultCard {
|
|
/* Toute la hauteur/largeur du DOCUMENT (retour utilisateur du
|
|
23/09/2026), pas juste une carte confortable centrée : la carte
|
|
elle-même (fond/bordure) couvre désormais toute la page ;
|
|
display:flex + justify-content:flex-start ALIGNE SON CONTENU EN
|
|
HAUT de cette grande surface (retour utilisateur du 23/09/2026 :
|
|
"le contenu aligner en haut", pas centré verticalement). */
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: flex-start;
|
|
background: linear-gradient(180deg, var(--doc-card), var(--doc-bg-2));
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 0;
|
|
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: 0;
|
|
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: 0;
|
|
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: 0;
|
|
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: 0;
|
|
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 {
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: flex-start;
|
|
background: linear-gradient(180deg, var(--doc-card), var(--doc-bg-2));
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 0;
|
|
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: 0;
|
|
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: 0;
|
|
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 {
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: flex-start;
|
|
background: linear-gradient(180deg, var(--doc-card), var(--doc-bg-2));
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 0;
|
|
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: 0;
|
|
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: 0;
|
|
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: 0;
|
|
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;
|
|
}
|
|
|
|
.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;
|
|
}
|
|
|
|
/* ---- Panneau GAUCHE à ONGLETS (retour utilisateur du 23/09/2026 :
|
|
"il dois avoir deux onglets [...] un onglet page [...] un onglet mise
|
|
en page avec les fonctionnalité et la disposition actuel sans la
|
|
section page") : "Pages" (gestion complète, panneau dédié plein
|
|
hauteur) et "Mise en page" (bibliothèque d'éléments, inchangée). ---- */
|
|
.docSidebarTabs {
|
|
display: flex;
|
|
flex-shrink: 0;
|
|
border-bottom: 1px solid var(--doc-border);
|
|
margin: -20px -16px 0;
|
|
padding: 0 8px;
|
|
}
|
|
|
|
.docSidebarTab {
|
|
flex: 1;
|
|
padding: 10px 8px;
|
|
border: none;
|
|
|
|
/* border-radius:0 obligatoire : le bouton Bulma générique (static/style.css)
|
|
pose border-radius:var(--bulma-control-radius) sur TOUT <button>, jamais
|
|
annulé par notre seule déclaration border-bottom ci-dessous (la cascade
|
|
CSS s'applique PROPRIÉTÉ PAR PROPRIÉTÉ, pas règle par règle) — sans ce
|
|
reset, le trait du bas suivait les coins arrondis et remontait sur les
|
|
côtés au lieu de rester plat (retour utilisateur du 23/09/2026). */
|
|
border-radius: 0;
|
|
border-bottom: 2px solid transparent;
|
|
background: none;
|
|
color: var(--doc-muted);
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.docSidebarTab:hover {
|
|
color: var(--doc-text);
|
|
}
|
|
|
|
.docSidebarTab.is-active {
|
|
/* Le plus simple des styles d'onglet Bulma (retour utilisateur du
|
|
23/09/2026) : SEUL le border-bottom change de couleur, le texte
|
|
reste neutre (pas de changement de couleur sur le libellé). */
|
|
border-bottom-color: var(--doc-accent);
|
|
}
|
|
|
|
.docSidebarTabPanel {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 22px;
|
|
min-height: 0;
|
|
flex: 1;
|
|
}
|
|
|
|
/* L'attribut natif [hidden] a la MÊME spécificité qu'une classe et cette
|
|
feuille de style charge APRÈS celle du navigateur : sans cette règle,
|
|
`.docSidebarTabPanel { display:flex; }` ci-dessus gagne la cascade et
|
|
annule le display:none natif de [hidden] — les deux panneaux
|
|
d'onglet restaient visibles empilés en même temps, peu importe
|
|
forgeDocSwitchSidebarTab (bug réel constaté le 23/09/2026). */
|
|
.docSidebarTabPanel[hidden] {
|
|
display: none;
|
|
}
|
|
|
|
#docTabPanelPages {
|
|
gap: 8px;
|
|
}
|
|
|
|
/* Bouton d'ajout EN HAUT, toujours visible (retour utilisateur du
|
|
23/09/2026 : "le bouton pour ajouter une page dois être en haut
|
|
toujours visible") — flex-shrink:0 pour ne jamais être compressé par
|
|
.docPageManagerList, seule cette dernière défile. */
|
|
.docPageManagerAdd {
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.docPageManagerList {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
flex: 1;
|
|
min-height: 0;
|
|
overflow-y: auto;
|
|
|
|
/* Espace autour des rangées (retour utilisateur du 23/09/2026) — le
|
|
padding-right laisse aussi un peu d'air entre le texte et la barre
|
|
de défilement fine ci-dessous, jamais collée au bord. */
|
|
padding: 2px 6px 2px 2px;
|
|
|
|
/* Barre de défilement fine et discrète (retour utilisateur du
|
|
23/09/2026) — Firefox/Chromium récents (scrollbar-width/-color) ET
|
|
WebKit/Blink plus anciens (::-webkit-scrollbar) pour une couverture
|
|
large, jamais la barre large par défaut du navigateur. */
|
|
scrollbar-width: thin;
|
|
scrollbar-color: var(--doc-border) transparent;
|
|
}
|
|
|
|
.docPageManagerList::-webkit-scrollbar {
|
|
width: 6px;
|
|
}
|
|
|
|
.docPageManagerList::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
|
|
.docPageManagerList::-webkit-scrollbar-thumb {
|
|
background: var(--doc-border);
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.docPageManagerList::-webkit-scrollbar-thumb:hover {
|
|
background: var(--doc-muted);
|
|
}
|
|
|
|
.docPageRow {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 7px 8px;
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 8px;
|
|
background: var(--doc-card);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.docPageRow.is-dragging {
|
|
opacity: 0.4;
|
|
}
|
|
|
|
.docPageRow:hover {
|
|
border-color: var(--doc-accent);
|
|
}
|
|
|
|
.docPageRow.is-active {
|
|
border-color: var(--doc-accent);
|
|
box-shadow: 0 0 0 1px var(--doc-accent);
|
|
}
|
|
|
|
.docPageRow:focus-visible {
|
|
outline: 2px solid var(--doc-accent);
|
|
outline-offset: 1px;
|
|
}
|
|
|
|
.docPageRowHandle {
|
|
flex-shrink: 0;
|
|
color: var(--doc-muted);
|
|
font-size: 13px;
|
|
cursor: grab;
|
|
}
|
|
|
|
.docPageRowNumber {
|
|
flex-shrink: 0;
|
|
width: 20px;
|
|
height: 20px;
|
|
border-radius: 5px;
|
|
background: var(--doc-bg-2);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 11px;
|
|
font-weight: 700;
|
|
color: var(--doc-muted);
|
|
}
|
|
|
|
.docPageRow.is-active .docPageRowNumber {
|
|
color: var(--doc-accent);
|
|
}
|
|
|
|
/* Titre tronqué, qui défile au survol si tronqué (retour utilisateur du
|
|
21/09/2026) — .has-overflow n'est posé que si le texte dépasse
|
|
réellement (voir forgeDocApplyTitleMarquee), jamais un défilement
|
|
systématique qui ferait sauter les titres courts. */
|
|
.docPageRowTitle {
|
|
flex: 1;
|
|
min-width: 0;
|
|
overflow: hidden;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.docPageRowTitleText {
|
|
display: inline-block;
|
|
font-size: 12.5px;
|
|
font-weight: 600;
|
|
color: var(--doc-muted);
|
|
transition: transform 0.6s ease;
|
|
}
|
|
|
|
.docPageRow.is-active .docPageRowTitleText {
|
|
color: var(--doc-text);
|
|
}
|
|
|
|
.docPageRowTitle.has-overflow:hover .docPageRowTitleText {
|
|
transform: translateX(calc(-1 * var(--marquee-distance, 0px)));
|
|
}
|
|
|
|
.docPageRowTitle[contenteditable="true"] {
|
|
outline: none;
|
|
cursor: text;
|
|
user-select: text;
|
|
}
|
|
|
|
.docPageRowTitle[contenteditable="true"] .docPageRowTitleText {
|
|
border-bottom: 1px solid var(--doc-accent);
|
|
color: var(--doc-text);
|
|
}
|
|
|
|
.docPageRowActions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 2px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.docPageRowActions button {
|
|
width: 20px;
|
|
height: 20px;
|
|
border: none;
|
|
border-radius: 5px;
|
|
background: transparent;
|
|
color: var(--doc-muted);
|
|
font-size: 11px;
|
|
cursor: pointer;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.docPageRowActions button:hover:not(:disabled) {
|
|
background: var(--doc-bg-2);
|
|
color: var(--doc-text);
|
|
}
|
|
|
|
.docPageRowActions button:disabled {
|
|
opacity: 0.3;
|
|
cursor: default;
|
|
}
|
|
|
|
.docPageRowDelete:hover:not(:disabled) {
|
|
color: var(--doc-quiz-danger-border);
|
|
}
|
|
|
|
/* ---- 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 : plein écran RÉEL (Fullscreen API, voir
|
|
forgeDocSetPreviewMode), tout le chrome d'édition masqué — y compris
|
|
.docTopbar désormais (retour utilisateur du 23/09/2026 : "en mode
|
|
apercu en plein ecrant"), avec .docPreviewExitBtn comme seul moyen
|
|
de revenir à l'éditeur. ===== */
|
|
|
|
/* Bug réel constaté le 24/09/2026 (capture à l'appui : bande noire en
|
|
bas, la page ne fait pas toute la hauteur) : .docEditor3 s'appuie
|
|
sur flex:1 1 auto (voir sa règle de base) pour sa taille, une
|
|
propriété qui n'a de sens QUE comme enfant d'un conteneur flex
|
|
normal — une fois réellement passé en plein écran (élément peint
|
|
dans le "top layer" du navigateur, hors du flux normal), flex-basis
|
|
n'a plus de contexte parent et peut se résoudre à la taille du
|
|
CONTENU plutôt qu'à tout l'écran. width/height explicites en
|
|
secours, qui ne dépendent d'aucun mécanisme de layout implicite. */
|
|
.docEditor3:fullscreen,
|
|
.docEditor3.docEditor3--preview {
|
|
width: 100vw;
|
|
height: 100vh;
|
|
}
|
|
|
|
.docEditor3.docEditor3--preview .docTopbar,
|
|
.docEditor3.docEditor3--preview .docSidebarLeft,
|
|
.docEditor3.docEditor3--preview .docSidebarRight,
|
|
.docEditor3.docEditor3--preview .docBottomNav,
|
|
.docEditor3.docEditor3--preview .docZoomPill {
|
|
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;
|
|
}
|
|
|
|
/* Aucun espace autour du mini-jeu en Aperçu (retour utilisateur du
|
|
23/09/2026 : "pourquoi je voi de l'espace autour du composant mini
|
|
jeu en apercu ? il doit prendre toute la place") — la page elle-même
|
|
abandonne son format A4 fixe pour remplir tout l'écran plein écran,
|
|
plutôt qu'un rectangle A4 plus petit flottant avec des bords vides
|
|
autour. */
|
|
.docEditor3.docEditor3--preview .docCanvasArea {
|
|
padding: 0;
|
|
}
|
|
|
|
.docEditor3.docEditor3--preview .docPage {
|
|
width: 100%;
|
|
height: 100%;
|
|
aspect-ratio: unset;
|
|
|
|
/* Bug réel constaté le 24/09/2026 (capture + diagnostic console à
|
|
l'appui : .docEditor3/.docBodyWrap/.docCanvasArea faisaient bien
|
|
960px = window.innerHeight, mais .docPage seule ne faisait que
|
|
678.78px) — max-height (posé en édition pour plafonner la page au
|
|
format A4, voir la règle de base .docPage) reste actif ici et
|
|
plafonne la page à cette même hauteur MÊME EN PLEIN ÉCRAN, malgré
|
|
height:100% ci-dessus (max-height gagne toujours sur height dans
|
|
la résolution CSS). Neutralisé ici pour laisser height:100% agir
|
|
réellement. */
|
|
max-height: none;
|
|
min-height: 0;
|
|
border: none;
|
|
border-radius: 0;
|
|
box-shadow: none;
|
|
}
|
|
|
|
.docPreviewExitBtn {
|
|
display: none;
|
|
position: absolute;
|
|
top: 20px;
|
|
right: 20px;
|
|
z-index: 50;
|
|
align-items: center;
|
|
gap: 6px;
|
|
height: 36px;
|
|
padding: 0 16px;
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 10px;
|
|
background: var(--doc-bg-2);
|
|
color: var(--doc-text);
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.docPreviewExitBtn:hover {
|
|
border-color: var(--doc-accent);
|
|
}
|
|
|
|
.docEditor3.docEditor3--preview .docPreviewExitBtn {
|
|
display: flex;
|
|
}
|
|
|
|
/* ---- 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;
|
|
}
|
|
}
|
|
|
|
/* ---- Modale "Utiliser un modèle" (voir static/document/js/
|
|
document-editor.js, forgeDocOpenTemplateModal) : liste des thèmes du
|
|
catalogue à gauche, aperçu RÉEL du thème sélectionné (chargé dans un
|
|
<iframe> pointant sur /document/<slug>/theme/<id>/preview, jamais un
|
|
style recopié à la main) à droite. Plus large que la modale générique
|
|
par défaut (comme #docScenarioTreeModal ci-dessus), pour laisser de
|
|
la place à l'aperçu. ---- */
|
|
.docModalDialog--wide {
|
|
width: min(980px, 95vw);
|
|
max-height: min(85vh, 760px);
|
|
}
|
|
|
|
.docTemplateModalBody {
|
|
display: flex;
|
|
gap: 0;
|
|
padding: 0;
|
|
overflow: hidden;
|
|
min-height: 0;
|
|
}
|
|
|
|
.docTemplateList {
|
|
width: 260px;
|
|
flex-shrink: 0;
|
|
border-right: 1px solid var(--doc-border);
|
|
overflow-y: auto;
|
|
padding: 12px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
}
|
|
|
|
.docTemplateCard {
|
|
display: block;
|
|
width: 100%;
|
|
text-align: left;
|
|
border: 1px solid var(--doc-border);
|
|
border-radius: 10px;
|
|
background: var(--doc-card);
|
|
color: var(--doc-text);
|
|
padding: 10px 12px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.docTemplateCard.is-active {
|
|
border-color: var(--doc-accent);
|
|
box-shadow: 0 0 0 1px var(--doc-accent);
|
|
}
|
|
|
|
.docTemplateCardName {
|
|
font-size: 13.5px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.docTemplateCardCategory {
|
|
font-size: 11.5px;
|
|
color: var(--doc-muted);
|
|
margin-top: 2px;
|
|
}
|
|
|
|
.docTemplatePreviewPane {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
background: var(--doc-bg);
|
|
}
|
|
|
|
.docTemplatePreviewFrame {
|
|
flex: 1;
|
|
min-height: 0;
|
|
border: none;
|
|
display: none;
|
|
}
|
|
|
|
.docTemplatePreviewFrame.is-visible {
|
|
display: block;
|
|
}
|
|
|
|
.docTemplatePreviewEmpty {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: var(--doc-muted);
|
|
font-size: 13px;
|
|
text-align: center;
|
|
padding: 24px;
|
|
}
|
|
|
|
.docTemplatePreviewActions {
|
|
flex-shrink: 0;
|
|
display: flex;
|
|
gap: 10px;
|
|
padding: 14px 16px;
|
|
border-top: 1px solid var(--doc-border);
|
|
}
|
|
|
|
.docTemplatePreviewActions[hidden] {
|
|
display: none;
|
|
}
|
|
|
|
.docTemplatePreviewActions .docBtnSecondary {
|
|
flex: 1;
|
|
justify-content: center;
|
|
}
|
|
|
|
@media (width <= 720px) {
|
|
.docTemplateModalBody {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.docTemplateList {
|
|
width: 100%;
|
|
max-height: 160px;
|
|
border-right: none;
|
|
border-bottom: 1px solid var(--doc-border);
|
|
}
|
|
}
|