/* Style de l'application, selon la charte graphique (ADR 0010, * docs/identite/charte/) : les couleurs, tailles et rayons sont les * variables de tokens.json, sous le même nom, redéfinies pour le thème * sombre ; les composants reprennent bundle.css. */ /* ------------------------------------------------------------ Police */ /* Alegreya Sans (SIL Open Font License 1.1, voir fonts/OFL.txt), réduite * aux caractères latins et convertie en woff2 (voir docs/identite/README.md). * Embarquée dans le site, donc dans le cache du service worker : elle * fonctionne hors ligne. */ @font-face { font-family: "Alegreya Sans"; src: url("fonts/AlegreyaSans-Regular.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; } @font-face { font-family: "Alegreya Sans"; src: url("fonts/AlegreyaSans-Italic.woff2") format("woff2"); font-weight: 400; font-style: italic; font-display: swap; } @font-face { font-family: "Alegreya Sans"; src: url("fonts/AlegreyaSans-Medium.woff2") format("woff2"); font-weight: 500; font-style: normal; font-display: swap; } @font-face { font-family: "Alegreya Sans"; src: url("fonts/AlegreyaSans-Bold.woff2") format("woff2"); font-weight: 700; font-style: normal; font-display: swap; } @font-face { font-family: "Alegreya Sans"; src: url("fonts/AlegreyaSans-ExtraBold.woff2") format("woff2"); font-weight: 800; font-style: normal; font-display: swap; } /* ------------------------------------------------------------ Couleurs */ /* Thème clair par défaut ; thème sombre selon le système, ou forcé par le * menu (data-theme sur ). Les deux blocs sombres sont identiques * (vérifié par app/tests/ui.test.ts). */ :root { --background: #f5efe5; --surface: #fffcf6; --surface-sunken: #ece3d4; --text: #2b1d15; --muted: #6b5c50; --border: #ded2c0; --border-strong: #8e7d6a; --accent: #a8432b; --on-accent: #ffffff; --accent-soft: #f1d9cf; --focus: #a8432b; --error: #8c1c3a; --verdict-good: #2f6b4f; --verdict-doubtful: #7e5a0f; --verdict-bad: #8c1c3a; --brand-noyer: #4a2e1f; --brand-ivoire: #f3ead8; --brand-laiton: #8f6a1c; --frame: #4a2e1f; --bed: #e9dcc4; --bar: #3f2619; --tray: #3a2418; --point-a: #6b4430; --point-b: #c9ae88; --point-number: #f3ead8; --checker-player: #fafaf7; --checker-player-edge: #55554f; --checker-computer: #1c1c1c; --checker-computer-edge: #b5b5b0; --count-player: #1c1c1c; --count-computer: #fafaf7; --die-player: #fafaf7; --pip-player: #1c1c1c; --die-computer: #1c1c1c; --pip-computer: #fafaf7; --cube: #fafaf7; --cube-text: #1c1c1c; --highlight: #a8432b; --target: rgba(168, 67, 43, 0.32); /* Dernier coup joué (marques et traces) : le laiton, rien d'autre. */ --last-move: var(--brand-laiton); --shadow-raised: 0 1px 2px rgba(43, 29, 21, 0.1); --font-sans: "Alegreya Sans", "Gill Sans", "Segoe UI", system-ui, sans-serif; --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px; --space-6: 24px; --space-8: 32px; --button-height: 48px; --page-width: 40rem; --radius-sm: 6px; --radius-md: 12px; --radius-pill: 999px; /* Mouvement (interaction.md). */ --duration-instant: 90ms; --duration-fast: 160ms; --duration-moderate: 240ms; --easing-standard: cubic-bezier(0.2, 0, 0, 1); --easing-exit: cubic-bezier(0.4, 0, 1, 1); /* Voile sous le menu et les panneaux. */ --scrim: rgba(43, 29, 21, 0.5); /* Hauteurs fixes de l'écran de jeu en portrait : rien ne bouge sous le * doigt (interaction.md, principe 2). */ --topbar-height: 60px; --strip-height: 44px; --infoline-height: 44px; --prompt-height: 50px; --actions-height: 76px; color-scheme: light; } @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { --background: #17110d; --surface: #221913; --surface-sunken: #2c211a; --text: #efe5d5; --muted: #a99886; --border: #3a2d24; --border-strong: #7d6b5b; --accent: #d0694b; --on-accent: #1a110c; --accent-soft: #4a2519; --focus: #e58a6e; --error: #f08fa6; --verdict-good: #7ccba4; --verdict-doubtful: #e0b04f; --verdict-bad: #f08fa6; --brand-laiton: #d6a64a; --frame: #2a1a10; --bed: #4a3626; --bar: #24170e; --tray: #22150d; --point-a: #3a2519; --point-b: #7e6142; --point-number: #cdbfa8; --checker-player: #f2f2ef; --checker-player-edge: #6e6e69; --checker-computer: #141414; --checker-computer-edge: #b5b5b0; --count-player: #1c1c1c; --count-computer: #fafaf7; --die-player: #f2f2ef; --pip-player: #1c1c1c; --die-computer: #141414; --pip-computer: #f2f2ef; --cube: #f2f2ef; --cube-text: #1c1c1c; --highlight: #e07a5a; --target: rgba(224, 122, 90, 0.38); --shadow-raised: 0 1px 2px rgba(0, 0, 0, 0.4); --scrim: rgba(0, 0, 0, 0.6); color-scheme: dark; } } :root[data-theme="dark"] { --background: #17110d; --surface: #221913; --surface-sunken: #2c211a; --text: #efe5d5; --muted: #a99886; --border: #3a2d24; --border-strong: #7d6b5b; --accent: #d0694b; --on-accent: #1a110c; --accent-soft: #4a2519; --focus: #e58a6e; --error: #f08fa6; --verdict-good: #7ccba4; --verdict-doubtful: #e0b04f; --verdict-bad: #f08fa6; --brand-laiton: #d6a64a; --frame: #2a1a10; --bed: #4a3626; --bar: #24170e; --tray: #22150d; --point-a: #3a2519; --point-b: #7e6142; --point-number: #cdbfa8; --checker-player: #f2f2ef; --checker-player-edge: #6e6e69; --checker-computer: #141414; --checker-computer-edge: #b5b5b0; --count-player: #1c1c1c; --count-computer: #fafaf7; --die-player: #f2f2ef; --pip-player: #1c1c1c; --die-computer: #141414; --pip-computer: #f2f2ef; --cube: #f2f2ef; --cube-text: #1c1c1c; --highlight: #e07a5a; --target: rgba(224, 122, 90, 0.38); --shadow-raised: 0 1px 2px rgba(0, 0, 0, 0.4); --scrim: rgba(0, 0, 0, 0.6); color-scheme: dark; } /* -------------------------------------------------------------- Page */ * { box-sizing: border-box; } /* Pas de « tirer pour rafraîchir » ni de rebond qui recharge la page en * pleine partie ; pas de texte sélectionné par un appui long. */ html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; overscroll-behavior: none; } body { margin: 0 auto; max-width: var(--page-width); padding: 0 var(--space-4); font-family: var(--font-sans); font-size: 17px; line-height: 25px; color: var(--text); background: var(--background); overscroll-behavior: none; user-select: none; -webkit-user-select: none; } [hidden] { display: none !important; } a { color: var(--accent); text-underline-offset: 3px; } /* Anneau de focus clavier, sur tout élément interactif. */ :focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; } /* L'écran de jeu en portrait : des zones de hauteur fixe, de haut en bas ; * la barre d'actions reste collée en bas de l'écran. */ .screen { display: flex; flex-direction: column; min-height: 100vh; min-height: 100dvh; } .game { display: flex; flex: 1; flex-direction: column; } /* Les pages sont des éléments de la colonne flexible #screen. Leurs marges * automatiques les centrent, mais annulent leur étirement en largeur : sans * largeur explicite, une page au contenu court se réduit à la largeur de ce * contenu (le plateau de l'étape 6 « à vous » du tutoriel passait de 396 à * 300 px en 412 × 915). Leur max-width les borne toujours. */ .screen > .page { width: 100%; } .spacer { flex: 1; } /* Logo clair ou sombre selon le thème. */ .logo-dark { display: none; } @media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .logo-light { display: none; } :root:not([data-theme="light"]) .logo-dark { display: block; } } :root[data-theme="dark"] .logo-light { display: none; } :root[data-theme="dark"] .logo-dark { display: block; } /* -------------------------------------------------------- Barre haute */ .topbar { display: flex; flex: none; gap: var(--space-1); align-items: center; height: var(--topbar-height); } .topbar .brand { flex: 1; display: flex; align-items: center; min-width: 0; margin: 0; } .topbar .brand img { width: auto; max-width: 100%; height: 28px; } /* ------------------------------------------------------- Pictogrammes */ .icon { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; flex: none; } /* ------------------------------------------------------------ Boutons */ /* Ronds comme des pions ; un seul bouton principal (brique) par écran, * les autres en contour. */ button { min-height: var(--button-height); padding: 0 20px; font: inherit; font-weight: 700; color: var(--on-accent); background: var(--accent); border: 2px solid var(--accent); border-radius: var(--radius-pill); cursor: pointer; /* Un bouton tient sur une ligne (charte). */ white-space: nowrap; /* Pas de délai ni de zoom sur un double toucher. */ touch-action: manipulation; -webkit-tap-highlight-color: transparent; transition: background-color var(--duration-instant) var(--easing-standard), filter var(--duration-instant) var(--easing-standard); } button.secondary { color: var(--text); background: transparent; border-color: var(--border-strong); } @media (hover: hover) { button.secondary:hover:not(:disabled) { background: var(--surface-sunken); } } /* Retour visuel au toucher : le bouton descend de 1 px et s'assombrit de * 6 %, sans ombre. */ button:active:not(:disabled) { transform: translateY(1px); filter: brightness(0.94); } button:disabled { opacity: 0.45; cursor: default; } /* Pictogramme seul (ou avec son libellé de 13 px dessous, barre haute). */ .icon-button { display: inline-flex; flex-direction: column; flex: none; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; padding: 0; color: var(--text); background: transparent; border: 0; border-radius: var(--radius-md); } .icon-button.labelled { position: relative; width: 60px; height: 52px; } /* Raccourci clavier d'un bouton, en petite touche (interaction.md) : * seulement quand un clavier est détecté (classe keyboard, app.ts). */ .shortcut { display: none; } .keyboard .shortcut { display: inline-block; min-width: 1.6em; padding: 0 4px; font: inherit; font-size: 13px; font-weight: 500; line-height: 18px; text-align: center; border: 1px solid currentColor; border-radius: 4px; opacity: 0.8; } /* Barre haute : la touche en coin, sans changer la taille du bouton. */ .keyboard .icon-button.labelled .shortcut { position: absolute; top: 0; right: 2px; min-width: 0; padding: 0 3px; line-height: 15px; background: var(--background); } .icon-button .label { font-size: 13px; line-height: 15px; font-weight: 500; } @media (hover: hover) { .icon-button:hover:not(:disabled) { background: var(--surface-sunken); } } /* ------------------------------------------- Bandeaux des joueurs */ /* Un bandeau par camp, au-dessus du plateau pour l'ordinateur, dessous pour * vous ; celui qui a la main est teinté. */ .strip { display: flex; flex: none; gap: 10px; align-items: center; height: var(--strip-height); padding: 0 4px 0 12px; border-radius: var(--radius-md); } .strip.turn { background: var(--accent-soft); } .strip .who { flex: 1; min-width: 0; overflow: hidden; font-weight: 700; white-space: nowrap; text-overflow: ellipsis; } .strip .lead { font-size: 15px; line-height: 20px; color: var(--muted); white-space: nowrap; } /* Le compte de points : 20 px gras, chiffres alignés ; il se touche pour * ouvrir la bulle du glossaire. */ .strip .count { min-width: 56px; min-height: 44px; padding: 0 8px; font-size: 20px; line-height: 24px; font-weight: 700; font-variant-numeric: tabular-nums lining-nums; text-align: right; color: var(--text); background: transparent; border: 0; border-radius: var(--radius-md); } .mini { flex: none; width: 18px; height: 18px; border-radius: 50%; } .mini-player { background: var(--checker-player); border: 2px solid var(--checker-player-edge); } .mini-computer { background: var(--checker-computer); border: 2px solid var(--checker-computer-edge); } /* La bulle du compte de points, posée sur le plateau, du côté du bandeau * touché : elle ne déplace rien. */ .board-box { position: relative; flex: none; } /* Téléphone en portrait : le plateau, héros de l'écran, prend la largeur * presque entière (8 px de marge au lieu de 16). */ @media (max-width: 40rem) { html:not(.landscape) .board-box { margin-inline: calc(var(--space-2) - var(--space-4)); } } .bubble { position: absolute; left: var(--space-2); right: var(--space-2); z-index: 5; margin: 0; padding: 10px 14px; font-size: 15px; line-height: 20px; color: var(--text); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md); box-shadow: var(--shadow-raised); animation: fade-in var(--duration-fast) var(--easing-standard); } .bubble[data-side="computer"] { top: var(--space-2); } .bubble[data-side="player"] { bottom: var(--space-2); } .bubble p { margin: 0 0 var(--space-1); } .bubble-term { font-weight: 700; } .link-button { min-height: 44px; padding: 0; color: var(--accent); background: transparent; border: 0; border-radius: 0; text-decoration: underline; text-underline-offset: 3px; } /* ----------------------------------------------- Sous le plateau */ /* Ligne d'information, toujours réservée : ce qui vient de se passer (coup * de l'ordinateur, jet d'ouverture, chargement), et « Revoir » au bout. */ .infoline { display: flex; flex: none; gap: var(--space-2); align-items: center; height: var(--infoline-height); padding-left: 12px; } .info { display: -webkit-box; flex: 1; margin: 0; overflow: hidden; font-size: 15px; line-height: 20px; color: var(--muted); -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; } /* Un coup en notation du moteur, lien vers sa fiche du glossaire (ligne de * l'ordinateur, feuille du maître) : couleur du texte, soulignement en * pointillé discret. Le remplissage vertical d'un élément en ligne agrandit * la zone d'appui sans changer la hauteur des lignes. */ a.notation { padding-block: 8px; color: inherit; text-decoration-line: underline; text-decoration-style: dotted; text-decoration-color: var(--muted); text-decoration-thickness: 1px; } .infoline .replay { color: var(--accent); } /* La consigne du moment : ce qui doit ressortir de l'écran. Texte nu, * 17 px gras, sur une hauteur réservée de deux lignes ; rien quand il n'y a * rien à dire. La teinte « c'est à vous » est celle du bandeau du joueur. */ .prompt { flex: none; height: var(--prompt-height); margin: var(--space-2) 0 0; padding: 0 12px; overflow: hidden; font-weight: 700; } .prompt.neutral { font-weight: 400; color: var(--muted); } /* L'ordinateur réfléchit : trois points qui apparaissent l'un après * l'autre (la seule animation en boucle). */ .prompt.thinking::after { content: ""; animation: thinking 1.2s steps(4, end) infinite; } @keyframes thinking { 0% { content: ""; } 25% { content: "."; } 50% { content: ".."; } 75% { content: "..."; } } /* Barre d'actions collée en bas : l'action secondaire à gauche, la * principale à droite, sous le pouce. */ .actions { position: sticky; bottom: 0; z-index: 2; display: grid; flex: none; grid-template-columns: 1fr 1.6fr; gap: var(--space-2); height: var(--actions-height); padding: var(--space-3) 0 var(--space-4); background: var(--background); border-top: 1px solid var(--border); } .actions .slot { display: flex; min-width: 0; } .actions .slot button { flex: 1; min-width: 0; padding: 0 12px; display: inline-flex; gap: 6px; align-items: center; justify-content: center; } .actions .slot .icon { width: 20px; height: 20px; } .move-hint { align-self: center; margin: 0; font-size: 15px; line-height: 20px; color: var(--muted); text-align: center; } /* --------------------------------------------------- Fenêtres */ /* Voile sous le menu et les panneaux : il assombrit l'écran derrière. */ .veil { position: fixed; inset: 0; z-index: 10; display: flex; align-items: center; justify-content: center; padding: var(--space-4); background: var(--scrim); animation: fade-in var(--duration-fast) var(--easing-standard); } /* Le menu : une feuille qui monte du bas. */ .sheet-veil { z-index: 11; /* ouvert, le menu passe devant un panneau */ align-items: flex-end; padding: 0; } .sheet { width: min(100%, var(--page-width)); max-height: 100%; overflow-y: auto; padding: var(--space-2) var(--space-4) var(--space-6); color: var(--text); background: var(--surface); border-radius: 20px 20px 0 0; box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.2); animation: sheet-up var(--duration-moderate) var(--easing-standard); } .sheet-head { position: relative; display: flex; align-items: center; justify-content: space-between; min-height: 52px; } .grip { position: absolute; top: 0; left: 50%; width: 40px; height: 4px; margin-left: -20px; background: var(--border-strong); border-radius: 2px; } .sheet-title { margin: 0; font-size: 20px; line-height: 26px; } .sheet .item { display: flex; gap: 14px; align-items: center; width: 100%; min-height: 52px; padding: 0; font-weight: 500; text-align: left; color: var(--text); background: transparent; border: 0; border-bottom: 1px solid var(--border); border-radius: 0; } .sheet .item:last-child { border-bottom: 0; } .sheet .item .icon { color: var(--muted); } .item-value { margin-left: auto; font-size: 15px; color: var(--muted); } /* Interrupteur (Vibrations, écran allumé). */ .switch { position: relative; flex: none; width: 44px; height: 26px; margin-left: auto; background: var(--border-strong); border-radius: 13px; transition: background-color var(--duration-fast) var(--easing-standard); } .switch::after { content: ""; position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; background: var(--surface); border-radius: 50%; transition: transform var(--duration-fast) var(--easing-standard); } .item[aria-checked="true"] .switch { background: var(--accent); } .item[aria-checked="true"] .switch::after { background: var(--on-accent); transform: translateX(18px); } /* Panneau centré : confirmation du double, fin de partie, double de * l'ordinateur en paysage. */ .panel { width: min(24rem, 100%); padding: var(--space-4); color: var(--text); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md); box-shadow: var(--shadow-raised); animation: panel-in var(--duration-moderate) var(--easing-standard); } .panel p { margin: 0 0 var(--space-4); font-size: 20px; line-height: 26px; font-weight: 700; } .panel p:empty { display: none; } /* Fin de partie : surtitre, titre de 24 px, puis le détail des points. */ .panel .overline { margin: 0 0 var(--space-1); font-size: 13px; line-height: 16px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); } .panel .panel-title { margin-bottom: var(--space-1); font-size: 24px; line-height: 30px; } .panel .panel-detail { font-size: 17px; line-height: 25px; font-weight: 400; } .panel-actions { display: flex; gap: var(--space-2); } .panel-actions button { flex: 1 1 0; padding: 0 12px; } /* Fin de partie : « Changer les réglages », lien secondaire sous les * boutons. */ .panel-link { display: flex; align-items: center; justify-content: center; min-height: 44px; margin-top: var(--space-2); font-weight: 700; } /* « Nouvelle partie commencée. Reprendre l'ancienne », 10 s, avec sa * barre de temps. */ .banner { position: fixed; left: 50%; bottom: calc(var(--actions-height) + var(--space-2)); z-index: 9; display: flex; gap: var(--space-3); align-items: center; width: min(24rem, calc(100% - 2 * var(--space-4))); padding: var(--space-2) var(--space-3) var(--space-3); overflow: hidden; font-size: 15px; line-height: 20px; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md); box-shadow: var(--shadow-raised); transform: translateX(-50%); animation: fade-in var(--duration-fast) var(--easing-standard); } .banner span:first-child { flex: 1; } .banner button { flex: none; min-height: 44px; padding: 0 14px; } .banner-timer { position: absolute; left: 0; bottom: 0; width: 100%; height: 4px; background: var(--accent); transform-origin: left; } .banner-timer.running { animation: banner-timer 10s linear forwards; } @keyframes banner-timer { from { transform: scaleX(1); } to { transform: scaleX(0); } } @keyframes fade-in { from { opacity: 0; } to { opacity: 1; } } @keyframes sheet-up { from { transform: translateY(100%); } to { transform: translateY(0); } } @keyframes panel-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } /* Mouvement réduit : tout est instantané, rien ne roule ni ne glisse ; la * barre de temps reste visible, immobile. */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0s !important; transition-duration: 0s !important; } .prompt.thinking::after { animation: none; content: "…"; } .banner-timer.running { animation: none; } button:active:not(:disabled) { transform: none; } } /* ------------------------------------------------------------ Plateau */ .board { display: block; width: 100%; height: auto; aspect-ratio: 640 / 520; user-select: none; -webkit-user-select: none; /* Le doigt fait glisser les pions : le plateau ne fait ni défiler ni * zoomer la page (glisser-déposer, double toucher ; charte, 1i). */ touch-action: none; -webkit-tap-highlight-color: transparent; font-family: var(--font-sans); /* Alegreya Sans dessine par défaut des chiffres elzéviriens (de hauteurs * variables) : sur le plateau, des chiffres alignés se lisent mieux. */ font-variant-numeric: tabular-nums lining-nums; } .board-frame { fill: var(--frame); } .board-bed { fill: var(--bed); } .board-bar { fill: var(--bar); } .board-tray { fill: var(--tray); } .point-a { fill: var(--point-a); } .point-b { fill: var(--point-b); } /* Numéros des flèches : leur taille est une mesure du dessin (attribut * font-size), calculée pour faire au moins 12 px à l'écran (geometry.ts). * Posés sur le cadre, en portrait comme en paysage. */ .point-number { fill: var(--point-number); font-weight: 500; text-anchor: middle; } .checker { stroke-width: 2; } .checker-player { fill: var(--checker-player); stroke: var(--checker-player-edge); } .checker-computer { fill: var(--checker-computer); stroke: var(--checker-computer-edge); } /* Filet intérieur des pions (bundle.css), l'écho du logo : un cercle aux * trois cinquièmes du rayon, dessiné avec chaque pion (board-view.ts). */ .checker-ring { fill: none; stroke-width: 1.5; } .ring-player { stroke: var(--checker-player-edge); } .ring-computer { stroke: var(--checker-computer-edge); } .checker.movable { stroke: var(--highlight); stroke-width: 3; } .checker.selected { stroke: var(--highlight); stroke-width: 6; } /* Glisser-déposer (charte, 1i) : le pion parti laisse un fantôme en * pointillés ; le pion glissé, agrandi, passe devant tout. */ .drag-origin .checker { fill: none; stroke: var(--highlight); stroke-width: 2.5; stroke-dasharray: 5 4; } .drag-origin .checker-ring, .drag-origin .checker-count { display: none; } .drag-piece { pointer-events: none; } .checker.dragged { stroke: var(--highlight); stroke-width: 4; } /* Clavier : le pion ou la case d'arrivée choisis aux flèches. */ .key-focus { fill: none; stroke: var(--focus); stroke-width: 3; } /* Tailles des chiffres du plateau : attribut font-size, calculé par * geometry.ts (13 px au moins à l'écran). */ .checker-count { font-weight: 700; text-anchor: middle; } .count-player { fill: var(--count-player); } .count-computer { fill: var(--count-computer); } .target-zone { fill: var(--target); } /* Le dernier coup de l'ordinateur (couleur laiton, charte) : une flèche par * demi-coup, les pions arrivés et frappés entourés, un pion fantôme là où * un pion est parti. */ /* Épaisseur des flèches : attribut stroke-width (2,5 px à l'écran, * geometry.ts) ; elles passent sous les pions. */ .trace-arrow { stroke: var(--last-move); stroke-linecap: round; opacity: 0.85; } .trace-head { fill: var(--last-move); } /* Où le pion touché se posera : un anneau pointillé, comme les fantômes du * coup de l'ordinateur, en brique. */ .landing { fill: none; stroke: var(--highlight); stroke-width: 3; stroke-dasharray: 6 5; } .trace-landed, .trace-hit { fill: none; stroke: var(--last-move); stroke-width: 4; } .trace-hit { stroke-dasharray: 6 4; } .trace-ghost { fill: none; stroke: var(--last-move); stroke-width: 2.5; stroke-dasharray: 5 4; } .die-player { fill: var(--die-player); stroke: var(--checker-player-edge); stroke-width: 1.5; } .die-computer { fill: var(--die-computer); stroke: var(--checker-computer-edge); stroke-width: 1.5; } .pip-player { fill: var(--pip-player); } .pip-computer { fill: var(--pip-computer); } /* Dés du joueur en attente d'être lancés, et videau quand il peut doubler : * ils se touchent comme les boutons. */ .die-waiting rect { stroke: var(--highlight); stroke-width: 2.5; stroke-dasharray: 5 4; } .cube.can-double .cube-face { stroke: var(--highlight); stroke-width: 3; } /* Jet sans coup possible : dés estompés et barrés. */ .die-blocked { opacity: 0.5; } .die-strike { stroke: var(--text); stroke-width: 3; stroke-linecap: round; } /* Dé déjà utilisé par un demi-coup du coup en cours : estompé, sans être * barré (il n'est pas perdu : « Annuler » le rend). */ .die-used { opacity: 0.35; } .cube-face { fill: var(--cube); stroke: var(--checker-player-edge); stroke-width: 2; } .cube-value { fill: var(--cube-text); font-weight: 800; text-anchor: middle; } .hit { fill: transparent; cursor: pointer; } /* Paysage : comptes de points en pastilles aux bouts de la barre, consigne * dans la bande, commandes à gauche. */ .pip-chip { stroke-width: 1.5; } .chip-player { fill: var(--checker-player); stroke: var(--checker-player-edge); } .chip-computer { fill: var(--checker-computer); stroke: var(--checker-computer-edge); } .pip-chip-text { font-weight: 700; text-anchor: middle; } .chip-text-player { fill: var(--pip-player); } .chip-text-computer { fill: var(--pip-computer); } .band-pill { fill: var(--accent-soft); } .band-text { fill: var(--text); font-weight: 700; text-anchor: middle; } .control-circle { fill: none; stroke: var(--point-number); stroke-width: 2; } .control-icon { fill: none; stroke: var(--point-number); stroke-width: 2px; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; } .control.disabled { opacity: 0.35; } /* ------------------------------------------------------- Mode paysage */ /* Téléphone tourné (ADR 0012) : le plateau est l'écran, sur la hauteur * réelle (barre d'adresse comprise), cadre noyer jusqu'aux bords. La * classe vient de app.ts (rotation du téléphone, ou repli du plein écran). */ html.landscape, html.landscape body { height: 100%; overflow: hidden; } html.landscape body { max-width: none; padding: 0; background: var(--frame); } html.landscape .topbar, html.landscape .strip, html.landscape .infoline, html.landscape .prompt, html.landscape .spacer, html.landscape .actions { display: none; } html.landscape .board-box { position: fixed; inset: 0; } html.landscape .board { width: 100%; height: 100%; aspect-ratio: auto; } html.landscape .banner { bottom: var(--space-2); } /* L'aide et la vue d'une position restent des pages qu'on fait défiler. */ html.landscape.page-open, html.landscape.page-open body { height: auto; overflow: visible; } html.landscape.page-open body { padding: 0 var(--space-4); background: var(--background); } /* Repli du plein écran : le paysage ne peut pas être verrouillé, l'écran * entier tourne d'un quart de tour dans le portrait. Les touchers suivent * la rotation ; le plateau et les fenêtres sont posés dans #screen. */ html.rotated .screen { position: fixed; top: 0; left: 0; width: 100vh; width: 100dvh; height: 100vw; min-height: 0; overflow: hidden; transform-origin: top left; transform: rotate(90deg) translateY(-100%); } html.rotated .board-box, html.rotated .veil { position: absolute; } html.rotated .banner { position: absolute; } /* ------------------------------------------------------- Mes parties */ /* La feuille « Mes parties » (app/src/ui/games-view.ts) : une partie par * ligne, ses actions dessous, ses branches dépliables. */ .games-list, .games-branches { margin: 0; padding: 0; list-style: none; } .games-item { padding: var(--space-2) 0 var(--space-4); border-bottom: 1px solid var(--border); } .games-item:last-child { border-bottom: 0; } .games-item p { margin: 0 0 var(--space-2); font-weight: 500; } .games-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); } .games-branch { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; justify-content: space-between; padding-top: var(--space-2); color: var(--muted); } /* --------------------------------------------------------------- Aide */ .help { max-width: var(--page-width); margin: 0 auto; } .help h2 { margin: var(--space-8) 0 var(--space-2); font-size: 24px; line-height: 30px; font-weight: 700; } .help h3 { margin: var(--space-6) 0 var(--space-1); font-size: 20px; line-height: 26px; font-weight: 700; } .help p { margin: var(--space-2) 0; } .steps { margin: var(--space-2) 0; padding-left: var(--space-6); } .steps li + li { margin-top: var(--space-1); } .steps li::marker { color: var(--muted); } /* Glossaire : une liste de définitions, sur une carte. */ .glossary { margin: var(--space-3) 0 0; padding: var(--space-1) var(--space-4); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md); box-shadow: var(--shadow-raised); } .glossary .term-card { padding-top: var(--space-3); font-weight: 700; } .glossary dd { margin: 0; padding-bottom: var(--space-3); } .glossary dd + dt { border-top: 1px solid var(--border); } .about { margin-top: var(--space-2); font-size: 15px; line-height: 20px; color: var(--muted); } .help { user-select: text; -webkit-user-select: text; } /* La barre haute de l'aide (« Retour » et onglets) reste en vue quand la * page défile. Sa hauteur est fixe (12 + 48 + 8 + 44 + 1 px de filet) : une * question ou un terme ouvert par son adresse s'arrête 12 px sous elle. */ .help { --help-bar-height: 113px; } .help-bar { position: sticky; top: env(safe-area-inset-top); z-index: 3; height: var(--help-bar-height); margin-inline: calc(-1 * var(--space-4)); padding: var(--space-3) var(--space-4) 0; background: var(--background); } .help .question, .glossary .term-card { scroll-margin-top: calc(var(--help-bar-height) + 12px + env(safe-area-inset-top)); } /* Les onglets de l'aide : des liens, l'onglet courant souligné en brique ; * sur une ligne, qui défile si l'écran est trop étroit. */ .help-tabs { display: flex; gap: var(--space-1); overflow-x: auto; white-space: nowrap; margin-top: var(--space-2); border-bottom: 1px solid var(--border); } .help-tabs a { display: inline-flex; align-items: center; min-height: 44px; padding: 0 var(--space-3); font-weight: 500; color: var(--muted); text-decoration: none; border-bottom: 3px solid transparent; } .help-tabs a[aria-current="page"] { font-weight: 700; color: var(--text); border-bottom-color: var(--accent); } .help-notice:empty { display: none; } /* Une question de l'aide : son titre est un lien qui l'ouvre (son adresse), * ou la referme quand elle est ouverte. */ .question { border-bottom: 1px solid var(--border); } .help .question h3 { margin: 0; } .question h3 a { display: flex; gap: var(--space-2); align-items: center; min-height: 52px; padding: var(--space-2) 0; color: var(--text); text-decoration: none; } .question h3 a::after { content: ""; flex: none; width: 9px; height: 9px; margin: 0 6px 4px auto; border-right: 2px solid var(--muted); border-bottom: 2px solid var(--muted); transform: rotate(45deg); } .question.open h3 a::after { margin-bottom: -4px; transform: rotate(-135deg); } .answer { padding-bottom: var(--space-3); } /* Le terme ouvert par son adresse (#/glossaire/). */ .glossary dt a { color: var(--text); text-decoration: none; } .glossary dt.current, .glossary dt.current + dd { margin-inline: calc(-1 * var(--space-4)); padding-inline: var(--space-4); background: var(--accent-soft); /* Le surlignage montre où l'on arrive, puis s'efface après 2 s. */ animation: term-fade var(--duration-moderate) var(--easing-exit) 2s forwards; } @keyframes term-fade { to { background-color: transparent; } } /* -------------------------------------------------- Liens et boutons */ /* Un lien qui mène à un écran, avec l'allure d'un bouton. */ a.button { display: inline-flex; gap: 6px; align-items: center; justify-content: center; min-height: var(--button-height); padding: 0 20px; font-weight: 700; color: var(--on-accent); text-decoration: none; background: var(--accent); border: 2px solid var(--accent); border-radius: var(--radius-pill); white-space: nowrap; touch-action: manipulation; -webkit-tap-highlight-color: transparent; } a.button.secondary { color: var(--text); background: transparent; border-color: var(--border-strong); } a.button:active { transform: translateY(1px); filter: brightness(0.94); } a.icon-button, a.item, a.link-button { text-decoration: none; touch-action: manipulation; -webkit-tap-highlight-color: transparent; } a.link-button { display: inline-flex; align-items: center; text-decoration: underline; } .infoline .copy-link { color: var(--accent); } /* Zone de texte de la copie de repli (presse-papiers), hors de l'écran. */ .offscreen { position: fixed; top: 0; left: -10000px; opacity: 0; } /* ------------------------------------------------ Vue d'une position */ .position-view { max-width: var(--page-width); margin: 0 auto; padding-bottom: var(--space-6); } .position-title { margin: var(--space-6) 0 var(--space-2); font-size: 24px; line-height: 30px; } .position-message:empty { display: none; } .position-board { margin: var(--space-2) 0; } .position-detail { margin: var(--space-2) 0 var(--space-4); font-size: 15px; line-height: 20px; color: var(--muted); } /* Les actions empilées sur toute la largeur, la principale en premier. */ .position-actions { display: flex; flex-direction: column; gap: var(--space-2); } .position-actions > * { width: 100%; } #position-copy { display: inline-flex; gap: 6px; align-items: center; justify-content: center; } #position-copy .icon { width: 20px; height: 20px; } /* ------------------------------------------- Accueil, tutoriel, aide */ /* Maquettes « Étape 0e.5 » et « Accueil » ; règles : * docs/identite/charte/accueil-tutoriel-aide.md. */ .brand-link { display: flex; align-items: center; min-height: 44px; } /* Le tutoriel et le premier lancement ont leur propre en-tête. */ html.bare .topbar { display: none; } .home, .tutorial { max-width: var(--page-width); margin: 0 auto; padding-bottom: var(--space-6); } .overline { margin: 0; font-size: 13px; line-height: 16px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); } .muted { color: var(--muted); } .caption { margin: 0; font-size: 13px; line-height: 18px; color: var(--muted); } .card { display: block; padding: var(--space-4); color: var(--text); text-decoration: none; background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md); box-shadow: var(--shadow-raised); } /* Une étiquette d'action dans une carte-lien, en contour. */ .pill { display: inline-flex; flex: none; align-items: center; min-height: 44px; padding: 0 16px; font-weight: 700; color: var(--text); border: 2px solid var(--border-strong); border-radius: var(--radius-pill); } /* Premier lancement. */ .welcome { display: flex; flex-direction: column; gap: var(--space-6); min-height: 100vh; min-height: 100dvh; padding: var(--space-6) 0 var(--space-6); } .welcome-logo { margin: 0; } .welcome-logo img { height: 34px; width: auto; } .welcome-board { margin-inline: calc(-1 * var(--space-2)); width: calc(100% + 2 * var(--space-2)); } .welcome-text h2, .home-intro h2 { margin: 0 0 var(--space-2); font-size: 32px; line-height: 36px; font-weight: 800; letter-spacing: -0.01em; } .welcome-text p, .home-intro p { margin: 0; } .welcome-choices { display: flex; flex-direction: column; gap: 10px; margin-top: auto; } .choice { display: flex; flex-direction: column; justify-content: center; min-height: 64px; padding: 10px 20px; color: var(--text); text-decoration: none; border: 2px solid var(--border-strong); border-radius: 16px; touch-action: manipulation; -webkit-tap-highlight-color: transparent; } .choice span { font-size: 15px; line-height: 20px; color: var(--muted); } .choice.primary { color: var(--on-accent); background: var(--accent); border-color: var(--accent); } .choice.primary span { color: var(--on-accent); } .choice:active { transform: translateY(1px); filter: brightness(0.94); } .welcome-choices .caption { margin-top: var(--space-1); text-align: center; } /* Accueil. */ .home-main { display: flex; flex-direction: column; gap: 20px; padding-top: var(--space-2); } .home-game { display: flex; flex-direction: column; gap: 14px; } .home-game-top { display: flex; gap: 14px; align-items: center; } .home-mini { flex: none; width: 132px; } /* La miniature ne porte pas les numéros des flèches, illisibles à cette * taille. */ .home-board .point-number { display: none; } .home-facts { display: flex; flex-direction: column; gap: 2px; min-width: 0; } .home-pips { font-size: 15px; line-height: 20px; color: var(--muted); font-variant-numeric: tabular-nums lining-nums; } .home-actions { display: flex; gap: var(--space-2); } .home-actions > * { flex: 1 1 0; min-width: 0; padding: 0 12px; } .install-card { display: flex; gap: var(--space-3); align-items: center; margin-top: calc(-1 * var(--space-2)); padding: 10px 4px 10px 14px; background: var(--surface-sunken); border-radius: var(--radius-md); } .install-card .install-symbol { flex: none; width: 32px; height: 32px; } .install-text { display: flex; flex: 1; flex-direction: column; min-width: 0; font-size: 15px; line-height: 20px; } .install-card button.secondary { flex: none; min-height: 44px; padding: 0 14px; background: var(--surface); } .install-card .icon-button .icon { width: 18px; height: 18px; color: var(--muted); } .home-rules { display: flex; gap: var(--space-3); align-items: center; } .home-rules-text { display: flex; flex: 1; flex-direction: column; min-width: 0; } .home-rules-text .muted { font-size: 15px; line-height: 20px; } .home-progress:empty { display: none; } .home-footer { display: flex; gap: 10px; align-items: center; margin-top: var(--space-4); font-size: 13px; line-height: 18px; color: var(--muted); } .home-footer img { flex: none; width: 28px; height: 28px; } /* Feuille d'installation. */ .sheet-head .install-symbol { width: 40px; height: 40px; margin-right: var(--space-3); } #install-sheet .sheet-title { flex: 1; } .install-lead { margin: var(--space-2) 0 var(--space-4); color: var(--muted); } .install-steps { display: flex; flex-direction: column; gap: var(--space-3); margin: 0 0 var(--space-4); padding: 0; list-style: none; } .install-steps li { display: flex; gap: var(--space-3); align-items: flex-start; } .step-number { display: flex; flex: none; align-items: center; justify-content: center; width: 28px; height: 28px; font-weight: 800; font-variant-numeric: tabular-nums lining-nums; background: var(--accent-soft); border-radius: 50%; } .install-done { width: 100%; } /* Tutoriel. */ .tutorial-head { display: flex; gap: var(--space-3); align-items: center; min-height: 52px; padding-top: var(--space-2); } .tutorial-progress { display: grid; flex: 1; grid-template-columns: repeat(8, minmax(0, 1fr)); gap: var(--space-1); } /* Un segment : 4 px de trait, dans une zone de 44 px de haut quand c'est * un lien vers une étape déjà vue. */ .tutorial-progress .segment { position: relative; display: block; height: 44px; } .tutorial-progress .segment::before { content: ""; position: absolute; top: 20px; left: 0; right: 0; height: 4px; background: var(--border); border-radius: var(--radius-pill); } .tutorial-progress .segment.done::before { background: var(--accent); } .tutorial-progress .segment[aria-current]::before { top: 19px; height: 6px; } .tutorial-skip { display: inline-flex; align-items: center; min-height: 44px; font-size: 15px; font-weight: 500; color: var(--muted); } .tutorial-title { margin: var(--space-1) 0 var(--space-3); font-size: 24px; line-height: 30px; } .tutorial-board-box { position: relative; } @media (max-width: 40rem) { .tutorial-board-box { margin-inline: calc(var(--space-2) - var(--space-4)); } } #tutorial-replay { position: absolute; right: var(--space-1); bottom: calc(-44px - var(--space-1)); color: var(--accent); } .tutorial-text { margin-top: var(--space-3); padding-right: 52px; } .tutorial-text p { margin: 0 0 10px; } .tutorial-task { margin: var(--space-3) 0 0; padding: 10px 14px; font-weight: 700; background: var(--accent-soft); border-radius: var(--radius-md); } .tutorial-feedback { display: flex; gap: var(--space-2); align-items: flex-start; min-height: 25px; margin: var(--space-3) 0 0; } .tutorial-feedback:empty { min-height: 0; } .tutorial-feedback[data-verdict="right"] { font-weight: 700; } .tutorial-feedback[data-verdict="right"] .icon { color: var(--verdict-good); } .tutorial-feedback[data-verdict="info"] { color: var(--muted); } .tutorial-cube { display: flex; gap: var(--space-2); margin-top: var(--space-3); } .tutorial-cube button { flex: 1 1 0; } .tutorial-nav { display: grid; grid-template-columns: 1fr 1.4fr; gap: var(--space-2); margin-top: var(--space-6); } .tutorial-nav .button { min-width: 0; } /* Le sens du jeu : deux flèches circulaires, claire pour le joueur, sombre * pour l'ordinateur, aux couleurs de leurs pions, chacune avec un liseré de * la couleur opposée pour rester lisible sur le bois ; jamais en brique (la * brique veut dire « à faire »). */ .tutorial-path path { fill: none; stroke-linecap: round; stroke-linejoin: round; pointer-events: none; } .tutorial-path .direction-player.direction-line { stroke: var(--checker-player); } .tutorial-path .direction-player.direction-edge { stroke: var(--checker-computer); } .tutorial-path .direction-computer.direction-line { stroke: var(--checker-computer); } .tutorial-path .direction-computer.direction-edge { stroke: var(--checker-player); } .tutorial-path .direction-head.direction-player { fill: var(--checker-player); stroke: var(--checker-computer); } .tutorial-path .direction-head.direction-computer { fill: var(--checker-computer); stroke: var(--checker-player); } /* En paysage, comme la position partagée : le plateau à gauche sur toute * la hauteur (avec la colonne gauche du cadre), et à droite une colonne * avec la barre de progression, le titre, le texte, la consigne « à vous » * et les boutons, le tout sans défilement de la page. Si le texte est trop * long pour l'écran, lui seul défile : les boutons restent en vue. */ html.landscape .tutorial { position: fixed; inset: 0; display: grid; grid-template-columns: minmax(0, 1fr) clamp(288px, 44vw, 400px); grid-template-rows: auto auto minmax(0, 1fr) auto auto; max-width: none; margin: 0; padding: 0; overflow: hidden; background: var(--background); } html.landscape .tutorial > * { grid-column: 2; margin-inline: 0; padding-inline: var(--space-4) calc(var(--space-4) + env(safe-area-inset-right)); } html.landscape .tutorial .overline { display: none; } html.landscape .tutorial-head { grid-row: 1; min-height: 44px; padding-top: 0; } html.landscape .tutorial-title { grid-row: 2; margin: 0 0 var(--space-1); padding-right: calc(44px + var(--space-4)); font-size: 20px; line-height: 26px; } /* La boîte du plateau s'efface : le plateau et « Revoir » se placent * eux-mêmes dans la grille. */ html.landscape .tutorial-board-box { display: contents; } html.landscape .tutorial-board { grid-row: 1 / -1; grid-column: 1; width: 100%; height: 100%; margin: 0; aspect-ratio: auto; background: var(--frame); } html.landscape #tutorial-replay { position: static; grid-row: 2; grid-column: 2; justify-self: end; align-self: start; margin: -9px calc(var(--space-2) + env(safe-area-inset-right)) 0 0; padding: 0; } html.landscape .tutorial-body { grid-row: 3; min-height: 0; overflow-y: auto; font-size: 15px; line-height: 20px; } html.landscape .tutorial-text { margin: 0; padding-right: 0; } html.landscape .tutorial-text p { margin-bottom: var(--space-2); } html.landscape .tutorial-task { margin: 0; padding: var(--space-2) 12px; } html.landscape .tutorial-feedback { min-height: 0; margin: var(--space-2) 0 0; } html.landscape .tutorial-cube { grid-row: 4; margin-top: var(--space-2); } /* La réponse au double donnée et saluée, les deux boutons n'ont plus * d'usage : en paysage, leur place revient au texte. */ html.landscape .tutorial[data-solved] .tutorial-cube { display: none; } /* Écran très bas (844 × 320) : texte un cran plus petit. */ @media (max-height: 360px) { html.landscape .tutorial-body { font-size: 14px; line-height: 18px; } html.landscape .tutorial-title { font-size: 18px; line-height: 24px; } } html.landscape .tutorial-nav { grid-row: 5; margin: 0; padding-block: var(--space-2) calc(var(--space-2) + env(safe-area-inset-bottom)); } /* Le premier lancement, en paysage : le plateau tient dans la hauteur. */ html.landscape .welcome-board { width: min(100%, calc(70vh * 640 / 520)); height: auto; margin-inline: auto; aspect-ratio: 640 / 520; } html.landscape .welcome { min-height: 0; } /* Aide. */ .help-tutorial { display: flex; gap: var(--space-3); align-items: center; margin-top: var(--space-4); } .help-tutorial-text { display: flex; flex: 1; flex-direction: column; min-width: 0; } .help-tutorial-text .muted { font-size: 15px; line-height: 20px; } .help-intro { margin-top: var(--space-4); } .see-board { display: inline-flex; align-items: center; min-height: 44px; font-weight: 500; } .glossary-search { display: flex; flex-direction: column; gap: var(--space-1); margin-top: var(--space-3); } .search-label { font-size: 15px; line-height: 20px; font-weight: 500; } .glossary-search input { min-height: 48px; padding: 0 var(--space-3); font: inherit; color: var(--text); background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius-md); user-select: text; -webkit-user-select: text; } .term-card + .term-card { border-top: 1px solid var(--border); } .glossary .english { margin-left: var(--space-2); font-size: 13px; font-style: italic; font-weight: 400; color: var(--muted); } .glossary dd p { margin: 0 0 var(--space-1); } .see-also { font-size: 15px; line-height: 20px; color: var(--muted); } .glossary .term-card.current { margin-inline: calc(-1 * var(--space-4)); padding-inline: var(--space-4); background: var(--accent-soft); } .glossary .term-card.current dt, .glossary .term-card.current dd { margin-inline: 0; padding-inline: 0; background: none; } /* ------------------------------------------------------------ Match */ /* « Match en 5 points » sous la barre haute (portrait), hauteur fixe. */ .match-line { flex: none; height: 20px; text-align: center; } /* Le score de chaque camp, dans son bandeau, juste avant son compte de * points : une pastille carrée. */ .strip .match-score { display: inline-flex; flex: none; align-items: center; justify-content: center; min-width: 28px; height: 28px; padding: 0 4px; font-weight: 700; font-variant-numeric: tabular-nums lining-nums; border: 2px solid var(--border-strong); border-radius: var(--radius-sm); } /* Paysage : le score dans la colonne de commandes, sous le menu. */ .match-box-frame { fill: none; stroke: var(--point-number); stroke-opacity: 0.5; stroke-width: 1.5; } .match-box-score { font-weight: 700; fill: var(--point-number); } .match-box-of { fill: var(--point-number); } /* ------------------------------------------------- Nouvelle partie */ /* La page remplit l'écran : la barre d'action reste collée en bas, même * quand le contenu est plus court que l'écran. */ .new-game { display: flex; flex: 1; flex-direction: column; width: 100%; max-width: var(--page-width); margin: 0 auto; } .new-game .new-game-body { flex: 1; } .new-game-head { display: flex; gap: var(--space-2); align-items: center; min-height: 60px; } .new-game-head h2 { margin: 0; font-size: 22px; line-height: 28px; } .new-game-body { display: flex; flex-direction: column; gap: var(--space-3); padding-bottom: var(--space-6); } /* Portrait : les deux colonnes l'une sous l'autre, comme une seule. */ .new-game-column { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; } .new-game-body .overline { margin-top: var(--space-4); } /* Sélecteur à deux segments : fond creusé, segment choisi en relief. */ .segmented { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-1); padding: var(--space-1); background: var(--surface-sunken); border-radius: var(--radius-pill); } .segmented .segment { color: var(--text); background: transparent; border-color: transparent; } .segmented .segment[aria-checked="true"] { background: var(--surface); box-shadow: var(--shadow-raised); } .new-game-options { display: flex; flex-direction: column; gap: var(--space-2); } .lengths { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; } .lengths .length { width: 44px; min-width: 44px; height: 44px; min-height: 44px; padding: 0; color: var(--text); background: transparent; border-color: var(--border-strong); border-radius: var(--radius-sm); } .lengths .length[aria-checked="true"] { color: var(--on-accent); background: var(--accent); border-color: var(--accent); } .new-game-rule { margin: 0; font-size: 15px; line-height: 21px; color: var(--muted); } /* Six barres de hauteur croissante ; celles jusqu'au niveau choisi sont en * brique, le niveau choisi porte l'anneau de focus. */ .level-bars { display: flex; gap: var(--space-2); align-items: flex-end; } .level-bars .level { display: flex; flex: 1; align-items: flex-end; min-width: 44px; height: 84px; padding: 4px; background: transparent; border: 0; border-radius: var(--radius-sm); } .level-bar { display: block; width: 100%; background: var(--border); border-radius: 4px; } .level-1 .level-bar { height: 18px; } .level-2 .level-bar { height: 28px; } .level-3 .level-bar { height: 38px; } .level-4 .level-bar { height: 50px; } .level-5 .level-bar { height: 62px; } .level-6 .level-bar { height: 76px; } .level-bars .level.reached .level-bar { background: var(--accent); } .level-bars .level[aria-checked="true"] { outline: 3px solid var(--focus); outline-offset: 0; } .level-name { margin: 0; font-size: 22px; line-height: 28px; font-weight: 700; } .level-text { margin: 0; color: var(--muted); } .new-game-body .item { display: flex; gap: 14px; align-items: center; width: 100%; min-height: 60px; padding: 0; font-weight: 500; text-align: left; color: var(--text); background: transparent; border: 0; border-radius: 0; } .new-game-body .item-label { display: flex; flex-direction: column; } .new-game-body .item small { font-size: 15px; line-height: 20px; font-weight: 400; color: var(--muted); } /* Barre d'action collée en bas : le seul bouton principal de l'écran. */ .new-game-actions { position: sticky; bottom: 0; display: flex; flex-direction: column; gap: var(--space-1); align-items: stretch; padding: var(--space-3) 0 var(--space-4); background: var(--background); border-top: 1px solid var(--border); } .new-game-kept { margin: 0; font-size: 13px; line-height: 18px; text-align: center; color: var(--muted); } /* En paysage (915 × 412, et jusqu'à 844 × 320), tout tient sans défiler : * deux colonnes au-dessus de la barre d'action, le type de partie à * gauche, le niveau et le conseil du maître à droite ; tout est resserré, * et la barre d'action tient sur une ligne, le bouton à droite sous le * pouce. */ html.landscape .new-game { max-width: 60rem; } html.landscape .new-game-head { min-height: 44px; } html.landscape .new-game-head h2 { font-size: 20px; line-height: 24px; } html.landscape .new-game-body { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--space-6); align-content: start; padding-bottom: var(--space-2); } html.landscape .new-game-column { gap: var(--space-2); } html.landscape .new-game-body .overline { margin-top: 0; } html.landscape .new-game-body .during-game { display: none; } html.landscape .level-bars .level { height: 48px; padding: 3px; } html.landscape .level-1 .level-bar { height: 10px; } html.landscape .level-2 .level-bar { height: 17px; } html.landscape .level-3 .level-bar { height: 24px; } html.landscape .level-4 .level-bar { height: 30px; } html.landscape .level-5 .level-bar { height: 36px; } html.landscape .level-6 .level-bar { height: 42px; } html.landscape .level-name { font-size: 18px; line-height: 22px; } html.landscape .level-text { font-size: 15px; line-height: 20px; } html.landscape .new-game-body .item { min-height: 44px; } html.landscape .new-game-body .item-label { line-height: 22px; } html.landscape .new-game-body .item small { line-height: 18px; } html.landscape .new-game-actions { flex-direction: row; gap: var(--space-4); align-items: center; justify-content: flex-end; padding: var(--space-2) 0; } html.landscape .new-game-kept { order: -1; text-align: right; } /* Un peu plus d'air quand la hauteur le permet (915 × 412). */ @media (min-height: 380px) { html.landscape .new-game-column { gap: var(--space-3); } html.landscape .level-bars .level { height: 64px; } html.landscape .level-1 .level-bar { height: 14px; } html.landscape .level-2 .level-bar { height: 22px; } html.landscape .level-3 .level-bar { height: 30px; } html.landscape .level-4 .level-bar { height: 39px; } html.landscape .level-5 .level-bar { height: 48px; } html.landscape .level-6 .level-bar { height: 58px; } } /* En paysage, comme l'écran de jeu : le plateau à gauche sur toute la * hauteur (colonne gauche du cadre avec le videau seul), le titre, le contexte et les actions * dans une colonne à droite. */ html.landscape .position-view.with-board { position: fixed; inset: 0; display: grid; grid-template-columns: minmax(0, 1fr) 288px; grid-template-rows: auto auto auto auto 1fr; max-width: none; margin: 0; padding: 0; overflow-y: auto; background: var(--background); } html.landscape .position-view.with-board > * { grid-column: 2; margin-inline: 0; padding-inline: var(--space-4) calc(var(--space-4) + env(safe-area-inset-right)); } html.landscape .position-view.with-board .position-title { margin-top: var(--space-4); } html.landscape .position-view.with-board .position-board { grid-row: 1 / -1; grid-column: 1; width: 100%; height: 100%; margin: 0; padding: 0; aspect-ratio: auto; background: var(--frame); } html.landscape .position-view.with-board .position-actions { padding-bottom: var(--space-4); } html.landscape .position-view.with-board .position-actions > * { padding-inline: 16px; } /* ------------------------------------------- Conseil du maître (1d) */ /* La ligne du maître : à la place du dernier coup, dans la ligne réservée * de 44 px (elle ne pousse rien). Pictogramme et mot en couleur * d'appréciation, phrase en italique tronquée, chevron. */ .advice-line { display: flex; flex: 1; gap: var(--space-2); align-items: center; min-width: 0; height: 44px; font-size: 15px; line-height: 20px; color: var(--text); text-decoration: none; border-radius: 12px; -webkit-tap-highlight-color: transparent; } .advice-line.appear { animation: fade-in var(--duration-fast) var(--easing-standard); } .advice-line .icon { width: 20px; height: 20px; } .advice-line .icon-chevron { width: 16px; height: 16px; color: var(--muted); } .advice-text { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .advice-word { font-weight: 700; } [data-verdict="good"] .icon:not(.icon-chevron), [data-verdict="good"] .advice-word { color: var(--verdict-good); } [data-verdict="doubtful"] .icon:not(.icon-chevron), [data-verdict="doubtful"] .advice-word { color: var(--verdict-doubtful); } [data-verdict="bad"] .icon:not(.icon-chevron), [data-verdict="bad"] .advice-word, [data-verdict="veryBad"] .icon:not(.icon-chevron), [data-verdict="veryBad"] .advice-word { color: var(--verdict-bad); } /* Paysage : la pastille du mot, au début de la bande centrale. */ .advice-chip-pill { fill: var(--surface); stroke: currentColor; stroke-width: 2; } .advice-chip-icon { fill: none; stroke: currentColor; stroke-width: 2px; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; } .advice-chip-text { fill: currentColor; font-weight: 700; } .advice-chip.verdict-good { color: var(--verdict-good); } .advice-chip.verdict-doubtful { color: var(--verdict-doubtful); } .advice-chip.verdict-bad, .advice-chip.verdict-veryBad { color: var(--verdict-bad); } /* La feuille d'explication : elle monte du bas, au-dessus du plateau * voilé. */ .explain-sheet { display: flex; flex-direction: column; gap: var(--space-3); } .explain-verdict { display: inline-flex; gap: 6px; align-items: center; margin: 0; font-size: 15px; line-height: 20px; font-weight: 700; } .explain-verdict .icon { width: 18px; height: 18px; } .explain-verdict[data-verdict="good"] { color: var(--verdict-good); } .explain-verdict[data-verdict="doubtful"] { color: var(--verdict-doubtful); } .explain-verdict[data-verdict="bad"], .explain-verdict[data-verdict="veryBad"] { color: var(--verdict-bad); } .explain-title { margin: 0; font-size: 24px; line-height: 30px; } .explain-phrase { margin: 0; font-style: italic; } .explain-body { display: flex; flex-direction: column; gap: var(--space-2); } .explain-body .segment { min-height: 44px; font-size: 15px; } /* Le coup montré : le vôtre en couleur du texte, celui du maître en * couleur du bon coup (charte 1d). */ .explain-board[data-move="mine"] { --last-move: var(--text); } .explain-board[data-move="best"] { --last-move: var(--verdict-good); } .explain-legend { margin: 0; font-size: 13px; line-height: 18px; color: var(--muted); } .explain-numbers { border-top: 1px solid var(--border); } .explain-numbers summary { display: flex; align-items: center; min-height: 44px; font-size: 15px; font-weight: 500; cursor: pointer; } .explain-table { display: grid; grid-template-columns: auto 1fr auto; gap: 4px 12px; font-size: 15px; line-height: 20px; font-variant-numeric: tabular-nums; } .explain-table .strong { font-weight: 700; } .explain-note { margin: var(--space-2) 0 0; font-size: 13px; line-height: 18px; color: var(--muted); } .explain-glossary { min-height: 44px; } .explain-actions { display: flex; justify-content: flex-end; } .explain-actions button { min-width: 140px; } /* En paysage : la feuille prend la hauteur de l'écran ; le plateau à * gauche, le texte à droite. */ /* Le plateau tient dans la hauteur, sous le sélecteur et sa légende. */ html.landscape .explain-board { width: auto; max-width: 100%; height: calc(100vh - 140px); height: calc(100dvh - 140px); aspect-ratio: 640 / 520; } html.landscape .explain-sheet { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-auto-flow: dense; column-gap: var(--space-4); align-content: start; width: min(100%, 60rem); height: 100%; border-radius: 0; } html.landscape .explain-sheet > * { grid-column: 2; } html.landscape .explain-sheet > .explain-body { grid-column: 1; grid-row: 1 / span 8; } /* ------------------------------------------ Bilan de fin de partie (1e) */ /* Maquette « Bilan de fin de partie » (charte, ecrans-etape-1.md) : un * panneau centré, à la place du panneau de fin de partie ; il défile si * l'écran est trop bas. */ .summary-panel { display: flex; flex-direction: column; gap: var(--space-3); width: min(26rem, 100%); max-height: 100%; overflow-y: auto; } .summary-panel p { margin: 0; font-size: 17px; line-height: 25px; font-weight: 400; } .summary-head { display: flex; align-items: center; justify-content: space-between; min-height: 44px; margin: calc(-1 * var(--space-2)) 0; } .summary-title { margin: 0; font-size: 28px; line-height: 34px; } .summary-panel .summary-status { font-size: 15px; line-height: 20px; color: var(--muted); } /* Trois cases : bons coups, imprécisions, erreurs. */ .summary-counts { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-2); } .summary-count { display: flex; flex-direction: column; align-items: center; padding: var(--space-2) var(--space-1); background: var(--surface-sunken); border-radius: var(--radius-sm); } .summary-number { font-size: 28px; line-height: 34px; font-weight: 800; font-variant-numeric: tabular-nums lining-nums; } .summary-word { display: inline-flex; gap: 4px; align-items: center; font-size: 13px; line-height: 18px; font-weight: 700; text-align: center; } .summary-word .icon { width: 16px; height: 16px; } [data-verdict="good"] > .summary-word, .summary-verdict[data-verdict="good"], .row-word[data-verdict="good"], .viewer-phrase[data-verdict="good"] strong { color: var(--verdict-good); } [data-verdict="doubtful"] > .summary-word, .summary-verdict[data-verdict="doubtful"], .row-word[data-verdict="doubtful"], .viewer-phrase[data-verdict="doubtful"] strong { color: var(--verdict-doubtful); } [data-verdict="bad"] > .summary-word, .summary-verdict[data-verdict="bad"], .summary-verdict[data-verdict="veryBad"], .row-word[data-verdict="bad"], .row-word[data-verdict="veryBad"], .viewer-phrase[data-verdict="bad"] strong, .viewer-phrase[data-verdict="veryBad"] strong { color: var(--verdict-bad); } /* Deux jauges sur une ligne chacune : votre jeu (niveau), les dés. */ .summary-gauges { display: flex; flex-direction: column; gap: var(--space-1); } .gauge { display: grid; grid-template-columns: 5.5rem minmax(3rem, 1fr) auto; gap: var(--space-2); align-items: center; min-height: 28px; font-size: 15px; line-height: 20px; } .gauge-name { font-weight: 700; } .gauge-value { color: var(--muted); text-align: right; } .gauge-track { position: relative; height: 8px; overflow: hidden; background: var(--surface-sunken); border-radius: 4px; } .gauge-fill { position: absolute; top: 0; bottom: 0; left: 0; background: var(--verdict-good); border-radius: 4px; } .gauge-fill.level-1 { width: 16.7%; } .gauge-fill.level-2 { width: 33.3%; } .gauge-fill.level-3 { width: 50%; } .gauge-fill.level-4 { width: 66.7%; } .gauge-fill.level-5 { width: 83.3%; } .gauge-fill.level-6 { width: 100%; } /* La chance : jauge centrée, à droite si elle vous a aidé. */ .gauge-track.centred::after { content: ""; position: absolute; top: 0; bottom: 0; left: calc(50% - 1px); width: 2px; background: var(--muted); } .gauge-fill.dice { background: var(--border-strong); border-radius: 0; } .gauge-fill.dice.for-1 { left: 50%; width: 25%; } .gauge-fill.dice.for-2 { left: 50%; width: 50%; } .gauge-fill.dice.against-1 { left: 25%; width: 25%; } .gauge-fill.dice.against-2 { left: 0; width: 50%; } .gauge-fill.dice.even { left: calc(50% - 6px); width: 12px; } .summary-glossary { display: inline-flex; align-items: center; align-self: flex-start; font-size: 15px; } /* Le coup à revoir : mini-plateau, mot, votre coup, celui du maître. */ .summary-review { display: flex; flex-direction: column; gap: var(--space-2); padding-top: var(--space-3); border-top: 1px solid var(--border); } .summary-review-body { display: grid; grid-template-columns: minmax(0, 9rem) minmax(0, 1fr); gap: var(--space-3); align-items: start; } .summary-panel .summary-verdict { display: inline-flex; gap: 6px; align-items: center; font-size: 15px; line-height: 20px; font-weight: 700; } .summary-verdict .icon { width: 18px; height: 18px; } .summary-panel .summary-played, .summary-panel .summary-master { font-size: 15px; line-height: 20px; } .summary-panel .summary-master { font-style: italic; } .summary-review-actions, .viewer-actions { display: flex; gap: var(--space-2); align-items: center; } /* L'emplacement de « Demander à une IA » (étape 1f) : réservé, vide. */ .ask-ai-slot { flex: 1 1 0; min-height: 44px; } /* Deux boutons de 48 px, côte à côte, jamais étirés en hauteur. */ .summary-actions { align-items: center; } .summary-actions > * { flex: 1 1 0; height: var(--button-height); padding: 0 12px; } /* En paysage : deux colonnes, le résultat et les mesures à gauche, le * coup à revoir et les actions à droite. */ html.landscape .summary-panel { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-auto-flow: row dense; column-gap: var(--space-6); align-content: start; width: min(52rem, 100%); } html.landscape .summary-panel > * { grid-column: 1; } html.landscape .summary-panel > .summary-review, html.landscape .summary-panel > .summary-end { grid-column: 2; } html.landscape .summary-panel > .summary-review { grid-row: 1 / span 4; padding-top: 0; border-top: 0; } /* Les actions en bas de la colonne de droite, « Changer les réglages » * dessous, comme en portrait. */ html.landscape .summary-panel > .summary-end { grid-row: 5 / span 2; align-self: end; } /* ------------------------------------------- Analyse d'une partie (1e) */ /* Maquette interactive « Analyse » : une page qui défile. */ .analysis { max-width: var(--page-width); margin: 0 auto; padding-bottom: var(--space-6); } .analysis-head { display: flex; gap: var(--space-3); align-items: center; margin: var(--space-3) 0; } .analysis-title { margin: 0; font-size: 24px; line-height: 30px; } .analysis-message:empty { display: none; } .analysis-card { margin: 0 0 var(--space-4); padding: var(--space-3); background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-md); } .analysis-card > .overline { margin-bottom: var(--space-2); } /* La courbe de vos chances de gagner : on la parcourt du doigt. */ .analysis-curve { display: block; width: 100%; height: 168px; touch-action: pan-y; user-select: none; -webkit-user-select: none; -webkit-tap-highlight-color: transparent; font-family: var(--font-sans); } .analysis-curve:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; } .curve-axis { stroke: var(--border); stroke-width: 1; } .curve-half { stroke: var(--border-strong); stroke-width: 1; stroke-dasharray: 4 4; } .curve-label { fill: var(--muted); font-size: 12px; } .curve-area { fill: var(--accent-soft); } .curve-line { fill: none; stroke: var(--text); stroke-width: 2; stroke-linejoin: round; } .curve-dot.other { fill: var(--text); } .curve-dot.verdict-good { fill: var(--verdict-good); } .curve-dot.verdict-doubtful { fill: var(--verdict-doubtful); } .curve-dot.verdict-bad, .curve-dot.verdict-veryBad { fill: var(--verdict-bad); } .curve-cursor { stroke: var(--accent); stroke-width: 2; } .curve-ring { fill: none; stroke: var(--accent); stroke-width: 2; } .curve-band { fill: transparent; cursor: pointer; } .curve-band.selected { fill: var(--accent); fill-opacity: 0.08; } /* Le visualiseur : « ‹ Coup 7 sur 24 · 6-1 › », le plateau d'avant le * coup, la phrase du maître, votre coup contre celui du maître. */ .viewer-nav { display: flex; gap: var(--space-2); align-items: center; justify-content: space-between; } #analysis-prev .icon { transform: rotate(180deg); } .viewer-step[aria-disabled="true"] { opacity: 0.35; pointer-events: none; } .viewer-turn { margin: 0; font-weight: 700; text-align: center; font-variant-numeric: tabular-nums lining-nums; } .viewer-body, .viewer-text { display: flex; flex-direction: column; gap: var(--space-2); } .viewer-phrase { margin: 0; } .viewer-phrase strong { display: inline-flex; gap: 4px; align-items: center; } .viewer-phrase strong .icon { width: 18px; height: 18px; } .viewer-legend { margin: 0; font-size: 13px; line-height: 18px; color: var(--muted); } .viewer-chance { margin: 0; font-size: 15px; line-height: 20px; } .viewer-chance:empty { display: none; } .viewer-text .segment { min-height: 44px; font-size: 15px; } .analysis-board[data-move="mine"] { --last-move: var(--text); } .analysis-board[data-move="best"] { --last-move: var(--verdict-good); } /* Les coups à revoir : filtres en pastilles, une ligne par coup. */ .filters { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-bottom: var(--space-2); } .filters .filter { min-height: 44px; padding: 0 14px; font-size: 15px; font-weight: 500; color: var(--text); background: var(--surface-sunken); border: 1px solid var(--border); } /* Le filtre choisi en accent-soft, comme l'onglet choisi de l'aide. */ .filters .filter[aria-pressed="true"] { font-weight: 700; color: var(--text); background: var(--accent-soft); border-color: var(--accent); } .analysis-list { margin: 0; padding: 0; list-style: none; } .analysis-row { display: grid; grid-template-columns: 2rem 2.5rem minmax(0, 1fr) auto; gap: var(--space-2); align-items: center; min-height: 44px; padding: 0 var(--space-1); color: var(--text); text-decoration: none; border-bottom: 1px solid var(--border); } .analysis-row[aria-current="true"] { background: var(--accent-soft); } .row-number, .row-roll { color: var(--muted); font-variant-numeric: tabular-nums lining-nums; } .row-move { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .row-word { display: inline-flex; gap: 4px; align-items: center; font-size: 15px; font-weight: 700; } .row-word .icon { width: 16px; height: 16px; } .analysis-empty { padding: var(--space-2) 0; color: var(--muted); } .analysis-table { grid-template-columns: minmax(0, 1fr) auto auto; } /* En paysage : le plateau du visualiseur à gauche, tenu dans la hauteur * de l'écran ; le texte à droite. */ html.landscape .viewer-body { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--space-4); align-items: start; } html.landscape .analysis-board { width: auto; max-width: 100%; height: calc(100vh - 120px); height: calc(100dvh - 120px); aspect-ratio: 640 / 520; } /* Le mini-plateau du coup à revoir : trop petit pour les numéros des * flèches, qui se chevaucheraient. */ .summary-board .point-number { display: none; } /* ------------------------------------------- Demander à une IA (1f) */ /* Le bouton : secondaire partout, avec l'icône de copie (charte, 1f). */ a.button.ask-ai .icon, #prompt-copy .icon { flex: none; width: 20px; height: 20px; } .ask-ai-slot .ask-ai { width: 100%; } .explain-actions { gap: var(--space-2); flex-wrap: wrap; } .term-card .ask-ai { margin-top: var(--space-2); } /* Sa propre feuille, au-dessus de l'analyse et du glossaire. */ .prompt-veil { z-index: 12; } .prompt-page { display: flex; flex-direction: column; gap: var(--space-2); } /* Depuis la feuille du maître ou le bilan, la page du prompt prend la place * de leur contenu, à la même hauteur : la feuille garde sa taille, son * contenu reste en place mais invisible, et la page la couvre ; le texte du * prompt défile dans la hauteur qui reste. Une seule feuille, une seule * poignée, une seule croix. */ .sheet.prompting, .panel.prompting { position: relative; overflow: hidden; } .prompting > :not(.prompt-page) { visibility: hidden; } /* (plus précis que « html.landscape .explain-sheet > * », qui la placerait * dans une colonne) */ .sheet.prompting > .prompt-page, .panel.prompting > .prompt-page { position: absolute; inset: 0; grid-area: auto; padding: inherit; } .prompting .prompt-text { flex: 1 1 auto; max-height: none; } /* Le bilan est un panneau : pas de poignée. */ .panel .prompt-page .grip { display: none; } /* « ‹ Explication » : revenir à l'écran d'où l'on vient. */ a.prompt-back { display: inline-flex; align-items: center; min-height: 44px; padding-right: var(--space-2); font-weight: 500; color: var(--accent); text-decoration: none; touch-action: manipulation; -webkit-tap-highlight-color: transparent; } .prompt-lead { margin: 0; color: var(--muted); } /* Le texte du prompt, à relire avant de le copier : une zone en creux qui * défile, en chasse fixe pour que le diagramme garde ses colonnes. */ .prompt-text { min-height: 0; max-height: 50vh; max-height: 50dvh; margin: 0; padding: var(--space-3); overflow: auto; font-family: ui-monospace, "DejaVu Sans Mono", "Roboto Mono", monospace; font-size: 13px; line-height: 18px; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--text); background: var(--surface-sunken); border: 1px solid var(--border); border-radius: var(--radius-md); user-select: text; } .prompt-status { margin: 0; color: var(--muted); } .prompt-status:empty { display: none; } .prompt-actions { display: flex; flex: none; gap: var(--space-2); justify-content: flex-end; } .prompt-actions button { display: inline-flex; gap: 6px; align-items: center; justify-content: center; min-width: 120px; } /* « ✓ Copié », 2 s : la copie se confirme sur le bouton (recette de 1f). */ #prompt-copy.copied { color: var(--verdict-good); background: var(--surface); border-color: var(--verdict-good); } html.landscape .prompt-text { max-height: calc(100vh - 190px); max-height: calc(100dvh - 190px); } html.landscape .prompting .prompt-text { max-height: none; } /* Le bouton ne se coupe pas : trop étroit à côté de « Revoir ce coup » ou * « Rejouer d'ici », il passe à la ligne. */ .summary-review-actions, .viewer-actions { flex-wrap: wrap; }