Zum Inhalt springen

MediaWiki:Common.js: Unterschied zwischen den Versionen

Aus transformal GmbH
Keine Bearbeitungszusammenfassung
Keine Bearbeitungszusammenfassung
Zeile 1: Zeile 1:
/* === PREPRINT START === */
/* === PREPRINT START === */
/* ============================================
/* ============================================
   PREPRINT v7.17 - JS v260210r2
   PREPRINT v7.17 - CSS v260207r4
  Implements: R2 (Layout), R3 (Place), R5 (Tables),
              R8 (Portrait), R9 (Validation), R10 (BookNav)
    
    
   ARCHITECTURE:
   ARCHITECTURE: Safe default, enhance when confirmed
   - Platform Adaptation Layer (PAL): isolates Minerva workarounds
   - Default: block layout (works on any screen width)
   - Model Layer: dimensional intent, linkage fabric, projection
   - Landscape: grid layout (enabled by JS after width confirmed)
   - Feature Layer: BookNav, TOC, Snippets
   - Portrait: explicit block (JS-applied, matches default)
   ============================================ */
   ============================================ */


   (function() {
/* ============================================
     'use strict';
  1. OVERFLOW CONTROL
  Clips at browser edge, required for Minerva compatibility
  ============================================ */
 
  html {
    overflow-x: hidden;
}
 
/* ============================================
  1b. MINERVA COLLAPSE SUPPRESSION
  Scoped to Preprint pages only. CSS acts instantly;
  JS sweep follows for structural cleanup.
  ============================================ */
 
  .preprint-layout .mf-icon,
  .preprint-book .mf-icon {
      display: none !important;
  }
 
  .preprint-layout .section-heading,
  .preprint-book .section-heading {
      pointer-events: none !important;
  }
 
/* ============================================
  2. SAFE DEFAULTS - Block Layout
  Works during JS initialization gap and on narrow screens
   ============================================ */
 
.preprint-layout {
    display: block;
    margin: 2rem auto;
}
 
.inner-column {
    display: block;
    width: 100%;
}
 
.outer-column {
    display: none;  /* Hidden by default; shown only in landscape */
}
 
.inner-column p {
    margin-top: 0;
    padding-top: 0;
}
 
/* OC/LH elements: inline flow in default mode */
.place-oc {
    display: block;
    line-height: 1.6;
    background: #eaecf0;
    padding: 0.75em;
    margin: 0.5em 0;
    border-radius: 3px;
}
 
/* Adjacent OC elements (no content between): remove gap */
.place-oc + .oc-marker + .place-oc {
    margin-top: 0;
}
 
.place-oc + .oc-marker + .preprint-table-oc-wrapper {
    margin-top: 0;
}
 
.preprint-table-oc-wrapper + .oc-marker + .place-oc {
    margin-top: 0;
}
 
.preprint-table-oc-wrapper + .oc-marker + .preprint-table-oc-wrapper {
    margin-top: 0;
}
 
.place-lh {
    display: inline-block;
    height: 1.6em;
    line-height: 1.6em;
    vertical-align: baseline;
    font-size: 1em;
    float: right;
    background: #eaecf0;
    padding: 0.1em 0.4em;
    border-radius: 2px;
    margin: 0 0 0.2em 0.3em;
}
 
.place-lh.lh-has-media {
    background: none;
    padding: 0.1em;
}
 
.place-lh img {
    height: 1.4em;
    width: auto;
}
 
/* Hide during init to prevent FOUC */
#preprint-magic ~ * {
    visibility: hidden;
}
 
.preprint-layout:not(.preprint-ready) {
     visibility: hidden;
}
 
.preprint-table-oc-wrapper {
    display: block;
    width: 100%;
    background: #eaecf0;
    padding: 0.75em;
    margin: 0.5em 0;
    border-radius: 3px;
    box-sizing: border-box;
}
 
/* Images: constrained by default */
.preprint-layout img {
    max-width: 100%;
    height: auto;
}
 
/* 2C wrapper: full width block by default */
.preprint-2c-wrapper {
    display: table;
    width: 100%;
    margin: 0;
    padding: 0;
    border: none;
    border-collapse: collapse;
    border-spacing: 0;
    line-height: normal;
}
 
.preprint-2c-wrapper > tbody > tr > td {
    padding: 0;
    border: none;
    line-height: normal;
    vertical-align: top;
}
 
/* ============================================
  3. LANDSCAPE MODE - Grid Enhancement
  Applied by JS when screen.width >= 768
  ============================================ */
 
body.preprint-landscape .preprint-layout {
    display: grid;
    grid-template-columns: 2fr 1fr;
    column-gap: 2rem;
    row-gap: 0;
}
 
body.preprint-landscape .preprint-layout > .inner-column {
    grid-column: 1;
    min-width: 0;  /* Respect fr allocation */
}
 
body.preprint-landscape .preprint-layout > .outer-column {
    display: block;
    grid-column: 2;
    position: relative;
    min-width: 0;  /* Respect fr allocation */
}
 
body.preprint-landscape .outer-column:empty {
    display: none;
}
 
/* 2C spans both columns in grid */
body.preprint-landscape .preprint-2c-wrapper {
    grid-column: 1 / -1;
}
 
/* OC elements: positioned in outer column */
body.preprint-landscape .place-oc {
    display: block;
    background: none;
    padding: 0;
    margin: 0;
    margin-top: -0.25em;
    border-radius: 0;
}
 
body.preprint-landscape .place-oc.place-small {
    font-size: 0.875em;
    margin-top: -0.1em;
    line-height: 1.5;
}
 
body.preprint-landscape .place-oc.place-bg {
    background-color: inherit;  /* Allow bg= parameter */
    padding: 0.5em;
    margin-top: -0.75em;
    border-radius: 3px;
}
 
/* LH elements: positioned markers */
body.preprint-landscape .place-lh {
    float: none;
    background: none;
    padding: 0;
    margin: 0;
    margin-top: -0.1em;
    margin-right: 0.5em;
    z-index: 10;
}
 
body.preprint-landscape .place-lh img {
    height: 1.4em !important;
    width: auto !important;
    max-width: 3em !important;
    margin-top: -0.45em;
}
 
body.preprint-landscape .place-lh a {
    display: block;
    height: 100%;
    text-decoration: none;
    color: #36c;
}
 
body.preprint-landscape .place-lh a:hover {
    text-decoration: underline;
}
 
/* OC table wrapper: positioned */
body.preprint-landscape .preprint-table-oc-wrapper {
    background: none;
    padding: 0;
    margin: 0;
    margin-top: -0.4em;
    border-radius: 0;
}
 
body.preprint-landscape .preprint-table-oc-wrapper table {
    width: 100%;
    max-width: 100%;
}
 
body.preprint-landscape.skin-minerva .preprint-table-oc-wrapper table {
    font-size: 0.75em;
}
 
body.preprint-landscape .preprint-table-oc-wrapper table.wikitable,
body.preprint-landscape .preprint-table-oc-wrapper table.wikitable td {
    background-color: #fff;
}
 
/* OC images */
body.preprint-landscape .place-oc img {
    max-width: 100%;
    height: auto;
}
 
/* ============================================
  4. PORTRAIT MODE - Explicit Block
  Applied by JS when screen.width < 768
  Mostly matches defaults, but explicit for clarity
  ============================================ */
 
body.preprint-portrait .preprint-layout {
    display: block;
}
 
body.preprint-portrait .preprint-layout > * {
    width: 100%;
    max-width: 100%;
}
 
body.preprint-portrait .inner-column {
    display: block;
    position: static;
    width: 100%;
}
 
body.preprint-portrait .outer-column {
    display: none;
}
 
body.preprint-portrait .oc-marker {
    visibility: hidden;
    width: 0;
    height: 0;
    overflow: hidden;
}
 
/* OC: gray background box */
body.preprint-portrait .place-oc {
    display: block;
    position: static;
    top: auto;
    background: #eaecf0;
    padding: 0.75em;
    margin: 0.5em 0;
    border-radius: 3px;
}
 
/* LH: float right */
body.preprint-portrait .place-lh {
    float: right !important;
    display: inline-block !important;
    position: static !important;
    top: auto !important;
    background: #eaecf0;
    padding: 0.1em 0.4em;
    border-radius: 2px;
    margin: 0 0 0.2em 0.3em;
}
 
body.preprint-portrait .place-lh.lh-has-media {
    background: none;
    padding: 0.1em;
}
 
body.preprint-portrait .place-lh img,
body.preprint-portrait .place-lh .lazy-image-placeholder {
    height: 1.4em !important;
    width: auto !important;
    max-width: 3em !important;
}
 
/* OC table wrapper */
body.preprint-portrait .preprint-table-oc-wrapper {
    display: block;
    position: static;
    top: auto;
    background: none;
    padding: 0.75em;
    margin: 0.5em 0;
    border-radius: 3px;
    box-sizing: border-box;
    width: 100%;
}
 
body.preprint-portrait .preprint-table-oc-wrapper table.wikitable {
    margin: 0;
}
 
/* Tables */
 
body.preprint-portrait.skin-minerva .preprint-table-oc-wrapper table {
    font-size: 0.75em;
}
 
body.preprint-portrait table.wikitable {
    display: table;
    width: 100%;
}
 
body.preprint-portrait .preprint-2c-wrapper {
    display: table;
    width: 100%;
}
 
body.preprint-portrait .preprint-table-oc-wrapper.place-oc {
    background: #eaecf0;
    padding: 0.75em;
}


     // Prevent repeated initialization
body.preprint-portrait .preprint-table-oc-wrapper table.wikitable,
     if (window.preprintInitialized) {
body.preprint-portrait .preprint-table-oc-wrapper table.wikitable td {
        console.log('Preprint: Already initialized, skipping');
     background: #fff;
        return;
}
     }
 
    window.preprintInitialized = true;
/* Images */
body.preprint-portrait .preprint-layout img {
     max-width: 100%;
    height: auto;
}
 
body.preprint-portrait .place-2c img,
body.preprint-portrait .preprint-2c-wrapper img {
    width: 100%;
    height: auto;
}
 
/* Lazy-load stability */
body.preprint-portrait .lazy-image-placeholder {
     min-height: 1.4em;
}
 
/* ============================================
  5. PLACE TEMPLATE - Mode-Agnostic Rules
  ============================================ */


    /* ============================================
.place-ic {
      PLATFORM ADAPTATION LAYER (PAL)
    display: block;
     
    margin: 1rem 0;
      Contracts:
}
      - PAL.ready(): Promise that resolves when platform is stable
      - PAL.getDeviceWidth(): returns actual device width in CSS pixels
      - PAL.isNarrow(): returns true if width < 768
      - PAL.onProjectionChange(callback): fires when orientation stable
      ============================================ */


    var PAL = (function() {
.place-ic img {
        var isMinerva = mw.config.get('skin') === 'minerva';
    max-width: 100%;
        var viewportSource = 'unknown';
    height: auto;
        var domStabilized = false;
}
        var imagesStabilized = false;
       
        // Debug logging with source tracking
        function log(adapter, message) {
            console.log('Preprint PAL.' + adapter + ': ' + message);
        }


        /* --- G.2.1 DOM Readiness Adapter --- */
.place-small {
       
    font-size: 0.875em;
        function waitForDOMStability() {
}
            return new Promise(function(resolve) {
                if (!isMinerva) {
                    // Desktop: DOM is ready immediately after document.ready
                    domStabilized = true;
                    log('DOM', 'Desktop mode, DOM ready');
                    resolve();
                    return;
                }
               
                // Minerva: poll for section wrappers and unwrap them
                var attempts = 0;
                var maxAttempts = 20; // 20 * 50ms = 1000ms max
                var lastSectionCount = -1;
               
                function poll() {
                    attempts++;
                    var sections = $('.mw-parser-output > section');
                    var currentCount = sections.length;
                   
                    if (currentCount > 0) {
                        // Unwrap sections
                        sections.each(function() {
                            $(this).replaceWith($(this).contents());
                        });
                        log('DOM', 'Unwrapped ' + currentCount + ' sections');
                        lastSectionCount = currentCount;
                       
                        // Continue polling - more may appear
                        if (attempts < maxAttempts) {
                            setTimeout(poll, 50);
                            return;
                        }
                    }
                   
                    if (currentCount === 0 && lastSectionCount === 0) {
                        // Stable: no sections for two consecutive polls
                        disableMinervaCollapse();
                        domStabilized = true;
                        log('DOM', 'Stable after ' + attempts + ' polls');
                        resolve();
                    } else if (attempts >= maxAttempts) {
                        // Timeout fallback
                        disableMinervaCollapse();
                        domStabilized = true;
                        log('DOM', 'Timeout after ' + attempts + ' polls (fallback)');
                        resolve();
                    } else {
                        lastSectionCount = currentCount;
                        setTimeout(poll, 50);
                    }
                }
               
                // Start polling after initial delay for Minerva to begin its work
                setTimeout(poll, 100);
            });
        }
       
        function disableMinervaCollapse() {
            function sweep() {
                $('.mw-parser-output section').removeClass('collapsible-block');
                $('[hidden]').removeAttr('hidden');
                $('.collapsible-heading').removeClass('collapsible-heading');
                $('.section-heading').removeClass('section-heading');
                $('.mf-section-hidden').removeClass('mf-section-hidden');
                $('.toggle-indicator, .mf-icon').remove();
            }
            sweep();
            // Minerva re-creates collapse after PAL cleanup; CSS hides
            // instantly (no flash), delayed sweep cleans DOM structure
            setTimeout(sweep, 2000);
            log('DOM', 'Minerva collapse disabled (CSS+delayed sweep)');
        }


        /* --- G.2.2 Viewport Adapter --- */
.skin-minerva .place-small {
       
    font-size: 0.75em;
        function initViewport() {
}
            if (!isMinerva) {
                viewportSource = 'standard';
                log('Viewport', 'Using window.innerWidth (standard)');
                return;
            }
           
            // Check if Minerva has set problematic viewport
            var viewportMeta = $('meta[name="viewport"]');
            var content = viewportMeta.attr('content') || '';
           
            if (content.indexOf('width=1120') !== -1 ||
                content.indexOf('width=980') !== -1) {
                // Override to device-width
                viewportMeta.attr('content', 'width=device-width, initial-scale=1');
                viewportSource = 'minerva-override';
                log('Viewport', 'Overrode Minerva viewport meta');
            } else {
                viewportSource = 'minerva-native';
                log('Viewport', 'Minerva viewport already sane');
            }
           
            // Add data attribute for debugging
            document.body.setAttribute('data-viewport-source', viewportSource);
        }
       
        function getDeviceWidth() {
            if (isMinerva) {
                // Minerva: always use screen.width (viewport meta unreliable)
                return screen.width;
            }
           
            // Desktop: use innerWidth if sane, fallback to screen.width
            if (window.innerWidth > 0 && window.innerWidth < screen.width * 1.5) {
                return window.innerWidth;
            }
            return screen.width;
        }
       
        function isNarrow() {
            return getDeviceWidth() < 768;
        }


        /* --- G.2.3 Image Stability Adapter --- */
.place-bg {
       
    border-radius: 3px;
        function ensureImageStability() {
}
            return new Promise(function(resolve) {
                if (!isMinerva) {
                    // Desktop: images load normally
                    imagesStabilized = true;
                    log('Image', 'Desktop mode, no lazy placeholders');
                    resolve();
                    return;
                }
               
                var placeholders = $('.mw-parser-output .lazy-image-placeholder');
                if (placeholders.length === 0) {
                    imagesStabilized = true;
                    log('Image', 'No lazy placeholders found');
                    resolve();
                    return;
                }
               
                var total = placeholders.length;
                var loaded = 0;
                var failed = 0;
                var timeout = null;
               
                function checkComplete() {
                    if (loaded + failed >= total) {
                        clearTimeout(timeout);
                        imagesStabilized = true;
                        log('Image', 'Complete: ' + loaded + ' loaded, ' + failed + ' failed');
                        resolve();
                    }
                }
               
                placeholders.each(function() {
                    var $placeholder = $(this);
                    var src = $placeholder.attr('data-mw-src');
                    var width = $placeholder.attr('data-width');
                    var height = $placeholder.attr('data-height');
                    var imgClass = $placeholder.attr('data-class') || '';
                   
                    if (!src) {
                        failed++;
                        checkComplete();
                        return;
                    }
                   
                    var $img = $('<img>')
                        .attr('width', width)
                        .attr('height', height)
                        .addClass(imgClass)
                        .on('load', function() {
                            loaded++;
                            checkComplete();
                        })
                        .on('error', function() {
                            failed++;
                            $(this).addClass('preprint-image-failed');
                            checkComplete();
                        })
                        .attr('src', src);  // Set src last to trigger load
                   
                    $placeholder.replaceWith($img);
                });
               
                // Timeout fallback: 2000ms
                timeout = setTimeout(function() {
                    log('Image', 'Timeout: ' + loaded + ' loaded, ' + (total - loaded) + ' pending');
                    imagesStabilized = true;
                    resolve();
                }, 2000);
            });
        }


        /* --- G.2.5 TOC Stability Adapter (Minerva) --- */
.place-2c {
       
    width: 100%;
        function waitForTOCStability() {
    line-height: 1.6;
            return new Promise(function(resolve) {
}
                if (!isMinerva) {
                    log('TOC', 'Desktop mode, no wait needed');
                    resolve();
                    return;
                }
               
                var attempts = 0;
                var maxAttempts = 20;  // 20 * 50ms = 1000ms max
               
                function poll() {
                    attempts++;
                    if ($('.toc').length > 0) {
                        log('TOC', 'Found after ' + attempts + ' polls');
                        resolve();
                    } else if (attempts >= maxAttempts) {
                        log('TOC', 'Timeout, no TOC found (OK for pages without headings)');
                        resolve();
                    } else {
                        setTimeout(poll, 50);
                    }
                }
               
                poll();
            });
        }


        /* --- G.2.4 Orientation Adapter --- */
.place-2c img {
       
    max-width: 100%;
        var orientationCallbacks = [];
    height: auto;
        var preChangeCallbacks = [];
}
        var currentOrientation = null;
        var orientationLocked = false;
       
        function initOrientationHandler() {
            currentOrientation = isNarrow() ? 'portrait' : 'landscape';
            setOrientationClass(currentOrientation);
           
            var resizeTimer = null;
           
            $(window).on('resize orientationchange', function() {
                var targetOrientation = isNarrow() ? 'portrait' : 'landscape';
               
                if (targetOrientation === currentOrientation) {
                    return;  // No mode change
                }
               
                // Fire pre-change callbacks IMMEDIATELY (anchor capture)
                preChangeCallbacks.forEach(function(cb) {
                    try {
                        cb({ from: currentOrientation, to: targetOrientation });
                    } catch (e) {
                        console.error('Preprint: Pre-change callback error', e);
                    }
                });
               
                // Lock during transition
                orientationLocked = true;
               
                clearTimeout(resizeTimer);
                resizeTimer = setTimeout(function() {
                    setOrientationClass(targetOrientation);
                   
                    waitForLayoutStability(targetOrientation).then(function() {
                        var previousOrientation = currentOrientation;
                        currentOrientation = targetOrientation;
                       
                        // Fire callbacks
                        var event = {
                            from: previousOrientation,
                            to: targetOrientation
                        };
                        orientationCallbacks.forEach(function(cb) {
                            try {
                                cb(event);
                            } catch (e) {
                                console.error('Preprint: Orientation callback error', e);
                            }
                        });
                       
                        orientationLocked = false;
                    });
                }, 150);
            });
            // Wake-detect: device sleep doesn't fire resize/orientationchange
            document.addEventListener('visibilitychange', function() {
                if (document.visibilityState !== 'visible') return;
                var targetOrientation = isNarrow() ? 'portrait' : 'landscape';
                if (targetOrientation !== currentOrientation) {
                    log('Orientation', 'Wake: ' + currentOrientation + ' → ' + targetOrientation);
                    $(window).trigger('resize');
                }
            });
        }
       
        function waitForLayoutStability(targetOrientation) {
            return new Promise(function(resolve) {
                // Set class first so CSS applies
                setOrientationClass(targetOrientation);
               
                var layout = document.querySelector('.preprint-layout');
                if (!layout) {
                    resolve();
                    return;
                }
               
                var lastWidth = -1;
                var lastHeight = -1;
                var stableCount = 0;
                var attempts = 0;
                var maxAttempts = 20;  // 20 * 50ms = 1000ms max
               
                function poll() {
                    attempts++;
                    var rect = layout.getBoundingClientRect();
                   
                    if (rect.width === lastWidth && rect.height === lastHeight) {
                        stableCount++;
                        if (stableCount >= 2) {
                            // Stable for 100ms
                            log('Orientation', 'Layout stable after ' + attempts + ' polls');
                            resolve();
                            return;
                        }
                    } else {
                        stableCount = 0;
                        lastWidth = rect.width;
                        lastHeight = rect.height;
                    }
                   
                    if (attempts >= maxAttempts) {
                        log('Orientation', 'Timeout after ' + attempts + ' polls (fallback)');
                        resolve();
                        return;
                    }
                   
                    setTimeout(poll, 50);
                }
               
                // Start after a reflow
                requestAnimationFrame(function() {
                    poll();
                });
            });
        }
       
        function setOrientationClass(orientation) {
            if (orientation === 'portrait') {
                $('body').addClass('preprint-portrait').removeClass('preprint-landscape');
            } else {
                $('body').addClass('preprint-landscape').removeClass('preprint-portrait');
            }
        }
       
        function onProjectionChange(callback) {
            orientationCallbacks.push(callback);
        }
       
        function onPreProjectionChange(callback) {
            preChangeCallbacks.push(callback);
        }
       
        function isOrientationLocked() {
            return orientationLocked;
        }


        /* --- PAL.ready() --- */
/* ============================================
       
  6. TABLES - Wrappers and Validation
        function ready() {
  ============================================ */
            return new Promise(function(resolve) {
                $(document).ready(function() {
                    // Check activation first
                    if (!$('#preprint-magic').length) {
                        log('ready', 'No #preprint-magic, skipping');
                        resolve({ activated: false });
                        return;
                    }
                   
                    if ($('#mw-content-text').hasClass('preprint-processed')) {
                        log('ready', 'Already processed, skipping');
                        resolve({ activated: false });
                        return;
                    }
                   
                    log('ready', 'Starting PAL initialization');
                   
                    // Sequential initialization
                    initViewport();
                   
                    waitForDOMStability()
                        .then(function() {
                            return ensureImageStability();
                        })
                        .then(function() {
                            return waitForTOCStability();
                        })
                        .then(function() {
                            initOrientationHandler();
                            log('ready', 'PAL ready (Minerva=' + isMinerva + ')');
                            resolve({
                                activated: true,
                                isMinerva: isMinerva,
                                viewportSource: viewportSource
                            });
                        });
                });
            });
        }


        // Public API
.preprint-table-ic-wrapper {
        return {
    width: 100%;
            ready: ready,
}
            getDeviceWidth: getDeviceWidth,
            isNarrow: isNarrow,
            onProjectionChange: onProjectionChange,
            onPreProjectionChange: onPreProjectionChange,
            isOrientationLocked: isOrientationLocked,
            isMinerva: function() { return isMinerva; },
            disableMinervaCollapse: disableMinervaCollapse
        };
    })();


    /* ============================================
.preprint-table-ic-wrapper table {
      MODEL LAYER
    width: 100%;
     
    max-width: 100%;
      Implements: Projection-Invariant Content Linkage
}
     
      - Dimensional Intent: IC, OC, LH, 2C (from markup)
      - Linkage Fabric: markers ↔ elements (bidirectional)
      - Projection: landscape grid | portrait flow
      - User State: element-relative position tracking
      ============================================ */


    var Model = (function() {
/* Validation: Red border for unwrapped tables */
        var linkage = [];  // Array of {marker: $marker, element: $element, type: string}
.preprint-layout table:not(.preprint-2c-wrapper):not(.preprint-table-ic-wrapper table):not(.preprint-table-oc-wrapper table):not(.preprint-2c-wrapper table) {
        var layout = null;
    border: 3px solid #d33 !important;
        var positionAnchor = null;  // {element: DOM, type: string, headingId: string}
}
        var returnTarget = null;


        /* --- Element Classification (Dimensional Intent) --- */
/* Exclude tables inside wrappers from red border */
       
.preprint-table-ic-wrapper table,
        function classifyElement($el) {
.preprint-table-oc-wrapper table,
            // 2C types
.preprint-2c-wrapper table {
            if ($el.hasClass('preprint-2c-wrapper')) return 'wrapper-2c';
    border: revert !important;
            if ($el.hasClass('place-2c')) return 'place-2c';
}
           
            // OC types (will create linkage)
            if ($el.hasClass('place-oc')) return 'place-oc';
            if ($el.hasClass('place-lh')) return 'place-lh';
            if ($el.hasClass('preprint-table-oc-wrapper')) return 'table-oc';
           
            // IC types
            if ($el.hasClass('place-ic')) return 'place-ic';
            if ($el.hasClass('preprint-table-ic-wrapper')) return 'table-ic';
           
            // Structure
            if ($el.hasClass('book-nav') || $el.hasClass('book-progress')) return 'book-nav';
            if ($el.is('h1, h2, h3, h4, h5, h6')) return 'heading';
           
            // Skip
            if ($el.attr('id') === 'preprint-magic') return 'skip';
           
            // Default: content for IC
            return 'content';
        }


        /* --- Linkage Fabric --- */
/* IC table width enforcement */
       
.preprint-table-ic-wrapper > table {
        function createMarker($element, type) {
    max-width: 100%;
            var $marker = $('<span class="oc-marker"></span>');
    box-sizing: border-box;
            $marker.data('preprintTarget', $element);
}
            $marker.data('preprintType', type);
           
            // Reverse link: element knows its marker
            $element.data('preprintMarker', $marker);
           
            linkage.push({
                marker: $marker,
                element: $element,
                type: type
            });
           
            return $marker;
        }
       
        function getLinkage() {
            return linkage;
        }


        /* --- Structure Building --- */
/* ============================================
       
  8. SNIPPET (Collaboration)
        function buildStructure() {
  ============================================ */
            var parserOutput = $('.mw-parser-output');
            var elements = parserOutput.children().toArray();
           
            layout = $('<div class="preprint-layout"></div>');
            var currentInner = null;
            var currentOuter = null;
            var pendingOC = [];
            var bookNavElements = [];
           
            // Process each element
            elements.forEach(function(el) {
                var $el = $(el);
                var type = classifyElement($el);
               
                switch (type) {
                    case 'wrapper-2c':
                    case 'place-2c':
                        // 2C: flush columns, append at grid root
                        flushColumns();
                        layout.append($el);
                        break;
                   
                    case 'place-oc':
                    case 'place-lh':
                    case 'table-oc':
                        // OC types: create marker in IC, collect element
                        ensureColumns();
                        var marker = createMarker($el, type);
                        currentInner.append(marker);
                        pendingOC.push($el);
                        break;
                   
                    case 'book-nav':
                        // Collect for later insertion before layout
                        bookNavElements.push($el);
                        break;
                   
                    case 'heading':
                        // Headings create section boundaries
                        flushColumns();
                        ensureColumns();
                        currentInner.append($el);
                        break;
                   
                    case 'skip':
                        // Remove magic div
                        $el.remove();
                        break;
                   
                    default:
                        // Content: append to IC, check for nested OC
                        ensureColumns();
                        currentInner.append($el);
                       
                        // Find nested OC/LH elements
                        $el.find('.place-oc, .place-lh').each(function() {
                            var $nested = $(this);
                            var nestedType = $nested.hasClass('place-lh') ? 'place-lh' : 'place-oc';
                            var nestedMarker = createMarker($nested, nestedType);
                            $nested.before(nestedMarker);
                            pendingOC.push($nested.detach());
                        });
                }
            });
           
            // Final flush
            flushColumns();
           
            // Replace content
            parserOutput.empty().append(layout);
           
            // Prepend BookNav before layout
            bookNavElements.reverse().forEach(function($el) {
                layout.before($el);
            });
           
            // Mark processed
            $('#mw-content-text').addClass('preprint-processed');
           
            // Mark LH elements with media
            $('.place-lh').each(function() {
                var $lh = $(this);
                if ($lh.find('img, .lazy-image-placeholder, .mw-file-element').length > 0) {
                    $lh.addClass('lh-has-media');
                }
            });
           
            console.log('Preprint Model: Structure built, ' + linkage.length + ' linkages');
           
            // Helper functions (closure)
            function ensureColumns() {
                if (!currentInner) {
                    currentInner = $('<div class="inner-column"></div>');
                    currentOuter = $('<div class="outer-column"></div>');
                }
            }
           
            function flushColumns() {
                if (currentInner && currentInner.children().length > 0) {
                    // Move pending OC to outer column
                    pendingOC.forEach(function($el) {
                        currentOuter.append($el);
                    });
                    pendingOC = [];
                   
                    // Append columns
                    layout.append(currentInner);
                    layout.append(currentOuter);
                   
                    currentInner = null;
                    currentOuter = null;
                }
            }
           
            return layout;
        }


        /* --- Projection --- */
.snippet {
       
    margin: 1rem 0;
        function applyProjection(mode) {
    padding: 1rem;
            if (mode === 'portrait') {
    background: #f8f9fa;
                applyPortraitProjection();
    border-left: 3px solid #72777d;
            } else {
}
                applyLandscapeProjection();
            }
        }
       
        function applyPortraitProjection() {
            if (layout && layout[0]) layout[0].style.paddingBottom = '';
            // Clear inline positioning
            linkage.forEach(function(link) {
                link.element[0].style.position = '';
                link.element[0].style.top = '';
            });
           
            // Separate elements by portrait placement
            var topElements = [];
            var bottomElements = [];
            var defaultElements = [];
           
            linkage.forEach(function(link) {
                var placement = link.element.attr('data-portrait');
                if (placement === 'top') {
                    topElements.push(link);
                } else if (placement === 'bottom') {
                    bottomElements.push(link);
                } else {
                    defaultElements.push(link);
                }
            });
           
            // Default: move to marker position
            defaultElements.forEach(function(link) {
                link.element.detach();
                link.marker.after(link.element);
            });
           
            // Top: insert before first content in first IC
            if (topElements.length > 0) {
                var $firstIC = layout.find('.inner-column').first();
                if ($firstIC.length) {
                    var $firstChild = $firstIC.children().first();
                    topElements.forEach(function(link) {
                        link.element.detach();
                        if ($firstChild.length) {
                            $firstChild.before(link.element);
                        } else {
                            $firstIC.prepend(link.element);
                        }
                    });
                }
            }
           
            // Bottom: append after last content in last IC
            if (bottomElements.length > 0) {
                var $lastIC = layout.find('.inner-column').last();
                if ($lastIC.length) {
                    bottomElements.forEach(function(link) {
                        link.element.detach();
                        $lastIC.append(link.element);
                    });
                }
            }
           
            scrollToHash();
            console.log('Preprint Model: Portrait projection applied');
        }
       
        function applyLandscapeProjection() {
            // Move elements back to their correct outer columns via linkage
            linkage.forEach(function(link) {
                var $oc = link.marker.closest('.inner-column').next('.outer-column');
                if ($oc.length) {
                    link.element.detach();
                    $oc.append(link.element);
                }
            });
           
            // Reset scroll for accurate offset calculations
            var savedBodyScroll = document.body.scrollTop;
            var savedHtmlScroll = document.documentElement.scrollTop;
            document.body.scrollTop = 0;
            document.documentElement.scrollTop = 0;
           
            // Force reflow then position
            if (layout && layout[0]) {
                void layout[0].offsetHeight;
            }
           
            positionOCElements();
           
            // Restore scroll
            document.body.scrollTop = savedBodyScroll;
            document.documentElement.scrollTop = savedHtmlScroll;
           
            scrollToHash();
            console.log('Preprint Model: Landscape projection applied');
        }
       
        function positionOCElements() {
            // Force reflow
            if (layout && layout[0]) {
                void layout[0].offsetHeight;
            }
           
            // Position each element relative to its marker
            linkage.forEach(function(link) {
                var $marker = link.marker;
                var $element = link.element;
                var $oc = $element.closest('.outer-column');
               
                if (!$oc.length) return;
               
                var markerOffset = $marker.offset();
                var ocOffset = $oc.offset();
               
                if (!markerOffset || !ocOffset) return;
               
                var relativeTop = markerOffset.top - ocOffset.top;
               
                $element.css({
                    'position': 'absolute',
                    'top': relativeTop + 'px'
                });
            });
           
            // Fix overlaps
            fixOverlaps();
            // Correct OC position to align with adjacent IC content
            linkage.forEach(function(link) {
                var nextIC = link.marker[0].nextElementSibling;
                if (!nextIC) return;
                // Only correct for block-level siblings (tables, divs), not inline elements within a paragraph
                if (nextIC.parentNode && nextIC.parentNode.tagName === 'P') return;
                var ocEl = link.element[0];
                var icInner = nextIC.querySelector('table') || nextIC;
                var ocInner = ocEl.querySelector('table') || ocEl;
                var delta = icInner.getBoundingClientRect().top - ocInner.getBoundingClientRect().top;
                if (Math.abs(delta) > 1) {
                    var current = parseFloat(link.element.css('top')) || 0;
                    link.element.css('top', (current + delta) + 'px');
                }
            });
            adjustLayoutPadding();
           
            // Watch for collapsible table expand/collapse
            if (!layout._ocObserver) {
                layout._ocObserver = new MutationObserver(adjustLayoutPadding);
                layout._ocObserver.observe(layout[0], { subtree: true, attributes: true, attributeFilter: ['class'] });
            }
        }
       
        function fixOverlaps() {
            // Force reflow first
            void document.body.offsetHeight;
           
            // Collect elements with document-relative positions
            var scrollY = window.pageYOffset || document.documentElement.scrollTop;
            var allOC = [];
            $('.outer-column').each(function() {
                var $oc = $(this);
                $oc.children('.place-oc, .place-lh, .preprint-table-oc-wrapper').each(function() {
                    var $el = $(this);
                    var rect = this.getBoundingClientRect();
                    allOC.push({
                        el: $el,
                        oc: $oc,
                        top: rect.top + scrollY,
                        bottom: rect.bottom + scrollY
                    });
                });
            });


            // Sort by document-relative top
.snippet-quote {
            allOC.sort(function(a, b) { return a.top - b.top; });
    margin-bottom: 0.5em;
           
}
            // Fix overlaps using document-relative positions
            var lastBottom = 0;
            allOC.forEach(function(item, index) {
                var overlap = lastBottom - item.top;
                if (overlap > 0) {
                    // Push down by overlap amount
                    var currentTop = parseFloat(item.el.css('top')) || 0;
                    item.el.css('top', (currentTop + overlap) + 'px');
                    item.top += overlap;
                    item.bottom += overlap;
                }
               
                // Gap 4px
                var nextIsTable = (index + 1 < allOC.length) &&
                    allOC[index + 1].el.hasClass('preprint-table-oc-wrapper');
                var gap = 4;
               
                lastBottom = item.bottom + gap;
            });
        }


        function adjustLayoutPadding() {
.snippet-context {
            var el = document.querySelector('.preprint-layout');
    font-size: 0.875em;
            if (!el) return;
    color: #54595d;
            var layoutRect = el.getBoundingClientRect();
    margin-bottom: 0.5em;
            var currentPad = parseFloat(el.style.paddingBottom || 0);
}
            var maxBottom = 0;
            $('.outer-column .place-oc, .outer-column .place-lh, .outer-column .preprint-table-oc-wrapper').each(function() {
                var rect = this.getBoundingClientRect();
                if (rect.bottom > maxBottom) maxBottom = rect.bottom;
            });
            var overflow = maxBottom - layoutRect.bottom + currentPad;
            if (overflow > 0) {
                el.style.paddingBottom = overflow + 'px';
            }
        }


        // Reposition after images load (initial positioning may precede image render)
.snippet-source {
        $('.preprint-layout img').on('load', function() {
    font-size: 0.875em;
            if (document.body.classList.contains('preprint-landscape')) {
    text-align: right;
                positionOCElements();
}
            }
        });


        $(window).on('resize', (function() {
/* ============================================
            var timer;
  9. SELECTION-TO-TALK BUTTON
            return function() {
  ============================================ */
                clearTimeout(timer);
                timer = setTimeout(function() {
                    if (document.body.classList.contains('preprint-landscape')) {
                        positionOCElements();
                    }
                }, 200);
            };
        })());


        function scrollToHash() {
.snippet-button {
            // Check return target first (set by Features.initBookNavFooter)
    background: #fc3;
            if (returnTarget) {
    color: #222;
                var el = document.getElementById(returnTarget);
    border: none;
                if (el) {
    border-radius: 2px;
                    el.scrollIntoView({ block: 'start' });
    padding: 0.4em 0.8em;
                    window.scrollBy(0, -60);
    font-size: 0.875em;
                    console.log('Preprint Model: Scrolled to return target ' + returnTarget);
    cursor: pointer;
                }
    box-shadow: 0 2px 4px rgba(0,0,0,0.2);
                returnTarget = null;
}
                return;
            }
           
            // Existing hash handling
            if (!window.location.hash) return;
            var target = document.getElementById(window.location.hash.slice(1));
            if (target) {
                target.scrollIntoView({ block: 'start' });
                window.scrollBy(0, -60);
                console.log('Preprint Model: Scrolled to hash ' + window.location.hash);
            }
        }


        /* --- User State (Position Tracking) --- */
.snippet-button:hover {
       
    background: #eb2;
        function captureAnchor() {
}
            if (PAL.isOrientationLocked()) return;
           
            var viewportTop = 70;  // Account for fixed headers
            var isLandscape = !PAL.isNarrow();
            positionAnchor = null;
            var headingFallback = null;
           
            // Find fallback heading
            var fallbackHeadingId = null;
            $('.preprint-layout').find('h2, h3, h4, h5').each(function() {
                var rect = this.getBoundingClientRect();
                if (rect.top <= viewportTop + 50) {
                    var id = $(this).find('.mw-headline').attr('id') || this.id;
                    if (id && id !== 'mw-toc-heading') {
                        fallbackHeadingId = id;
                    }
                }
            });
           
            // Check 2C elements first
            var found = false;
            $('.preprint-layout > .preprint-2c-wrapper').each(function() {
                var rect = this.getBoundingClientRect();
                if (rect.top < viewportTop + 50 && rect.bottom > 0 && rect.height > 10) {
                    positionAnchor = { element: this, type: '2C', headingId: fallbackHeadingId };
                    found = true;
                    return false;
                }
            });
            if (found) return;
           
            // Check IC children (all inner-columns)
            $('.preprint-layout .inner-column').each(function() {
                if (found) return false;
                $(this).children().each(function() {
                var $el = $(this);
               
                // Skip OC elements in portrait
                if (!isLandscape && ($el.hasClass('place-oc') || $el.hasClass('place-lh') ||
                    $el.hasClass('preprint-table-oc-wrapper'))) {
                    return true;
                }
               
                // Skip headings (prefer content), but remember as fallback
                if ($el.is('h1,h2,h3,h4,h5,h6')) {
                    if (!headingFallback) {
                        var hRect = $el[0].getBoundingClientRect();
                        if (hRect.top < viewportTop + 50 && hRect.bottom > 0 && hRect.height > 10) {
                            headingFallback = { element: $el[0], type: 'heading', headingId: fallbackHeadingId };
                        }
                    }
                    return true;
                }
               
                var rect = this.getBoundingClientRect();
                if (rect.top < viewportTop + 50 && rect.bottom > 0 && rect.height > 10) {
                    positionAnchor = { element: this, type: this.tagName.toLowerCase(), headingId: fallbackHeadingId };
                    found = true;
                    return false;
                }
                });
            });
            if (found) return;
           
            // Check OC elements in landscape
            if (isLandscape) {
                $('.preprint-layout .outer-column').children().each(function() {
                    var rect = this.getBoundingClientRect();
                    if (rect.top < viewportTop + 50 && rect.bottom > 0 && rect.height > 10) {
                        positionAnchor = { element: this, type: 'oc', headingId: fallbackHeadingId };
                        return false;
                    }
                });
            }
           
            // Use heading if no content element found
            if (!positionAnchor && headingFallback) {
                positionAnchor = headingFallback;
            }
        }
       
        function restoreAnchor() {
            if (!positionAnchor) return;
           
            if (positionAnchor.element && document.contains(positionAnchor.element)) {
                positionAnchor.element.scrollIntoView({ block: 'start' });
                window.scrollBy(0, -60);
            } else if (positionAnchor.headingId) {
                var el = document.getElementById(positionAnchor.headingId);
                if (el) {
                    el.scrollIntoView({ block: 'start' });
                    window.scrollBy(0, -60);
                }
            }
        }
       
        function getPositionAnchor() {
            return positionAnchor;
        }


        // Public API
/* ============================================
        return {
  10. ERROR STYLING
            classifyElement: classifyElement,
  ============================================ */
            buildStructure: buildStructure,
            getLinkage: getLinkage,
            applyProjection: applyProjection,
            positionOCElements: positionOCElements,
            captureAnchor: captureAnchor,
            restoreAnchor: restoreAnchor,
            getPositionAnchor: getPositionAnchor,
            getLayout: function() { return layout; },
            set returnTarget(id) { returnTarget = id; }
        };
    })();


     /* ============================================
.error {
      FEATURE LAYER
     color: #d33;
     
    font-weight: bold;
      Built on Model, provides user-facing features
}
      ============================================ */


    var Features = (function() {
/* Book navigation sidebar link (Vector only) */
       
@media (max-width: 850px) {
        /* --- Icon Positioning --- */
    .book-nav-sidebar {
       
        display: none;
        var iconMetrics = null;  // Calculated once from landscape
    }
       
}
        function calculateIconMetrics() {
            var layout = document.querySelector('.preprint-layout');
            if (!layout) return;
            var icon = document.querySelector('.book-nav-icon');
            if (!icon) return;
           
            var layoutRect = layout.getBoundingClientRect();
            var viewportWidth = document.documentElement.clientWidth;
            var deadZone = viewportWidth - layoutRect.right;
            var iconWidth = icon.getBoundingClientRect().width;
           
            if (deadZone < iconWidth + 4) return;  // Doesn't fit
           
            var margin = Math.round((deadZone - iconWidth) / 2);
            iconMetrics = { margin: margin };
            applyIconMetrics();
        }
       
        function applyIconMetrics() {
            var icon = document.querySelector('.book-nav-icon');
            if (!icon) return;
           
            if (PAL.isNarrow() || !iconMetrics) {
                icon.style.right = '';
                icon.style.bottom = '';
                return;
            }
           
            icon.style.right = iconMetrics.margin + 'px';
            icon.style.bottom = iconMetrics.margin + 'px';
        }


        /* --- BookNav Icon --- */
/* ============================================
       
  12. BOOK NAVIGATION ICON
        function initBookNavFooter() {
  ============================================ */
            var bookNavData = $('.book-nav-data');
            if (!bookNavData.length) return;
           
            var book = bookNavData.data('book');
            var rootAnchor = bookNavData.data('root-anchor');
           
            var currentPage = mw.config.get('wgPageName').replace(/_/g, ' ');
            var bookRoot = (book || '').replace(/_/g, ' ');
            var isBookRoot = (currentPage === bookRoot);
           
            // Navigation icon: Minerva only
            if (PAL.isMinerva()) {
                var $icon = $('<a class="book-nav-icon"></a>');
                if (isBookRoot) {
                    $icon.attr('href', '/');
                } else {
                    $icon.attr('href', '/wiki/' + book + (rootAnchor ? '#' + rootAnchor : ''));
                }
                var logos = mw.config.get('wgLogos') || {};
                var iconSrc = logos.svg || logos.icon || '/images/logo/logo.svg';
                $icon.append($('<img>').attr('src', iconSrc).attr('alt', ''));
               
                if (isBookRoot) {
                    $icon.on('click', function(e) {
                        e.preventDefault();
                        var saved = sessionStorage.getItem('preprint-position');
                        if (saved) {
                            var pos = JSON.parse(saved);
                            window.location.href = '/wiki/' + pos.article;
                        } else {
                            window.location.href = '/';
                        }
                    });
                } else {
                    $icon.on('click', function(e) {
                        e.preventDefault();
                        Model.captureAnchor();
                        var anchor = Model.getPositionAnchor();
                        var pos = {
                            article: mw.config.get('wgPageName'),
                            headingId: anchor ? anchor.headingId : null
                        };
                        sessionStorage.setItem('preprint-position', JSON.stringify(pos));
                        window.location.href = '/wiki/' + book + (rootAnchor ? '#' + rootAnchor : '');
                    });
                }
               
                $('body').append($icon);
            }


            if (!isBookRoot && !window.location.hash) {
  .book-nav-icon {
                var saved = sessionStorage.getItem('preprint-position');
    position: fixed;
                if (saved) {
    bottom: 1em;
                    var pos = JSON.parse(saved);
    right: 1em;
                    if (pos.article === mw.config.get('wgPageName') && pos.headingId) {
    z-index: 1000;
                        Model.returnTarget = pos.headingId;
    padding: 5px;
                        sessionStorage.removeItem('preprint-position');
    border-radius: 3px;
                    }
    opacity: 0.6;
                }
    transition: opacity 0.2s;
            }
    line-height: 0;
           
}
            console.log('Preprint Features: BookNav icon (root=' + isBookRoot + ')');
        }


        /* --- Sidebar Navigation Link (Vector only) --- */
.book-nav-icon:hover,
       
.book-nav-icon:active {
        function initSidebarNav() {
    opacity: 1;
            if (PAL.isMinerva()) return;
}
           
            var bookNavData = $('.book-nav-data');
            if (!bookNavData.length) return;
           
            var book = bookNavData.data('book');
            if (!book) return;
           
            var rootAnchor = bookNavData.data('root-anchor') || '';
           
            var $landmark = $('.vector-page-tools-landmark').first();
            if (!$landmark.length) return;
           
            var currentPage = mw.config.get('wgPageName').replace(/_/g, ' ');
            var bookRoot = (book || '').replace(/_/g, ' ');
            var isBookRoot = (currentPage === bookRoot);
           
            var lang = mw.config.get('wgContentLanguage') || '';
           
            var $link = $('<a class="book-nav-sidebar"></a>')
                .css({ display: 'inline-block', padding: '12px 12px 7px', fontSize: '14px', color: '#36c' });
           
            if (isBookRoot) {
                var label = lang.indexOf('de') === 0
                    ? 'Letzte Seite' : 'Last Page';
                $link.attr('href', '#').text(label);
               
                var saved = sessionStorage.getItem('preprint-position');
                if (!saved) {
                    $link.css({ color: '#ccc', pointerEvents: 'none' });
                }
               
                $link.on('click', function(e) {
                    e.preventDefault();
                    var pos = JSON.parse(sessionStorage.getItem('preprint-position') || 'null');
                    if (pos) {
                        window.location.href = '/wiki/' + pos.article;
                    } else {
                        window.location.href = '/';
                    }
                });
            } else {
                var label = lang.indexOf('de') === 0
                    ? 'Inhaltsverzeichnis' : 'Table of Contents';
                $link.attr('href', '/wiki/' + book).text(label);
               
                $link.on('click', function(e) {
                    e.preventDefault();
                    Model.captureAnchor();
                    var anchor = Model.getPositionAnchor();
                    var pos = {
                        article: mw.config.get('wgPageName'),
                        headingId: anchor ? anchor.headingId : null
                    };
                    sessionStorage.setItem('preprint-position', JSON.stringify(pos));
                    window.location.href = '/wiki/' + book + (rootAnchor ? '#' + rootAnchor : '');
                });
            }
           
            $landmark.before($link);
            console.log('Preprint Features: Sidebar nav (root=' + isBookRoot + ')');
        }


        /* --- Snippet Support --- */
.book-nav-icon img {
       
    display: block;
        function initSnippetSupport() {
    height: 1.6em;
            if (!$('.preprint-layout').length) return;
    width: auto;
           
}
            var $button = null;
           
            function createButton() {
                if (!$button) {
                    $button = $('<button class="snippet-button"></button>');
                    $button.text($('#ca-talk a').text() || 'Diskussion');
                    $button.hide();
                    $('body').append($button);
                   
                    $button.on('click', function() {
                        var selection = window.getSelection();
                        var text = selection.toString().trim();
                       
                        if (text.length < 2) {
                            $button.hide();
                            return;
                        }
                       
                        // Find nearest heading
                        var range = selection.getRangeAt(0);
                        var $container = $(range.startContainer).closest('p, div, li').first();
                        var $heading = $container.prevAll('h1,h2,h3,h4,h5,h6').first();
                       
                        if (!$heading.length) {
                            $heading = $container.parent().prevAll('h1,h2,h3,h4,h5,h6').first();
                        }
                        if (!$heading.length) {
                            $heading = $container.closest('.inner-column').find('h1,h2,h3,h4,h5,h6').last();
                        }
                       
                        var anchorId = $heading.find('.mw-headline').attr('id') || $heading.attr('id') || '';
                        var pageName = mw.config.get('wgPageName').replace(/_/g, ' ');
                       
                        localStorage.setItem('preprint-snippet', JSON.stringify({
                            page: pageName,
                            anchor: anchorId,
                            text: text,
                            timestamp: Date.now()
                        }));
                       
                        var talkPage = $('#ca-talk a').attr('href');
                        if (talkPage) {
                            window.location.href = talkPage + '?action=edit&section=new';
                        }
                       
                        $button.hide();
                    });
                }
                return $button;
            }
           
            $(document).on('mouseup', function() {
                setTimeout(function() {
                    var selection = window.getSelection();
                    var text = selection.toString().trim();
                   
                    if (text.length >= 2) {
                        var btn = createButton();
                        var range = selection.getRangeAt(0);
                        var rect = range.getBoundingClientRect();
                       
                        btn.css({
                            position: 'fixed',
                            top: (rect.bottom + 5) + 'px',
                            left: (rect.left + rect.width / 2 - 40) + 'px',
                            zIndex: 10000
                        });
                        btn.show();
                    } else if ($button) {
                        $button.hide();
                    }
                }, 10);
            });
           
            $(document).on('mousedown', function(e) {
                if ($button && !$(e.target).is('.snippet-button')) {
                    $button.hide();
                }
            });
        }


        /* --- Snippet Pre-fill (edit pages) --- */
/* Minerva: dark pill with inverted logo */
       
.skin-minerva .book-nav-icon {
        function initSnippetPrefill() {
    background: #000;
            if (mw.config.get('wgAction') !== 'edit') return;
    opacity: 0.85;
           
}
            var data = localStorage.getItem('preprint-snippet');
            if (!data) return;
           
            var snippet = JSON.parse(data);
           
            // Only use if recent (5 minutes)
            if (Date.now() - snippet.timestamp > 300000) {
                localStorage.removeItem('preprint-snippet');
                return;
            }
           
            var sectionTitle = snippet.anchor ? 'Re: ' + snippet.anchor.replace(/_/g, ' ') : 'Anmerkung';
            var text = '== ' + sectionTitle + ' ==\n';
            text += '{{Snippet\n';
            text += '|page=' + snippet.page + '\n';
            if (snippet.anchor) {
                text += '|anchor=' + snippet.anchor + '\n';
            }
            text += '|text=' + snippet.text + '\n';
            text += '}}\n\n';
            text += '~~' + '~~';
           
            var $textarea = $('#wpTextbox1');
            if ($textarea.length && $textarea.val() === '') {
                $textarea.val(text);
                var cursorPos = text.lastIndexOf('\n~~' + '~~');
                $textarea[0].setSelectionRange(cursorPos, cursorPos);
                $textarea.focus();
            }
           
            localStorage.removeItem('preprint-snippet');
        }


        /* --- TOC Suppression --- */
.skin-minerva .book-nav-icon img {
        function suppressTOC() {
    filter: invert(1);
            var isPreprintLayout = $('.preprint-layout').length > 0;
}
           
            var bookNavData = $('.book-nav-data');
            var isBookRoot = false;
            if (bookNavData.length) {
                var currentPage = mw.config.get('wgPageName').replace(/_/g, ' ');
                var bookRoot = (bookNavData.data('book') || '').replace(/_/g, ' ');
                isBookRoot = (currentPage === bookRoot);
            }
           
            if (isBookRoot && PAL.isMinerva()) {
                // Mobile: root page IS the TOC, hide MW-generated one
                $('.toc, #toc').hide();
                console.log('Preprint Features: TOC suppressed (bookRoot+Minerva)');
            } else if (isPreprintLayout && bookNavData.length && PAL.isMinerva()) {
                // Mobile book chapters: linear reading, no TOC
                $('.toc, #toc').hide();
                console.log('Preprint Features: TOC suppressed (chapter+BookNav+Minerva)');
            }
            // Desktop: TOC always visible (sticky sidebar navigation)
        }


        // Public API
        return {
            calculateIconMetrics: calculateIconMetrics,
            applyIconMetrics: applyIconMetrics,
            initBookNavFooter: initBookNavFooter,
            initSidebarNav: initSidebarNav,
            suppressTOC: suppressTOC,
            initSnippetSupport: initSnippetSupport,
            initSnippetPrefill: initSnippetPrefill
        };
    })();


    /* ============================================
/* ============================================
      INITIALIZATION
  11. DEBUG STYLES (optional)
      ============================================ */
  ============================================ */


    // Snippet pre-fill runs on any edit page
.debug-preprint .preprint-layout {
     $(document).ready(function() {
     border: 2px solid blue;
        Features.initSnippetPrefill();
    padding: 10px;
     });
     position: relative;
}


    // Main initialization
.debug-preprint .preprint-layout::before {
    PAL.ready().then(function(status) {
    content: "LAYOUT";
        if (!status.activated) {
    position: absolute;
            // Still init BookNav footer on book root pages
    top: -10px;
            if ($('.book-nav-data').length) {
    left: 10px;
                $('body').addClass('preprint-book');
    background: white;
                if (PAL.isMinerva()) {
    color: blue;
                    PAL.disableMinervaCollapse();
    font-size: 10px;
                }
    padding: 0 5px;
                setTimeout(function() {
}
                    Features.initBookNavFooter();
                    if (!PAL.isMinerva()) {
                        Features.initSidebarNav();
                    }
                    Features.suppressTOC();
                }, 100);
            }
            return;
        }
       
        console.log('Preprint: PAL ready, building structure');
       
        // Build structure (Model)
        Model.buildStructure();
       
        // Apply initial projection after brief delay for layout to settle
        var initialMode = PAL.isNarrow() ? 'portrait' : 'landscape';
        setTimeout(function() {
            Model.applyProjection(initialMode);
            $('.preprint-layout').addClass('preprint-ready');
        }, 50);


        // Initialize features
.debug-preprint .inner-column {
        Features.initBookNavFooter();
    background: rgba(0, 255, 0, 0.1);
        if (!PAL.isMinerva()) {
    border: 1px dashed green;
            Features.initSidebarNav();
    position: relative;
        }
}
        Features.suppressTOC();
        Features.initSnippetSupport();
       
        // Set up anchor tracking
        $(document).on('touchend', function() {
            setTimeout(function() {
                Model.captureAnchor();
            }, 400);
        });
       
        // Initial anchor capture
        setTimeout(function() {
            Model.captureAnchor();
        }, 500);


        // Hide icon early on rotation (before browser reflows)
.debug-preprint .inner-column::before {
        window.addEventListener('orientationchange', function() {
    content: "IC";
            var icon = document.querySelector('.book-nav-icon');
    position: absolute;
            if (icon) icon.style.visibility = 'hidden';
    top: 0;
        });
    right: 0;
       
    background: green;
        // Calculate icon metrics from landscape
    color: white;
        if (!PAL.isNarrow()) {
    font-size: 10px;
            setTimeout(function() { Features.calculateIconMetrics(); }, 100);
    padding: 2px;
        }
}


        // Hide icon early on rotation (before reflow causes visible jump)
.debug-preprint .outer-column {
        PAL.onPreProjectionChange(function() {
    background: rgba(255, 0, 255, 0.1);
            var icon = document.querySelector('.book-nav-icon');
    border: 1px dashed purple;
            if (icon) icon.style.visibility = 'hidden';
    position: relative;
        });
}
       
        PAL.onProjectionChange(function(event) {
            console.log('Preprint: Projection change ' + event.from + ' → ' + event.to);
           
            // Icon already hidden by onPreProjectionChange
            var icon = document.querySelector('.book-nav-icon');
           
            // Apply new projection
            Model.applyProjection(event.to);


            // Recalculate icon from landscape geometry, then reveal
.debug-preprint .outer-column::before {
            if (event.to === 'landscape') {
    content: "OC";
                setTimeout(function() {
    position: absolute;
                    Features.calculateIconMetrics();
    top: 0;
                    if (icon) icon.style.visibility = '';
    right: 0;
                }, 100);
    background: purple;
            } else {
    color: white;
                Features.applyIconMetrics();
    font-size: 10px;
                setTimeout(function() {
    padding: 2px;
                    if (icon) icon.style.visibility = '';
}
                }, 150);
            }
           
            // Restore position
            Model.restoreAnchor();
           
        });
       
        // Expose for debugging and external access
        window.Preprint = {
            PAL: PAL,
            Model: Model,
            Features: Features
        };
       
        // Also expose positionAnchor for BookNav compatibility
        Object.defineProperty(window, 'positionAnchor', {
            get: function() { return Model.getPositionAnchor(); }
        });
       
        mw.loader.load('/wiki/MediaWiki:Common.js/debug.js?action=raw&ctype=text/javascript');      
        console.log('Preprint: Initialization complete (v7.17)');
    });


})();
.debug-preprint .place-lh {
    outline: 1px solid orange;
}


.debug-preprint .place-2c {
    outline: 1px solid red;
}


/* === PREPRINT END === */
/* === PREPRINT END === */

Version vom 10. Februar 2026, 11:19 Uhr

/* === PREPRINT START === */
/* ============================================
   PREPRINT v7.17 - CSS v260207r4
   Implements: R2 (Layout), R3 (Place), R5 (Tables),
               R8 (Portrait), R9 (Validation), R10 (BookNav)
   
   ARCHITECTURE: Safe default, enhance when confirmed
   - Default: block layout (works on any screen width)
   - Landscape: grid layout (enabled by JS after width confirmed)
   - Portrait: explicit block (JS-applied, matches default)
   ============================================ */

/* ============================================
   1. OVERFLOW CONTROL
   Clips at browser edge, required for Minerva compatibility
   ============================================ */

   html {
    overflow-x: hidden;
}

/* ============================================
   1b. MINERVA COLLAPSE SUPPRESSION
   Scoped to Preprint pages only. CSS acts instantly;
   JS sweep follows for structural cleanup.
   ============================================ */

   .preprint-layout .mf-icon,
   .preprint-book .mf-icon {
       display: none !important;
   }
   
   .preprint-layout .section-heading,
   .preprint-book .section-heading {
       pointer-events: none !important;
   }

/* ============================================
   2. SAFE DEFAULTS - Block Layout
   Works during JS initialization gap and on narrow screens
   ============================================ */

.preprint-layout {
    display: block;
    margin: 2rem auto;
}

.inner-column {
    display: block;
    width: 100%;
}

.outer-column {
    display: none;  /* Hidden by default; shown only in landscape */
}

.inner-column p {
    margin-top: 0;
    padding-top: 0;
}

/* OC/LH elements: inline flow in default mode */
.place-oc {
    display: block;
    line-height: 1.6;
    background: #eaecf0;
    padding: 0.75em;
    margin: 0.5em 0;
    border-radius: 3px;
}

/* Adjacent OC elements (no content between): remove gap */
.place-oc + .oc-marker + .place-oc {
    margin-top: 0;
}

.place-oc + .oc-marker + .preprint-table-oc-wrapper {
    margin-top: 0;
}

.preprint-table-oc-wrapper + .oc-marker + .place-oc {
    margin-top: 0;
}

.preprint-table-oc-wrapper + .oc-marker + .preprint-table-oc-wrapper {
    margin-top: 0;
}

.place-lh {
    display: inline-block;
    height: 1.6em;
    line-height: 1.6em;
    vertical-align: baseline;
    font-size: 1em;
    float: right;
    background: #eaecf0;
    padding: 0.1em 0.4em;
    border-radius: 2px;
    margin: 0 0 0.2em 0.3em;
}

.place-lh.lh-has-media {
    background: none;
    padding: 0.1em;
}

.place-lh img {
    height: 1.4em;
    width: auto;
}

/* Hide during init to prevent FOUC */
#preprint-magic ~ * {
    visibility: hidden;
}

.preprint-layout:not(.preprint-ready) {
    visibility: hidden;
}

.preprint-table-oc-wrapper {
    display: block;
    width: 100%;
    background: #eaecf0;
    padding: 0.75em;
    margin: 0.5em 0;
    border-radius: 3px;
    box-sizing: border-box;
}

/* Images: constrained by default */
.preprint-layout img {
    max-width: 100%;
    height: auto;
}

/* 2C wrapper: full width block by default */
.preprint-2c-wrapper {
    display: table;
    width: 100%;
    margin: 0;
    padding: 0;
    border: none;
    border-collapse: collapse;
    border-spacing: 0;
    line-height: normal;
}

.preprint-2c-wrapper > tbody > tr > td {
    padding: 0;
    border: none;
    line-height: normal;
    vertical-align: top;
}

/* ============================================
   3. LANDSCAPE MODE - Grid Enhancement
   Applied by JS when screen.width >= 768
   ============================================ */

body.preprint-landscape .preprint-layout {
    display: grid;
    grid-template-columns: 2fr 1fr;
    column-gap: 2rem;
    row-gap: 0;
}

body.preprint-landscape .preprint-layout > .inner-column {
    grid-column: 1;
    min-width: 0;  /* Respect fr allocation */
}

body.preprint-landscape .preprint-layout > .outer-column {
    display: block;
    grid-column: 2;
    position: relative;
    min-width: 0;  /* Respect fr allocation */
}

body.preprint-landscape .outer-column:empty {
    display: none;
}

/* 2C spans both columns in grid */
body.preprint-landscape .preprint-2c-wrapper {
    grid-column: 1 / -1;
}

/* OC elements: positioned in outer column */
body.preprint-landscape .place-oc {
    display: block;
    background: none;
    padding: 0;
    margin: 0;
    margin-top: -0.25em;
    border-radius: 0;
}

body.preprint-landscape .place-oc.place-small {
    font-size: 0.875em;
    margin-top: -0.1em;
    line-height: 1.5;
}

body.preprint-landscape .place-oc.place-bg {
    background-color: inherit;  /* Allow bg= parameter */
    padding: 0.5em;
    margin-top: -0.75em;
    border-radius: 3px;
}

/* LH elements: positioned markers */
body.preprint-landscape .place-lh {
    float: none;
    background: none;
    padding: 0;
    margin: 0;
    margin-top: -0.1em;
    margin-right: 0.5em;
    z-index: 10;
}

body.preprint-landscape .place-lh img {
    height: 1.4em !important;
    width: auto !important;
    max-width: 3em !important;
    margin-top: -0.45em;
}

body.preprint-landscape .place-lh a {
    display: block;
    height: 100%;
    text-decoration: none;
    color: #36c;
}

body.preprint-landscape .place-lh a:hover {
    text-decoration: underline;
}

/* OC table wrapper: positioned */
body.preprint-landscape .preprint-table-oc-wrapper {
    background: none;
    padding: 0;
    margin: 0;
    margin-top: -0.4em;
    border-radius: 0;
}

body.preprint-landscape .preprint-table-oc-wrapper table {
    width: 100%;
    max-width: 100%;
}

body.preprint-landscape.skin-minerva .preprint-table-oc-wrapper table {
    font-size: 0.75em;
}

body.preprint-landscape .preprint-table-oc-wrapper table.wikitable,
body.preprint-landscape .preprint-table-oc-wrapper table.wikitable td {
    background-color: #fff;
}

/* OC images */
body.preprint-landscape .place-oc img {
    max-width: 100%;
    height: auto;
}

/* ============================================
   4. PORTRAIT MODE - Explicit Block
   Applied by JS when screen.width < 768
   Mostly matches defaults, but explicit for clarity
   ============================================ */

body.preprint-portrait .preprint-layout {
    display: block;
}

body.preprint-portrait .preprint-layout > * {
    width: 100%;
    max-width: 100%;
}

body.preprint-portrait .inner-column {
    display: block;
    position: static;
    width: 100%;
}

body.preprint-portrait .outer-column {
    display: none;
}

body.preprint-portrait .oc-marker {
    visibility: hidden;
    width: 0;
    height: 0;
    overflow: hidden;
}

/* OC: gray background box */
body.preprint-portrait .place-oc {
    display: block;
    position: static;
    top: auto;
    background: #eaecf0;
    padding: 0.75em;
    margin: 0.5em 0;
    border-radius: 3px;
}

/* LH: float right */
body.preprint-portrait .place-lh {
    float: right !important;
    display: inline-block !important;
    position: static !important;
    top: auto !important;
    background: #eaecf0;
    padding: 0.1em 0.4em;
    border-radius: 2px;
    margin: 0 0 0.2em 0.3em;
}

body.preprint-portrait .place-lh.lh-has-media {
    background: none;
    padding: 0.1em;
}

body.preprint-portrait .place-lh img,
body.preprint-portrait .place-lh .lazy-image-placeholder {
    height: 1.4em !important;
    width: auto !important;
    max-width: 3em !important;
}

/* OC table wrapper */
body.preprint-portrait .preprint-table-oc-wrapper {
    display: block;
    position: static;
    top: auto;
    background: none;
    padding: 0.75em;
    margin: 0.5em 0;
    border-radius: 3px;
    box-sizing: border-box;
    width: 100%;
}

body.preprint-portrait .preprint-table-oc-wrapper table.wikitable {
    margin: 0;
}

/* Tables */

body.preprint-portrait.skin-minerva .preprint-table-oc-wrapper table {
    font-size: 0.75em;
}

body.preprint-portrait table.wikitable {
    display: table;
    width: 100%;
}

body.preprint-portrait .preprint-2c-wrapper {
    display: table;
    width: 100%;
}

body.preprint-portrait .preprint-table-oc-wrapper.place-oc {
    background: #eaecf0;
    padding: 0.75em;
}

body.preprint-portrait .preprint-table-oc-wrapper table.wikitable,
body.preprint-portrait .preprint-table-oc-wrapper table.wikitable td {
    background: #fff;
}

/* Images */
body.preprint-portrait .preprint-layout img {
    max-width: 100%;
    height: auto;
}

body.preprint-portrait .place-2c img,
body.preprint-portrait .preprint-2c-wrapper img {
    width: 100%;
    height: auto;
}

/* Lazy-load stability */
body.preprint-portrait .lazy-image-placeholder {
    min-height: 1.4em;
}

/* ============================================
   5. PLACE TEMPLATE - Mode-Agnostic Rules
   ============================================ */

.place-ic {
    display: block;
    margin: 1rem 0;
}

.place-ic img {
    max-width: 100%;
    height: auto;
}

.place-small {
    font-size: 0.875em;
}

.skin-minerva .place-small {
    font-size: 0.75em;
}

.place-bg {
    border-radius: 3px;
}

.place-2c {
    width: 100%;
    line-height: 1.6;
}

.place-2c img {
    max-width: 100%;
    height: auto;
}

/* ============================================
   6. TABLES - Wrappers and Validation
   ============================================ */

.preprint-table-ic-wrapper {
    width: 100%;
}

.preprint-table-ic-wrapper table {
    width: 100%;
    max-width: 100%;
}

/* Validation: Red border for unwrapped tables */
.preprint-layout table:not(.preprint-2c-wrapper):not(.preprint-table-ic-wrapper table):not(.preprint-table-oc-wrapper table):not(.preprint-2c-wrapper table) {
    border: 3px solid #d33 !important;
}

/* Exclude tables inside wrappers from red border */
.preprint-table-ic-wrapper table,
.preprint-table-oc-wrapper table,
.preprint-2c-wrapper table {
    border: revert !important;
}

/* IC table width enforcement */
.preprint-table-ic-wrapper > table {
    max-width: 100%;
    box-sizing: border-box;
}

/* ============================================
   8. SNIPPET (Collaboration)
   ============================================ */

.snippet {
    margin: 1rem 0;
    padding: 1rem;
    background: #f8f9fa;
    border-left: 3px solid #72777d;
}

.snippet-quote {
    margin-bottom: 0.5em;
}

.snippet-context {
    font-size: 0.875em;
    color: #54595d;
    margin-bottom: 0.5em;
}

.snippet-source {
    font-size: 0.875em;
    text-align: right;
}

/* ============================================
   9. SELECTION-TO-TALK BUTTON
   ============================================ */

.snippet-button {
    background: #fc3;
    color: #222;
    border: none;
    border-radius: 2px;
    padding: 0.4em 0.8em;
    font-size: 0.875em;
    cursor: pointer;
    box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}

.snippet-button:hover {
    background: #eb2;
}

/* ============================================
   10. ERROR STYLING
   ============================================ */

.error {
    color: #d33;
    font-weight: bold;
}

/* Book navigation sidebar link (Vector only) */
@media (max-width: 850px) {
    .book-nav-sidebar {
        display: none;
    }
}

/* ============================================
   12. BOOK NAVIGATION ICON
   ============================================ */

   .book-nav-icon {
    position: fixed;
    bottom: 1em;
    right: 1em;
    z-index: 1000;
    padding: 5px;
    border-radius: 3px;
    opacity: 0.6;
    transition: opacity 0.2s;
    line-height: 0;
}

.book-nav-icon:hover,
.book-nav-icon:active {
    opacity: 1;
}

.book-nav-icon img {
    display: block;
    height: 1.6em;
    width: auto;
}

/* Minerva: dark pill with inverted logo */
.skin-minerva .book-nav-icon {
    background: #000;
    opacity: 0.85;
}

.skin-minerva .book-nav-icon img {
    filter: invert(1);
}


/* ============================================
   11. DEBUG STYLES (optional)
   ============================================ */

.debug-preprint .preprint-layout {
    border: 2px solid blue;
    padding: 10px;
    position: relative;
}

.debug-preprint .preprint-layout::before {
    content: "LAYOUT";
    position: absolute;
    top: -10px;
    left: 10px;
    background: white;
    color: blue;
    font-size: 10px;
    padding: 0 5px;
}

.debug-preprint .inner-column {
    background: rgba(0, 255, 0, 0.1);
    border: 1px dashed green;
    position: relative;
}

.debug-preprint .inner-column::before {
    content: "IC";
    position: absolute;
    top: 0;
    right: 0;
    background: green;
    color: white;
    font-size: 10px;
    padding: 2px;
}

.debug-preprint .outer-column {
    background: rgba(255, 0, 255, 0.1);
    border: 1px dashed purple;
    position: relative;
}

.debug-preprint .outer-column::before {
    content: "OC";
    position: absolute;
    top: 0;
    right: 0;
    background: purple;
    color: white;
    font-size: 10px;
    padding: 2px;
}

.debug-preprint .place-lh {
    outline: 1px solid orange;
}

.debug-preprint .place-2c {
    outline: 1px solid red;
}

/* === PREPRINT END === */


/* Unified Consent System JavaScript v250911r1 
 * Single file for both EN and DE versions
 * Automatically detects language based on domain
 */
/* Common JavaScript - Keep only consent system if needed */

// Language detection helper
function getCurrentLanguage() {
    var hostname = window.location.hostname;
    return (hostname === 'de.transformal.com' || 
            hostname === 'de.olaflangmack.info' ||
            hostname === 'de.mediawiki.transformal.com') ? 'de' : 'en';
}

// Localized strings
var strings = {
    en: {
        journalingEnabled: 'Journaling enabled',
        journalingDisabled: 'Journaling disabled',
        proofOfWorkEnabled: 'Proof-of-work enabled',
        proofOfWorkDisabled: 'Proof-of-work disabled',
        preferencesSaved: 'Your preferences are being saved',
        checkboxLabel: 'Enabled'
    },
    de: {
        journalingEnabled: 'Aufzeichnungen zugelassen',
        journalingDisabled: 'Aufzeichnungen nicht zugelassen',
        proofOfWorkEnabled: 'Proof-of-work zugelassen',
        proofOfWorkDisabled: 'Proof-of-work nicht zugelassen',
        preferencesSaved: 'Ihre Einstellungen werden gespeichert',
        checkboxLabel: 'Erlaubt'
    }
};

function getString(key) {
    var lang = getCurrentLanguage();
    return strings[lang][key] || strings.en[key];
}

// Handle consent acquisition box - 90-day reminder for everyone
$(document).ready(function() {
    var consentBox = document.getElementById('consent-acquisition-box');
    if (!consentBox) return;
    
    // Check if dismissed within last 90 days
    var dismissedUntil = localStorage.getItem('consent-acquisition-dismissed-until');
    var now = Date.now();
    
    if (dismissedUntil && parseInt(dismissedUntil) > now) {
        // Still within 90-day dismissal period
        consentBox.style.display = 'none';
    } else {
        // Show box - dismissal expired or never dismissed
        consentBox.style.display = 'block';
        consentBox.classList.add('show');
    }
    
    consentBox.addEventListener('click', function() {
        sessionStorage.setItem('consent-acquisition-dismissed', 'true');
        // ADD THIS LINE:
        localStorage.setItem('consent-acquisition-dismissed-until', Date.now() + (90 * 24 * 60 * 60 * 1000));
        var targetPage = getCurrentLanguage() === 'de' ? 
            '/wiki/Transformal_GmbH:Einstellungen' : 
            '/wiki/Transformal_GmbH:Settings';
        window.location.href = targetPage;
    });
});

// Convert template placeholders to actual checkboxes
$(function() {
    $('.consent-option').each(function() {
        var $option = $(this);
        if ($option.find('.consent-checkbox').length > 0) return;
        
        var consentId = $option.find('.consent-data').text().trim();
        var status = $option.find('.consent-status-data').text().trim();
        
        var $checkboxRow = $option.find('.checkbox-row');
        if (!$checkboxRow.length) return;
        
        var $container = $('<span class="checkbox-container"></span>');
        var $checkbox = $('<input>', {
            type: 'checkbox',
            id: consentId,
            class: 'consent-checkbox'
        });
        
        if (status === 'disabled') {
            $checkbox.prop('disabled', true);
        }
        
        var label = $option.find('.checkbox-placeholder').attr('data-label') || getString('checkboxLabel');
        var $label = $('<label>', {
          'for': consentId,
          text: ' ' + label,
          style: 'cursor: pointer;'
        });
        
        $container.append($checkbox).append($label);
        $checkboxRow.append($container);
        $checkboxRow.find('.checkbox-placeholder').remove();
        
        var saved = localStorage.getItem(consentId) === 'true';
        $checkbox.prop('checked', saved);
        
        $checkbox.on('change', function() {
            localStorage.setItem(consentId, this.checked);
            if (typeof handleConsentChange === 'function') {
              handleConsentChange(consentId, this.checked);
            }
            if (typeof showConsentFeedback === 'function') {
                showConsentFeedback(consentId, this.checked);
            }
        });
    });
});

// Handle consent changes for different features
function handleConsentChange(feature, enabled) {
    if (feature === 'matomo-consent') {
        if (enabled) {
            if (window._paq) {
                // Enable enhanced tracking
                _paq.push(['setCookieConsentGiven']);
                _paq.push(['setConsentGiven']);
                // Track this page view again with full details
                _paq.push(['trackPageView']);
                _paq.push(['enableHeartBeatTimer']);
                _paq.push(['trackVisibleContentImpressions']);
            }
        } else {
            if (window._paq) {
                // Revert to anonymous tracking only
                _paq.push(['forgetCookieConsentGiven']);
                _paq.push(['forgetConsentGiven']);
                // Note: Already tracked pages remain in this session
            }
        }
    }
}

// Show feedback when consent changes
function showConsentFeedback(feature, enabled) {
    var feedbackDiv = document.getElementById('consent-feedback');
    if (!feedbackDiv) return;
    
    var message = '';
    if (feature === 'matomo-consent') {
        message = enabled ? getString('journalingEnabled') : getString('journalingDisabled');
    } else if (feature === 'altcha-consent') {
        message = enabled ? getString('proofOfWorkEnabled') : getString('proofOfWorkDisabled');
    }
    
    if (message) {
        feedbackDiv.innerHTML = message + ' &ndash; ' + getString('preferencesSaved');
        feedbackDiv.style.display = 'block';
        
        setTimeout(function() {
            feedbackDiv.style.display = 'none';
        }, 3000);
    }
}

/* Template 'ConditionalContent' - v250925r8 */
/* Two templates for alternative running text (ConditionalContent) and alternative sections (ConditionalSection) */ 

mw.loader.using('mediawiki.user').then(function() {
    if (mw.user.isAnon()) {
        // Inline content
        $('.logged-out-only').each(function() {
            if ($(this).is(':empty') || !$(this).text().replace(/\s/g, '')) {
                $(this).css('display', 'none');
            } else {
                $(this).css('display', 'inline');
            }
        });
        $('.logged-in-only').css('display', 'none');
        
        // Block content
        $('.logged-out-section').each(function() {
            if ($(this).is(':empty') || !$(this).text().replace(/\s/g, '')) {
                $(this).css('display', 'none');
            } else {
                $(this).css('display', 'block');
            }
        });
        $('.logged-in-section').css('display', 'none');
    } else {
        // Inline content
        $('.logged-in-only').each(function() {
            if ($(this).is(':empty') || !$(this).text().replace(/\s/g, '')) {
                $(this).css('display', 'none');
            } else {
                $(this).css('display', 'inline');
            }
        });
        $('.logged-out-only').css('display', 'none');
        
        // Block content
        $('.logged-in-section').each(function() {
            if ($(this).is(':empty') || !$(this).text().replace(/\s/g, '')) {
                $(this).css('display', 'none');
            } else {
                $(this).css('display', 'block');
            }
        });
        $('.logged-out-section').css('display', 'none');
    }

    // Clean TOC
    var toc = $('.vector-toc')[0] || $('#toc')[0] || $('.toc')[0];
    if (toc) {
        $('.logged-in-section:hidden, .logged-out-section:hidden').each(function() {
            $(this).find('h1, h2, h3, h4, h5, h6').each(function() {
                var id = this.id || $(this).find('.mw-headline').attr('id');
                if (id) {
                    $(toc).find('a[href="#' + id + '"]').closest('li').remove();
                }
            });
        });
    }
});