<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://cinnamontoastcrunch.colourversalwikis.com/index.php?action=history&amp;feed=atom&amp;title=MediaWiki%3ACommon.js</id>
	<title>MediaWiki:Common.js - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://cinnamontoastcrunch.colourversalwikis.com/index.php?action=history&amp;feed=atom&amp;title=MediaWiki%3ACommon.js"/>
	<link rel="alternate" type="text/html" href="https://cinnamontoastcrunch.colourversalwikis.com/index.php?title=MediaWiki:Common.js&amp;action=history"/>
	<updated>2026-05-01T14:25:27Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.45.3</generator>
	<entry>
		<id>https://cinnamontoastcrunch.colourversalwikis.com/index.php?title=MediaWiki:Common.js&amp;diff=3&amp;oldid=prev</id>
		<title>Cinnadust: Created page with &quot;/* ============================================================    GOLDEN GATE BRIDGE — MediaWiki Theme JavaScript    Paste into: MediaWiki:Common.js        Features:      1. Reading progress bar      2. Fog canvas animation      3. Scroll-reveal for content sections      4. Back-to-top button      5. Anchor links on headings      6. Time-based dynamic header gradient      7. Sidebar cable decoration      8. Smooth image lazy-load effect    ============================...&quot;</title>
		<link rel="alternate" type="text/html" href="https://cinnamontoastcrunch.colourversalwikis.com/index.php?title=MediaWiki:Common.js&amp;diff=3&amp;oldid=prev"/>
		<updated>2026-05-01T08:44:27Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;/* ============================================================    GOLDEN GATE BRIDGE — MediaWiki Theme JavaScript    Paste into: MediaWiki:Common.js        Features:      1. Reading progress bar      2. Fog canvas animation      3. Scroll-reveal for content sections      4. Back-to-top button      5. Anchor links on headings      6. Time-based dynamic header gradient      7. Sidebar cable decoration      8. Smooth image lazy-load effect    ============================...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;/* ============================================================&lt;br /&gt;
   GOLDEN GATE BRIDGE — MediaWiki Theme JavaScript&lt;br /&gt;
   Paste into: MediaWiki:Common.js&lt;br /&gt;
   &lt;br /&gt;
   Features:&lt;br /&gt;
     1. Reading progress bar&lt;br /&gt;
     2. Fog canvas animation&lt;br /&gt;
     3. Scroll-reveal for content sections&lt;br /&gt;
     4. Back-to-top button&lt;br /&gt;
     5. Anchor links on headings&lt;br /&gt;
     6. Time-based dynamic header gradient&lt;br /&gt;
     7. Sidebar cable decoration&lt;br /&gt;
     8. Smooth image lazy-load effect&lt;br /&gt;
   ============================================================ */&lt;br /&gt;
&lt;br /&gt;
( function () {&lt;br /&gt;
  &amp;#039;use strict&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
  /* ─────────────────────────────────────────────────────────&lt;br /&gt;
     1. READING PROGRESS BAR&lt;br /&gt;
     A slim orange bar at the very top of the viewport that&lt;br /&gt;
     fills as the user scrolls through the page.&lt;br /&gt;
  ───────────────────────────────────────────────────────── */&lt;br /&gt;
  function initProgress() {&lt;br /&gt;
    var bar = document.createElement( &amp;#039;div&amp;#039; );&lt;br /&gt;
    bar.id = &amp;#039;ggb-progress&amp;#039;;&lt;br /&gt;
    document.body.insertBefore( bar, document.body.firstChild );&lt;br /&gt;
&lt;br /&gt;
    function update() {&lt;br /&gt;
      var scrollTop = window.pageYOffset || document.documentElement.scrollTop;&lt;br /&gt;
      var docH = document.documentElement.scrollHeight - window.innerHeight;&lt;br /&gt;
      var pct  = docH &amp;gt; 0 ? Math.min( ( scrollTop / docH ) * 100, 100 ) : 0;&lt;br /&gt;
      bar.style.width = pct + &amp;#039;%&amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener( &amp;#039;scroll&amp;#039;, update, { passive: true } );&lt;br /&gt;
    update();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ─────────────────────────────────────────────────────────&lt;br /&gt;
     2. FOG CANVAS ANIMATION&lt;br /&gt;
     A canvas layered over the page renders softly drifting&lt;br /&gt;
     fog puffs that become visible as the user scrolls — &lt;br /&gt;
     simulating San Francisco&amp;#039;s famous marine layer.&lt;br /&gt;
  ───────────────────────────────────────────────────────── */&lt;br /&gt;
  function initFog() {&lt;br /&gt;
    var canvas = document.createElement( &amp;#039;canvas&amp;#039; );&lt;br /&gt;
    canvas.id = &amp;#039;ggb-fog-canvas&amp;#039;;&lt;br /&gt;
    Object.assign( canvas.style, {&lt;br /&gt;
      position:      &amp;#039;fixed&amp;#039;,&lt;br /&gt;
      top:           &amp;#039;0&amp;#039;,&lt;br /&gt;
      left:          &amp;#039;0&amp;#039;,&lt;br /&gt;
      width:         &amp;#039;100%&amp;#039;,&lt;br /&gt;
      height:        &amp;#039;100%&amp;#039;,&lt;br /&gt;
      pointerEvents: &amp;#039;none&amp;#039;,&lt;br /&gt;
      zIndex:        &amp;#039;9998&amp;#039;,&lt;br /&gt;
      opacity:       &amp;#039;0&amp;#039;,&lt;br /&gt;
      transition:    &amp;#039;opacity 2.5s ease&amp;#039;,&lt;br /&gt;
    } );&lt;br /&gt;
    document.body.appendChild( canvas );&lt;br /&gt;
&lt;br /&gt;
    var ctx = canvas.getContext( &amp;#039;2d&amp;#039; );&lt;br /&gt;
    var W, H, particles = [], raf;&lt;br /&gt;
&lt;br /&gt;
    function resize() {&lt;br /&gt;
      W = canvas.width  = window.innerWidth;&lt;br /&gt;
      H = canvas.height = window.innerHeight;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // A single fog puff&lt;br /&gt;
    function Puff() {&lt;br /&gt;
      var self = this;&lt;br /&gt;
      self.reset = function () {&lt;br /&gt;
        self.x     = Math.random() * W * 1.5 - W * 0.25;&lt;br /&gt;
        self.y     = Math.random() * H;&lt;br /&gt;
        self.r     = 100 + Math.random() * 220;&lt;br /&gt;
        self.alpha = 0.008 + Math.random() * 0.028;&lt;br /&gt;
        self.vx    = -0.06 - Math.random() * 0.1;&lt;br /&gt;
        self.vy    = -0.01 + Math.random() * 0.018;&lt;br /&gt;
      };&lt;br /&gt;
      self.reset();&lt;br /&gt;
&lt;br /&gt;
      self.update = function () {&lt;br /&gt;
        self.x += self.vx;&lt;br /&gt;
        self.y += self.vy;&lt;br /&gt;
        if ( self.x + self.r &amp;lt; 0 ) self.reset();&lt;br /&gt;
      };&lt;br /&gt;
&lt;br /&gt;
      self.draw = function () {&lt;br /&gt;
        var g = ctx.createRadialGradient( self.x, self.y, 0, self.x, self.y, self.r );&lt;br /&gt;
        g.addColorStop( 0,   &amp;#039;rgba(245,242,236,&amp;#039; + self.alpha + &amp;#039;)&amp;#039; );&lt;br /&gt;
        g.addColorStop( 0.5, &amp;#039;rgba(240,236,228,&amp;#039; + ( self.alpha * 0.5 ) + &amp;#039;)&amp;#039; );&lt;br /&gt;
        g.addColorStop( 1,   &amp;#039;rgba(245,242,236,0)&amp;#039; );&lt;br /&gt;
        ctx.fillStyle = g;&lt;br /&gt;
        ctx.beginPath();&lt;br /&gt;
        ctx.arc( self.x, self.y, self.r, 0, Math.PI * 2 );&lt;br /&gt;
        ctx.fill();&lt;br /&gt;
      };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loop() {&lt;br /&gt;
      ctx.clearRect( 0, 0, W, H );&lt;br /&gt;
      for ( var i = 0; i &amp;lt; particles.length; i++ ) {&lt;br /&gt;
        particles[ i ].update();&lt;br /&gt;
        particles[ i ].draw();&lt;br /&gt;
      }&lt;br /&gt;
      raf = requestAnimationFrame( loop );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
      resize();&lt;br /&gt;
      particles = [];&lt;br /&gt;
      for ( var i = 0; i &amp;lt; 24; i++ ) {&lt;br /&gt;
        var p = new Puff();&lt;br /&gt;
        // Stagger starting positions so they don&amp;#039;t all start at the same x&lt;br /&gt;
        p.x = Math.random() * W;&lt;br /&gt;
        particles.push( p );&lt;br /&gt;
      }&lt;br /&gt;
      loop();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener( &amp;#039;resize&amp;#039;, resize, { passive: true } );&lt;br /&gt;
    init();&lt;br /&gt;
&lt;br /&gt;
    // Fog fades in after first scroll, fades out back to top&lt;br /&gt;
    var fogVisible = false;&lt;br /&gt;
    window.addEventListener( &amp;#039;scroll&amp;#039;, function () {&lt;br /&gt;
      var shouldShow = window.pageYOffset &amp;gt; 250;&lt;br /&gt;
      if ( shouldShow !== fogVisible ) {&lt;br /&gt;
        fogVisible = shouldShow;&lt;br /&gt;
        canvas.style.opacity = fogVisible ? &amp;#039;1&amp;#039; : &amp;#039;0&amp;#039;;&lt;br /&gt;
      }&lt;br /&gt;
    }, { passive: true } );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ─────────────────────────────────────────────────────────&lt;br /&gt;
     3. SCROLL REVEAL&lt;br /&gt;
     Uses IntersectionObserver to slide content elements in&lt;br /&gt;
     as they enter the viewport — staggered for a wave feel.&lt;br /&gt;
  ───────────────────────────────────────────────────────── */&lt;br /&gt;
  function initScrollReveal() {&lt;br /&gt;
    if ( !window.IntersectionObserver ) return;&lt;br /&gt;
&lt;br /&gt;
    var selectors = [&lt;br /&gt;
      &amp;#039;.mw-body-content h2&amp;#039;,&lt;br /&gt;
      &amp;#039;.mw-body-content h3&amp;#039;,&lt;br /&gt;
      &amp;#039;.mw-body-content table.wikitable&amp;#039;,&lt;br /&gt;
      &amp;#039;.mw-body-content .thumb&amp;#039;,&lt;br /&gt;
      &amp;#039;.mw-body-content blockquote&amp;#039;,&lt;br /&gt;
      &amp;#039;.mw-body-content .infobox&amp;#039;,&lt;br /&gt;
      &amp;#039;.mw-body-content .hatnote&amp;#039;,&lt;br /&gt;
      &amp;#039;#toc&amp;#039;,&lt;br /&gt;
    ].join( &amp;#039;, &amp;#039; );&lt;br /&gt;
&lt;br /&gt;
    var targets = document.querySelectorAll( selectors );&lt;br /&gt;
&lt;br /&gt;
    var observer = new IntersectionObserver( function ( entries ) {&lt;br /&gt;
      entries.forEach( function ( entry, i ) {&lt;br /&gt;
        if ( entry.isIntersecting ) {&lt;br /&gt;
          var el = entry.target;&lt;br /&gt;
          var delay = ( i % 4 ) * 70;&lt;br /&gt;
          setTimeout( function () {&lt;br /&gt;
            el.classList.add( &amp;#039;ggb-in&amp;#039; );&lt;br /&gt;
            if ( i % 2 === 1 ) el.classList.add( &amp;#039;ggb-in-delay&amp;#039; );&lt;br /&gt;
          }, delay );&lt;br /&gt;
          observer.unobserve( el );&lt;br /&gt;
        }&lt;br /&gt;
      } );&lt;br /&gt;
    }, {&lt;br /&gt;
      threshold:  0.1,&lt;br /&gt;
      rootMargin: &amp;#039;0px 0px -50px 0px&amp;#039;,&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
    targets.forEach( function ( el ) {&lt;br /&gt;
      el.classList.add( &amp;#039;ggb-reveal&amp;#039; );&lt;br /&gt;
      observer.observe( el );&lt;br /&gt;
    } );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ─────────────────────────────────────────────────────────&lt;br /&gt;
     4. BACK-TO-TOP BUTTON&lt;br /&gt;
     An orange circular button appears after scrolling 400px.&lt;br /&gt;
     Clicking it smoothly returns to the top of the page.&lt;br /&gt;
  ───────────────────────────────────────────────────────── */&lt;br /&gt;
  function initBackToTop() {&lt;br /&gt;
    var btn = document.createElement( &amp;#039;button&amp;#039; );&lt;br /&gt;
    btn.id          = &amp;#039;ggb-top-btn&amp;#039;;&lt;br /&gt;
    btn.title       = &amp;#039;Back to top&amp;#039;;&lt;br /&gt;
    btn.textContent = &amp;#039;↑&amp;#039;;&lt;br /&gt;
    btn.setAttribute( &amp;#039;aria-label&amp;#039;, &amp;#039;Back to top&amp;#039; );&lt;br /&gt;
    document.body.appendChild( btn );&lt;br /&gt;
&lt;br /&gt;
    btn.addEventListener( &amp;#039;click&amp;#039;, function () {&lt;br /&gt;
      window.scrollTo( { top: 0, behavior: &amp;#039;smooth&amp;#039; } );&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener( &amp;#039;scroll&amp;#039;, function () {&lt;br /&gt;
      if ( window.pageYOffset &amp;gt; 400 ) {&lt;br /&gt;
        btn.classList.add( &amp;#039;visible&amp;#039; );&lt;br /&gt;
      } else {&lt;br /&gt;
        btn.classList.remove( &amp;#039;visible&amp;#039; );&lt;br /&gt;
      }&lt;br /&gt;
    }, { passive: true } );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ─────────────────────────────────────────────────────────&lt;br /&gt;
     5. HEADING ANCHOR LINKS&lt;br /&gt;
     Adds a ⚓ anchor icon that appears on heading hover,&lt;br /&gt;
     making it easy to copy links to specific sections.&lt;br /&gt;
  ───────────────────────────────────────────────────────── */&lt;br /&gt;
  function initAnchorLinks() {&lt;br /&gt;
    var headlines = document.querySelectorAll( &amp;#039;.mw-headline[id]&amp;#039; );&lt;br /&gt;
&lt;br /&gt;
    headlines.forEach( function ( el ) {&lt;br /&gt;
      var icon = document.createElement( &amp;#039;a&amp;#039; );&lt;br /&gt;
      icon.href      = &amp;#039;#&amp;#039; + el.id;&lt;br /&gt;
      icon.className = &amp;#039;ggb-anchor-link&amp;#039;;&lt;br /&gt;
      icon.textContent = &amp;#039; ¶&amp;#039;;&lt;br /&gt;
      icon.title     = &amp;#039;Link to this section&amp;#039;;&lt;br /&gt;
      icon.setAttribute( &amp;#039;aria-label&amp;#039;, &amp;#039;Link to section: &amp;#039; + el.textContent );&lt;br /&gt;
      Object.assign( icon.style, {&lt;br /&gt;
        color:          &amp;#039;rgba(200,92,43,0)&amp;#039;,&lt;br /&gt;
        fontSize:       &amp;#039;0.65em&amp;#039;,&lt;br /&gt;
        fontFamily:     &amp;quot;&amp;#039;Josefin Sans&amp;#039;, sans-serif&amp;quot;,&lt;br /&gt;
        letterSpacing:  &amp;#039;0&amp;#039;,&lt;br /&gt;
        marginLeft:     &amp;#039;8px&amp;#039;,&lt;br /&gt;
        textDecoration: &amp;#039;none&amp;#039;,&lt;br /&gt;
        transition:     &amp;#039;color 0.2s ease&amp;#039;,&lt;br /&gt;
        verticalAlign:  &amp;#039;middle&amp;#039;,&lt;br /&gt;
      } );&lt;br /&gt;
&lt;br /&gt;
      var heading = el.parentElement;&lt;br /&gt;
      heading.addEventListener( &amp;#039;mouseenter&amp;#039;, function () {&lt;br /&gt;
        icon.style.color = &amp;#039;rgba(200,92,43,0.55)&amp;#039;;&lt;br /&gt;
      } );&lt;br /&gt;
      heading.addEventListener( &amp;#039;mouseleave&amp;#039;, function () {&lt;br /&gt;
        icon.style.color = &amp;#039;rgba(200,92,43,0)&amp;#039;;&lt;br /&gt;
      } );&lt;br /&gt;
      icon.addEventListener( &amp;#039;mouseenter&amp;#039;, function () {&lt;br /&gt;
        this.style.color = &amp;#039;#C85C2B&amp;#039;;&lt;br /&gt;
      } );&lt;br /&gt;
      icon.addEventListener( &amp;#039;mouseleave&amp;#039;, function () {&lt;br /&gt;
        this.style.color = &amp;#039;rgba(200,92,43,0.55)&amp;#039;;&lt;br /&gt;
      } );&lt;br /&gt;
&lt;br /&gt;
      el.appendChild( icon );&lt;br /&gt;
    } );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ─────────────────────────────────────────────────────────&lt;br /&gt;
     6. TIME-BASED DYNAMIC GRADIENT&lt;br /&gt;
     The header and page background shift colour based on&lt;br /&gt;
     the time of day — from cold foggy dawn to warm sunset&lt;br /&gt;
     to deep bay-at-night, just like the real bridge.&lt;br /&gt;
  ───────────────────────────────────────────────────────── */&lt;br /&gt;
  function initDynamicGradient() {&lt;br /&gt;
    var hour = new Date().getHours();&lt;br /&gt;
    var gradient;&lt;br /&gt;
&lt;br /&gt;
    if ( hour &amp;gt;= 5 &amp;amp;&amp;amp; hour &amp;lt; 8 ) {&lt;br /&gt;
      // Pre-dawn / dawn: lavender sky, orange horizon&lt;br /&gt;
      gradient = &amp;#039;linear-gradient(150deg, #2A1A38 0%, #7A3020 45%, #C85C2B 100%)&amp;#039;;&lt;br /&gt;
    } else if ( hour &amp;gt;= 8 &amp;amp;&amp;amp; hour &amp;lt; 11 ) {&lt;br /&gt;
      // Morning: fog burning off, cool deep navy&lt;br /&gt;
      gradient = &amp;#039;linear-gradient(150deg, #0B1B2B 0%, #1A3A58 60%, #162840 100%)&amp;#039;;&lt;br /&gt;
    } else if ( hour &amp;gt;= 11 &amp;amp;&amp;amp; hour &amp;lt; 16 ) {&lt;br /&gt;
      // Midday: classic deep bay navy&lt;br /&gt;
      gradient = &amp;#039;linear-gradient(150deg, #0B1B2B 0%, #162840 50%, #0B1B2B 100%)&amp;#039;;&lt;br /&gt;
    } else if ( hour &amp;gt;= 16 &amp;amp;&amp;amp; hour &amp;lt; 19 ) {&lt;br /&gt;
      // Golden hour: warm amber bleeds in from right&lt;br /&gt;
      gradient = &amp;#039;linear-gradient(150deg, #0B1B2B 0%, #5A2010 55%, #C9A84C 100%)&amp;#039;;&lt;br /&gt;
    } else if ( hour &amp;gt;= 19 &amp;amp;&amp;amp; hour &amp;lt; 21 ) {&lt;br /&gt;
      // Sunset: deep vermillion, spectacular&lt;br /&gt;
      gradient = &amp;#039;linear-gradient(150deg, #180808 0%, #6A2010 35%, #C85C2B 80%, #C9A84C 100%)&amp;#039;;&lt;br /&gt;
    } else {&lt;br /&gt;
      // Night: starless black bay&lt;br /&gt;
      gradient = &amp;#039;linear-gradient(150deg, #04090E 0%, #0B1B2B 55%, #0F2235 100%)&amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Apply to header elements&lt;br /&gt;
    var targets = [&lt;br /&gt;
      document.getElementById( &amp;#039;mw-head&amp;#039; ),&lt;br /&gt;
      document.getElementById( &amp;#039;mw-head-base&amp;#039; ),&lt;br /&gt;
      document.getElementById( &amp;#039;mw-page-base&amp;#039; ),&lt;br /&gt;
      document.querySelector( &amp;#039;.vector-header&amp;#039; ),&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    targets.forEach( function ( el ) {&lt;br /&gt;
      if ( el ) el.style.background = gradient;&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
    // Also subtly tint the sidebar top&lt;br /&gt;
    var panel = document.getElementById( &amp;#039;mw-panel&amp;#039; ) ||&lt;br /&gt;
                document.querySelector( &amp;#039;.vector-column-start&amp;#039; );&lt;br /&gt;
    if ( panel ) {&lt;br /&gt;
      panel.style.background =&lt;br /&gt;
        &amp;#039;linear-gradient(180deg, #0D1F30 0%, #0B1B2B 20%)&amp;#039;;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ─────────────────────────────────────────────────────────&lt;br /&gt;
     7. SIDEBAR CABLE DECORATION&lt;br /&gt;
     Injects an animated SVG suspension cable graphic into&lt;br /&gt;
     the sidebar — a subtle architectural nod to the bridge.&lt;br /&gt;
  ───────────────────────────────────────────────────────── */&lt;br /&gt;
  function initSidebarCable() {&lt;br /&gt;
    var panel = document.getElementById( &amp;#039;mw-panel&amp;#039; ) ||&lt;br /&gt;
                document.querySelector( &amp;#039;.vector-column-start&amp;#039; );&lt;br /&gt;
    if ( !panel ) return;&lt;br /&gt;
&lt;br /&gt;
    var svg = document.createElementNS( &amp;#039;http://www.w3.org/2000/svg&amp;#039;, &amp;#039;svg&amp;#039; );&lt;br /&gt;
    svg.setAttribute( &amp;#039;viewBox&amp;#039;, &amp;#039;0 0 20 300&amp;#039; );&lt;br /&gt;
    svg.setAttribute( &amp;#039;preserveAspectRatio&amp;#039;, &amp;#039;none&amp;#039; );&lt;br /&gt;
    Object.assign( svg.style, {&lt;br /&gt;
      position:   &amp;#039;absolute&amp;#039;,&lt;br /&gt;
      top:        &amp;#039;0&amp;#039;,&lt;br /&gt;
      right:      &amp;#039;0&amp;#039;,&lt;br /&gt;
      width:      &amp;#039;20px&amp;#039;,&lt;br /&gt;
      height:     &amp;#039;100%&amp;#039;,&lt;br /&gt;
      opacity:    &amp;#039;0.6&amp;#039;,&lt;br /&gt;
      pointerEvents: &amp;#039;none&amp;#039;,&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
    // Main cable strand&lt;br /&gt;
    var cable = document.createElementNS( &amp;#039;http://www.w3.org/2000/svg&amp;#039;, &amp;#039;line&amp;#039; );&lt;br /&gt;
    cable.setAttribute( &amp;#039;x1&amp;#039;, &amp;#039;10&amp;#039; ); cable.setAttribute( &amp;#039;y1&amp;#039;, &amp;#039;0&amp;#039; );&lt;br /&gt;
    cable.setAttribute( &amp;#039;x2&amp;#039;, &amp;#039;10&amp;#039; ); cable.setAttribute( &amp;#039;y2&amp;#039;, &amp;#039;300&amp;#039; );&lt;br /&gt;
    cable.setAttribute( &amp;#039;stroke&amp;#039;, &amp;#039;#C85C2B&amp;#039; );&lt;br /&gt;
    cable.setAttribute( &amp;#039;stroke-width&amp;#039;, &amp;#039;1.5&amp;#039; );&lt;br /&gt;
    cable.setAttribute( &amp;#039;stroke-opacity&amp;#039;, &amp;#039;0.5&amp;#039; );&lt;br /&gt;
    svg.appendChild( cable );&lt;br /&gt;
&lt;br /&gt;
    // Horizontal suspender ticks&lt;br /&gt;
    for ( var y = 20; y &amp;lt; 300; y += 28 ) {&lt;br /&gt;
      var tick = document.createElementNS( &amp;#039;http://www.w3.org/2000/svg&amp;#039;, &amp;#039;line&amp;#039; );&lt;br /&gt;
      tick.setAttribute( &amp;#039;x1&amp;#039;, &amp;#039;6&amp;#039; );  tick.setAttribute( &amp;#039;y1&amp;#039;, String( y ) );&lt;br /&gt;
      tick.setAttribute( &amp;#039;x2&amp;#039;, &amp;#039;14&amp;#039; ); tick.setAttribute( &amp;#039;y2&amp;#039;, String( y ) );&lt;br /&gt;
      tick.setAttribute( &amp;#039;stroke&amp;#039;, &amp;#039;#C85C2B&amp;#039; );&lt;br /&gt;
      tick.setAttribute( &amp;#039;stroke-width&amp;#039;, &amp;#039;0.8&amp;#039; );&lt;br /&gt;
      tick.setAttribute( &amp;#039;stroke-opacity&amp;#039;, &amp;#039;0.35&amp;#039; );&lt;br /&gt;
      svg.appendChild( tick );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Animate: gentle cable-sway using CSS animation on the element&lt;br /&gt;
    var style = document.createElement( &amp;#039;style&amp;#039; );&lt;br /&gt;
    style.textContent = [&lt;br /&gt;
      &amp;#039;@keyframes ggbCableSway {&amp;#039;,&lt;br /&gt;
      &amp;#039;  0%,100% { transform: scaleY(1.0) translateY(0px); }&amp;#039;,&lt;br /&gt;
      &amp;#039;  50%      { transform: scaleY(1.002) translateY(-2px); }&amp;#039;,&lt;br /&gt;
      &amp;#039;}&amp;#039;,&lt;br /&gt;
      &amp;#039;#ggb-cable-svg {&amp;#039;,&lt;br /&gt;
      &amp;#039;  animation: ggbCableSway 6s ease-in-out infinite;&amp;#039;,&lt;br /&gt;
      &amp;#039;  transform-origin: top center;&amp;#039;,&lt;br /&gt;
      &amp;#039;}&amp;#039;,&lt;br /&gt;
    ].join( &amp;#039;\n&amp;#039; );&lt;br /&gt;
    document.head.appendChild( style );&lt;br /&gt;
    svg.id = &amp;#039;ggb-cable-svg&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
    panel.style.position = &amp;#039;relative&amp;#039;;&lt;br /&gt;
    panel.style.overflow = &amp;#039;hidden&amp;#039;;&lt;br /&gt;
    panel.appendChild( svg );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ─────────────────────────────────────────────────────────&lt;br /&gt;
     8. IMAGE LAZY-LOAD FADE&lt;br /&gt;
     Images in the article content fade in as they load,&lt;br /&gt;
     rather than popping in abruptly.&lt;br /&gt;
  ───────────────────────────────────────────────────────── */&lt;br /&gt;
  function initImageFade() {&lt;br /&gt;
    var images = document.querySelectorAll( &amp;#039;.mw-body-content img&amp;#039; );&lt;br /&gt;
    var fadeStyle = document.createElement( &amp;#039;style&amp;#039; );&lt;br /&gt;
    fadeStyle.textContent = [&lt;br /&gt;
      &amp;#039;.mw-body-content img { opacity: 0; transition: opacity 0.6s ease; }&amp;#039;,&lt;br /&gt;
      &amp;#039;.mw-body-content img.ggb-img-loaded { opacity: 1; }&amp;#039;,&lt;br /&gt;
    ].join( &amp;#039;\n&amp;#039; );&lt;br /&gt;
    document.head.appendChild( fadeStyle );&lt;br /&gt;
&lt;br /&gt;
    images.forEach( function ( img ) {&lt;br /&gt;
      if ( img.complete ) {&lt;br /&gt;
        img.classList.add( &amp;#039;ggb-img-loaded&amp;#039; );&lt;br /&gt;
      } else {&lt;br /&gt;
        img.addEventListener( &amp;#039;load&amp;#039;, function () {&lt;br /&gt;
          img.classList.add( &amp;#039;ggb-img-loaded&amp;#039; );&lt;br /&gt;
        } );&lt;br /&gt;
        img.addEventListener( &amp;#039;error&amp;#039;, function () {&lt;br /&gt;
          img.classList.add( &amp;#039;ggb-img-loaded&amp;#039; ); // show broken icon&lt;br /&gt;
        } );&lt;br /&gt;
      }&lt;br /&gt;
    } );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ─────────────────────────────────────────────────────────&lt;br /&gt;
     9. ACTIVE TOC HIGHLIGHT&lt;br /&gt;
     Highlights the current section in the Table of Contents&lt;br /&gt;
     as the user scrolls through the article.&lt;br /&gt;
  ───────────────────────────────────────────────────────── */&lt;br /&gt;
  function initTocHighlight() {&lt;br /&gt;
    var toc = document.getElementById( &amp;#039;toc&amp;#039; ) || document.querySelector( &amp;#039;.toc&amp;#039; );&lt;br /&gt;
    if ( !toc ) return;&lt;br /&gt;
&lt;br /&gt;
    var headings = Array.from(&lt;br /&gt;
      document.querySelectorAll( &amp;#039;.mw-body-content h2[id], .mw-body-content h3[id]&amp;#039; )&lt;br /&gt;
    );&lt;br /&gt;
    if ( !headings.length ) return;&lt;br /&gt;
&lt;br /&gt;
    // Inject highlight style&lt;br /&gt;
    var style = document.createElement( &amp;#039;style&amp;#039; );&lt;br /&gt;
    style.textContent = [&lt;br /&gt;
      &amp;#039;.toc a.ggb-toc-active {&amp;#039;,&lt;br /&gt;
      &amp;#039;  color: #C85C2B !important;&amp;#039;,&lt;br /&gt;
      &amp;#039;  font-weight: 600;&amp;#039;,&lt;br /&gt;
      &amp;#039;  padding-left: 4px;&amp;#039;,&lt;br /&gt;
      &amp;#039;  border-left: 2px solid #C85C2B;&amp;#039;,&lt;br /&gt;
      &amp;#039;}&amp;#039;,&lt;br /&gt;
    ].join( &amp;#039;\n&amp;#039; );&lt;br /&gt;
    document.head.appendChild( style );&lt;br /&gt;
&lt;br /&gt;
    function onScroll() {&lt;br /&gt;
      var scrollY = window.pageYOffset + 100;&lt;br /&gt;
      var current = headings[ 0 ];&lt;br /&gt;
&lt;br /&gt;
      for ( var i = 0; i &amp;lt; headings.length; i++ ) {&lt;br /&gt;
        if ( headings[ i ].getBoundingClientRect().top + window.pageYOffset &amp;lt;= scrollY ) {&lt;br /&gt;
          current = headings[ i ];&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      // Find the matching TOC link&lt;br /&gt;
      var id = current.id || ( current.querySelector( &amp;#039;.mw-headline&amp;#039; ) &amp;amp;&amp;amp; current.querySelector( &amp;#039;.mw-headline&amp;#039; ).id );&lt;br /&gt;
      if ( !id ) return;&lt;br /&gt;
&lt;br /&gt;
      toc.querySelectorAll( &amp;#039;a&amp;#039; ).forEach( function ( a ) {&lt;br /&gt;
        a.classList.remove( &amp;#039;ggb-toc-active&amp;#039; );&lt;br /&gt;
      } );&lt;br /&gt;
&lt;br /&gt;
      var active = toc.querySelector( &amp;#039;a[href=&amp;quot;#&amp;#039; + id + &amp;#039;&amp;quot;]&amp;#039; );&lt;br /&gt;
      if ( active ) active.classList.add( &amp;#039;ggb-toc-active&amp;#039; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    window.addEventListener( &amp;#039;scroll&amp;#039;, onScroll, { passive: true } );&lt;br /&gt;
    onScroll();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ─────────────────────────────────────────────────────────&lt;br /&gt;
     10. SEARCH BOX ENHANCEMENT&lt;br /&gt;
     Adds a keyboard shortcut (/) to focus the search box,&lt;br /&gt;
     and shows a subtle hint label near the input.&lt;br /&gt;
  ───────────────────────────────────────────────────────── */&lt;br /&gt;
  function initSearchShortcut() {&lt;br /&gt;
    var searchInput = document.getElementById( &amp;#039;searchInput&amp;#039; ) ||&lt;br /&gt;
                      document.querySelector( &amp;#039;.vector-search-box input[type=&amp;quot;search&amp;quot;]&amp;#039; );&lt;br /&gt;
    if ( !searchInput ) return;&lt;br /&gt;
&lt;br /&gt;
    // Keyboard shortcut: / focuses search (unless user is in an input)&lt;br /&gt;
    document.addEventListener( &amp;#039;keydown&amp;#039;, function ( e ) {&lt;br /&gt;
      if ( e.key === &amp;#039;/&amp;#039; &amp;amp;&amp;amp;&lt;br /&gt;
           document.activeElement.tagName !== &amp;#039;INPUT&amp;#039; &amp;amp;&amp;amp;&lt;br /&gt;
           document.activeElement.tagName !== &amp;#039;TEXTAREA&amp;#039; ) {&lt;br /&gt;
        e.preventDefault();&lt;br /&gt;
        searchInput.focus();&lt;br /&gt;
        searchInput.select();&lt;br /&gt;
      }&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
    // ESC blurs search&lt;br /&gt;
    searchInput.addEventListener( &amp;#039;keydown&amp;#039;, function ( e ) {&lt;br /&gt;
      if ( e.key === &amp;#039;Escape&amp;#039; ) searchInput.blur();&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
    // Hint label&lt;br /&gt;
    var hint = document.createElement( &amp;#039;span&amp;#039; );&lt;br /&gt;
    hint.textContent = &amp;#039;/&amp;#039;;&lt;br /&gt;
    Object.assign( hint.style, {&lt;br /&gt;
      background:    &amp;#039;rgba(138,155,176,0.15)&amp;#039;,&lt;br /&gt;
      border:        &amp;#039;1px solid rgba(138,155,176,0.3)&amp;#039;,&lt;br /&gt;
      borderRadius:  &amp;#039;3px&amp;#039;,&lt;br /&gt;
      color:         &amp;#039;rgba(138,155,176,0.7)&amp;#039;,&lt;br /&gt;
      fontFamily:    &amp;quot;&amp;#039;Josefin Sans&amp;#039;, sans-serif&amp;quot;,&lt;br /&gt;
      fontSize:      &amp;#039;10px&amp;#039;,&lt;br /&gt;
      fontWeight:    &amp;#039;600&amp;#039;,&lt;br /&gt;
      letterSpacing: &amp;#039;0.05em&amp;#039;,&lt;br /&gt;
      padding:       &amp;#039;1px 5px&amp;#039;,&lt;br /&gt;
      pointerEvents: &amp;#039;none&amp;#039;,&lt;br /&gt;
      position:      &amp;#039;absolute&amp;#039;,&lt;br /&gt;
      right:         &amp;#039;40px&amp;#039;,&lt;br /&gt;
      top:           &amp;#039;50%&amp;#039;,&lt;br /&gt;
      transform:     &amp;#039;translateY(-50%)&amp;#039;,&lt;br /&gt;
      transition:    &amp;#039;opacity 0.2s ease&amp;#039;,&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
    var searchForm = searchInput.closest( &amp;#039;form&amp;#039; ) || searchInput.parentElement;&lt;br /&gt;
    if ( searchForm ) {&lt;br /&gt;
      searchForm.style.position = &amp;#039;relative&amp;#039;;&lt;br /&gt;
      searchForm.appendChild( hint );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    searchInput.addEventListener( &amp;#039;focus&amp;#039;, function () { hint.style.opacity = &amp;#039;0&amp;#039;; } );&lt;br /&gt;
    searchInput.addEventListener( &amp;#039;blur&amp;#039;,  function () { hint.style.opacity = &amp;#039;1&amp;#039;; } );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ─────────────────────────────────────────────────────────&lt;br /&gt;
     11. EXTERNAL LINK ICON&lt;br /&gt;
     Adds a small external link indicator after links that&lt;br /&gt;
     point outside the wiki.&lt;br /&gt;
  ───────────────────────────────────────────────────────── */&lt;br /&gt;
  function initExternalLinkIcons() {&lt;br /&gt;
    var style = document.createElement( &amp;#039;style&amp;#039; );&lt;br /&gt;
    style.textContent = [&lt;br /&gt;
      &amp;#039;.mw-body-content a.external::after {&amp;#039;,&lt;br /&gt;
      &amp;#039;  content: &amp;quot; ↗&amp;quot;;&amp;#039;,&lt;br /&gt;
      &amp;#039;  color: rgba(200,92,43,0.6);&amp;#039;,&lt;br /&gt;
      &amp;#039;  font-size: 0.75em;&amp;#039;,&lt;br /&gt;
      &amp;#039;  vertical-align: super;&amp;#039;,&lt;br /&gt;
      &amp;#039;  margin-left: 1px;&amp;#039;,&lt;br /&gt;
      &amp;#039;}&amp;#039;,&lt;br /&gt;
    ].join( &amp;#039;\n&amp;#039; );&lt;br /&gt;
    document.head.appendChild( style );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* ─────────────────────────────────────────────────────────&lt;br /&gt;
     INIT — run everything after DOM is ready&lt;br /&gt;
  ───────────────────────────────────────────────────────── */&lt;br /&gt;
  function run() {&lt;br /&gt;
    // Core UI enhancements&lt;br /&gt;
    initProgress();&lt;br /&gt;
    initBackToTop();&lt;br /&gt;
    initDynamicGradient();&lt;br /&gt;
    initSidebarCable();&lt;br /&gt;
&lt;br /&gt;
    // Content enhancements&lt;br /&gt;
    initScrollReveal();&lt;br /&gt;
    initAnchorLinks();&lt;br /&gt;
    initTocHighlight();&lt;br /&gt;
    initImageFade();&lt;br /&gt;
&lt;br /&gt;
    // Search&lt;br /&gt;
    initSearchShortcut();&lt;br /&gt;
&lt;br /&gt;
    // Link icons&lt;br /&gt;
    initExternalLinkIcons();&lt;br /&gt;
&lt;br /&gt;
    // Fog — init last as it starts a canvas animation loop&lt;br /&gt;
    initFog();&lt;br /&gt;
&lt;br /&gt;
    // Console credit&lt;br /&gt;
    if ( window.console &amp;amp;&amp;amp; console.log ) {&lt;br /&gt;
      console.log( &amp;#039;%c🌉 Golden Gate Bridge Theme active&amp;#039;, &amp;#039;color:#C85C2B;font-family:Georgia,serif;font-size:14px;&amp;#039; );&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  if ( document.readyState === &amp;#039;loading&amp;#039; ) {&lt;br /&gt;
    document.addEventListener( &amp;#039;DOMContentLoaded&amp;#039;, run );&lt;br /&gt;
  } else {&lt;br /&gt;
    // mw.loader available: use MediaWiki&amp;#039;s ready hook if possible&lt;br /&gt;
    if ( typeof mw !== &amp;#039;undefined&amp;#039; &amp;amp;&amp;amp; mw.hook ) {&lt;br /&gt;
      mw.hook( &amp;#039;wikipage.content&amp;#039; ).add( function () { run(); } );&lt;br /&gt;
    } else {&lt;br /&gt;
      run();&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
} )();&lt;/div&gt;</summary>
		<author><name>Cinnadust</name></author>
	</entry>
</feed>