/* ==========================================================================
   ML Formation Styles
   Following Sotis AI Design System
   ========================================================================== */

/* Container */
.mlformation-container {
    max-width: 1000px;
    width: 100%;
    margin: 0 auto;
    padding: var(--space-md);
    overflow-x: hidden;
}

/* Catalogue view only. A card grid has no reading-measure argument for
   re-cropping 200px narrower than the column `main` already provides, and the
   narrow cap was what forced 19 modules into 2 columns. `main` supplies the
   gutter, so the horizontal padding goes too. The detail page keeps the plain
   container: its long paragraphs do want a reading measure. */
.mlformation-container--wide {
    max-width: none;
    padding-left: 0;
    padding-right: 0;
}

/* Header uses global .page-header from style.css */

.header-actions {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-top: 1rem;
    flex-wrap: wrap;
}


/* Downloads. These used to sit on the module header's solid violet card and
   were styled as white-on-violet; that card is now the canvas, so they are
   ordinary hairline buttons. */
.module-downloads {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-sm);
    margin-top: var(--space-md);
}

.btn-download {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.45rem 0.9rem;
    background: transparent;
    color: var(--text-secondary);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    text-decoration: none;
    font-size: var(--text-base);
    transition: color var(--transition-fast), background-color var(--transition-fast),
                border-color var(--transition-fast);
}

.btn-download:hover {
    background: rgba(255, 255, 255, 0.06);
    color: var(--text-primary);
    border-color: rgba(255, 255, 255, 0.22);
}

.objectives-content {
    color: var(--text-secondary);
}


.overview-tab-panel .md-separator {
    margin: var(--space-sm) 0;
}


/* ==========================================================================
   Mermaid Diagrams
   ========================================================================== */

.mermaid {
    display: block;
    margin: 1.5em 0;
    padding: 1em;
    background: var(--code-chrome-bg);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    overflow-x: auto;
    max-width: 100%;
}

/* Dark theme adjustments for Mermaid */

/* Ensure Mermaid SVG scales properly */
.mermaid svg {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 0 auto;
}

/* Mermaid node text styling */
.mermaid .nodeLabel {
    font-family: inherit;
}

/* Mermaid edge labels */
.mermaid .edgeLabel {
    background: var(--card-bg);
    padding: 0.2em 0.4em;
    border-radius: 4px;
}

/* Hide empty edge labels (arrows without text) */
.mermaid .edgeLabel:empty,
.mermaid .edgeLabel span:empty {
    display: none;
}

/* ==========================================================================
   Code colours (CodeMirror editors, and the read-only blocks it colours)

   The vendored CodeMirror bundle writes no colour: it reads these custom
   properties (vendor/codemirror-<version>/editor.mjs, header comment).
   They are DERIVED from the palette tokens of style.css, never literal
   colours. Scoped to the two places code appears: the course column and the
   console window (which lives outside it).
   ========================================================================== */

.mlformation-container,
.scratch-pad-overlay {
    --code-keyword: var(--violet-primary);
    --code-string: var(--success-text);
    --code-number: var(--warning-text);
    --code-function: var(--info-text);
    /* Secondary, not tertiary: in this course the comments carry the
       explanation, so they must stay readable (7:1, not 3.6:1). */
    --code-comment: var(--text-secondary);
    --code-operator: var(--code-text);
    --code-variable: var(--text-primary);
    --code-type: var(--violet-light);
    --code-property: var(--code-text);
    --code-punctuation: var(--text-secondary);
    --code-cursor: var(--text-primary);
    --code-selection: rgba(var(--violet-primary-rgb), 0.32);
    --code-active-line: rgba(var(--violet-primary-rgb), 0.05);
    --code-gutter-bg: var(--code-bg);
    --code-gutter-text: var(--text-tertiary);
    --code-matching-bracket: rgba(var(--violet-primary-rgb), 0.28);
    --code-focus-ring: var(--violet-primary);
}

/* Visually hidden, still read by screen readers (the editors' help text). */
.ml-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Where the code runs, said once above the first cell. */
.sandbox-notice {
    margin: 0 0 var(--space-sm);
    padding: 0.1rem 0 0.1rem 0.75rem;
    border-left: 2px solid var(--violet-dark);
    color: var(--text-secondary);
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
}

/* Code Editor */
.code-cell {
    display: flex;
    flex-direction: column;
}

.code-editor {
    position: relative;
    background: var(--code-bg);
}

/* The code as text until CodeMirror mounts: same metrics as the editor, so
   the cell does not jump when it is replaced. */
.code-fallback {
    margin: 0;
    padding: 12px 16px;
    max-height: 300px;
    overflow: auto;
    background: var(--code-bg);
    color: var(--code-text);
    font-family: var(--font-mono);
    font-size: 0.875rem;
    line-height: 1.6;
    white-space: pre;
    tab-size: 4;
}

.code-fallback code {
    font: inherit;
    color: inherit;
    background: none;
}

.code-editor.is-expanded .code-fallback,
.code-editor.is-expanded .cm-editor {
    max-height: none;
}

/* CodeMirror (selectors one class more specific than the editor's own theme,
   which is injected after this sheet). */
.code-editor .cm-editor {
    max-height: 300px;
}

.code-editor .cm-editor .cm-scroller {
    overflow: auto;
}

.code-editor .cm-editor .cm-content {
    padding: 12px 0;
}

.code-editor .cm-editor .cm-line {
    padding: 0 16px;
}

/* The line of the cursor is marked only in the editor that has the focus:
   otherwise every cell of the page shows its first line highlighted. */
.code-editor .cm-editor:not(.cm-focused) .cm-activeLine {
    background-color: transparent;
}

/* Code Toolbar - contains copy and expand buttons */
.code-toolbar {
    position: absolute;
    top: 8px;
    right: 8px;
    display: flex;
    gap: 4px;
    opacity: 0;
    transition: var(--transition-fast);
    z-index: 10;
}

/* Shown on hover, and to keyboard users as soon as focus enters the cell. */
.code-editor:hover .code-toolbar,
.code-editor:focus-within .code-toolbar {
    opacity: 1;
}

/* No hover on a touch screen: always visible there. */
@media (hover: none) {
    .code-toolbar {
        opacity: 1;
    }
}

.code-toolbar-btn {
    padding: 6px;
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: var(--radius-sm);
    color: rgba(255, 255, 255, 0.6);
    cursor: pointer;
    transition: var(--transition-fast);
    display: flex;
    align-items: center;
    justify-content: center;
}

.code-toolbar-btn:hover {
    background: rgba(255, 255, 255, 0.2);
    color: var(--white);
    border-color: rgba(255, 255, 255, 0.4);
}

.code-toolbar-btn.copied {
    background: var(--success-text);
    border-color: var(--success-text);
    color: var(--bg-primary);
}

.code-toolbar-btn svg {
    display: block;
}

/* Two icons per button, swapped by a class (no innerHTML): copy then a check
   mark, expand then collapse. */
.copy-icon .icon-done,
.expand-icon .icon-done {
    display: none;
}

.copy-icon.copied .icon-idle,
.expand-icon.expanded .icon-idle {
    display: none;
}

.copy-icon.copied .icon-done,
.expand-icon.expanded .icon-done {
    display: block;
}

/* Expand button active state. --violet-fill is the only violet dark enough to
   carry white ink. */
.expand-icon.expanded {
    background: var(--violet-fill);
    border-color: var(--violet-fill);
    color: var(--white);
}

/* Legacy Copy Icon - for solution wrapper */
.copy-icon:not(.code-toolbar-btn) {
    position: absolute;
    top: 8px;
    right: 8px;
    padding: 6px;
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: var(--radius-sm);
    color: rgba(255, 255, 255, 0.6);
    cursor: pointer;
    opacity: 0;
    transition: var(--transition-fast);
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: center;
}

.solution-wrapper:hover .copy-icon:not(.code-toolbar-btn),
.solution-wrapper:focus-within .copy-icon:not(.code-toolbar-btn) {
    opacity: 1;
}

@media (hover: none) {
    .copy-icon:not(.code-toolbar-btn) {
        opacity: 1;
    }
}

.copy-icon:not(.code-toolbar-btn):hover {
    background: rgba(255, 255, 255, 0.2);
    color: var(--white);
    border-color: rgba(255, 255, 255, 0.4);
}

.copy-icon:not(.code-toolbar-btn).copied {
    background: var(--success-text);
    border-color: var(--success-text);
    color: var(--bg-primary);
}

.copy-icon:not(.code-toolbar-btn) svg {
    display: block;
}


.code-actions {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    padding: var(--space-xs) var(--space-sm);
    background: var(--bg-secondary);
    border-top: 1px solid var(--border-color);
}

.btn-run,
.btn-reset,
.btn-copy {
    padding: 0.4rem 1rem;
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
    cursor: pointer;
    transition: var(--transition-fast);
}

/* Brand wash + hairline instead of a solid brand fill: this button repeats six
   to ten times down a page, and a saturated block that often stops signalling
   anything. It stays unmistakably the brand action. */
.btn-run {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    background: var(--violet-wash);
    color: var(--violet-primary);
    border: 1px solid var(--violet-dark);
    font-family: var(--font-sans);
    font-weight: var(--font-weight-medium);
}

.btn-run:hover:not(:disabled) {
    background: var(--violet-wash-strong);
}

.btn-run svg {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
}

/* Disabled while another cell runs, or when Python cannot run here. */
.btn-run:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

/* The running cell's button becomes Arrêter: the danger tone, a square. */
.btn-run .btn-run-icon-stop {
    display: none;
}

.btn-run.is-running {
    background: var(--danger-bg);
    color: var(--danger-text);
    border-color: var(--danger);
}

.btn-run.is-running .btn-run-icon-play {
    display: none;
}

.btn-run.is-running .btn-run-icon-stop {
    display: block;
}

.btn-reset,
.btn-copy {
    background: transparent;
    color: var(--text-secondary);
    border: 1px solid var(--border-strong);
}

.btn-reset:hover,
.btn-copy:hover {
    background: var(--surface-raised);
    border-color: var(--border-strong);
    color: var(--text-primary);
}

/* The Ctrl+Enter shortcut works in every editor; the action bar is where it
   is discoverable. Cmd on Apple devices (sandbox_page.mjs). */
.code-hint {
    margin-left: auto;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    color: var(--text-tertiary);
}

/* ==========================================================================
   Output area (sandbox/js/output_renderer.mjs): the SAME markup in the cells
   and in the console.
   ========================================================================== */

.code-output {
    padding: var(--space-sm) var(--space-md);
    background: var(--bg-primary);
    border-top: 1px solid var(--border-color);
    min-height: 50px;
}

.ml-output-placeholder,
.ml-output-empty {
    margin: 0;
    color: var(--text-secondary);
    font-style: italic;
    font-size: var(--text-sm);
}

.ml-output-items {
    display: flex;
    flex-direction: column;
    gap: var(--space-xs);
    min-width: 0;
}

.ml-output-stream {
    margin: 0;
    padding: var(--space-xs) 0.75rem;
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    line-height: 1.55;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    color: var(--text-primary);
    background: var(--code-chrome-bg);
    border-radius: var(--radius-sm);
}

/* stderr: warnings (sklearn, PyMC, shap) are not errors. A warning-toned rule
   and ink tell it apart from stdout without dressing it as a failure. */
.ml-output-stderr {
    color: var(--warning-text);
    background: var(--warning-bg);
    border-left: 3px solid var(--warning);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

/* Notes of the engine and the harness: discreet, but readable. */
.ml-output-note {
    margin: 0;
    padding-left: 0.75rem;
    border-left: 2px solid var(--border-strong);
    color: var(--text-secondary);
    font-size: var(--text-sm);
    font-style: italic;
    line-height: var(--leading-normal);
}

.ml-output-figure {
    margin: 0;
}

.ml-output-figure img {
    display: block;
    max-width: 100%;
    height: auto;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    background: var(--card-bg);
}

.ml-output-error {
    padding: var(--space-xs) 0.75rem;
    background: var(--danger-bg);
    border-left: 3px solid var(--danger);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

.ml-output-error-message {
    margin: 0;
    font-family: var(--font-mono);
    font-size: 0.8125rem;
    line-height: 1.55;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    color: var(--danger-text);
}

.ml-output-error-type {
    font-weight: var(--font-weight-bold);
}

.ml-output-traceback {
    margin-top: 0.4rem;
}

.ml-output-traceback summary {
    cursor: pointer;
    color: var(--text-secondary);
    font-size: var(--text-sm);
}

.ml-output-traceback summary:hover {
    color: var(--text-primary);
}

.ml-output-traceback pre {
    margin: 0.4rem 0 0;
    max-height: 320px;
    overflow: auto;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    line-height: 1.5;
    white-space: pre;
    color: var(--text-secondary);
}

.ml-output-running {
    display: flex;
    align-items: center;
    gap: var(--space-xs);
    margin-top: var(--space-xs);
    color: var(--violet-primary);
    font-size: var(--text-sm);
    font-weight: var(--font-weight-medium);
}

.ml-output-items + .ml-output-running {
    margin-top: var(--space-sm);
}

.ml-output-running:first-child {
    margin-top: 0;
}

.ml-output-elapsed {
    margin-left: auto;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    font-weight: var(--font-weight-normal);
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
}

.ml-output-spinner {
    flex: 0 0 auto;
    width: 14px;
    height: 14px;
    border: 2px solid var(--border-strong);
    border-top-color: var(--violet-primary);
    border-radius: 50%;
    animation: spin 0.9s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
    .ml-output-spinner {
        animation-duration: 2.5s;
    }
}

/* Outcome and duration, under the output. */
.ml-output-status {
    margin-top: var(--space-xs);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    text-align: right;
    color: var(--text-secondary);
}

.ml-output-status--error {
    color: var(--danger-text);
}

.ml-output-status--warning {
    color: var(--warning-text);
}

/* Former markup still emitted by the YAML sync button (module_list.html). */
.loading {
    display: flex;
    align-items: center;
    gap: var(--space-xs);
    color: var(--violet-primary);
    font-weight: 500;
}

.loading::before {
    content: '';
    width: 16px;
    height: 16px;
    border: 2px solid var(--border-strong);
    border-top-color: var(--violet-primary);
    border-radius: 50%;
    animation: spin 1s linear infinite;
}

@keyframes spin {
    to {
        transform: rotate(360deg);
    }
}

/* Advanced (Collapsible) Section.
   The markup nests this <details> inside an <article class="cell cell-advanced">
   which already draws the frame, so the dashed pastel outline drew a second one
   inside the first. Borderless here; the summary plays the .cell-header role. */
.advanced-section {
    background: none;
    border: none;
    border-radius: 0;
}

.advanced-section summary {
    padding: var(--space-sm) var(--space-md);
    background: var(--surface-raised);
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    font-weight: var(--font-weight-medium);
    list-style: none;
}

.advanced-section summary::-webkit-details-marker {
    display: none;
}

.advanced-section summary::before {
    content: '+';
    font-size: var(--text-md);
    color: var(--text-tertiary);
    margin-right: var(--space-xs);
}

.advanced-section[open] summary::before {
    content: '-';
}

/* A hover state, not a fill: the summary used to flip to a solid brand slab. */
.advanced-section summary:hover {
    background: var(--violet-wash);
}

.advanced-section summary:hover::before {
    color: var(--violet-primary);
}

.advanced-section .cell-content {
    border-top: 1px solid var(--border-color);
}

/* The status colour now travels through a left rule instead of a fill. The
   fills became translucent veils over a near-black canvas, and the body ink
   they were paired with was a dark olive inherited from the light theme:
   unreadable. */
.exercise-hint {
    padding: var(--space-xs) var(--space-sm);
    background: var(--warning-bg);
    border-top: 1px solid var(--border-color);
    border-left: 3px solid var(--warning);
}

.exercise-hint summary {
    cursor: pointer;
    color: var(--warning-text);
    font-weight: 500;
    font-size: 0.9rem;
}

.exercise-hint p {
    margin: var(--space-xs) 0 0;
    color: var(--text-secondary);
}

/* Exercise Solution */
.exercise-solution {
    padding: var(--space-xs) var(--space-sm);
    background: var(--success-bg);
    border-top: 1px solid var(--border-color);
    border-left: 3px solid var(--success);
}

.exercise-solution summary {
    cursor: pointer;
    color: var(--success-text);
    font-weight: 500;
    font-size: 0.9rem;
}

.exercise-solution .solution-wrapper {
    position: relative;
    margin: var(--space-xs) 0 0;
}

.exercise-solution .solution-code {
    margin: 0;
    padding: var(--space-sm);
    padding-right: 40px;
    background: var(--code-bg);
    border-radius: var(--radius-sm);
    overflow-x: auto;
}

.exercise-solution .solution-code code {
    color: var(--code-text);
    font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;
    font-size: 0.85rem;
    line-height: 1.5;
    white-space: pre;
}


/* ==========================================================================
   DataFrame Tables (from display(), built node by node by output_renderer.mjs:
   a header row, then one <th scope="row"> index cell per row)
   ========================================================================== */

.dataframe-container {
    min-width: 0;
}

.dataframe-title {
    font-weight: 600;
    color: var(--text-primary);
    font-size: 0.9rem;
    margin-bottom: 0.5rem;
    padding-left: 0.5rem;
    border-left: 3px solid var(--violet-fill);
}

/* Wide tables scroll inside their own frame, reachable with the keyboard; a
   narrow one keeps its natural width (numbers next to their header). */
.dataframe-scroll {
    width: fit-content;
    max-width: 100%;
    overflow-x: auto;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-sm);
    background: var(--code-chrome-bg);
}

.dataframe-scroll:focus-visible {
    outline: 2px solid var(--violet-primary);
    outline-offset: 2px;
}

.dataframe-info {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    color: var(--text-secondary);
    margin-top: 0.5rem;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
}

.dataframe-shape {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    background: var(--bg-secondary);
    padding: 0.2rem 0.5rem;
    border-radius: var(--radius-sm);
}

.dataframe-truncated {
    color: var(--text-secondary);
    font-style: italic;
}

.dataframe-table {
    border-collapse: collapse;
    font-size: 0.8125rem;
    font-family: var(--font-mono);
}

/* Header band matches the Markdown tables: monospace small caps on a raised
   surface, not a violet fill inside body copy. */
.dataframe-table thead th {
    padding: 0.55rem 0.8rem;
    text-align: right;
    font-weight: 600;
    color: var(--text-secondary);
    font-size: var(--text-xs);
    letter-spacing: var(--tracking-wide);
    background: var(--surface-raised);
    border-bottom: 1px solid var(--border-strong);
    white-space: nowrap;
}

.dataframe-table td {
    padding: 0.45rem 0.8rem;
    border-bottom: 1px solid var(--border-color);
    color: var(--text-primary);
    white-space: nowrap;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Index column: the corner cell (index name) and the row headers. Darker than
   the table, sticky when the table scrolls sideways. */
.dataframe-table thead th:first-child,
.dataframe-table tbody th {
    position: sticky;
    left: 0;
    z-index: 1;
    text-align: left;
    background: var(--bg-secondary);
    border-right: 1px solid var(--border-color);
}

.dataframe-table tbody th {
    padding: 0.45rem 0.8rem;
    font-weight: 500;
    color: var(--text-secondary);
    border-bottom: 1px solid var(--border-color);
    white-space: nowrap;
}

.dataframe-table tbody tr:last-child th,
.dataframe-table tbody tr:last-child td {
    border-bottom: none;
}

.dataframe-table tbody tr:nth-child(even) td {
    background: rgba(255, 255, 255, 0.02);
}

.dataframe-table tbody tr:hover td {
    background: var(--violet-wash);
}

.dataframe-table tbody tr:hover th {
    color: var(--text-primary);
}


/* Responsive */
@media (max-width: 768px) {
    .mlformation-container {
        padding: var(--space-sm);
    }

    .code-actions {
        flex-wrap: wrap;
    }

    .module-downloads {
        flex-direction: column;
    }

    .btn-download {
        justify-content: center;
    }

    .code-output {
        padding: var(--space-sm);
    }

    /* Mermaid diagrams - smaller padding on mobile */
    .mermaid {
        padding: 0.5em;
        margin: 1em 0;
    }

    .overview-diagram .mermaid {
        padding: 0.5em;
    }
}

/* Safari on iOS zooms the page when a field under 16px takes the focus, and
   the zoom stays: on a phone, the editors (0.875rem in the bundle's theme;
   more specific here, no rebuild of the bundle) go to 16px. Same rule as pj4. */
@media (max-width: 899.98px) {
    .code-editor .cm-editor,
    .scratch-code-editor .cm-editor {
        font-size: 16px;
    }

    .code-fallback {
        font-size: 16px;
    }

    /* Keyboard shortcuts mean little on a touch screen, and the bar is narrow. */
    .code-hint,
    .scratch-hint {
        display: none;
    }
}

/* Extra small screens */
@media (max-width: 480px) {
    .mlformation-container {
        padding: 0;
    }

    .overview-content {
        padding: var(--space-xs);
    }

}

/* ==========================================================================
   Category Overview Panel
   ========================================================================== */

.category-overview {
    margin-bottom: var(--space-md);
    background: var(--card-bg);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    overflow: hidden;
    transition: var(--transition-base);
    max-width: 100%;
}

.category-overview:hover {
    border-color: var(--violet-primary);
}

.category-overview[open] {
    border-color: var(--violet-primary);
    box-shadow: var(--shadow-md);
}

/* Flat band, no gradient: this is the first block of the page and a violet
   ramp here is the same pattern the redesign removed from the module header. */
.overview-summary {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    padding: var(--space-sm) var(--space-md);
    cursor: pointer;
    background: var(--bg-secondary);
    transition: var(--transition-fast);
    list-style: none;
}

.overview-summary::-webkit-details-marker {
    display: none;
}

.overview-summary::after {
    content: '+';
    margin-left: auto;
    font-size: var(--text-md);
    color: var(--text-tertiary);
    transition: var(--transition-fast);
}

.category-overview[open] .overview-summary::after {
    content: '-';
}

.overview-summary:hover {
    background: var(--surface-raised);
}

/* The system's accented-icon tile (wash + brand ink), replacing an emoji. */
.overview-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: var(--radius-sm);
    background: var(--violet-wash);
    color: var(--violet-primary);
    flex-shrink: 0;
}

.overview-icon svg {
    width: 16px;
    height: 16px;
}

.overview-title {
    font-size: var(--text-md);
    font-weight: var(--font-weight-medium);
    color: var(--text-primary);
}

.overview-hint {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    color: var(--text-tertiary);
    font-style: normal;
}

.overview-content {
    padding: var(--space-md);
    border-top: 1px solid var(--border-color);
    animation: slideDown 0.3s ease;
    overflow-x: hidden;
    max-width: 100%;
}

@keyframes slideDown {
    from {
        opacity: 0;
        transform: translateY(-10px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Overview Text Styling */
.overview-text {
    line-height: 1.8;
    color: var(--text-primary);
    max-width: 100%;
    overflow-wrap: break-word;
    word-wrap: break-word;
}

.overview-text p {
    margin-bottom: 1em;
}

.overview-text strong {
    color: var(--text-primary);
}

/* Tables and lists are NOT restyled here. Both render paths (the server-side
   render_markdown filter and the AJAX renderMarkdown below it) emit the same
   md-table / md-list classes, and .overview-text also carries
   .markdown-content, so module_system.css owns them. This block used to be a
   hand-copy of those rules and had already drifted from them. */

/* Overview Diagram Section */
.overview-diagram {
    margin-top: var(--space-md);
    padding-top: var(--space-md);
    border-top: 1px dashed var(--border-color);
}

.overview-diagram h4 {
    margin: 0 0 var(--space-sm);
    color: var(--text-secondary);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    font-weight: var(--font-weight-medium);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    display: flex;
    align-items: center;
    gap: var(--space-xs);
}

.overview-diagram .mermaid {
    margin: 0;
    padding: var(--space-md);
    background: var(--bg-secondary);
}



/* Responsive */
@media (max-width: 768px) {
    .overview-summary {
        padding: var(--space-sm);
    }

    /* The +/- affordance is enough on a narrow screen; wrapping the hint onto
       its own line pushed the toggle down with it. */
    .overview-hint {
        display: none;
    }

    .overview-content {
        padding: var(--space-sm);
    }
}

/* ==========================================================================
   Overview Tabs Component
   ========================================================================== */

/* Pills, not an underlined tab strip. The strip compensated the container
   border with margin-bottom: -2px, which only works on a single row; with five
   tabs the bar wraps and the active underline was left orphaned on the first
   row. Pills wrap without any of that arithmetic. */
.overview-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-xs);
    margin-bottom: var(--space-md);
    padding-bottom: var(--space-sm);
    border-bottom: 1px solid var(--border-color);
}

.overview-tab {
    padding: 0.35rem 0.75rem;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    cursor: pointer;
    font-size: var(--text-sm);
    font-weight: var(--font-weight-medium);
    color: var(--text-secondary);
    transition: var(--transition-fast);
    display: flex;
    align-items: center;
    gap: var(--space-xs);
}

.overview-tab:hover {
    background: var(--violet-wash);
    color: var(--text-primary);
}

.overview-tab.active {
    background: var(--violet-wash-strong);
    color: var(--violet-primary);
    border-color: var(--violet-dark);
}

/* The glyph comes from `tab.icon` in the YAML, i.e. an emoji. Icons on this
   site are inline SVG, so the slot stays in the markup and stays hidden. */
.overview-tab-icon {
    display: none;
}

.overview-tab-panels {
    position: relative;
    max-width: 100%;
    overflow-x: hidden;
}

.overview-tab-panel {
    display: none;
    animation: fadeIn 0.3s ease;
    max-width: 100%;
    overflow-x: hidden;
}

.overview-tab-panel.active {
    display: block;
}

@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateY(5px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Tab panel content spacing */
.overview-tab-panel .overview-text {
    margin-bottom: var(--space-md);
}

.overview-tab-panel .overview-diagram {
    margin-top: 0;
    padding-top: 0;
    border-top: none;
}

/* Section headers in tabs */
.overview-tab-panel h2 {
    font-size: var(--text-xl);
    color: var(--text-primary);
    margin: var(--space-md) 0 var(--space-sm);
    padding-bottom: var(--space-xs);
    border-bottom: 1px solid var(--border-color);
}

.overview-tab-panel h2:first-child {
    margin-top: 0;
}

.overview-tab-panel h3 {
    font-size: 1.1rem;
    color: var(--text-primary);
    margin: var(--space-sm) 0 var(--space-xs);
}

.overview-tab-panel h4 {
    font-size: var(--text-md);
    color: var(--text-secondary);
    margin: var(--space-sm) 0 var(--space-xs);
}

/* A vertical rule, not a filled panel: the system's way of accenting a block. */
.overview-tab-panel blockquote {
    margin: var(--space-sm) 0;
    padding: 0 0 0 var(--space-sm);
    border-left: 2px solid var(--violet-fill);
    background: none;
    border-radius: 0;
}

.overview-tab-panel blockquote p {
    margin: 0;
    font-style: italic;
    color: var(--text-primary);
}

/* Code blocks in tabs */
.overview-tab-panel code {
    background: rgba(255, 255, 255, 0.1);
    padding: 0.15em 0.4em;
    border-radius: 4px;
    font-family: 'Monaco', 'Menlo', monospace;
    font-size: 0.85em;
}

.overview-tab-panel pre {
    background: var(--code-bg);
    color: var(--code-text);
    padding: var(--space-sm);
    border-radius: var(--radius-sm);
    overflow-x: auto;
    margin: var(--space-sm) 0;
}

.overview-tab-panel pre code {
    background: none;
    padding: 0;
    color: inherit;
}



/* Responsive tabs. Pills already wrap on their own, so the only thing left to
   do on small screens is shrink them; the former column layout and its border
   bookkeeping are gone with the underline strip. */
@media (max-width: 768px) {
    .overview-tab {
        padding: 0.3rem 0.6rem;
        font-size: var(--text-xs);
    }
}

/* ==========================================================================
   Scratch Pad (Python Brouillon)
   ========================================================================== */

/* Python Console Button - Fixed position bottom right */
.python-console-btn {
    position: fixed;
    bottom: 5rem;  /* Above sync-btn when both are present */
    right: 2rem;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    padding: 0;
    background: var(--surface-raised);
    color: var(--text-secondary);
    border: 1px solid var(--border-strong);
    border-radius: 50%;
    cursor: pointer;
    transition: all var(--transition-fast);
    box-shadow: var(--shadow-md);
    z-index: 100;
    opacity: 1;
}

.python-console-btn:hover {
    background: var(--violet-wash);
    color: var(--violet-primary);
    border-color: var(--violet-dark);
    transform: translateY(-2px);
    box-shadow: var(--shadow-lg);
}

.python-console-btn.active {
    background: var(--violet-wash-strong);
    color: var(--violet-primary);
    border-color: var(--violet-dark);
}

.python-console-btn svg {
    width: 20px;
    height: 20px;
}

/* Scratch Pad Overlay */
/* Above the site's sticky navbar (app 1, body > header, z-index 1100): under
   it, the navbar covered the console's own header in full screen, Plein écran
   and Fermer included, and a window dragged to the top of the screen stayed
   stuck under it. The console is modal (veil, focus trap, Escape), so the
   veil covers the navbar too, as SotisDialog.confirm does (1200, still above
   the console). Invisible and not clickable while closed (visibility). */
.scratch-pad-overlay {
    position: fixed;
    inset: 0;
    background: var(--overlay-dark);
    z-index: 1150;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    visibility: hidden;
    /* Visibility flips at the END of the fade when closing, at once when
       opening: a visibility transition starts at "hidden", and the editor
       cannot take the focus inside a hidden overlay. */
    transition: opacity var(--transition-fast), visibility 0s linear 0.12s;
}

.scratch-pad-overlay.open {
    opacity: 1;
    visibility: visible;
    transition: opacity var(--transition-fast), visibility 0s;
}

/* Toasts (SotisDialog.notify, the .messages stack of app 1, z-index 1000)
   above the open console: under the overlay, they hid "Une autre cellule est
   en cours d'exécution" and "copie non disponible". 1160 also clears the
   sandbox status bar (1155 while the console is open). App 1's rule is left
   as it is. */
body:has(.scratch-pad-overlay.open) .messages {
    z-index: 1160;
}

/* Scratch Pad Modal */
/* Opens tall enough to be usable: at the former auto height the console showed
   about three lines of code and two of output before needing a resize. */
.scratch-pad-modal {
    position: relative;
    width: min(90%, 800px);
    min-width: 250px;
    min-height: 200px;
    height: min(70vh, 560px);
    background: var(--card-bg);
    border-radius: var(--radius-md);
    border: 1px solid var(--border-strong);
    box-shadow: var(--shadow-lg), var(--ring-inset);
    display: flex;
    flex-direction: column;
    transform: scale(0.95) translateY(20px);
    transition: transform var(--transition-base), opacity var(--transition-base);
    overflow: hidden;
}

.scratch-pad-overlay.open .scratch-pad-modal {
    transform: scale(1) translateY(0);
}

/* The window takes the focus itself when no editor can (python-console.js,
   tabindex="-1"): no ring around the whole window for that. */
.scratch-pad-modal:focus {
    outline: none;
}

/* Resize Handles */
.resize-handle {
    position: absolute;
    z-index: 10;
}

/* Vertical edge handles (top/bottom) */
.resize-handle-n,
.resize-handle-s {
    left: 16px;
    right: 16px;
    height: 10px;
    cursor: ns-resize;
}

/* Horizontal edge handles (left/right) */
.resize-handle-e,
.resize-handle-w {
    top: 16px;
    bottom: 16px;
    width: 10px;
    cursor: ew-resize;
}

.resize-handle-n { top: -5px; }
.resize-handle-s { bottom: -5px; }
.resize-handle-e { right: -5px; }
.resize-handle-w { left: -5px; }

/* Corner handles */
.resize-handle-nw,
.resize-handle-ne,
.resize-handle-sw,
.resize-handle-se {
    width: 16px;
    height: 16px;
}

.resize-handle-nw { top: -5px; left: -5px; cursor: nwse-resize; }
.resize-handle-ne { top: -5px; right: -5px; cursor: nesw-resize; }
.resize-handle-sw { bottom: -5px; left: -5px; cursor: nesw-resize; }
.resize-handle-se { bottom: -5px; right: -5px; cursor: nwse-resize; }

/* Visual indicator on horizontal edge handles */
.resize-handle-e::after,
.resize-handle-w::after {
    content: '';
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 3px;
    height: 30px;
    background: var(--violet-primary);
    border-radius: 2px;
    opacity: 0;
    transition: opacity var(--transition-fast);
}

.resize-handle-e::after { right: 2px; }
.resize-handle-w::after { left: 2px; }

.resize-handle-e:hover::after,
.resize-handle-w:hover::after {
    opacity: 0.6;
}

/* Visual indicator on vertical edge handles */
.resize-handle-n::after,
.resize-handle-s::after {
    content: '';
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    height: 3px;
    width: 30px;
    background: var(--violet-primary);
    border-radius: 2px;
    opacity: 0;
    transition: opacity var(--transition-fast);
}

.resize-handle-n::after { top: 2px; }
.resize-handle-s::after { bottom: 2px; }

.resize-handle-n:hover::after,
.resize-handle-s:hover::after {
    opacity: 0.6;
}

/* Visual indicator on corner handles */
.resize-handle-se::after {
    content: '';
    position: absolute;
    bottom: 3px;
    right: 3px;
    width: 10px;
    height: 10px;
    border-right: 2px solid var(--violet-primary);
    border-bottom: 2px solid var(--violet-primary);
    opacity: 0.4;
    transition: opacity var(--transition-fast);
}

.resize-handle-se:hover::after {
    opacity: 1;
}

.resize-handle-nw::after {
    content: '';
    position: absolute;
    top: 3px;
    left: 3px;
    width: 10px;
    height: 10px;
    border-left: 2px solid var(--violet-primary);
    border-top: 2px solid var(--violet-primary);
    opacity: 0.4;
    transition: opacity var(--transition-fast);
}

.resize-handle-nw:hover::after {
    opacity: 1;
}

/* Disable text selection during resize */
.scratch-pad-modal.resizing {
    user-select: none;
}

.scratch-pad-modal.resizing * {
    pointer-events: none;
}

/* Scratch Pad Header - Draggable */
/* Was the last solid violet slab on screen, floating above every page of both
   views. Same treatment the module header got: a raised surface and a hairline. */
.scratch-pad-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-sm) var(--space-md);
    background: var(--surface-raised);
    color: var(--text-primary);
    border-bottom: 1px solid var(--border-color);
    cursor: move;
    user-select: none;
}

.scratch-pad-header:active {
    cursor: grabbing;
}

/* When modal is being dragged */
.scratch-pad-modal.dragging {
    transition: none !important;
}

.scratch-pad-header h3 {
    margin: 0;
    font-size: var(--text-base);
    font-weight: var(--font-weight-medium);
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.scratch-pad-header h3 svg {
    color: var(--violet-primary);
}

.scratch-icon {
    font-size: 1.2rem;
}

.scratch-pad-actions {
    display: flex;
    gap: 0.5rem;
}

.scratch-action-btn {
    padding: 0.4rem;
    background: transparent;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    cursor: pointer;
    transition: var(--transition-fast);
    display: flex;
    align-items: center;
    justify-content: center;
}

.scratch-action-btn:hover {
    background: var(--surface-raised);
    color: var(--text-primary);
}

.scratch-action-btn svg {
    display: block;
}

/* Scratch Pad Body */
.scratch-pad-body {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    overflow: hidden;
}

/* Scratch Code Editor: CodeMirror fills the pane and scrolls inside it. */
.scratch-code-editor {
    background: var(--code-bg);
    flex: 1 1 50%;
    min-height: 40px;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.scratch-editor-host {
    position: relative;
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.scratch-editor-host .code-editor-mount {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.scratch-code-editor .cm-editor {
    flex: 1 1 auto;
    min-height: 0;
    height: 100%;
}

.scratch-code-editor .cm-editor .cm-scroller {
    overflow: auto;
}

.scratch-code-editor .cm-editor .cm-content {
    padding: 12px 0;
}

.scratch-code-editor .cm-editor .cm-line {
    padding: 0 16px;
}

/* Placeholder of an empty console (CodeMirror's placeholder extension is not
   in the bundle): a class toggled by sandbox_page.mjs. */
.scratch-editor-host.is-empty::before {
    content: attr(data-placeholder);
    position: absolute;
    top: 12px;
    left: 16px;
    z-index: 1;
    color: var(--text-tertiary);
    font-family: var(--font-mono);
    font-size: 0.875rem;
    line-height: 1.6;
    pointer-events: none;
}

/* Scratch Code Actions */
.scratch-code-actions {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    padding: var(--space-xs) var(--space-sm);
    background: var(--bg-secondary);
    border-top: 1px solid var(--border-color);
}

.scratch-hint {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    color: var(--text-tertiary);
    font-style: normal;
}

/* Shortcuts Help Tooltip */
.shortcuts-help {
    position: relative;
    display: inline-block;
}

.shortcuts-tooltip {
    display: none;
    position: absolute;
    top: 100%;
    right: 0;
    margin-top: 8px;
    background: var(--surface-raised);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-lg), var(--ring-inset);
    padding: var(--space-sm);
    min-width: 220px;
    z-index: 1000;
}

.shortcuts-help:focus-within .shortcuts-tooltip,
.shortcuts-help:hover .shortcuts-tooltip {
    display: block;
}

.shortcuts-title {
    font-weight: 600;
    font-size: 0.85rem;
    color: var(--text-primary);
    margin-bottom: var(--space-xs);
    padding-bottom: var(--space-xs);
    border-bottom: 1px solid var(--border-color);
}

.shortcut-item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.8rem;
    color: var(--text-secondary);
    padding: 4px 0;
}

.shortcut-item kbd {
    display: inline-block;
    padding: 2px 6px;
    font-size: 0.75rem;
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: 4px;
    box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
    color: var(--text-primary);
}

/* Splitter between code and output */
.scratch-splitter {
    flex: 0 0 8px;
    background: var(--bg-secondary);
    border-top: 1px solid var(--border-color);
    border-bottom: 1px solid var(--border-color);
    cursor: row-resize;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background var(--transition-fast);
}

.scratch-splitter:hover {
    background: var(--violet-wash);
}

.scratch-splitter::before {
    content: '';
    width: 40px;
    height: 4px;
    background: var(--border-color);
    border-radius: 2px;
    transition: background var(--transition-fast);
}

.scratch-splitter:hover::before {
    background: var(--violet-primary);
}

.scratch-splitter.dragging {
    background: var(--violet-wash);
}

/* Scratch Code Output: the shared output area (.code-output), in a pane that
   scrolls. */
.scratch-code-output {
    flex: 1 1 50%;
    min-height: 40px;
    overflow-y: auto;
    padding: var(--space-sm) var(--space-md);
    background: var(--bg-primary);
    border-top: none;
}

/* Where the code runs, at the foot of the console window. */
.sandbox-console-notice {
    margin: 0;
    padding: 0.45rem var(--space-md);
    border-top: 1px solid var(--border-color);
    background: var(--bg-secondary);
    color: var(--text-secondary);
    font-size: var(--text-xs);
    line-height: var(--leading-snug);
}

/* ==========================================================================
   Status of Python (download, restart, failure), fixed at the bottom left,
   above the console overlay. Shared by the cells and the console.
   ========================================================================== */

.ml-sandbox-status {
    position: fixed;
    left: 1.5rem;
    bottom: 1.5rem;
    z-index: 1001;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    width: min(440px, calc(100vw - 7.5rem));
    padding: 0.7rem 0.8rem 0.7rem 0.9rem;
    background: var(--surface-raised);
    color: var(--text-primary);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg), var(--ring-inset);
    font-size: var(--text-sm);
}

/* Above the open console (overlay 1150), where it reports the console's
   runs; under the site's navbar (1100) and its mobile menu otherwise. */
body:has(.scratch-pad-overlay.open) .ml-sandbox-status {
    z-index: 1155;
}

.ml-sandbox-status[hidden],
.ml-sandbox-status [hidden] {
    display: none;
}

.ml-sandbox-status-body {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
}

.ml-sandbox-status-message {
    margin: 0;
    line-height: var(--leading-snug);
}

.ml-sandbox-progress-row {
    display: flex;
}

.ml-sandbox-progress {
    width: 100%;
    height: 6px;
    border: none;
    border-radius: var(--radius-full);
    background: var(--border-strong);
    overflow: hidden;
    appearance: none;
}

.ml-sandbox-progress::-webkit-progress-bar {
    background: var(--border-strong);
    border-radius: var(--radius-full);
}

.ml-sandbox-progress::-webkit-progress-value {
    background: var(--violet-primary);
    border-radius: var(--radius-full);
}

.ml-sandbox-progress::-moz-progress-bar {
    background: var(--violet-primary);
    border-radius: var(--radius-full);
}

.ml-sandbox-stop {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.35rem 0.75rem;
    background: var(--danger-bg);
    color: var(--danger-text);
    border: 1px solid var(--danger);
    border-radius: var(--radius-sm);
    font-family: var(--font-sans);
    font-size: var(--text-sm);
    font-weight: var(--font-weight-medium);
    cursor: pointer;
}

.ml-sandbox-stop:hover {
    background: rgba(var(--danger-rgb), 0.2);
}

.ml-sandbox-status-close {
    flex: 0 0 auto;
    display: inline-flex;
    padding: 0.3rem;
    background: transparent;
    color: var(--text-secondary);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.ml-sandbox-status-close:hover {
    color: var(--text-primary);
    background: var(--bg-secondary);
}

.ml-sandbox-status[data-tone="ready"] .ml-output-spinner,
.ml-sandbox-status[data-tone="failed"] .ml-output-spinner {
    display: none;
}

.ml-sandbox-status[data-tone="ready"] .ml-sandbox-status-message {
    color: var(--success-text);
}

.ml-sandbox-status[data-tone="failed"] {
    border-color: var(--danger);
}

.ml-sandbox-status[data-tone="failed"] .ml-sandbox-status-message {
    color: var(--danger-text);
}

/* Touch: move the console window and its handles with a finger. */
.scratch-pad-header,
.resize-handle,
.scratch-splitter {
    touch-action: none;
}

.scratch-splitter:focus-visible {
    outline: 2px solid var(--violet-primary);
    outline-offset: -2px;
}

/* Responsive adjustments for scratch pad */
@media (max-width: 768px) {
    .python-console-btn {
        bottom: 4rem;
        right: 1rem;
        width: 40px;
        height: 40px;
    }

    .python-console-btn svg {
        width: 18px;
        height: 18px;
    }

    .scratch-pad-modal {
        width: 95%;
        max-height: 90vh;
    }

    /* Full screen fills the screen here too: the height python-console.js
       gives it (the visible height minus 10 px above and below), not 90vh,
       which made it 61 px too short on a 375 x 812 phone. */
    .scratch-pad-modal.fullscreen {
        max-height: none;
    }

    .scratch-pad-header {
        padding: var(--space-xs) var(--space-sm);
    }

    .scratch-code-editor {
        min-height: 150px;
    }

    .scratch-code-output {
        padding: var(--space-sm);
    }

    .sandbox-console-notice {
        padding: 0.45rem var(--space-sm);
    }

    /* Along the bottom edge, leaving the console button's column free. */
    .ml-sandbox-status {
        left: 0.75rem;
        right: 4rem;
        bottom: 0.75rem;
        width: auto;
    }
}
