<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://pegasusxi.com/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=PegasusAdmin</id>
	<title>PegasusXI Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://pegasusxi.com/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=PegasusAdmin"/>
	<link rel="alternate" type="text/html" href="https://pegasusxi.com/wiki/Special:Contributions/PegasusAdmin"/>
	<updated>2026-09-02T11:11:51Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.9</generator>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=MediaWiki:Timeless.css&amp;diff=142078</id>
		<title>MediaWiki:Timeless.css</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=MediaWiki:Timeless.css&amp;diff=142078"/>
		<updated>2026-08-30T00:35:56Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: PegasusXI theme (generated by scripts/build-wiki-css.mjs)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* PegasusXI Wiki - Timeless chrome and dark theme&lt;br /&gt;
 *&lt;br /&gt;
 * Generated by scripts/build-wiki-css.mjs from site/public/pegasus-theme.css and&lt;br /&gt;
 * site/public/pegasus-dark.css. Do not edit here; regenerate instead.&lt;br /&gt;
 *&lt;br /&gt;
 * Dark is the default. The original files keyed off html.skin-theme-clientpref-night, a class real&lt;br /&gt;
 * Timeless never emits, so the selectors are rewritten to html:not(.pg-day): the dark theme applies&lt;br /&gt;
 * with no script involvement and light mode is opted into by adding .pg-day to &amp;lt;html&amp;gt;. Doing it the&lt;br /&gt;
 * other way round would flash a light page on every load before the script ran.&lt;br /&gt;
 *&lt;br /&gt;
 * The article body is inverted rather than restyled. Content carries light backgrounds from hundreds&lt;br /&gt;
 * of rules in MediaWiki:Common.css plus inline styles on template tables; inverting flips background&lt;br /&gt;
 * and text together, so BG-Wiki&#039;s colour coding survives and contrast cannot collapse to&lt;br /&gt;
 * light-on-light. Images are inverted a second time so they render true to colour.&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/* PegasusXI theme over Timeless — layout stays BG-Wiki, chrome uses Pegasus colors */&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* Carried as a mask rather than an &amp;lt;img&amp;gt; so it takes the button&#039;s own text colour. Held in a&lt;br /&gt;
   variable because MediaWiki:Timeless.css is generated, and a data URI is easier to find and&lt;br /&gt;
   replace in one place than inline in a rule. */&lt;br /&gt;
:root {&lt;br /&gt;
  --pg-discord-mark: url(&amp;quot;data:image/svg+xml,%3Csvg xmlns=&#039;http://www.w3.org/2000/svg&#039; viewBox=&#039;0 0 24 24&#039;%3E%3Cpath d=&#039;M20.317 4.369a19.79 19.79 0 00-4.885-1.515.074.074 0 00-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 00-5.487 0 12.64 12.64 0 00-.617-1.25.077.077 0 00-.079-.037A19.736 19.736 0 003.677 4.37a.07.07 0 00-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 00.031.057 19.9 19.9 0 005.993 3.03.078.078 0 00.084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 00-.041-.106 13.107 13.107 0 01-1.872-.892.077.077 0 01-.008-.128c.126-.094.252-.192.372-.291a.074.074 0 01.077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 01.078.009c.12.099.246.198.373.292a.077.077 0 01-.006.127 12.3 12.3 0 01-1.873.891.077.077 0 00-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 00.084.028 19.839 19.839 0 006.002-3.03.077.077 0 00.032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 00-.031-.03zM8.02 15.331c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.418 2.157-2.418 1.21 0 2.176 1.095 2.157 2.418 0 1.334-.956 2.419-2.157 2.419zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.418 2.157-2.418 1.21 0 2.176 1.095 2.157 2.418 0 1.334-.946 2.419-2.157 2.419z&#039;/%3E%3C/svg%3E&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#mw-header-container {&lt;br /&gt;
  background: #080b14 !important;&lt;br /&gt;
  color: #e4e8f0 !important;&lt;br /&gt;
}&lt;br /&gt;
#p-logo-text {&lt;br /&gt;
  max-width: 14em !important;&lt;br /&gt;
  min-width: 14em !important;&lt;br /&gt;
  width: 14em;&lt;br /&gt;
  padding-right: 0 !important;&lt;br /&gt;
  text-align: center !important;&lt;br /&gt;
}&lt;br /&gt;
#p-logo-text a,&lt;br /&gt;
#p-logo-text a.long {&lt;br /&gt;
  color: #c9a032 !important;&lt;br /&gt;
  font-family: Cinzel, Georgia, serif !important;&lt;br /&gt;
  font-variant: normal !important;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  letter-spacing: 0.03em;&lt;br /&gt;
  white-space: normal !important;&lt;br /&gt;
  font-size: 1.5em !important;&lt;br /&gt;
  line-height: 0.85 !important;&lt;br /&gt;
  height: 100% !important;&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  justify-content: center;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
}&lt;br /&gt;
#personal h2 span { color: #e4e8f0; }&lt;br /&gt;
#simpleSearch { background: #0c1020; border-color: rgba(140, 170, 220, 0.25); }&lt;br /&gt;
#searchInput { color: #e4e8f0; }&lt;br /&gt;
#searchButton, #mw-searchButton { filter: invert(1) brightness(1.35); }&lt;br /&gt;
#personal h2 { color: #e4e8f0 !important; }&lt;br /&gt;
#p-logo {&lt;br /&gt;
  width: calc(100% + 1em);&lt;br /&gt;
  box-sizing: border-box;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.mw-wiki-logo.timeless-logo img {&lt;br /&gt;
  width: 135px;&lt;br /&gt;
  height: auto;&lt;br /&gt;
  object-fit: contain;&lt;br /&gt;
  display: block;&lt;br /&gt;
  margin: 0.75em auto 0.5em;&lt;br /&gt;
  border-radius: 0;&lt;br /&gt;
  box-shadow: none;&lt;br /&gt;
  background: transparent;&lt;br /&gt;
}&lt;br /&gt;
#p-search h3 { color: #e4e8f0; }&lt;br /&gt;
#searchInput::placeholder { color: #8a92a8; }&lt;br /&gt;
.color-left { background: #c9a032 !important; }&lt;br /&gt;
.color-middle { background: #4a7ec2 !important; }&lt;br /&gt;
.color-right { background: #7cc5e8 !important; }&lt;br /&gt;
#mw-content-container {&lt;br /&gt;
  background: #0c1020 !important;&lt;br /&gt;
  border-bottom-color: #c9a032 !important;&lt;br /&gt;
  background-image: none !important;&lt;br /&gt;
}&lt;br /&gt;
html.pg-day #mw-content {&lt;br /&gt;
  background: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
#mw-site-navigation .sidebar-chunk &amp;gt; h2,&lt;br /&gt;
#mw-related-navigation .sidebar-chunk &amp;gt; h2 {&lt;br /&gt;
  color: #e4e8f0;&lt;br /&gt;
}&lt;br /&gt;
.mw-body h1.firstHeading {&lt;br /&gt;
  border-bottom-color: #c9a032 !important;&lt;br /&gt;
}&lt;br /&gt;
#mw-footer-container {&lt;br /&gt;
  background: #080b14 !important;&lt;br /&gt;
  border-top-color: rgba(140, 170, 220, 0.15) !important;&lt;br /&gt;
  color: #8a92a8 !important;&lt;br /&gt;
}&lt;br /&gt;
#mw-footer-container a { color: #7cc5e8 !important; }&lt;br /&gt;
#mw-site-navigation .sidebar-chunk h3,&lt;br /&gt;
#mw-related-navigation .sidebar-chunk h3 {&lt;br /&gt;
  border-bottom-color: #c9a032 !important;&lt;br /&gt;
}&lt;br /&gt;
a { color: #4a7ec2; }&lt;br /&gt;
a:hover { color: #6498da; }&lt;br /&gt;
.tools-inline li.selected { border-bottom-color: #c9a032 !important; }&lt;br /&gt;
#localNotice { background: #0c1020; }&lt;br /&gt;
.sitenotice { background: #111628 !important; color: #e4e8f0; }&lt;br /&gt;
.sitenotice td { border-color: rgba(74, 126, 194, 0.45) !important; }&lt;br /&gt;
.sitenotice a { color: #7cc5e8 !important; }&lt;br /&gt;
/* #mw-header is a flex row and Timeless assigns explicit orders: #p-logo-text 1, #p-search 2,&lt;br /&gt;
   #user-tools 3. Without an order of its own this button defaults to 0 and jumps ahead of all&lt;br /&gt;
   three, which is why it first rendered at the far left of the header. Order 4 puts it at the&lt;br /&gt;
   right-hand end, past the personal menu.&lt;br /&gt;
&lt;br /&gt;
   Hidden below 851px, where Timeless turns the user menu into an absolutely positioned icon that&lt;br /&gt;
   this would collide with. */&lt;br /&gt;
#p-discord-signin {&lt;br /&gt;
  display: none;&lt;br /&gt;
  order: 4;&lt;br /&gt;
  flex: 0 0 auto;&lt;br /&gt;
  margin-left: 1em;&lt;br /&gt;
  align-self: center;&lt;br /&gt;
}&lt;br /&gt;
@media screen and (min-width: 851px) {&lt;br /&gt;
  #p-discord-signin {&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
.pg-discord-signin {&lt;br /&gt;
  display: inline-flex;&lt;br /&gt;
  align-items: center;&lt;br /&gt;
  gap: 0.45em;&lt;br /&gt;
  background: #5865f2;&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  font-size: 0.85em;&lt;br /&gt;
  font-weight: 600;&lt;br /&gt;
  padding: 0.4em 0.9em;&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
}&lt;br /&gt;
.pg-discord-signin:hover {&lt;br /&gt;
  background: #4752c4;&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
/* Held while the login form is fetched. That is one same-origin request, so this is usually a single&lt;br /&gt;
   frame, but without it a slow connection looks like a dead button. */&lt;br /&gt;
.pg-discord-signin-busy {&lt;br /&gt;
  opacity: 0.65;&lt;br /&gt;
  pointer-events: none;&lt;br /&gt;
}&lt;br /&gt;
/* The Discord mark, so the button reads as Discord at a glance rather than relying on the word. */&lt;br /&gt;
.pg-discord-signin-mark {&lt;br /&gt;
  width: 1.15em;&lt;br /&gt;
  height: 1.15em;&lt;br /&gt;
  flex: 0 0 auto;&lt;br /&gt;
  background-color: currentColor;&lt;br /&gt;
  -webkit-mask: var(--pg-discord-mark) center / contain no-repeat;&lt;br /&gt;
  mask: var(--pg-discord-mark) center / contain no-repeat;&lt;br /&gt;
}&lt;br /&gt;
#discord-panel {&lt;br /&gt;
  display: none;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ---- Fluid three-column layout ----&lt;br /&gt;
   Stock Timeless only builds three columns at &amp;gt;=1340px. Below that it snaps into other layouts:&lt;br /&gt;
   1100-1339px stacks the right sidebar (#mw-content-wrapper goes float:right;width:100%), and&lt;br /&gt;
   851-1099px turns both sidebars into absolutely positioned dropdown toggles. On a display with&lt;br /&gt;
   Windows scaling at 125% even a maximized 1920px screen reports only ~1536px, so those bands are&lt;br /&gt;
   easy to land in. These overrides start the flex layout at 1000px and let the sidebars shrink&lt;br /&gt;
   with the window, so the columns track the window instead of snapping between fixed sizes. */&lt;br /&gt;
@media screen and (min-width: 1000px) {&lt;br /&gt;
  #mw-content-block {&lt;br /&gt;
    display: flex !important;&lt;br /&gt;
    clear: both !important;&lt;br /&gt;
  }&lt;br /&gt;
  #mw-site-navigation,&lt;br /&gt;
  #mw-related-navigation {&lt;br /&gt;
    position: relative !important;&lt;br /&gt;
    display: block !important;&lt;br /&gt;
    top: auto !important;&lt;br /&gt;
    right: auto !important;&lt;br /&gt;
    width: auto !important;&lt;br /&gt;
    box-sizing: border-box;&lt;br /&gt;
  }&lt;br /&gt;
  #mw-site-navigation {&lt;br /&gt;
    order: 1;&lt;br /&gt;
    flex: 0 1 14em;&lt;br /&gt;
    min-width: 10.5em;&lt;br /&gt;
    padding-left: 0 !important;&lt;br /&gt;
    padding-right: 1em !important;&lt;br /&gt;
  }&lt;br /&gt;
  #mw-content-wrapper {&lt;br /&gt;
    order: 2;&lt;br /&gt;
    flex: 1 1 0%;&lt;br /&gt;
    float: none !important;&lt;br /&gt;
    width: auto !important;&lt;br /&gt;
    min-width: 0;&lt;br /&gt;
  }&lt;br /&gt;
  #mw-related-navigation {&lt;br /&gt;
    order: 3;&lt;br /&gt;
    flex: 0 1 16em;&lt;br /&gt;
    min-width: 11em;&lt;br /&gt;
    padding-left: 1em !important;&lt;br /&gt;
    padding-right: 0 !important;&lt;br /&gt;
  }&lt;br /&gt;
  #mw-content {&lt;br /&gt;
    margin-top: 0 !important;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* In column mode the sidebar panels are always open, not collapsible dropdowns. */&lt;br /&gt;
  #mw-site-navigation .sidebar-chunk,&lt;br /&gt;
  #mw-related-navigation .sidebar-chunk,&lt;br /&gt;
  #mw-site-navigation .sidebar-inner,&lt;br /&gt;
  #mw-related-navigation .sidebar-inner {&lt;br /&gt;
    display: block !important;&lt;br /&gt;
    position: static !important;&lt;br /&gt;
    min-width: 0 !important;&lt;br /&gt;
    margin-left: 0 !important;&lt;br /&gt;
  }&lt;br /&gt;
  #mw-site-navigation .sidebar-chunk &amp;gt; h2,&lt;br /&gt;
  #mw-related-navigation .sidebar-chunk &amp;gt; h2 {&lt;br /&gt;
    cursor: default;&lt;br /&gt;
  }&lt;br /&gt;
  #mw-site-navigation .sidebar-chunk &amp;gt; h2::after,&lt;br /&gt;
  #mw-related-navigation .sidebar-chunk &amp;gt; h2::after,&lt;br /&gt;
  #mw-site-navigation .sidebar-chunk h2 span::before,&lt;br /&gt;
  #mw-site-navigation .sidebar-chunk h2 span::after,&lt;br /&gt;
  #mw-site-navigation .sidebar-inner::before,&lt;br /&gt;
  #mw-site-navigation .sidebar-inner::after,&lt;br /&gt;
  #mw-related-navigation .sidebar-inner::before,&lt;br /&gt;
  #mw-related-navigation .sidebar-inner::after {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  /* The secondary color bar is a white block used only by the narrow layouts. */&lt;br /&gt;
  #mw-header-nav-hack {&lt;br /&gt;
    display: none !important;&lt;br /&gt;
  }&lt;br /&gt;
  #p-logo {&lt;br /&gt;
    display: block !important;&lt;br /&gt;
  }&lt;br /&gt;
  #discord-panel {&lt;br /&gt;
    display: block;&lt;br /&gt;
    margin: 0 0 1em;&lt;br /&gt;
    border-radius: 4px;&lt;br /&gt;
    overflow: hidden;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
/* ---- Discord presence panel ----&lt;br /&gt;
   The member list is fetched from widget.json and rendered by MediaWiki:Gadget-pegasus.js rather&lt;br /&gt;
   than embedded as Discord&#039;s own iframe, because that iframe is cross-origin and stuck on Discord&#039;s&lt;br /&gt;
   blurple. Rendering it ourselves means it takes this wiki&#039;s palette; only the join button keeps&lt;br /&gt;
   Discord&#039;s colour, where the brand cue is the point. */&lt;br /&gt;
.pg-discord {&lt;br /&gt;
  background: #111628;&lt;br /&gt;
  border: 1px solid rgba(140, 170, 220, 0.25);&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  padding: 0.75em;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
}&lt;br /&gt;
.pg-discord-head {&lt;br /&gt;
  margin-bottom: 0.6em;&lt;br /&gt;
}&lt;br /&gt;
.pg-discord-title {&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-family: Cinzel, Georgia, serif;&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
  letter-spacing: 0.03em;&lt;br /&gt;
  color: #c9a032;&lt;br /&gt;
}&lt;br /&gt;
.pg-discord-count {&lt;br /&gt;
  display: block;&lt;br /&gt;
  font-size: 0.85em;&lt;br /&gt;
  color: #98a1b9;&lt;br /&gt;
}&lt;br /&gt;
.pg-discord-members {&lt;br /&gt;
  list-style: none;&lt;br /&gt;
  margin: 0 0 0.7em;&lt;br /&gt;
  padding: 0;&lt;br /&gt;
  text-align: left;&lt;br /&gt;
  /* Roughly ten rows before scrolling, so a busy server cannot push the join button out of view. */&lt;br /&gt;
  max-height: 17em;&lt;br /&gt;
  overflow-y: auto;&lt;br /&gt;
}&lt;br /&gt;
.pg-discord-members::-webkit-scrollbar {&lt;br /&gt;
  width: 6px;&lt;br /&gt;
}&lt;br /&gt;
.pg-discord-members::-webkit-scrollbar-thumb {&lt;br /&gt;
  background: rgba(140, 170, 220, 0.3);&lt;br /&gt;
  border-radius: 3px;&lt;br /&gt;
}&lt;br /&gt;
.pg-discord-member {&lt;br /&gt;
  display: flex;&lt;br /&gt;
  align-items: center;&lt;br /&gt;
  gap: 0.45em;&lt;br /&gt;
  padding: 0.15em 0;&lt;br /&gt;
  font-size: 0.85em;&lt;br /&gt;
  color: #c8cee0;&lt;br /&gt;
}&lt;br /&gt;
.pg-discord-avatar {&lt;br /&gt;
  width: 20px;&lt;br /&gt;
  height: 20px;&lt;br /&gt;
  flex: 0 0 auto;&lt;br /&gt;
  border-radius: 50%;&lt;br /&gt;
  background: #0c1020;&lt;br /&gt;
}&lt;br /&gt;
.pg-discord-member-name {&lt;br /&gt;
  overflow: hidden;&lt;br /&gt;
  white-space: nowrap;&lt;br /&gt;
  text-overflow: ellipsis;&lt;br /&gt;
}&lt;br /&gt;
/* Discord&#039;s own status colours: recognisable, and the only place they are used. A ring around the&lt;br /&gt;
   avatar rather than a separate dot, so a long name keeps the full width. */&lt;br /&gt;
.pg-discord-member[data-status=&amp;quot;online&amp;quot;] .pg-discord-avatar {&lt;br /&gt;
  box-shadow: 0 0 0 2px #23a55a;&lt;br /&gt;
}&lt;br /&gt;
.pg-discord-member[data-status=&amp;quot;idle&amp;quot;] .pg-discord-avatar {&lt;br /&gt;
  box-shadow: 0 0 0 2px #f0b232;&lt;br /&gt;
}&lt;br /&gt;
.pg-discord-member[data-status=&amp;quot;dnd&amp;quot;] .pg-discord-avatar {&lt;br /&gt;
  box-shadow: 0 0 0 2px #f23f43;&lt;br /&gt;
}&lt;br /&gt;
.pg-discord-member[data-status=&amp;quot;offline&amp;quot;] .pg-discord-avatar {&lt;br /&gt;
  box-shadow: 0 0 0 2px rgba(140, 170, 220, 0.35);&lt;br /&gt;
}&lt;br /&gt;
/* If the server widget is switched off, widget.json 403s and the panel falls back to a plain join&lt;br /&gt;
   box -- the empty list would otherwise leave a gap under the heading. */&lt;br /&gt;
.pg-discord[data-state=&amp;quot;unavailable&amp;quot;] .pg-discord-members {&lt;br /&gt;
  display: none;&lt;br /&gt;
}&lt;br /&gt;
.pg-discord-join {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  background: #5865f2;&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
  font-weight: 600;&lt;br /&gt;
  padding: 0.45em 1.1em;&lt;br /&gt;
  border-radius: 4px;&lt;br /&gt;
  text-decoration: none;&lt;br /&gt;
}&lt;br /&gt;
.pg-discord-join:hover {&lt;br /&gt;
  background: #4752c4;&lt;br /&gt;
  color: #fff !important;&lt;br /&gt;
}&lt;br /&gt;
img[usemap],&lt;br /&gt;
.noresize img[usemap],&lt;br /&gt;
figure.noresize img {&lt;br /&gt;
  max-width: none !important;&lt;br /&gt;
}&lt;br /&gt;
.noresize { overflow-x: auto; }&lt;br /&gt;
.imageMapHighlighter figure,&lt;br /&gt;
figure.mw-ext-imagemap-desc-bottom-right {&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  position: relative;&lt;br /&gt;
}&lt;br /&gt;
map area { cursor: pointer; }&lt;br /&gt;
&lt;br /&gt;
/* ---- Vana&#039;diel Time panel ----&lt;br /&gt;
   Lives in a normal Timeless portlet list item, so the list styling has to be undone. Weekday&lt;br /&gt;
   colors are the elemental colors used by the other Vana&#039;diel clocks; pegasus-dark.css lightens&lt;br /&gt;
   them for the night theme, where the darker ones (Watersday, Darksday) would be unreadable. */&lt;br /&gt;
#n-vanatime {&lt;br /&gt;
  list-style: none;&lt;br /&gt;
  margin: 0;&lt;br /&gt;
  padding: 0;&lt;br /&gt;
}&lt;br /&gt;
#n-vanatime::before {&lt;br /&gt;
  content: none;&lt;br /&gt;
}&lt;br /&gt;
.pg-vana {&lt;br /&gt;
  display: flex;&lt;br /&gt;
  flex-direction: column;&lt;br /&gt;
  gap: 0.3em;&lt;br /&gt;
  padding: 0.15em 0 0.25em;&lt;br /&gt;
  line-height: 1.35;&lt;br /&gt;
}&lt;br /&gt;
.pg-vana-line {&lt;br /&gt;
  display: flex;&lt;br /&gt;
  align-items: baseline;&lt;br /&gt;
  flex-wrap: wrap;&lt;br /&gt;
  gap: 0.35em;&lt;br /&gt;
}&lt;br /&gt;
.pg-vana-clock {&lt;br /&gt;
  font-size: 1.05em;&lt;br /&gt;
  font-variant-numeric: tabular-nums;&lt;br /&gt;
}&lt;br /&gt;
.pg-vana-tilde {&lt;br /&gt;
  opacity: 0.5;&lt;br /&gt;
}&lt;br /&gt;
.pg-vana-day {&lt;br /&gt;
  font-weight: 700;&lt;br /&gt;
}&lt;br /&gt;
.pg-vana-moon {&lt;br /&gt;
  display: inline-flex;&lt;br /&gt;
  align-items: baseline;&lt;br /&gt;
  gap: 0.3em;&lt;br /&gt;
}&lt;br /&gt;
.pg-vana-moon-icon {&lt;br /&gt;
  font-size: 1.05em;&lt;br /&gt;
}&lt;br /&gt;
.pg-vana-dot {&lt;br /&gt;
  flex: 0 0 auto;&lt;br /&gt;
  width: 0.55em;&lt;br /&gt;
  height: 0.55em;&lt;br /&gt;
  border-radius: 50%;&lt;br /&gt;
  background: #9aa0ac;&lt;br /&gt;
}&lt;br /&gt;
.pg-vana-server[data-state=&amp;quot;online&amp;quot;] .pg-vana-dot {&lt;br /&gt;
  background: #35c46a;&lt;br /&gt;
}&lt;br /&gt;
.pg-vana-server[data-state=&amp;quot;offline&amp;quot;] .pg-vana-dot {&lt;br /&gt;
  background: #e04747;&lt;br /&gt;
}&lt;br /&gt;
/* Hidden when there is no player count to show, so &amp;quot;Online&amp;quot; doesn&#039;t trail a stray gap. */&lt;br /&gt;
.pg-vana-server-count:empty {&lt;br /&gt;
  display: none;&lt;br /&gt;
}&lt;br /&gt;
.pg-vana-day-0 { color: #ff0000; }&lt;br /&gt;
.pg-vana-day-1 { color: #aaaa00; }&lt;br /&gt;
.pg-vana-day-2 { color: #0000dd; }&lt;br /&gt;
.pg-vana-day-3 { color: #00aa22; }&lt;br /&gt;
.pg-vana-day-4 { color: #7799ff; }&lt;br /&gt;
.pg-vana-day-5 { color: #aa00aa; }&lt;br /&gt;
.pg-vana-day-6 { color: #aaaaaa; }&lt;br /&gt;
.pg-vana-day-7 { color: #333333; }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* PegasusXI night theme — dark wiki over the Timeless layout.&lt;br /&gt;
   Scoped to html:not(.pg-day) (the default) so day mode keeps BG-Wiki&#039;s original look.&lt;br /&gt;
   Class names follow MediaWiki 1.43 / Extension:DarkMode so the markup matches HorizonXI&#039;s wiki.&lt;br /&gt;
&lt;br /&gt;
   Two different techniques, on purpose:&lt;br /&gt;
   - The chrome (header, sidebar, footer) is styled directly, because we own that markup.&lt;br /&gt;
   - The article body is inverted, because we don&#039;t. BG-Wiki&#039;s content carries light backgrounds&lt;br /&gt;
     from ~281 declarations in site-styles.css (.category-header, table.item td, .altrowgrey-*)&lt;br /&gt;
     plus inline styles on template tables. Restyling those by hand means guessing at each&lt;br /&gt;
     matching text color; inverting flips background and text together, so BG-Wiki&#039;s color&lt;br /&gt;
     coding survives and contrast can never collapse to light-on-light. */&lt;br /&gt;
&lt;br /&gt;
html:not(.pg-day) {&lt;br /&gt;
  --pg-bg: #0c1020;&lt;br /&gt;
  --pg-panel: #111628;&lt;br /&gt;
  --pg-panel-alt: #161c31;&lt;br /&gt;
  --pg-text: #e4e8f0;&lt;br /&gt;
  --pg-muted: #98a1b9;&lt;br /&gt;
  --pg-line: rgba(140, 170, 220, 0.22);&lt;br /&gt;
  --pg-gold: #c9a032;&lt;br /&gt;
  --pg-link: #7cc5e8;&lt;br /&gt;
  color-scheme: dark;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
html:not(.pg-day) body {&lt;br /&gt;
  background: var(--pg-bg);&lt;br /&gt;
  color: var(--pg-text);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ---- Chrome: styled directly, never inverted ---- */&lt;br /&gt;
&lt;br /&gt;
html:not(.pg-day) #mw-content,&lt;br /&gt;
html:not(.pg-day) #mw-content-wrapper,&lt;br /&gt;
html:not(.pg-day) .mw-body {&lt;br /&gt;
  background: var(--pg-panel) !important;&lt;br /&gt;
  color: var(--pg-text);&lt;br /&gt;
  border-color: var(--pg-line) !important;&lt;br /&gt;
}&lt;br /&gt;
html:not(.pg-day) .mw-body h1.firstHeading {&lt;br /&gt;
  color: #f1f3f9;&lt;br /&gt;
}&lt;br /&gt;
html:not(.pg-day) #siteSub,&lt;br /&gt;
html:not(.pg-day) #contentSub,&lt;br /&gt;
html:not(.pg-day) #mw-content-subtitle,&lt;br /&gt;
html:not(.pg-day) .printfooter,&lt;br /&gt;
html:not(.pg-day) #mw-page-header-links h3 {&lt;br /&gt;
  color: var(--pg-muted);&lt;br /&gt;
}&lt;br /&gt;
html:not(.pg-day) .printfooter a {&lt;br /&gt;
  color: var(--pg-link);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Sidebar and dropdown panels.&lt;br /&gt;
   Timeless paints BOTH the outer .sidebar-chunk and the inner .sidebar-inner, so the chunk needs&lt;br /&gt;
   the dark treatment too or its light background shows through around the panel.&lt;br /&gt;
&lt;br /&gt;
   These selectors are deliberately ID-scoped. BG-Wiki&#039;s site-styles.css ships this at all widths:&lt;br /&gt;
     #mw-site-navigation .sidebar-chunk,#mw-related-navigation .sidebar-chunk&lt;br /&gt;
       { background:#f8f9fa !important; border:solid #eaecf0 !important }&lt;br /&gt;
   When two declarations are both !important the cascade falls back to specificity, so a&lt;br /&gt;
   class-only selector loses to their ID and the sidebar renders light. Adding the ID here wins.&lt;br /&gt;
   Their `border` shorthand also resets border-width to medium (3px), hence border-width below. */&lt;br /&gt;
html:not(.pg-day) .sidebar-chunk,&lt;br /&gt;
html:not(.pg-day) .sidebar-inner,&lt;br /&gt;
html:not(.pg-day) .dropdown,&lt;br /&gt;
html:not(.pg-day) #mw-site-navigation .sidebar-chunk,&lt;br /&gt;
html:not(.pg-day) #mw-site-navigation .sidebar-inner,&lt;br /&gt;
html:not(.pg-day) #mw-related-navigation .sidebar-chunk,&lt;br /&gt;
html:not(.pg-day) #mw-related-navigation .sidebar-inner {&lt;br /&gt;
  background: var(--pg-panel) !important;&lt;br /&gt;
  border-color: var(--pg-line) !important;&lt;br /&gt;
  border-width: 1px !important;&lt;br /&gt;
  box-shadow: none !important;&lt;br /&gt;
}&lt;br /&gt;
html:not(.pg-day) .sidebar-chunk h3,&lt;br /&gt;
html:not(.pg-day) .sidebar-chunk label,&lt;br /&gt;
html:not(.pg-day) .sidebar-inner h3,&lt;br /&gt;
html:not(.pg-day) .sidebar-inner label,&lt;br /&gt;
html:not(.pg-day) .dropdown h3,&lt;br /&gt;
html:not(.pg-day) #mw-site-navigation .sidebar-chunk h3,&lt;br /&gt;
html:not(.pg-day) #mw-site-navigation .sidebar-inner h3,&lt;br /&gt;
html:not(.pg-day) #mw-related-navigation .sidebar-chunk h3,&lt;br /&gt;
html:not(.pg-day) #mw-related-navigation .sidebar-inner h3 {&lt;br /&gt;
  color: var(--pg-gold) !important;&lt;br /&gt;
  border-bottom-color: var(--pg-line) !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The little pointer triangles above the dropdown panels are light-colored borders. */&lt;br /&gt;
html:not(.pg-day) .sidebar-inner::before,&lt;br /&gt;
html:not(.pg-day) .sidebar-inner::after,&lt;br /&gt;
html:not(.pg-day) .sidebar-chunk h2 span::before,&lt;br /&gt;
html:not(.pg-day) .sidebar-chunk h2 span::after {&lt;br /&gt;
  border-bottom-color: var(--pg-panel) !important;&lt;br /&gt;
}&lt;br /&gt;
/* Also ID-scoped: Timeless colors these as &amp;quot;#mw-site-navigation .sidebar-inner a&amp;quot;, which outranks&lt;br /&gt;
   a class-only selector even though it isn&#039;t !important. */&lt;br /&gt;
html:not(.pg-day) .sidebar-inner a,&lt;br /&gt;
html:not(.pg-day) .dropdown a,&lt;br /&gt;
html:not(.pg-day) #mw-site-navigation .sidebar-inner a,&lt;br /&gt;
html:not(.pg-day) #mw-related-navigation .sidebar-inner a {&lt;br /&gt;
  color: var(--pg-link) !important;&lt;br /&gt;
}&lt;br /&gt;
html:not(.pg-day) #mw-site-navigation .sidebar-inner a.new,&lt;br /&gt;
html:not(.pg-day) #mw-related-navigation .sidebar-inner a.new {&lt;br /&gt;
  color: #e88a8a !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Page action tabs and category bar */&lt;br /&gt;
html:not(.pg-day) #mw-page-header-links .tools-inline li a {&lt;br /&gt;
  color: var(--pg-link);&lt;br /&gt;
}&lt;br /&gt;
html:not(.pg-day) .tools-inline li.selected a {&lt;br /&gt;
  color: var(--pg-text);&lt;br /&gt;
}&lt;br /&gt;
html:not(.pg-day) #catlinks,&lt;br /&gt;
html:not(.pg-day) .catlinks {&lt;br /&gt;
  background: var(--pg-panel-alt) !important;&lt;br /&gt;
  border-color: var(--pg-line) !important;&lt;br /&gt;
  color: var(--pg-text);&lt;br /&gt;
}&lt;br /&gt;
html:not(.pg-day) #catlinks a {&lt;br /&gt;
  color: var(--pg-link);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Form controls on special pages */&lt;br /&gt;
html:not(.pg-day) input[type=&amp;quot;text&amp;quot;],&lt;br /&gt;
html:not(.pg-day) input[type=&amp;quot;search&amp;quot;],&lt;br /&gt;
html:not(.pg-day) textarea,&lt;br /&gt;
html:not(.pg-day) select {&lt;br /&gt;
  background: var(--pg-panel-alt);&lt;br /&gt;
  color: var(--pg-text);&lt;br /&gt;
  border: 1px solid var(--pg-line);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ---- Article body: inverted ----&lt;br /&gt;
   Colors below are the pre-inversion (light-mode) values, chosen so that inverting&lt;br /&gt;
   them lands on readable light text. #mw-content-text has no background of its own,&lt;br /&gt;
   so the dark panel behind it shows through without being inverted. */&lt;br /&gt;
&lt;br /&gt;
html:not(.pg-day) #mw-content-text {&lt;br /&gt;
  color: #202122;&lt;br /&gt;
  filter: invert(1) hue-rotate(180deg);&lt;br /&gt;
}&lt;br /&gt;
html:not(.pg-day) #mw-content-text a {&lt;br /&gt;
  color: #0645ad;&lt;br /&gt;
}&lt;br /&gt;
html:not(.pg-day) #mw-content-text a:visited {&lt;br /&gt;
  color: #0b0080;&lt;br /&gt;
}&lt;br /&gt;
html:not(.pg-day) #mw-content-text a.new {&lt;br /&gt;
  color: #d73333;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Media is re-inverted so icons, maps and screenshots render true to color. */&lt;br /&gt;
html:not(.pg-day) #mw-content-text img,&lt;br /&gt;
html:not(.pg-day) #mw-content-text svg,&lt;br /&gt;
html:not(.pg-day) #mw-content-text video {&lt;br /&gt;
  filter: invert(1) hue-rotate(180deg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* ---- Theme toggle, styled after Extension:DarkMode&#039;s moon/sun affordance ---- */&lt;br /&gt;
.ext-darkmode-link { cursor: pointer; }&lt;br /&gt;
.ext-darkmode-link::before {&lt;br /&gt;
  content: &amp;quot;\263E&amp;quot;;&lt;br /&gt;
  display: inline-block;&lt;br /&gt;
  margin-right: 0.4em;&lt;br /&gt;
}&lt;br /&gt;
html:not(.pg-day) .ext-darkmode-link::before { content: &amp;quot;\263C&amp;quot;; }&lt;br /&gt;
&lt;br /&gt;
/* ---- Vana&#039;diel Time panel ----&lt;br /&gt;
   The elemental weekday colors are chosen for a white sidebar, so Watersday (#0000dd) and&lt;br /&gt;
   Darksday (#333) are nearly invisible on the dark panel. These keep each element&#039;s hue but lift&lt;br /&gt;
   it to a readable lightness. ID-scoped for the same reason as the sidebar rules above. */&lt;br /&gt;
html:not(.pg-day) #mw-site-navigation .pg-vana-day-0 { color: #ff6b6b; }&lt;br /&gt;
html:not(.pg-day) #mw-site-navigation .pg-vana-day-1 { color: #d8c34a; }&lt;br /&gt;
html:not(.pg-day) #mw-site-navigation .pg-vana-day-2 { color: #6ea8ff; }&lt;br /&gt;
html:not(.pg-day) #mw-site-navigation .pg-vana-day-3 { color: #4fd07a; }&lt;br /&gt;
html:not(.pg-day) #mw-site-navigation .pg-vana-day-4 { color: #a9c7ff; }&lt;br /&gt;
html:not(.pg-day) #mw-site-navigation .pg-vana-day-5 { color: #d98ada; }&lt;br /&gt;
html:not(.pg-day) #mw-site-navigation .pg-vana-day-6 { color: #ececec; }&lt;br /&gt;
html:not(.pg-day) #mw-site-navigation .pg-vana-day-7 { color: #99a3ba; }&lt;br /&gt;
html:not(.pg-day) #mw-site-navigation .pg-vana-clock { color: var(--pg-text); }&lt;br /&gt;
html:not(.pg-day) #mw-site-navigation .pg-vana-date,&lt;br /&gt;
html:not(.pg-day) #mw-site-navigation .pg-vana-server-text {&lt;br /&gt;
  color: var(--pg-muted);&lt;br /&gt;
}&lt;br /&gt;
html:not(.pg-day) #mw-site-navigation .pg-vana-server-count { color: var(--pg-text); }&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=MediaWiki:Gadget-pegasus.js&amp;diff=142077</id>
		<title>MediaWiki:Gadget-pegasus.js</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=MediaWiki:Gadget-pegasus.js&amp;diff=142077"/>
		<updated>2026-08-30T00:35:55Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: Header button starts Discord sign-in instead of linking to the invite&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* PegasusXI Wiki sidebar additions: Vana&#039;diel clock, theme toggle, Discord panel.&lt;br /&gt;
 *&lt;br /&gt;
 * The Node renderer this wiki replaced built all three server-side, so they were correct on first&lt;br /&gt;
 * paint and worked without JavaScript. MediaWiki has no hook for injecting arbitrary markup into a&lt;br /&gt;
 * Timeless sidebar chunk without a skin or extension, so they are built client-side here instead.&lt;br /&gt;
 * The trade-off is a brief gap before the panel appears; in exchange nothing about the wiki has to be&lt;br /&gt;
 * patched on disk, and every piece is editable on-wiki.&lt;br /&gt;
 *&lt;br /&gt;
 * Markup deliberately mirrors Timeless&#039;s own nesting -- .sidebar-chunk &amp;gt; h2 &amp;gt; span, then&lt;br /&gt;
 * .sidebar-inner &amp;gt; .mw-portlet &amp;gt; h3 + .mw-portlet-body -- because both the skin&#039;s collapse behaviour&lt;br /&gt;
 * at narrow widths and MediaWiki:Timeless.css key off that exact structure.&lt;br /&gt;
 */&lt;br /&gt;
( function () {&lt;br /&gt;
	&#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- Vana&#039;diel clock math&lt;br /&gt;
	 *&lt;br /&gt;
	 * Time runs 25x Earth time. The anchor is Vana&#039;diel&#039;s birthday: Earth 2002-06-23T15:00:00Z&lt;br /&gt;
	 * corresponds to Vana&#039;diel 0898-02-01 00:00, which is (898 * 360) + 30 days after the epoch.&lt;br /&gt;
	 * The calendar has no leap years and no short months -- 360-day years, 30-day months, 8-day&lt;br /&gt;
	 * weeks -- so everything else is modulo arithmetic on Vana&#039;diel minutes.&lt;br /&gt;
	 *&lt;br /&gt;
	 * The moon runs a separate 84-day cycle offset 26 days from the epoch. Phase comes from the&lt;br /&gt;
	 * percentage *and* the direction of travel, because each percentage occurs twice per cycle.&lt;br /&gt;
	 *&lt;br /&gt;
	 * Kept identical to site/public/vanadiel.mjs, which was cross-checked against HorizonXI&#039;s&lt;br /&gt;
	 * MediaWiki:VanaTime.js over a full moon cycle.&lt;br /&gt;
	 */&lt;br /&gt;
	var EARTH_ANCHOR_MS = 1024844400000;&lt;br /&gt;
	var VANA_ANCHOR_DAYS = 898 * 360 + 30;&lt;br /&gt;
	var RATE = 25;&lt;br /&gt;
&lt;br /&gt;
	var MIN_PER_HOUR = 60;&lt;br /&gt;
	var MIN_PER_DAY = 1440;&lt;br /&gt;
	var MIN_PER_WEEK = 8 * MIN_PER_DAY;&lt;br /&gt;
	var MIN_PER_MONTH = 30 * MIN_PER_DAY;&lt;br /&gt;
	var MIN_PER_YEAR = 360 * MIN_PER_DAY;&lt;br /&gt;
&lt;br /&gt;
	var MOON_CYCLE_DAYS = 84;&lt;br /&gt;
	var MOON_OFFSET_DAYS = 26;&lt;br /&gt;
&lt;br /&gt;
	var WEEKDAYS = [&lt;br /&gt;
		&#039;Firesday&#039;, &#039;Earthsday&#039;, &#039;Watersday&#039;, &#039;Windsday&#039;,&lt;br /&gt;
		&#039;Iceday&#039;, &#039;Lightningday&#039;, &#039;Lightsday&#039;, &#039;Darksday&#039;&lt;br /&gt;
	];&lt;br /&gt;
	var MOON_PHASES = [&lt;br /&gt;
		&#039;New Moon&#039;, &#039;Waxing Crescent&#039;, &#039;First Quarter&#039;, &#039;Waxing Gibbous&#039;,&lt;br /&gt;
		&#039;Full Moon&#039;, &#039;Waning Gibbous&#039;, &#039;Last Quarter&#039;, &#039;Waning Crescent&#039;&lt;br /&gt;
	];&lt;br /&gt;
	var MOON_ICONS = [ &#039;\uD83C\uDF11&#039;, &#039;\uD83C\uDF12&#039;, &#039;\uD83C\uDF13&#039;, &#039;\uD83C\uDF14&#039;,&lt;br /&gt;
		&#039;\uD83C\uDF15&#039;, &#039;\uD83C\uDF16&#039;, &#039;\uD83C\uDF17&#039;, &#039;\uD83C\uDF18&#039; ];&lt;br /&gt;
&lt;br /&gt;
	function moonPhaseIndex( percent, direction ) {&lt;br /&gt;
		// direction: 0 = turning point, 1 = falling toward new, 2 = rising toward full.&lt;br /&gt;
		if ( percent &amp;lt;= 5 || ( percent &amp;lt;= 10 &amp;amp;&amp;amp; direction === 1 ) ) { return 0; }&lt;br /&gt;
		if ( percent &amp;gt;= 7 &amp;amp;&amp;amp; percent &amp;lt;= 38 &amp;amp;&amp;amp; direction === 2 ) { return 1; }&lt;br /&gt;
		if ( percent &amp;gt;= 40 &amp;amp;&amp;amp; percent &amp;lt;= 55 &amp;amp;&amp;amp; direction === 2 ) { return 2; }&lt;br /&gt;
		if ( percent &amp;gt;= 57 &amp;amp;&amp;amp; percent &amp;lt;= 88 &amp;amp;&amp;amp; direction === 2 ) { return 3; }&lt;br /&gt;
		if ( percent &amp;gt;= 95 || ( percent &amp;gt;= 90 &amp;amp;&amp;amp; direction === 2 ) ) { return 4; }&lt;br /&gt;
		if ( percent &amp;gt;= 62 &amp;amp;&amp;amp; percent &amp;lt;= 93 &amp;amp;&amp;amp; direction === 1 ) { return 5; }&lt;br /&gt;
		if ( percent &amp;gt;= 45 &amp;amp;&amp;amp; percent &amp;lt;= 60 &amp;amp;&amp;amp; direction === 1 ) { return 6; }&lt;br /&gt;
		return 7;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function vanadiel() {&lt;br /&gt;
		var minutes = VANA_ANCHOR_DAYS * MIN_PER_DAY +&lt;br /&gt;
			( ( Date.now() - EARTH_ANCHOR_MS ) * RATE ) / 60000;&lt;br /&gt;
&lt;br /&gt;
		var moonDay = Math.floor( ( ( minutes / MIN_PER_DAY ) + MOON_OFFSET_DAYS ) % MOON_CYCLE_DAYS );&lt;br /&gt;
		var moonPercent = Math.abs( Math.round( ( ( 42 - moonDay ) / 42 ) * 100 ) );&lt;br /&gt;
		var moonDirection = moonDay === 0 || moonDay === 42 ? 0 : ( moonDay &amp;lt; 42 ? 1 : 2 );&lt;br /&gt;
		var moonIndex = moonPhaseIndex( moonPercent, moonDirection );&lt;br /&gt;
		var weekdayIndex = Math.floor( ( minutes % MIN_PER_WEEK ) / MIN_PER_DAY );&lt;br /&gt;
&lt;br /&gt;
		return {&lt;br /&gt;
			year: Math.floor( minutes / MIN_PER_YEAR ),&lt;br /&gt;
			month: Math.floor( ( minutes / MIN_PER_MONTH ) % 12 ) + 1,&lt;br /&gt;
			day: Math.floor( ( minutes / MIN_PER_DAY ) % 30 ) + 1,&lt;br /&gt;
			hour: Math.floor( ( minutes % MIN_PER_DAY ) / MIN_PER_HOUR ),&lt;br /&gt;
			minute: Math.floor( minutes % MIN_PER_HOUR ),&lt;br /&gt;
			weekdayIndex: weekdayIndex,&lt;br /&gt;
			weekday: WEEKDAYS[ weekdayIndex ],&lt;br /&gt;
			moonPercent: moonPercent,&lt;br /&gt;
			moonPhase: MOON_PHASES[ moonIndex ],&lt;br /&gt;
			moonIcon: MOON_ICONS[ moonIndex ]&lt;br /&gt;
		};&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function pad2( n ) {&lt;br /&gt;
		return ( n &amp;lt; 10 ? &#039;0&#039; : &#039;&#039; ) + n;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- markup helpers */&lt;br /&gt;
&lt;br /&gt;
	function el( tag, attrs, children ) {&lt;br /&gt;
		var node = document.createElement( tag );&lt;br /&gt;
		var key;&lt;br /&gt;
		for ( key in attrs || {} ) {&lt;br /&gt;
			if ( key === &#039;text&#039; ) {&lt;br /&gt;
				node.textContent = attrs[ key ];&lt;br /&gt;
			} else {&lt;br /&gt;
				node.setAttribute( key, attrs[ key ] );&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		( children || [] ).forEach( function ( child ) {&lt;br /&gt;
			node.appendChild( child );&lt;br /&gt;
		} );&lt;br /&gt;
		return node;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/** A Timeless portlet: heading plus body, ready to drop into a .sidebar-inner. */&lt;br /&gt;
	function portlet( id, label, body ) {&lt;br /&gt;
		return el( &#039;div&#039;, { role: &#039;navigation&#039;, &#039;class&#039;: &#039;mw-portlet&#039;, id: &#039;p-&#039; + id,&lt;br /&gt;
			&#039;aria-labelledby&#039;: &#039;p-&#039; + id + &#039;-label&#039; }, [&lt;br /&gt;
			el( &#039;h3&#039;, { id: &#039;p-&#039; + id + &#039;-label&#039;, text: label } ),&lt;br /&gt;
			el( &#039;div&#039;, { &#039;class&#039;: &#039;mw-portlet-body&#039; }, [ body ] )&lt;br /&gt;
		] );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/** A Timeless sidebar chunk: the collapsible outer container. */&lt;br /&gt;
	function chunk( id, heading, body ) {&lt;br /&gt;
		return el( &#039;div&#039;, { id: id, &#039;class&#039;: &#039;sidebar-chunk&#039; }, [&lt;br /&gt;
			el( &#039;h2&#039;, {}, [ el( &#039;span&#039;, { text: heading } ) ] ),&lt;br /&gt;
			el( &#039;div&#039;, { &#039;class&#039;: &#039;sidebar-inner&#039; }, [ body ] )&lt;br /&gt;
		] );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- Vana&#039;diel panel */&lt;br /&gt;
&lt;br /&gt;
	function buildClockPanel() {&lt;br /&gt;
		var clock = el( &#039;b&#039;, { &#039;class&#039;: &#039;pg-vana-clock&#039; } );&lt;br /&gt;
		var date = el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-date&#039; } );&lt;br /&gt;
		var day = el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-day&#039; } );&lt;br /&gt;
		var moonIcon = el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-moon-icon&#039; } );&lt;br /&gt;
		var moonPct = el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-moon-pct&#039; } );&lt;br /&gt;
		var serverText = el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-server-text&#039;, text: &#039;Checking status&#039; } );&lt;br /&gt;
		var serverCount = el( &#039;b&#039;, { &#039;class&#039;: &#039;pg-vana-server-count&#039; } );&lt;br /&gt;
		var server = el( &#039;div&#039;, { &#039;class&#039;: &#039;pg-vana-line pg-vana-server&#039;, &#039;data-state&#039;: &#039;unknown&#039; }, [&lt;br /&gt;
			el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-dot&#039;, &#039;aria-hidden&#039;: &#039;true&#039; } ),&lt;br /&gt;
			serverText,&lt;br /&gt;
			serverCount&lt;br /&gt;
		] );&lt;br /&gt;
&lt;br /&gt;
		var panel = el( &#039;div&#039;, { &#039;class&#039;: &#039;pg-vana&#039; }, [&lt;br /&gt;
			el( &#039;div&#039;, { &#039;class&#039;: &#039;pg-vana-line&#039; }, [&lt;br /&gt;
				clock, el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-tilde&#039;, text: &#039;~&#039; } ), date&lt;br /&gt;
			] ),&lt;br /&gt;
			el( &#039;div&#039;, { &#039;class&#039;: &#039;pg-vana-line&#039; }, [&lt;br /&gt;
				day,&lt;br /&gt;
				el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-tilde&#039;, text: &#039;~&#039; } ),&lt;br /&gt;
				el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-moon&#039; }, [ moonIcon, moonPct ] )&lt;br /&gt;
			] ),&lt;br /&gt;
			server&lt;br /&gt;
		] );&lt;br /&gt;
&lt;br /&gt;
		var list = el( &#039;ul&#039;, {}, [&lt;br /&gt;
			el( &#039;li&#039;, { id: &#039;n-vanatime&#039;, &#039;class&#039;: &#039;mw-list-item&#039; }, [ panel ] )&lt;br /&gt;
		] );&lt;br /&gt;
&lt;br /&gt;
		// A Vana&#039;diel minute is 2.4 Earth seconds, so a one-second tick looks smooth without&lt;br /&gt;
		// doing redundant work.&lt;br /&gt;
		function tick() {&lt;br /&gt;
			var v = vanadiel();&lt;br /&gt;
			clock.textContent = pad2( v.hour ) + &#039;:&#039; + pad2( v.minute );&lt;br /&gt;
			date.textContent = v.year + &#039;-&#039; + v.month + &#039;-&#039; + v.day;&lt;br /&gt;
			day.textContent = v.weekday;&lt;br /&gt;
			day.className = &#039;pg-vana-day pg-vana-day-&#039; + v.weekdayIndex;&lt;br /&gt;
			moonIcon.textContent = v.moonIcon;&lt;br /&gt;
			moonIcon.setAttribute( &#039;title&#039;, v.moonPhase );&lt;br /&gt;
			moonPct.textContent = v.moonPercent + &#039;%&#039;;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// Same origin as the wiki, so no CORS involved and no proxy needed.&lt;br /&gt;
		function poll() {&lt;br /&gt;
			fetch( &#039;/api/status&#039;, { headers: { Accept: &#039;application/json&#039; } } )&lt;br /&gt;
				.then( function ( res ) {&lt;br /&gt;
					if ( !res.ok ) {&lt;br /&gt;
						throw new Error( &#039;HTTP &#039; + res.status );&lt;br /&gt;
					}&lt;br /&gt;
					return res.json();&lt;br /&gt;
				} )&lt;br /&gt;
				.then( function ( data ) {&lt;br /&gt;
					var online = String( data &amp;amp;&amp;amp; data.status || &#039;&#039; ).toLowerCase() === &#039;online&#039;;&lt;br /&gt;
					var players = Number( data &amp;amp;&amp;amp; data.playerCount );&lt;br /&gt;
					server.setAttribute( &#039;data-state&#039;, online ? &#039;online&#039; : &#039;offline&#039; );&lt;br /&gt;
					serverText.textContent = online ? &#039;Online&#039; : &#039;Offline&#039;;&lt;br /&gt;
					// A count of 0 is indistinguishable from &amp;quot;not published&amp;quot;, and &amp;quot;Online 0&amp;quot; next&lt;br /&gt;
					// to a green dot reads as a bug, so only show a positive count.&lt;br /&gt;
					serverCount.textContent = online &amp;amp;&amp;amp; players &amp;gt; 0 ? String( players ) : &#039;&#039;;&lt;br /&gt;
				} )&lt;br /&gt;
				.catch( function () {&lt;br /&gt;
					// Keep whatever is on screen; the next poll may succeed.&lt;br /&gt;
					if ( server.getAttribute( &#039;data-state&#039; ) === &#039;unknown&#039; ) {&lt;br /&gt;
						serverText.textContent = &#039;Status unavailable&#039;;&lt;br /&gt;
					}&lt;br /&gt;
				} );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		tick();&lt;br /&gt;
		poll();&lt;br /&gt;
		setInterval( tick, 1000 );&lt;br /&gt;
		setInterval( poll, 60000 );&lt;br /&gt;
&lt;br /&gt;
		return portlet( &#039;vanatime&#039;, &amp;quot;Vana&#039;diel Time&amp;quot;, list );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- theme toggle&lt;br /&gt;
	 *&lt;br /&gt;
	 * Dark is the default and needs no class, so light mode is the opt-in state: MediaWiki:Timeless.css&lt;br /&gt;
	 * scopes every dark rule to html:not(.pg-day). That ordering means a reader on the default theme&lt;br /&gt;
	 * never sees a flash of light before this script runs.&lt;br /&gt;
	 */&lt;br /&gt;
	var THEME_KEY = &#039;pg-theme&#039;;&lt;br /&gt;
&lt;br /&gt;
	function applyTheme( theme ) {&lt;br /&gt;
		document.documentElement.classList.toggle( &#039;pg-day&#039;, theme === &#039;day&#039; );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function buildThemeToggle() {&lt;br /&gt;
		var link = el( &#039;a&#039;, { &#039;class&#039;: &#039;ext-darkmode-link&#039;, href: &#039;#&#039;, role: &#039;button&#039; } );&lt;br /&gt;
		var label = el( &#039;span&#039;, {} );&lt;br /&gt;
		link.appendChild( label );&lt;br /&gt;
&lt;br /&gt;
		function refresh() {&lt;br /&gt;
			var isDay = document.documentElement.classList.contains( &#039;pg-day&#039; );&lt;br /&gt;
			label.textContent = isDay ? &#039;Dark mode&#039; : &#039;Light mode&#039;;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		link.addEventListener( &#039;click&#039;, function ( e ) {&lt;br /&gt;
			e.preventDefault();&lt;br /&gt;
			var next = document.documentElement.classList.contains( &#039;pg-day&#039; ) ? &#039;night&#039; : &#039;day&#039;;&lt;br /&gt;
			applyTheme( next );&lt;br /&gt;
			try {&lt;br /&gt;
				localStorage.setItem( THEME_KEY, next );&lt;br /&gt;
			} catch ( err ) {&lt;br /&gt;
				// Private browsing or a full quota; the choice just will not persist.&lt;br /&gt;
			}&lt;br /&gt;
			refresh();&lt;br /&gt;
		} );&lt;br /&gt;
&lt;br /&gt;
		refresh();&lt;br /&gt;
&lt;br /&gt;
		return portlet( &#039;theme&#039;, &#039;Appearance&#039;, el( &#039;ul&#039;, {}, [&lt;br /&gt;
			el( &#039;li&#039;, { id: &#039;t-darkmode&#039;, &#039;class&#039;: &#039;mw-list-item&#039; }, [ link ] )&lt;br /&gt;
		] ) );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- Discord panel&lt;br /&gt;
	 *&lt;br /&gt;
	 * Discord&#039;s own embed (discord.com/widget) is a cross-origin iframe locked to Discord&#039;s blurple&lt;br /&gt;
	 * palette, so it cannot be themed to match this wiki. The member list is therefore fetched and&lt;br /&gt;
	 * rendered here instead: widget.json sends Access-Control-Allow-Origin for this host, so no proxy&lt;br /&gt;
	 * is involved and the markup is ours to style.&lt;br /&gt;
	 *&lt;br /&gt;
	 * That endpoint answers 403 whenever &amp;quot;Enable Server Widget&amp;quot; is switched off in Discord&#039;s server&lt;br /&gt;
	 * settings, which is why the static join panel is still kept as a fallback rather than deleted.&lt;br /&gt;
	 */&lt;br /&gt;
	var DISCORD_INVITE = &#039;https://discord.gg/M3NFvpm75E&#039;;&lt;br /&gt;
	var DISCORD_GUILD = &#039;1473308284349059126&#039;;&lt;br /&gt;
	var DISCORD_WIDGET = &#039;https://discord.com/api/guilds/&#039; + DISCORD_GUILD + &#039;/widget.json&#039;;&lt;br /&gt;
&lt;br /&gt;
	// Online first, then idle, then do-not-disturb, alphabetically within each.&lt;br /&gt;
	var STATUS_RANK = { online: 0, idle: 1, dnd: 2, offline: 3 };&lt;br /&gt;
&lt;br /&gt;
	function statusRank( status ) {&lt;br /&gt;
		return STATUS_RANK[ status ] === undefined ? STATUS_RANK.offline : STATUS_RANK[ status ];&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function discordMember( member ) {&lt;br /&gt;
		var status = STATUS_RANK[ member.status ] === undefined ? &#039;offline&#039; : member.status;&lt;br /&gt;
		var children = [];&lt;br /&gt;
&lt;br /&gt;
		if ( member.avatar_url ) {&lt;br /&gt;
			children.push( el( &#039;img&#039;, { &#039;class&#039;: &#039;pg-discord-avatar&#039;, src: member.avatar_url,&lt;br /&gt;
				alt: &#039;&#039;, width: &#039;20&#039;, height: &#039;20&#039;, loading: &#039;lazy&#039; } ) );&lt;br /&gt;
		} else {&lt;br /&gt;
			children.push( el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-discord-avatar pg-discord-avatar-blank&#039;,&lt;br /&gt;
				&#039;aria-hidden&#039;: &#039;true&#039; } ) );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		children.push( el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-discord-member-name&#039;, text: member.username } ) );&lt;br /&gt;
&lt;br /&gt;
		var li = el( &#039;li&#039;, { &#039;class&#039;: &#039;pg-discord-member&#039;, &#039;data-status&#039;: status }, children );&lt;br /&gt;
		// The status is conveyed by a colour swatch, so name it for anyone not seeing colour.&lt;br /&gt;
		li.setAttribute( &#039;title&#039;, member.game &amp;amp;&amp;amp; member.game.name ?&lt;br /&gt;
			member.username + &#039; - &#039; + status + &#039; - playing &#039; + member.game.name :&lt;br /&gt;
			member.username + &#039; - &#039; + status );&lt;br /&gt;
		return li;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function buildDiscordPanel() {&lt;br /&gt;
		var count = el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-discord-count&#039;, text: &#039;Loading&#039; } );&lt;br /&gt;
		var list = el( &#039;ul&#039;, { &#039;class&#039;: &#039;pg-discord-members&#039; } );&lt;br /&gt;
&lt;br /&gt;
		var panel = el( &#039;div&#039;, { &#039;class&#039;: &#039;pg-discord&#039; }, [&lt;br /&gt;
			el( &#039;div&#039;, { &#039;class&#039;: &#039;pg-discord-head&#039; }, [&lt;br /&gt;
				el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-discord-title&#039;, text: &#039;PegasusXI&#039; } ),&lt;br /&gt;
				count&lt;br /&gt;
			] ),&lt;br /&gt;
			list,&lt;br /&gt;
			el( &#039;a&#039;, { &#039;class&#039;: &#039;pg-discord-join&#039;, href: DISCORD_INVITE,&lt;br /&gt;
				target: &#039;_blank&#039;, rel: &#039;noopener&#039;, text: &#039;Join Discord&#039; } )&lt;br /&gt;
		] );&lt;br /&gt;
&lt;br /&gt;
		fetch( DISCORD_WIDGET, { headers: { Accept: &#039;application/json&#039; } } )&lt;br /&gt;
			.then( function ( res ) {&lt;br /&gt;
				if ( !res.ok ) {&lt;br /&gt;
					throw new Error( &#039;HTTP &#039; + res.status );&lt;br /&gt;
				}&lt;br /&gt;
				return res.json();&lt;br /&gt;
			} )&lt;br /&gt;
			.then( function ( data ) {&lt;br /&gt;
				var members = ( ( data &amp;amp;&amp;amp; data.members ) || [] ).slice();&lt;br /&gt;
				members.sort( function ( a, b ) {&lt;br /&gt;
					return statusRank( a.status ) - statusRank( b.status ) ||&lt;br /&gt;
						String( a.username ).localeCompare( String( b.username ) );&lt;br /&gt;
				} );&lt;br /&gt;
&lt;br /&gt;
				// presence_count counts everyone online; the members array is capped by Discord.&lt;br /&gt;
				var online = Number( data &amp;amp;&amp;amp; data.presence_count );&lt;br /&gt;
				if ( !isFinite( online ) ) {&lt;br /&gt;
					online = members.length;&lt;br /&gt;
				}&lt;br /&gt;
				count.textContent = online + ( online === 1 ? &#039; member online&#039; : &#039; members online&#039; );&lt;br /&gt;
&lt;br /&gt;
				members.forEach( function ( member ) {&lt;br /&gt;
					list.appendChild( discordMember( member ) );&lt;br /&gt;
				} );&lt;br /&gt;
&lt;br /&gt;
				panel.setAttribute( &#039;data-state&#039;, &#039;ready&#039; );&lt;br /&gt;
			} )&lt;br /&gt;
			.catch( function () {&lt;br /&gt;
				// Most likely the server widget was switched off again. Fall back to the plain join&lt;br /&gt;
				// panel rather than leaving &amp;quot;Loading&amp;quot; on screen for good.&lt;br /&gt;
				panel.setAttribute( &#039;data-state&#039;, &#039;unavailable&#039; );&lt;br /&gt;
				count.textContent = &#039;Chat, support and server announcements&#039;;&lt;br /&gt;
			} );&lt;br /&gt;
&lt;br /&gt;
		return el( &#039;div&#039;, { id: &#039;discord-panel&#039; }, [ panel ] );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- header button&lt;br /&gt;
	 *&lt;br /&gt;
	 * #mw-header is a flex row and Timeless assigns explicit orders: #p-logo-text 1, #p-search 2,&lt;br /&gt;
	 * #user-tools 3. A new child with no order defaults to 0 and jumps ahead of all of them, which is&lt;br /&gt;
	 * why this button first appeared at the far left of the header. MediaWiki:Timeless.css gives it&lt;br /&gt;
	 * order 4 so it sits at the right-hand end.&lt;br /&gt;
	 *&lt;br /&gt;
	 * This starts a sign-in rather than handing out an invite: the Discord invite is already on the&lt;br /&gt;
	 * sidebar panel, and a reader who wants an account is better served by the thing that makes one.&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	/* Getting from this button to Discord in one click takes some care.&lt;br /&gt;
	 *&lt;br /&gt;
	 * The flow cannot be started by a link: it begins with a POST of MediaWiki&#039;s login form carrying a&lt;br /&gt;
	 * login token, and Special:PluggableAuthLogin on its own has no plugin recorded in the session, so&lt;br /&gt;
	 * it errors. The obvious alternative -- send the reader to the login page and have the gadget press&lt;br /&gt;
	 * the button there -- cannot work either, because MediaWiki blocks gadgets and site scripts on&lt;br /&gt;
	 * login pages so that a compromised script cannot read a password out of the form. Nothing of ours&lt;br /&gt;
	 * runs there at all.&lt;br /&gt;
	 *&lt;br /&gt;
	 * So the work happens here, on an ordinary page where the gadget is allowed: fetch the login form,&lt;br /&gt;
	 * copy its hidden fields, and submit it with the Discord button&#039;s field set. Every token is one&lt;br /&gt;
	 * MediaWiki issued to this session; nothing is forged and no password is involved. If any of it&lt;br /&gt;
	 * fails the link is simply followed as-is, landing the reader on the login page with the Discord&lt;br /&gt;
	 * button waiting -- which is also what happens with JavaScript disabled.&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	function loginUrl( returnTo ) {&lt;br /&gt;
		var articlePath = mw.config.get( &#039;wgArticlePath&#039; ) || &#039;/wiki/$1&#039;;&lt;br /&gt;
		return articlePath.replace( &#039;$1&#039;, &#039;Special:UserLogin&#039; ) +&lt;br /&gt;
			&#039;?returnto=&#039; + encodeURIComponent( returnTo );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function hidden( name, value ) {&lt;br /&gt;
		var input = document.createElement( &#039;input&#039; );&lt;br /&gt;
		input.type = &#039;hidden&#039;;&lt;br /&gt;
		input.name = name;&lt;br /&gt;
		input.value = value;&lt;br /&gt;
		return input;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function startDiscordLogin( returnTo ) {&lt;br /&gt;
		return fetch( loginUrl( returnTo ), { credentials: &#039;same-origin&#039; } )&lt;br /&gt;
			.then( function ( res ) {&lt;br /&gt;
				if ( !res.ok ) {&lt;br /&gt;
					throw new Error( &#039;HTTP &#039; + res.status );&lt;br /&gt;
				}&lt;br /&gt;
				return res.text();&lt;br /&gt;
			} )&lt;br /&gt;
			.then( function ( html ) {&lt;br /&gt;
				var doc = new DOMParser().parseFromString( html, &#039;text/html&#039; );&lt;br /&gt;
				var form = doc.querySelector( &#039;form[name=&amp;quot;userlogin&amp;quot;]&#039; );&lt;br /&gt;
				// Matched by name prefix rather than index: the suffix is the plugin&#039;s position in&lt;br /&gt;
				// $wgPluggableAuth_Config, which would shift if a second provider were added.&lt;br /&gt;
				var button = form &amp;amp;&amp;amp; form.querySelector( &#039;[name^=&amp;quot;pluggableauthlogin&amp;quot;]&#039; );&lt;br /&gt;
				if ( !button ) {&lt;br /&gt;
					throw new Error( &#039;no Discord button on the login form&#039; );&lt;br /&gt;
				}&lt;br /&gt;
&lt;br /&gt;
				var post = document.createElement( &#039;form&#039; );&lt;br /&gt;
				post.method = &#039;post&#039;;&lt;br /&gt;
				post.action = form.getAttribute( &#039;action&#039; );&lt;br /&gt;
				post.style.display = &#039;none&#039;;&lt;br /&gt;
&lt;br /&gt;
				Array.prototype.forEach.call(&lt;br /&gt;
					form.querySelectorAll( &#039;input[type=&amp;quot;hidden&amp;quot;]&#039; ),&lt;br /&gt;
					function ( input ) {&lt;br /&gt;
						post.appendChild( hidden( input.name, input.value ) );&lt;br /&gt;
					}&lt;br /&gt;
				);&lt;br /&gt;
&lt;br /&gt;
				// returnto travels in the query string, not as a hidden field, so it has to be&lt;br /&gt;
				// restated or the reader is dropped on the Main Page after signing in.&lt;br /&gt;
				post.appendChild( hidden( &#039;returnto&#039;, returnTo ) );&lt;br /&gt;
				post.appendChild(&lt;br /&gt;
					hidden( button.getAttribute( &#039;name&#039; ), button.getAttribute( &#039;value&#039; ) || &#039;1&#039; )&lt;br /&gt;
				);&lt;br /&gt;
&lt;br /&gt;
				document.body.appendChild( post );&lt;br /&gt;
				post.submit();&lt;br /&gt;
			} );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function buildHeaderButton() {&lt;br /&gt;
		// Logged-in readers have the personal menu instead; a sign-in button would be noise.&lt;br /&gt;
		if ( mw.config.get( &#039;wgUserName&#039; ) ) {&lt;br /&gt;
			return null;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		var returnTo = mw.config.get( &#039;wgPageName&#039; ) || &#039;Main_Page&#039;;&lt;br /&gt;
		var link = el( &#039;a&#039;, { &#039;class&#039;: &#039;pg-discord-signin&#039;, href: loginUrl( returnTo ) }, [&lt;br /&gt;
			el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-discord-signin-mark&#039;, &#039;aria-hidden&#039;: &#039;true&#039; } ),&lt;br /&gt;
			el( &#039;span&#039;, { text: &#039;Sign in with Discord&#039; } )&lt;br /&gt;
		] );&lt;br /&gt;
&lt;br /&gt;
		var busy = false;&lt;br /&gt;
		link.addEventListener( &#039;click&#039;, function ( e ) {&lt;br /&gt;
			// Let modified clicks open the login page in a tab as the reader asked.&lt;br /&gt;
			if ( e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0 ) {&lt;br /&gt;
				return;&lt;br /&gt;
			}&lt;br /&gt;
			e.preventDefault();&lt;br /&gt;
			if ( busy ) {&lt;br /&gt;
				return;&lt;br /&gt;
			}&lt;br /&gt;
			busy = true;&lt;br /&gt;
			link.classList.add( &#039;pg-discord-signin-busy&#039; );&lt;br /&gt;
&lt;br /&gt;
			startDiscordLogin( returnTo ).catch( function () {&lt;br /&gt;
				// Fall back to the plain login page rather than stranding the reader here.&lt;br /&gt;
				window.location.href = loginUrl( returnTo );&lt;br /&gt;
			} );&lt;br /&gt;
		} );&lt;br /&gt;
&lt;br /&gt;
		return el( &#039;div&#039;, { id: &#039;p-discord-signin&#039; }, [ link ] );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- wire it up */&lt;br /&gt;
&lt;br /&gt;
	function init() {&lt;br /&gt;
		// Restore the saved theme first so the toggle&#039;s label starts out correct.&lt;br /&gt;
		try {&lt;br /&gt;
			if ( localStorage.getItem( THEME_KEY ) === &#039;day&#039; ) {&lt;br /&gt;
				applyTheme( &#039;day&#039; );&lt;br /&gt;
			}&lt;br /&gt;
		} catch ( err ) {}&lt;br /&gt;
&lt;br /&gt;
		var siteNav = document.getElementById( &#039;mw-site-navigation&#039; );&lt;br /&gt;
		var navChunk = document.getElementById( &#039;site-navigation&#039; );&lt;br /&gt;
&lt;br /&gt;
		if ( navChunk ) {&lt;br /&gt;
			var inner = navChunk.querySelector( &#039;.sidebar-inner&#039; );&lt;br /&gt;
			if ( inner ) {&lt;br /&gt;
				inner.insertBefore( buildClockPanel(), inner.firstChild );&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if ( siteNav &amp;amp;&amp;amp; !document.getElementById( &#039;theme-tools&#039; ) ) {&lt;br /&gt;
			siteNav.appendChild( chunk( &#039;theme-tools&#039;, &#039;Appearance&#039;, buildThemeToggle() ) );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		var related = document.getElementById( &#039;mw-related-navigation&#039; );&lt;br /&gt;
		if ( related &amp;amp;&amp;amp; !document.getElementById( &#039;discord-panel&#039; ) ) {&lt;br /&gt;
			related.insertBefore( buildDiscordPanel(), related.firstChild );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		var userTools = document.getElementById( &#039;user-tools&#039; );&lt;br /&gt;
		if ( userTools &amp;amp;&amp;amp; userTools.parentNode &amp;amp;&amp;amp; !document.getElementById( &#039;p-discord-signin&#039; ) ) {&lt;br /&gt;
			var button = buildHeaderButton();&lt;br /&gt;
			if ( button ) {&lt;br /&gt;
				userTools.parentNode.insertBefore( button, userTools );&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if ( document.readyState === &#039;loading&#039; ) {&lt;br /&gt;
		document.addEventListener( &#039;DOMContentLoaded&#039;, init );&lt;br /&gt;
	} else {&lt;br /&gt;
		init();&lt;br /&gt;
	}&lt;br /&gt;
}() );&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=MediaWiki:Gadget-pegasus.js&amp;diff=142076</id>
		<title>MediaWiki:Gadget-pegasus.js</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=MediaWiki:Gadget-pegasus.js&amp;diff=142076"/>
		<updated>2026-08-30T00:33:28Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: Header button starts Discord sign-in instead of linking to the invite&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* PegasusXI Wiki sidebar additions: Vana&#039;diel clock, theme toggle, Discord panel.&lt;br /&gt;
 *&lt;br /&gt;
 * The Node renderer this wiki replaced built all three server-side, so they were correct on first&lt;br /&gt;
 * paint and worked without JavaScript. MediaWiki has no hook for injecting arbitrary markup into a&lt;br /&gt;
 * Timeless sidebar chunk without a skin or extension, so they are built client-side here instead.&lt;br /&gt;
 * The trade-off is a brief gap before the panel appears; in exchange nothing about the wiki has to be&lt;br /&gt;
 * patched on disk, and every piece is editable on-wiki.&lt;br /&gt;
 *&lt;br /&gt;
 * Markup deliberately mirrors Timeless&#039;s own nesting -- .sidebar-chunk &amp;gt; h2 &amp;gt; span, then&lt;br /&gt;
 * .sidebar-inner &amp;gt; .mw-portlet &amp;gt; h3 + .mw-portlet-body -- because both the skin&#039;s collapse behaviour&lt;br /&gt;
 * at narrow widths and MediaWiki:Timeless.css key off that exact structure.&lt;br /&gt;
 */&lt;br /&gt;
( function () {&lt;br /&gt;
	&#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- Vana&#039;diel clock math&lt;br /&gt;
	 *&lt;br /&gt;
	 * Time runs 25x Earth time. The anchor is Vana&#039;diel&#039;s birthday: Earth 2002-06-23T15:00:00Z&lt;br /&gt;
	 * corresponds to Vana&#039;diel 0898-02-01 00:00, which is (898 * 360) + 30 days after the epoch.&lt;br /&gt;
	 * The calendar has no leap years and no short months -- 360-day years, 30-day months, 8-day&lt;br /&gt;
	 * weeks -- so everything else is modulo arithmetic on Vana&#039;diel minutes.&lt;br /&gt;
	 *&lt;br /&gt;
	 * The moon runs a separate 84-day cycle offset 26 days from the epoch. Phase comes from the&lt;br /&gt;
	 * percentage *and* the direction of travel, because each percentage occurs twice per cycle.&lt;br /&gt;
	 *&lt;br /&gt;
	 * Kept identical to site/public/vanadiel.mjs, which was cross-checked against HorizonXI&#039;s&lt;br /&gt;
	 * MediaWiki:VanaTime.js over a full moon cycle.&lt;br /&gt;
	 */&lt;br /&gt;
	var EARTH_ANCHOR_MS = 1024844400000;&lt;br /&gt;
	var VANA_ANCHOR_DAYS = 898 * 360 + 30;&lt;br /&gt;
	var RATE = 25;&lt;br /&gt;
&lt;br /&gt;
	var MIN_PER_HOUR = 60;&lt;br /&gt;
	var MIN_PER_DAY = 1440;&lt;br /&gt;
	var MIN_PER_WEEK = 8 * MIN_PER_DAY;&lt;br /&gt;
	var MIN_PER_MONTH = 30 * MIN_PER_DAY;&lt;br /&gt;
	var MIN_PER_YEAR = 360 * MIN_PER_DAY;&lt;br /&gt;
&lt;br /&gt;
	var MOON_CYCLE_DAYS = 84;&lt;br /&gt;
	var MOON_OFFSET_DAYS = 26;&lt;br /&gt;
&lt;br /&gt;
	var WEEKDAYS = [&lt;br /&gt;
		&#039;Firesday&#039;, &#039;Earthsday&#039;, &#039;Watersday&#039;, &#039;Windsday&#039;,&lt;br /&gt;
		&#039;Iceday&#039;, &#039;Lightningday&#039;, &#039;Lightsday&#039;, &#039;Darksday&#039;&lt;br /&gt;
	];&lt;br /&gt;
	var MOON_PHASES = [&lt;br /&gt;
		&#039;New Moon&#039;, &#039;Waxing Crescent&#039;, &#039;First Quarter&#039;, &#039;Waxing Gibbous&#039;,&lt;br /&gt;
		&#039;Full Moon&#039;, &#039;Waning Gibbous&#039;, &#039;Last Quarter&#039;, &#039;Waning Crescent&#039;&lt;br /&gt;
	];&lt;br /&gt;
	var MOON_ICONS = [ &#039;\uD83C\uDF11&#039;, &#039;\uD83C\uDF12&#039;, &#039;\uD83C\uDF13&#039;, &#039;\uD83C\uDF14&#039;,&lt;br /&gt;
		&#039;\uD83C\uDF15&#039;, &#039;\uD83C\uDF16&#039;, &#039;\uD83C\uDF17&#039;, &#039;\uD83C\uDF18&#039; ];&lt;br /&gt;
&lt;br /&gt;
	function moonPhaseIndex( percent, direction ) {&lt;br /&gt;
		// direction: 0 = turning point, 1 = falling toward new, 2 = rising toward full.&lt;br /&gt;
		if ( percent &amp;lt;= 5 || ( percent &amp;lt;= 10 &amp;amp;&amp;amp; direction === 1 ) ) { return 0; }&lt;br /&gt;
		if ( percent &amp;gt;= 7 &amp;amp;&amp;amp; percent &amp;lt;= 38 &amp;amp;&amp;amp; direction === 2 ) { return 1; }&lt;br /&gt;
		if ( percent &amp;gt;= 40 &amp;amp;&amp;amp; percent &amp;lt;= 55 &amp;amp;&amp;amp; direction === 2 ) { return 2; }&lt;br /&gt;
		if ( percent &amp;gt;= 57 &amp;amp;&amp;amp; percent &amp;lt;= 88 &amp;amp;&amp;amp; direction === 2 ) { return 3; }&lt;br /&gt;
		if ( percent &amp;gt;= 95 || ( percent &amp;gt;= 90 &amp;amp;&amp;amp; direction === 2 ) ) { return 4; }&lt;br /&gt;
		if ( percent &amp;gt;= 62 &amp;amp;&amp;amp; percent &amp;lt;= 93 &amp;amp;&amp;amp; direction === 1 ) { return 5; }&lt;br /&gt;
		if ( percent &amp;gt;= 45 &amp;amp;&amp;amp; percent &amp;lt;= 60 &amp;amp;&amp;amp; direction === 1 ) { return 6; }&lt;br /&gt;
		return 7;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function vanadiel() {&lt;br /&gt;
		var minutes = VANA_ANCHOR_DAYS * MIN_PER_DAY +&lt;br /&gt;
			( ( Date.now() - EARTH_ANCHOR_MS ) * RATE ) / 60000;&lt;br /&gt;
&lt;br /&gt;
		var moonDay = Math.floor( ( ( minutes / MIN_PER_DAY ) + MOON_OFFSET_DAYS ) % MOON_CYCLE_DAYS );&lt;br /&gt;
		var moonPercent = Math.abs( Math.round( ( ( 42 - moonDay ) / 42 ) * 100 ) );&lt;br /&gt;
		var moonDirection = moonDay === 0 || moonDay === 42 ? 0 : ( moonDay &amp;lt; 42 ? 1 : 2 );&lt;br /&gt;
		var moonIndex = moonPhaseIndex( moonPercent, moonDirection );&lt;br /&gt;
		var weekdayIndex = Math.floor( ( minutes % MIN_PER_WEEK ) / MIN_PER_DAY );&lt;br /&gt;
&lt;br /&gt;
		return {&lt;br /&gt;
			year: Math.floor( minutes / MIN_PER_YEAR ),&lt;br /&gt;
			month: Math.floor( ( minutes / MIN_PER_MONTH ) % 12 ) + 1,&lt;br /&gt;
			day: Math.floor( ( minutes / MIN_PER_DAY ) % 30 ) + 1,&lt;br /&gt;
			hour: Math.floor( ( minutes % MIN_PER_DAY ) / MIN_PER_HOUR ),&lt;br /&gt;
			minute: Math.floor( minutes % MIN_PER_HOUR ),&lt;br /&gt;
			weekdayIndex: weekdayIndex,&lt;br /&gt;
			weekday: WEEKDAYS[ weekdayIndex ],&lt;br /&gt;
			moonPercent: moonPercent,&lt;br /&gt;
			moonPhase: MOON_PHASES[ moonIndex ],&lt;br /&gt;
			moonIcon: MOON_ICONS[ moonIndex ]&lt;br /&gt;
		};&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function pad2( n ) {&lt;br /&gt;
		return ( n &amp;lt; 10 ? &#039;0&#039; : &#039;&#039; ) + n;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- markup helpers */&lt;br /&gt;
&lt;br /&gt;
	function el( tag, attrs, children ) {&lt;br /&gt;
		var node = document.createElement( tag );&lt;br /&gt;
		var key;&lt;br /&gt;
		for ( key in attrs || {} ) {&lt;br /&gt;
			if ( key === &#039;text&#039; ) {&lt;br /&gt;
				node.textContent = attrs[ key ];&lt;br /&gt;
			} else {&lt;br /&gt;
				node.setAttribute( key, attrs[ key ] );&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		( children || [] ).forEach( function ( child ) {&lt;br /&gt;
			node.appendChild( child );&lt;br /&gt;
		} );&lt;br /&gt;
		return node;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/** A Timeless portlet: heading plus body, ready to drop into a .sidebar-inner. */&lt;br /&gt;
	function portlet( id, label, body ) {&lt;br /&gt;
		return el( &#039;div&#039;, { role: &#039;navigation&#039;, &#039;class&#039;: &#039;mw-portlet&#039;, id: &#039;p-&#039; + id,&lt;br /&gt;
			&#039;aria-labelledby&#039;: &#039;p-&#039; + id + &#039;-label&#039; }, [&lt;br /&gt;
			el( &#039;h3&#039;, { id: &#039;p-&#039; + id + &#039;-label&#039;, text: label } ),&lt;br /&gt;
			el( &#039;div&#039;, { &#039;class&#039;: &#039;mw-portlet-body&#039; }, [ body ] )&lt;br /&gt;
		] );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/** A Timeless sidebar chunk: the collapsible outer container. */&lt;br /&gt;
	function chunk( id, heading, body ) {&lt;br /&gt;
		return el( &#039;div&#039;, { id: id, &#039;class&#039;: &#039;sidebar-chunk&#039; }, [&lt;br /&gt;
			el( &#039;h2&#039;, {}, [ el( &#039;span&#039;, { text: heading } ) ] ),&lt;br /&gt;
			el( &#039;div&#039;, { &#039;class&#039;: &#039;sidebar-inner&#039; }, [ body ] )&lt;br /&gt;
		] );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- Vana&#039;diel panel */&lt;br /&gt;
&lt;br /&gt;
	function buildClockPanel() {&lt;br /&gt;
		var clock = el( &#039;b&#039;, { &#039;class&#039;: &#039;pg-vana-clock&#039; } );&lt;br /&gt;
		var date = el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-date&#039; } );&lt;br /&gt;
		var day = el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-day&#039; } );&lt;br /&gt;
		var moonIcon = el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-moon-icon&#039; } );&lt;br /&gt;
		var moonPct = el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-moon-pct&#039; } );&lt;br /&gt;
		var serverText = el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-server-text&#039;, text: &#039;Checking status&#039; } );&lt;br /&gt;
		var serverCount = el( &#039;b&#039;, { &#039;class&#039;: &#039;pg-vana-server-count&#039; } );&lt;br /&gt;
		var server = el( &#039;div&#039;, { &#039;class&#039;: &#039;pg-vana-line pg-vana-server&#039;, &#039;data-state&#039;: &#039;unknown&#039; }, [&lt;br /&gt;
			el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-dot&#039;, &#039;aria-hidden&#039;: &#039;true&#039; } ),&lt;br /&gt;
			serverText,&lt;br /&gt;
			serverCount&lt;br /&gt;
		] );&lt;br /&gt;
&lt;br /&gt;
		var panel = el( &#039;div&#039;, { &#039;class&#039;: &#039;pg-vana&#039; }, [&lt;br /&gt;
			el( &#039;div&#039;, { &#039;class&#039;: &#039;pg-vana-line&#039; }, [&lt;br /&gt;
				clock, el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-tilde&#039;, text: &#039;~&#039; } ), date&lt;br /&gt;
			] ),&lt;br /&gt;
			el( &#039;div&#039;, { &#039;class&#039;: &#039;pg-vana-line&#039; }, [&lt;br /&gt;
				day,&lt;br /&gt;
				el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-tilde&#039;, text: &#039;~&#039; } ),&lt;br /&gt;
				el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-moon&#039; }, [ moonIcon, moonPct ] )&lt;br /&gt;
			] ),&lt;br /&gt;
			server&lt;br /&gt;
		] );&lt;br /&gt;
&lt;br /&gt;
		var list = el( &#039;ul&#039;, {}, [&lt;br /&gt;
			el( &#039;li&#039;, { id: &#039;n-vanatime&#039;, &#039;class&#039;: &#039;mw-list-item&#039; }, [ panel ] )&lt;br /&gt;
		] );&lt;br /&gt;
&lt;br /&gt;
		// A Vana&#039;diel minute is 2.4 Earth seconds, so a one-second tick looks smooth without&lt;br /&gt;
		// doing redundant work.&lt;br /&gt;
		function tick() {&lt;br /&gt;
			var v = vanadiel();&lt;br /&gt;
			clock.textContent = pad2( v.hour ) + &#039;:&#039; + pad2( v.minute );&lt;br /&gt;
			date.textContent = v.year + &#039;-&#039; + v.month + &#039;-&#039; + v.day;&lt;br /&gt;
			day.textContent = v.weekday;&lt;br /&gt;
			day.className = &#039;pg-vana-day pg-vana-day-&#039; + v.weekdayIndex;&lt;br /&gt;
			moonIcon.textContent = v.moonIcon;&lt;br /&gt;
			moonIcon.setAttribute( &#039;title&#039;, v.moonPhase );&lt;br /&gt;
			moonPct.textContent = v.moonPercent + &#039;%&#039;;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// Same origin as the wiki, so no CORS involved and no proxy needed.&lt;br /&gt;
		function poll() {&lt;br /&gt;
			fetch( &#039;/api/status&#039;, { headers: { Accept: &#039;application/json&#039; } } )&lt;br /&gt;
				.then( function ( res ) {&lt;br /&gt;
					if ( !res.ok ) {&lt;br /&gt;
						throw new Error( &#039;HTTP &#039; + res.status );&lt;br /&gt;
					}&lt;br /&gt;
					return res.json();&lt;br /&gt;
				} )&lt;br /&gt;
				.then( function ( data ) {&lt;br /&gt;
					var online = String( data &amp;amp;&amp;amp; data.status || &#039;&#039; ).toLowerCase() === &#039;online&#039;;&lt;br /&gt;
					var players = Number( data &amp;amp;&amp;amp; data.playerCount );&lt;br /&gt;
					server.setAttribute( &#039;data-state&#039;, online ? &#039;online&#039; : &#039;offline&#039; );&lt;br /&gt;
					serverText.textContent = online ? &#039;Online&#039; : &#039;Offline&#039;;&lt;br /&gt;
					// A count of 0 is indistinguishable from &amp;quot;not published&amp;quot;, and &amp;quot;Online 0&amp;quot; next&lt;br /&gt;
					// to a green dot reads as a bug, so only show a positive count.&lt;br /&gt;
					serverCount.textContent = online &amp;amp;&amp;amp; players &amp;gt; 0 ? String( players ) : &#039;&#039;;&lt;br /&gt;
				} )&lt;br /&gt;
				.catch( function () {&lt;br /&gt;
					// Keep whatever is on screen; the next poll may succeed.&lt;br /&gt;
					if ( server.getAttribute( &#039;data-state&#039; ) === &#039;unknown&#039; ) {&lt;br /&gt;
						serverText.textContent = &#039;Status unavailable&#039;;&lt;br /&gt;
					}&lt;br /&gt;
				} );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		tick();&lt;br /&gt;
		poll();&lt;br /&gt;
		setInterval( tick, 1000 );&lt;br /&gt;
		setInterval( poll, 60000 );&lt;br /&gt;
&lt;br /&gt;
		return portlet( &#039;vanatime&#039;, &amp;quot;Vana&#039;diel Time&amp;quot;, list );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- theme toggle&lt;br /&gt;
	 *&lt;br /&gt;
	 * Dark is the default and needs no class, so light mode is the opt-in state: MediaWiki:Timeless.css&lt;br /&gt;
	 * scopes every dark rule to html:not(.pg-day). That ordering means a reader on the default theme&lt;br /&gt;
	 * never sees a flash of light before this script runs.&lt;br /&gt;
	 */&lt;br /&gt;
	var THEME_KEY = &#039;pg-theme&#039;;&lt;br /&gt;
&lt;br /&gt;
	function applyTheme( theme ) {&lt;br /&gt;
		document.documentElement.classList.toggle( &#039;pg-day&#039;, theme === &#039;day&#039; );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function buildThemeToggle() {&lt;br /&gt;
		var link = el( &#039;a&#039;, { &#039;class&#039;: &#039;ext-darkmode-link&#039;, href: &#039;#&#039;, role: &#039;button&#039; } );&lt;br /&gt;
		var label = el( &#039;span&#039;, {} );&lt;br /&gt;
		link.appendChild( label );&lt;br /&gt;
&lt;br /&gt;
		function refresh() {&lt;br /&gt;
			var isDay = document.documentElement.classList.contains( &#039;pg-day&#039; );&lt;br /&gt;
			label.textContent = isDay ? &#039;Dark mode&#039; : &#039;Light mode&#039;;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		link.addEventListener( &#039;click&#039;, function ( e ) {&lt;br /&gt;
			e.preventDefault();&lt;br /&gt;
			var next = document.documentElement.classList.contains( &#039;pg-day&#039; ) ? &#039;night&#039; : &#039;day&#039;;&lt;br /&gt;
			applyTheme( next );&lt;br /&gt;
			try {&lt;br /&gt;
				localStorage.setItem( THEME_KEY, next );&lt;br /&gt;
			} catch ( err ) {&lt;br /&gt;
				// Private browsing or a full quota; the choice just will not persist.&lt;br /&gt;
			}&lt;br /&gt;
			refresh();&lt;br /&gt;
		} );&lt;br /&gt;
&lt;br /&gt;
		refresh();&lt;br /&gt;
&lt;br /&gt;
		return portlet( &#039;theme&#039;, &#039;Appearance&#039;, el( &#039;ul&#039;, {}, [&lt;br /&gt;
			el( &#039;li&#039;, { id: &#039;t-darkmode&#039;, &#039;class&#039;: &#039;mw-list-item&#039; }, [ link ] )&lt;br /&gt;
		] ) );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- Discord panel&lt;br /&gt;
	 *&lt;br /&gt;
	 * Discord&#039;s own embed (discord.com/widget) is a cross-origin iframe locked to Discord&#039;s blurple&lt;br /&gt;
	 * palette, so it cannot be themed to match this wiki. The member list is therefore fetched and&lt;br /&gt;
	 * rendered here instead: widget.json sends Access-Control-Allow-Origin for this host, so no proxy&lt;br /&gt;
	 * is involved and the markup is ours to style.&lt;br /&gt;
	 *&lt;br /&gt;
	 * That endpoint answers 403 whenever &amp;quot;Enable Server Widget&amp;quot; is switched off in Discord&#039;s server&lt;br /&gt;
	 * settings, which is why the static join panel is still kept as a fallback rather than deleted.&lt;br /&gt;
	 */&lt;br /&gt;
	var DISCORD_INVITE = &#039;https://discord.gg/M3NFvpm75E&#039;;&lt;br /&gt;
	var DISCORD_GUILD = &#039;1473308284349059126&#039;;&lt;br /&gt;
	var DISCORD_WIDGET = &#039;https://discord.com/api/guilds/&#039; + DISCORD_GUILD + &#039;/widget.json&#039;;&lt;br /&gt;
&lt;br /&gt;
	// Online first, then idle, then do-not-disturb, alphabetically within each.&lt;br /&gt;
	var STATUS_RANK = { online: 0, idle: 1, dnd: 2, offline: 3 };&lt;br /&gt;
&lt;br /&gt;
	function statusRank( status ) {&lt;br /&gt;
		return STATUS_RANK[ status ] === undefined ? STATUS_RANK.offline : STATUS_RANK[ status ];&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function discordMember( member ) {&lt;br /&gt;
		var status = STATUS_RANK[ member.status ] === undefined ? &#039;offline&#039; : member.status;&lt;br /&gt;
		var children = [];&lt;br /&gt;
&lt;br /&gt;
		if ( member.avatar_url ) {&lt;br /&gt;
			children.push( el( &#039;img&#039;, { &#039;class&#039;: &#039;pg-discord-avatar&#039;, src: member.avatar_url,&lt;br /&gt;
				alt: &#039;&#039;, width: &#039;20&#039;, height: &#039;20&#039;, loading: &#039;lazy&#039; } ) );&lt;br /&gt;
		} else {&lt;br /&gt;
			children.push( el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-discord-avatar pg-discord-avatar-blank&#039;,&lt;br /&gt;
				&#039;aria-hidden&#039;: &#039;true&#039; } ) );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		children.push( el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-discord-member-name&#039;, text: member.username } ) );&lt;br /&gt;
&lt;br /&gt;
		var li = el( &#039;li&#039;, { &#039;class&#039;: &#039;pg-discord-member&#039;, &#039;data-status&#039;: status }, children );&lt;br /&gt;
		// The status is conveyed by a colour swatch, so name it for anyone not seeing colour.&lt;br /&gt;
		li.setAttribute( &#039;title&#039;, member.game &amp;amp;&amp;amp; member.game.name ?&lt;br /&gt;
			member.username + &#039; - &#039; + status + &#039; - playing &#039; + member.game.name :&lt;br /&gt;
			member.username + &#039; - &#039; + status );&lt;br /&gt;
		return li;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function buildDiscordPanel() {&lt;br /&gt;
		var count = el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-discord-count&#039;, text: &#039;Loading&#039; } );&lt;br /&gt;
		var list = el( &#039;ul&#039;, { &#039;class&#039;: &#039;pg-discord-members&#039; } );&lt;br /&gt;
&lt;br /&gt;
		var panel = el( &#039;div&#039;, { &#039;class&#039;: &#039;pg-discord&#039; }, [&lt;br /&gt;
			el( &#039;div&#039;, { &#039;class&#039;: &#039;pg-discord-head&#039; }, [&lt;br /&gt;
				el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-discord-title&#039;, text: &#039;PegasusXI&#039; } ),&lt;br /&gt;
				count&lt;br /&gt;
			] ),&lt;br /&gt;
			list,&lt;br /&gt;
			el( &#039;a&#039;, { &#039;class&#039;: &#039;pg-discord-join&#039;, href: DISCORD_INVITE,&lt;br /&gt;
				target: &#039;_blank&#039;, rel: &#039;noopener&#039;, text: &#039;Join Discord&#039; } )&lt;br /&gt;
		] );&lt;br /&gt;
&lt;br /&gt;
		fetch( DISCORD_WIDGET, { headers: { Accept: &#039;application/json&#039; } } )&lt;br /&gt;
			.then( function ( res ) {&lt;br /&gt;
				if ( !res.ok ) {&lt;br /&gt;
					throw new Error( &#039;HTTP &#039; + res.status );&lt;br /&gt;
				}&lt;br /&gt;
				return res.json();&lt;br /&gt;
			} )&lt;br /&gt;
			.then( function ( data ) {&lt;br /&gt;
				var members = ( ( data &amp;amp;&amp;amp; data.members ) || [] ).slice();&lt;br /&gt;
				members.sort( function ( a, b ) {&lt;br /&gt;
					return statusRank( a.status ) - statusRank( b.status ) ||&lt;br /&gt;
						String( a.username ).localeCompare( String( b.username ) );&lt;br /&gt;
				} );&lt;br /&gt;
&lt;br /&gt;
				// presence_count counts everyone online; the members array is capped by Discord.&lt;br /&gt;
				var online = Number( data &amp;amp;&amp;amp; data.presence_count );&lt;br /&gt;
				if ( !isFinite( online ) ) {&lt;br /&gt;
					online = members.length;&lt;br /&gt;
				}&lt;br /&gt;
				count.textContent = online + ( online === 1 ? &#039; member online&#039; : &#039; members online&#039; );&lt;br /&gt;
&lt;br /&gt;
				members.forEach( function ( member ) {&lt;br /&gt;
					list.appendChild( discordMember( member ) );&lt;br /&gt;
				} );&lt;br /&gt;
&lt;br /&gt;
				panel.setAttribute( &#039;data-state&#039;, &#039;ready&#039; );&lt;br /&gt;
			} )&lt;br /&gt;
			.catch( function () {&lt;br /&gt;
				// Most likely the server widget was switched off again. Fall back to the plain join&lt;br /&gt;
				// panel rather than leaving &amp;quot;Loading&amp;quot; on screen for good.&lt;br /&gt;
				panel.setAttribute( &#039;data-state&#039;, &#039;unavailable&#039; );&lt;br /&gt;
				count.textContent = &#039;Chat, support and server announcements&#039;;&lt;br /&gt;
			} );&lt;br /&gt;
&lt;br /&gt;
		return el( &#039;div&#039;, { id: &#039;discord-panel&#039; }, [ panel ] );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- header button&lt;br /&gt;
	 *&lt;br /&gt;
	 * #mw-header is a flex row and Timeless assigns explicit orders: #p-logo-text 1, #p-search 2,&lt;br /&gt;
	 * #user-tools 3. A new child with no order defaults to 0 and jumps ahead of all of them, which is&lt;br /&gt;
	 * why this button first appeared at the far left of the header. MediaWiki:Timeless.css gives it&lt;br /&gt;
	 * order 4 so it sits at the right-hand end.&lt;br /&gt;
	 *&lt;br /&gt;
	 * This starts a sign-in rather than handing out an invite: the Discord invite is already on the&lt;br /&gt;
	 * sidebar panel, and a reader who wants an account is better served by the thing that makes one.&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	/* The flow cannot be started by a link. It begins with a POST of MediaWiki&#039;s login form carrying&lt;br /&gt;
	 * a login token, and Special:PluggableAuthLogin on its own has no plugin recorded in the session,&lt;br /&gt;
	 * so it just errors. Rather than leave the reader on the login page to press a second button, the&lt;br /&gt;
	 * header link carries this marker and the gadget presses the real button on arrival. The form and&lt;br /&gt;
	 * token are MediaWiki&#039;s own, so nothing is forged, and with JavaScript off the reader simply sees&lt;br /&gt;
	 * the login page with the Discord button waiting.&lt;br /&gt;
	 */&lt;br /&gt;
	var AUTOSTART_PARAM = &#039;pgdiscord&#039;;&lt;br /&gt;
&lt;br /&gt;
	function buildHeaderButton() {&lt;br /&gt;
		// Logged-in readers have the personal menu instead; a sign-in button would be noise.&lt;br /&gt;
		if ( mw.config.get( &#039;wgUserName&#039; ) ) {&lt;br /&gt;
			return null;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		var articlePath = mw.config.get( &#039;wgArticlePath&#039; ) || &#039;/wiki/$1&#039;;&lt;br /&gt;
		var params = new URLSearchParams();&lt;br /&gt;
		params.set( &#039;returnto&#039;, mw.config.get( &#039;wgPageName&#039; ) || &#039;Main_Page&#039; );&lt;br /&gt;
		params.set( AUTOSTART_PARAM, &#039;1&#039; );&lt;br /&gt;
		var href = articlePath.replace( &#039;$1&#039;, &#039;Special:UserLogin&#039; ) + &#039;?&#039; + params.toString();&lt;br /&gt;
&lt;br /&gt;
		return el( &#039;div&#039;, { id: &#039;p-discord-signin&#039; }, [&lt;br /&gt;
			el( &#039;a&#039;, { &#039;class&#039;: &#039;pg-discord-signin&#039;, href: href }, [&lt;br /&gt;
				el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-discord-signin-mark&#039;, &#039;aria-hidden&#039;: &#039;true&#039; } ),&lt;br /&gt;
				el( &#039;span&#039;, { text: &#039;Sign in with Discord&#039; } )&lt;br /&gt;
			] )&lt;br /&gt;
		] );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/** On the login page, press the Discord button if we were sent there to do exactly that. */&lt;br /&gt;
	function maybeAutostart() {&lt;br /&gt;
		if ( mw.config.get( &#039;wgCanonicalSpecialPageName&#039; ) !== &#039;Userlogin&#039; ) {&lt;br /&gt;
			return;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		var query = new URLSearchParams( location.search );&lt;br /&gt;
		if ( query.get( AUTOSTART_PARAM ) !== &#039;1&#039; ) {&lt;br /&gt;
			return;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// A refused or failed attempt comes back here with a message. Pressing the button again&lt;br /&gt;
		// would bounce the reader straight back out and hide the reason from them.&lt;br /&gt;
		if ( query.has( &#039;error&#039; ) || query.has( &#039;warning&#039; ) ) {&lt;br /&gt;
			return;&lt;br /&gt;
		}&lt;br /&gt;
		if ( document.querySelector(&lt;br /&gt;
			&#039;.cdx-message--error, .errorbox, .mw-message-box-error, .error&#039;&lt;br /&gt;
		) ) {&lt;br /&gt;
			return;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// Matched by name prefix rather than index: the suffix is the plugin&#039;s position in&lt;br /&gt;
		// $wgPluggableAuth_Config, which would shift if a second provider were ever added.&lt;br /&gt;
		var button = document.querySelector(&lt;br /&gt;
			&#039;button[name^=&amp;quot;pluggableauthlogin&amp;quot;], input[name^=&amp;quot;pluggableauthlogin&amp;quot;]&#039;&lt;br /&gt;
		);&lt;br /&gt;
		if ( button ) {&lt;br /&gt;
			button.click();&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- wire it up */&lt;br /&gt;
&lt;br /&gt;
	function init() {&lt;br /&gt;
		// Restore the saved theme first so the toggle&#039;s label starts out correct.&lt;br /&gt;
		try {&lt;br /&gt;
			if ( localStorage.getItem( THEME_KEY ) === &#039;day&#039; ) {&lt;br /&gt;
				applyTheme( &#039;day&#039; );&lt;br /&gt;
			}&lt;br /&gt;
		} catch ( err ) {}&lt;br /&gt;
&lt;br /&gt;
		// Before building anything else: if this is a hand-off to Discord the page is about to be&lt;br /&gt;
		// replaced, so there is no point populating a sidebar nobody will see.&lt;br /&gt;
		maybeAutostart();&lt;br /&gt;
&lt;br /&gt;
		var siteNav = document.getElementById( &#039;mw-site-navigation&#039; );&lt;br /&gt;
		var navChunk = document.getElementById( &#039;site-navigation&#039; );&lt;br /&gt;
&lt;br /&gt;
		if ( navChunk ) {&lt;br /&gt;
			var inner = navChunk.querySelector( &#039;.sidebar-inner&#039; );&lt;br /&gt;
			if ( inner ) {&lt;br /&gt;
				inner.insertBefore( buildClockPanel(), inner.firstChild );&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if ( siteNav &amp;amp;&amp;amp; !document.getElementById( &#039;theme-tools&#039; ) ) {&lt;br /&gt;
			siteNav.appendChild( chunk( &#039;theme-tools&#039;, &#039;Appearance&#039;, buildThemeToggle() ) );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		var related = document.getElementById( &#039;mw-related-navigation&#039; );&lt;br /&gt;
		if ( related &amp;amp;&amp;amp; !document.getElementById( &#039;discord-panel&#039; ) ) {&lt;br /&gt;
			related.insertBefore( buildDiscordPanel(), related.firstChild );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		var userTools = document.getElementById( &#039;user-tools&#039; );&lt;br /&gt;
		if ( userTools &amp;amp;&amp;amp; userTools.parentNode &amp;amp;&amp;amp; !document.getElementById( &#039;p-discord-signin&#039; ) ) {&lt;br /&gt;
			var button = buildHeaderButton();&lt;br /&gt;
			if ( button ) {&lt;br /&gt;
				userTools.parentNode.insertBefore( button, userTools );&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if ( document.readyState === &#039;loading&#039; ) {&lt;br /&gt;
		document.addEventListener( &#039;DOMContentLoaded&#039;, init );&lt;br /&gt;
	} else {&lt;br /&gt;
		init();&lt;br /&gt;
	}&lt;br /&gt;
}() );&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=MediaWiki:Gadget-pegasus.js&amp;diff=142075</id>
		<title>MediaWiki:Gadget-pegasus.js</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=MediaWiki:Gadget-pegasus.js&amp;diff=142075"/>
		<updated>2026-08-30T00:28:35Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: Header button starts Discord sign-in instead of linking to the invite&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* PegasusXI Wiki sidebar additions: Vana&#039;diel clock, theme toggle, Discord panel.&lt;br /&gt;
 *&lt;br /&gt;
 * The Node renderer this wiki replaced built all three server-side, so they were correct on first&lt;br /&gt;
 * paint and worked without JavaScript. MediaWiki has no hook for injecting arbitrary markup into a&lt;br /&gt;
 * Timeless sidebar chunk without a skin or extension, so they are built client-side here instead.&lt;br /&gt;
 * The trade-off is a brief gap before the panel appears; in exchange nothing about the wiki has to be&lt;br /&gt;
 * patched on disk, and every piece is editable on-wiki.&lt;br /&gt;
 *&lt;br /&gt;
 * Markup deliberately mirrors Timeless&#039;s own nesting -- .sidebar-chunk &amp;gt; h2 &amp;gt; span, then&lt;br /&gt;
 * .sidebar-inner &amp;gt; .mw-portlet &amp;gt; h3 + .mw-portlet-body -- because both the skin&#039;s collapse behaviour&lt;br /&gt;
 * at narrow widths and MediaWiki:Timeless.css key off that exact structure.&lt;br /&gt;
 */&lt;br /&gt;
( function () {&lt;br /&gt;
	&#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- Vana&#039;diel clock math&lt;br /&gt;
	 *&lt;br /&gt;
	 * Time runs 25x Earth time. The anchor is Vana&#039;diel&#039;s birthday: Earth 2002-06-23T15:00:00Z&lt;br /&gt;
	 * corresponds to Vana&#039;diel 0898-02-01 00:00, which is (898 * 360) + 30 days after the epoch.&lt;br /&gt;
	 * The calendar has no leap years and no short months -- 360-day years, 30-day months, 8-day&lt;br /&gt;
	 * weeks -- so everything else is modulo arithmetic on Vana&#039;diel minutes.&lt;br /&gt;
	 *&lt;br /&gt;
	 * The moon runs a separate 84-day cycle offset 26 days from the epoch. Phase comes from the&lt;br /&gt;
	 * percentage *and* the direction of travel, because each percentage occurs twice per cycle.&lt;br /&gt;
	 *&lt;br /&gt;
	 * Kept identical to site/public/vanadiel.mjs, which was cross-checked against HorizonXI&#039;s&lt;br /&gt;
	 * MediaWiki:VanaTime.js over a full moon cycle.&lt;br /&gt;
	 */&lt;br /&gt;
	var EARTH_ANCHOR_MS = 1024844400000;&lt;br /&gt;
	var VANA_ANCHOR_DAYS = 898 * 360 + 30;&lt;br /&gt;
	var RATE = 25;&lt;br /&gt;
&lt;br /&gt;
	var MIN_PER_HOUR = 60;&lt;br /&gt;
	var MIN_PER_DAY = 1440;&lt;br /&gt;
	var MIN_PER_WEEK = 8 * MIN_PER_DAY;&lt;br /&gt;
	var MIN_PER_MONTH = 30 * MIN_PER_DAY;&lt;br /&gt;
	var MIN_PER_YEAR = 360 * MIN_PER_DAY;&lt;br /&gt;
&lt;br /&gt;
	var MOON_CYCLE_DAYS = 84;&lt;br /&gt;
	var MOON_OFFSET_DAYS = 26;&lt;br /&gt;
&lt;br /&gt;
	var WEEKDAYS = [&lt;br /&gt;
		&#039;Firesday&#039;, &#039;Earthsday&#039;, &#039;Watersday&#039;, &#039;Windsday&#039;,&lt;br /&gt;
		&#039;Iceday&#039;, &#039;Lightningday&#039;, &#039;Lightsday&#039;, &#039;Darksday&#039;&lt;br /&gt;
	];&lt;br /&gt;
	var MOON_PHASES = [&lt;br /&gt;
		&#039;New Moon&#039;, &#039;Waxing Crescent&#039;, &#039;First Quarter&#039;, &#039;Waxing Gibbous&#039;,&lt;br /&gt;
		&#039;Full Moon&#039;, &#039;Waning Gibbous&#039;, &#039;Last Quarter&#039;, &#039;Waning Crescent&#039;&lt;br /&gt;
	];&lt;br /&gt;
	var MOON_ICONS = [ &#039;\uD83C\uDF11&#039;, &#039;\uD83C\uDF12&#039;, &#039;\uD83C\uDF13&#039;, &#039;\uD83C\uDF14&#039;,&lt;br /&gt;
		&#039;\uD83C\uDF15&#039;, &#039;\uD83C\uDF16&#039;, &#039;\uD83C\uDF17&#039;, &#039;\uD83C\uDF18&#039; ];&lt;br /&gt;
&lt;br /&gt;
	function moonPhaseIndex( percent, direction ) {&lt;br /&gt;
		// direction: 0 = turning point, 1 = falling toward new, 2 = rising toward full.&lt;br /&gt;
		if ( percent &amp;lt;= 5 || ( percent &amp;lt;= 10 &amp;amp;&amp;amp; direction === 1 ) ) { return 0; }&lt;br /&gt;
		if ( percent &amp;gt;= 7 &amp;amp;&amp;amp; percent &amp;lt;= 38 &amp;amp;&amp;amp; direction === 2 ) { return 1; }&lt;br /&gt;
		if ( percent &amp;gt;= 40 &amp;amp;&amp;amp; percent &amp;lt;= 55 &amp;amp;&amp;amp; direction === 2 ) { return 2; }&lt;br /&gt;
		if ( percent &amp;gt;= 57 &amp;amp;&amp;amp; percent &amp;lt;= 88 &amp;amp;&amp;amp; direction === 2 ) { return 3; }&lt;br /&gt;
		if ( percent &amp;gt;= 95 || ( percent &amp;gt;= 90 &amp;amp;&amp;amp; direction === 2 ) ) { return 4; }&lt;br /&gt;
		if ( percent &amp;gt;= 62 &amp;amp;&amp;amp; percent &amp;lt;= 93 &amp;amp;&amp;amp; direction === 1 ) { return 5; }&lt;br /&gt;
		if ( percent &amp;gt;= 45 &amp;amp;&amp;amp; percent &amp;lt;= 60 &amp;amp;&amp;amp; direction === 1 ) { return 6; }&lt;br /&gt;
		return 7;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function vanadiel() {&lt;br /&gt;
		var minutes = VANA_ANCHOR_DAYS * MIN_PER_DAY +&lt;br /&gt;
			( ( Date.now() - EARTH_ANCHOR_MS ) * RATE ) / 60000;&lt;br /&gt;
&lt;br /&gt;
		var moonDay = Math.floor( ( ( minutes / MIN_PER_DAY ) + MOON_OFFSET_DAYS ) % MOON_CYCLE_DAYS );&lt;br /&gt;
		var moonPercent = Math.abs( Math.round( ( ( 42 - moonDay ) / 42 ) * 100 ) );&lt;br /&gt;
		var moonDirection = moonDay === 0 || moonDay === 42 ? 0 : ( moonDay &amp;lt; 42 ? 1 : 2 );&lt;br /&gt;
		var moonIndex = moonPhaseIndex( moonPercent, moonDirection );&lt;br /&gt;
		var weekdayIndex = Math.floor( ( minutes % MIN_PER_WEEK ) / MIN_PER_DAY );&lt;br /&gt;
&lt;br /&gt;
		return {&lt;br /&gt;
			year: Math.floor( minutes / MIN_PER_YEAR ),&lt;br /&gt;
			month: Math.floor( ( minutes / MIN_PER_MONTH ) % 12 ) + 1,&lt;br /&gt;
			day: Math.floor( ( minutes / MIN_PER_DAY ) % 30 ) + 1,&lt;br /&gt;
			hour: Math.floor( ( minutes % MIN_PER_DAY ) / MIN_PER_HOUR ),&lt;br /&gt;
			minute: Math.floor( minutes % MIN_PER_HOUR ),&lt;br /&gt;
			weekdayIndex: weekdayIndex,&lt;br /&gt;
			weekday: WEEKDAYS[ weekdayIndex ],&lt;br /&gt;
			moonPercent: moonPercent,&lt;br /&gt;
			moonPhase: MOON_PHASES[ moonIndex ],&lt;br /&gt;
			moonIcon: MOON_ICONS[ moonIndex ]&lt;br /&gt;
		};&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function pad2( n ) {&lt;br /&gt;
		return ( n &amp;lt; 10 ? &#039;0&#039; : &#039;&#039; ) + n;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- markup helpers */&lt;br /&gt;
&lt;br /&gt;
	function el( tag, attrs, children ) {&lt;br /&gt;
		var node = document.createElement( tag );&lt;br /&gt;
		var key;&lt;br /&gt;
		for ( key in attrs || {} ) {&lt;br /&gt;
			if ( key === &#039;text&#039; ) {&lt;br /&gt;
				node.textContent = attrs[ key ];&lt;br /&gt;
			} else {&lt;br /&gt;
				node.setAttribute( key, attrs[ key ] );&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		( children || [] ).forEach( function ( child ) {&lt;br /&gt;
			node.appendChild( child );&lt;br /&gt;
		} );&lt;br /&gt;
		return node;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/** A Timeless portlet: heading plus body, ready to drop into a .sidebar-inner. */&lt;br /&gt;
	function portlet( id, label, body ) {&lt;br /&gt;
		return el( &#039;div&#039;, { role: &#039;navigation&#039;, &#039;class&#039;: &#039;mw-portlet&#039;, id: &#039;p-&#039; + id,&lt;br /&gt;
			&#039;aria-labelledby&#039;: &#039;p-&#039; + id + &#039;-label&#039; }, [&lt;br /&gt;
			el( &#039;h3&#039;, { id: &#039;p-&#039; + id + &#039;-label&#039;, text: label } ),&lt;br /&gt;
			el( &#039;div&#039;, { &#039;class&#039;: &#039;mw-portlet-body&#039; }, [ body ] )&lt;br /&gt;
		] );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/** A Timeless sidebar chunk: the collapsible outer container. */&lt;br /&gt;
	function chunk( id, heading, body ) {&lt;br /&gt;
		return el( &#039;div&#039;, { id: id, &#039;class&#039;: &#039;sidebar-chunk&#039; }, [&lt;br /&gt;
			el( &#039;h2&#039;, {}, [ el( &#039;span&#039;, { text: heading } ) ] ),&lt;br /&gt;
			el( &#039;div&#039;, { &#039;class&#039;: &#039;sidebar-inner&#039; }, [ body ] )&lt;br /&gt;
		] );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- Vana&#039;diel panel */&lt;br /&gt;
&lt;br /&gt;
	function buildClockPanel() {&lt;br /&gt;
		var clock = el( &#039;b&#039;, { &#039;class&#039;: &#039;pg-vana-clock&#039; } );&lt;br /&gt;
		var date = el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-date&#039; } );&lt;br /&gt;
		var day = el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-day&#039; } );&lt;br /&gt;
		var moonIcon = el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-moon-icon&#039; } );&lt;br /&gt;
		var moonPct = el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-moon-pct&#039; } );&lt;br /&gt;
		var serverText = el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-server-text&#039;, text: &#039;Checking status&#039; } );&lt;br /&gt;
		var serverCount = el( &#039;b&#039;, { &#039;class&#039;: &#039;pg-vana-server-count&#039; } );&lt;br /&gt;
		var server = el( &#039;div&#039;, { &#039;class&#039;: &#039;pg-vana-line pg-vana-server&#039;, &#039;data-state&#039;: &#039;unknown&#039; }, [&lt;br /&gt;
			el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-dot&#039;, &#039;aria-hidden&#039;: &#039;true&#039; } ),&lt;br /&gt;
			serverText,&lt;br /&gt;
			serverCount&lt;br /&gt;
		] );&lt;br /&gt;
&lt;br /&gt;
		var panel = el( &#039;div&#039;, { &#039;class&#039;: &#039;pg-vana&#039; }, [&lt;br /&gt;
			el( &#039;div&#039;, { &#039;class&#039;: &#039;pg-vana-line&#039; }, [&lt;br /&gt;
				clock, el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-tilde&#039;, text: &#039;~&#039; } ), date&lt;br /&gt;
			] ),&lt;br /&gt;
			el( &#039;div&#039;, { &#039;class&#039;: &#039;pg-vana-line&#039; }, [&lt;br /&gt;
				day,&lt;br /&gt;
				el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-tilde&#039;, text: &#039;~&#039; } ),&lt;br /&gt;
				el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-vana-moon&#039; }, [ moonIcon, moonPct ] )&lt;br /&gt;
			] ),&lt;br /&gt;
			server&lt;br /&gt;
		] );&lt;br /&gt;
&lt;br /&gt;
		var list = el( &#039;ul&#039;, {}, [&lt;br /&gt;
			el( &#039;li&#039;, { id: &#039;n-vanatime&#039;, &#039;class&#039;: &#039;mw-list-item&#039; }, [ panel ] )&lt;br /&gt;
		] );&lt;br /&gt;
&lt;br /&gt;
		// A Vana&#039;diel minute is 2.4 Earth seconds, so a one-second tick looks smooth without&lt;br /&gt;
		// doing redundant work.&lt;br /&gt;
		function tick() {&lt;br /&gt;
			var v = vanadiel();&lt;br /&gt;
			clock.textContent = pad2( v.hour ) + &#039;:&#039; + pad2( v.minute );&lt;br /&gt;
			date.textContent = v.year + &#039;-&#039; + v.month + &#039;-&#039; + v.day;&lt;br /&gt;
			day.textContent = v.weekday;&lt;br /&gt;
			day.className = &#039;pg-vana-day pg-vana-day-&#039; + v.weekdayIndex;&lt;br /&gt;
			moonIcon.textContent = v.moonIcon;&lt;br /&gt;
			moonIcon.setAttribute( &#039;title&#039;, v.moonPhase );&lt;br /&gt;
			moonPct.textContent = v.moonPercent + &#039;%&#039;;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// Same origin as the wiki, so no CORS involved and no proxy needed.&lt;br /&gt;
		function poll() {&lt;br /&gt;
			fetch( &#039;/api/status&#039;, { headers: { Accept: &#039;application/json&#039; } } )&lt;br /&gt;
				.then( function ( res ) {&lt;br /&gt;
					if ( !res.ok ) {&lt;br /&gt;
						throw new Error( &#039;HTTP &#039; + res.status );&lt;br /&gt;
					}&lt;br /&gt;
					return res.json();&lt;br /&gt;
				} )&lt;br /&gt;
				.then( function ( data ) {&lt;br /&gt;
					var online = String( data &amp;amp;&amp;amp; data.status || &#039;&#039; ).toLowerCase() === &#039;online&#039;;&lt;br /&gt;
					var players = Number( data &amp;amp;&amp;amp; data.playerCount );&lt;br /&gt;
					server.setAttribute( &#039;data-state&#039;, online ? &#039;online&#039; : &#039;offline&#039; );&lt;br /&gt;
					serverText.textContent = online ? &#039;Online&#039; : &#039;Offline&#039;;&lt;br /&gt;
					// A count of 0 is indistinguishable from &amp;quot;not published&amp;quot;, and &amp;quot;Online 0&amp;quot; next&lt;br /&gt;
					// to a green dot reads as a bug, so only show a positive count.&lt;br /&gt;
					serverCount.textContent = online &amp;amp;&amp;amp; players &amp;gt; 0 ? String( players ) : &#039;&#039;;&lt;br /&gt;
				} )&lt;br /&gt;
				.catch( function () {&lt;br /&gt;
					// Keep whatever is on screen; the next poll may succeed.&lt;br /&gt;
					if ( server.getAttribute( &#039;data-state&#039; ) === &#039;unknown&#039; ) {&lt;br /&gt;
						serverText.textContent = &#039;Status unavailable&#039;;&lt;br /&gt;
					}&lt;br /&gt;
				} );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		tick();&lt;br /&gt;
		poll();&lt;br /&gt;
		setInterval( tick, 1000 );&lt;br /&gt;
		setInterval( poll, 60000 );&lt;br /&gt;
&lt;br /&gt;
		return portlet( &#039;vanatime&#039;, &amp;quot;Vana&#039;diel Time&amp;quot;, list );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- theme toggle&lt;br /&gt;
	 *&lt;br /&gt;
	 * Dark is the default and needs no class, so light mode is the opt-in state: MediaWiki:Timeless.css&lt;br /&gt;
	 * scopes every dark rule to html:not(.pg-day). That ordering means a reader on the default theme&lt;br /&gt;
	 * never sees a flash of light before this script runs.&lt;br /&gt;
	 */&lt;br /&gt;
	var THEME_KEY = &#039;pg-theme&#039;;&lt;br /&gt;
&lt;br /&gt;
	function applyTheme( theme ) {&lt;br /&gt;
		document.documentElement.classList.toggle( &#039;pg-day&#039;, theme === &#039;day&#039; );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function buildThemeToggle() {&lt;br /&gt;
		var link = el( &#039;a&#039;, { &#039;class&#039;: &#039;ext-darkmode-link&#039;, href: &#039;#&#039;, role: &#039;button&#039; } );&lt;br /&gt;
		var label = el( &#039;span&#039;, {} );&lt;br /&gt;
		link.appendChild( label );&lt;br /&gt;
&lt;br /&gt;
		function refresh() {&lt;br /&gt;
			var isDay = document.documentElement.classList.contains( &#039;pg-day&#039; );&lt;br /&gt;
			label.textContent = isDay ? &#039;Dark mode&#039; : &#039;Light mode&#039;;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		link.addEventListener( &#039;click&#039;, function ( e ) {&lt;br /&gt;
			e.preventDefault();&lt;br /&gt;
			var next = document.documentElement.classList.contains( &#039;pg-day&#039; ) ? &#039;night&#039; : &#039;day&#039;;&lt;br /&gt;
			applyTheme( next );&lt;br /&gt;
			try {&lt;br /&gt;
				localStorage.setItem( THEME_KEY, next );&lt;br /&gt;
			} catch ( err ) {&lt;br /&gt;
				// Private browsing or a full quota; the choice just will not persist.&lt;br /&gt;
			}&lt;br /&gt;
			refresh();&lt;br /&gt;
		} );&lt;br /&gt;
&lt;br /&gt;
		refresh();&lt;br /&gt;
&lt;br /&gt;
		return portlet( &#039;theme&#039;, &#039;Appearance&#039;, el( &#039;ul&#039;, {}, [&lt;br /&gt;
			el( &#039;li&#039;, { id: &#039;t-darkmode&#039;, &#039;class&#039;: &#039;mw-list-item&#039; }, [ link ] )&lt;br /&gt;
		] ) );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- Discord panel&lt;br /&gt;
	 *&lt;br /&gt;
	 * Discord&#039;s own embed (discord.com/widget) is a cross-origin iframe locked to Discord&#039;s blurple&lt;br /&gt;
	 * palette, so it cannot be themed to match this wiki. The member list is therefore fetched and&lt;br /&gt;
	 * rendered here instead: widget.json sends Access-Control-Allow-Origin for this host, so no proxy&lt;br /&gt;
	 * is involved and the markup is ours to style.&lt;br /&gt;
	 *&lt;br /&gt;
	 * That endpoint answers 403 whenever &amp;quot;Enable Server Widget&amp;quot; is switched off in Discord&#039;s server&lt;br /&gt;
	 * settings, which is why the static join panel is still kept as a fallback rather than deleted.&lt;br /&gt;
	 */&lt;br /&gt;
	var DISCORD_INVITE = &#039;https://discord.gg/M3NFvpm75E&#039;;&lt;br /&gt;
	var DISCORD_GUILD = &#039;1473308284349059126&#039;;&lt;br /&gt;
	var DISCORD_WIDGET = &#039;https://discord.com/api/guilds/&#039; + DISCORD_GUILD + &#039;/widget.json&#039;;&lt;br /&gt;
&lt;br /&gt;
	// Online first, then idle, then do-not-disturb, alphabetically within each.&lt;br /&gt;
	var STATUS_RANK = { online: 0, idle: 1, dnd: 2, offline: 3 };&lt;br /&gt;
&lt;br /&gt;
	function statusRank( status ) {&lt;br /&gt;
		return STATUS_RANK[ status ] === undefined ? STATUS_RANK.offline : STATUS_RANK[ status ];&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function discordMember( member ) {&lt;br /&gt;
		var status = STATUS_RANK[ member.status ] === undefined ? &#039;offline&#039; : member.status;&lt;br /&gt;
		var children = [];&lt;br /&gt;
&lt;br /&gt;
		if ( member.avatar_url ) {&lt;br /&gt;
			children.push( el( &#039;img&#039;, { &#039;class&#039;: &#039;pg-discord-avatar&#039;, src: member.avatar_url,&lt;br /&gt;
				alt: &#039;&#039;, width: &#039;20&#039;, height: &#039;20&#039;, loading: &#039;lazy&#039; } ) );&lt;br /&gt;
		} else {&lt;br /&gt;
			children.push( el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-discord-avatar pg-discord-avatar-blank&#039;,&lt;br /&gt;
				&#039;aria-hidden&#039;: &#039;true&#039; } ) );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		children.push( el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-discord-member-name&#039;, text: member.username } ) );&lt;br /&gt;
&lt;br /&gt;
		var li = el( &#039;li&#039;, { &#039;class&#039;: &#039;pg-discord-member&#039;, &#039;data-status&#039;: status }, children );&lt;br /&gt;
		// The status is conveyed by a colour swatch, so name it for anyone not seeing colour.&lt;br /&gt;
		li.setAttribute( &#039;title&#039;, member.game &amp;amp;&amp;amp; member.game.name ?&lt;br /&gt;
			member.username + &#039; - &#039; + status + &#039; - playing &#039; + member.game.name :&lt;br /&gt;
			member.username + &#039; - &#039; + status );&lt;br /&gt;
		return li;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function buildDiscordPanel() {&lt;br /&gt;
		var count = el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-discord-count&#039;, text: &#039;Loading&#039; } );&lt;br /&gt;
		var list = el( &#039;ul&#039;, { &#039;class&#039;: &#039;pg-discord-members&#039; } );&lt;br /&gt;
&lt;br /&gt;
		var panel = el( &#039;div&#039;, { &#039;class&#039;: &#039;pg-discord&#039; }, [&lt;br /&gt;
			el( &#039;div&#039;, { &#039;class&#039;: &#039;pg-discord-head&#039; }, [&lt;br /&gt;
				el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-discord-title&#039;, text: &#039;PegasusXI&#039; } ),&lt;br /&gt;
				count&lt;br /&gt;
			] ),&lt;br /&gt;
			list,&lt;br /&gt;
			el( &#039;a&#039;, { &#039;class&#039;: &#039;pg-discord-join&#039;, href: DISCORD_INVITE,&lt;br /&gt;
				target: &#039;_blank&#039;, rel: &#039;noopener&#039;, text: &#039;Join Discord&#039; } )&lt;br /&gt;
		] );&lt;br /&gt;
&lt;br /&gt;
		fetch( DISCORD_WIDGET, { headers: { Accept: &#039;application/json&#039; } } )&lt;br /&gt;
			.then( function ( res ) {&lt;br /&gt;
				if ( !res.ok ) {&lt;br /&gt;
					throw new Error( &#039;HTTP &#039; + res.status );&lt;br /&gt;
				}&lt;br /&gt;
				return res.json();&lt;br /&gt;
			} )&lt;br /&gt;
			.then( function ( data ) {&lt;br /&gt;
				var members = ( ( data &amp;amp;&amp;amp; data.members ) || [] ).slice();&lt;br /&gt;
				members.sort( function ( a, b ) {&lt;br /&gt;
					return statusRank( a.status ) - statusRank( b.status ) ||&lt;br /&gt;
						String( a.username ).localeCompare( String( b.username ) );&lt;br /&gt;
				} );&lt;br /&gt;
&lt;br /&gt;
				// presence_count counts everyone online; the members array is capped by Discord.&lt;br /&gt;
				var online = Number( data &amp;amp;&amp;amp; data.presence_count );&lt;br /&gt;
				if ( !isFinite( online ) ) {&lt;br /&gt;
					online = members.length;&lt;br /&gt;
				}&lt;br /&gt;
				count.textContent = online + ( online === 1 ? &#039; member online&#039; : &#039; members online&#039; );&lt;br /&gt;
&lt;br /&gt;
				members.forEach( function ( member ) {&lt;br /&gt;
					list.appendChild( discordMember( member ) );&lt;br /&gt;
				} );&lt;br /&gt;
&lt;br /&gt;
				panel.setAttribute( &#039;data-state&#039;, &#039;ready&#039; );&lt;br /&gt;
			} )&lt;br /&gt;
			.catch( function () {&lt;br /&gt;
				// Most likely the server widget was switched off again. Fall back to the plain join&lt;br /&gt;
				// panel rather than leaving &amp;quot;Loading&amp;quot; on screen for good.&lt;br /&gt;
				panel.setAttribute( &#039;data-state&#039;, &#039;unavailable&#039; );&lt;br /&gt;
				count.textContent = &#039;Chat, support and server announcements&#039;;&lt;br /&gt;
			} );&lt;br /&gt;
&lt;br /&gt;
		return el( &#039;div&#039;, { id: &#039;discord-panel&#039; }, [ panel ] );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- header button&lt;br /&gt;
	 *&lt;br /&gt;
	 * #mw-header is a flex row and Timeless assigns explicit orders: #p-logo-text 1, #p-search 2,&lt;br /&gt;
	 * #user-tools 3. A new child with no order defaults to 0 and jumps ahead of all of them, which is&lt;br /&gt;
	 * why this button first appeared at the far left of the header. MediaWiki:Timeless.css gives it&lt;br /&gt;
	 * order 4 so it sits at the right-hand end.&lt;br /&gt;
	 *&lt;br /&gt;
	 * This starts a sign-in rather than handing out an invite: the Discord invite is already on the&lt;br /&gt;
	 * sidebar panel, and a reader who wants an account is better served by the thing that makes one.&lt;br /&gt;
	 * It points at Special:UserLogin rather than straight at Discord because PluggableAuth&#039;s button is&lt;br /&gt;
	 * a form POST carrying a login token, so a plain link cannot trigger it -- Special:PluggableAuthLogin&lt;br /&gt;
	 * on its own has no plugin selected in the session and errors out.&lt;br /&gt;
	 */&lt;br /&gt;
&lt;br /&gt;
	function buildHeaderButton() {&lt;br /&gt;
		// Logged-in readers have the personal menu instead; a sign-in button would be noise.&lt;br /&gt;
		if ( mw.config.get( &#039;wgUserName&#039; ) ) {&lt;br /&gt;
			return null;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		var articlePath = mw.config.get( &#039;wgArticlePath&#039; ) || &#039;/wiki/$1&#039;;&lt;br /&gt;
		var href = articlePath.replace( &#039;$1&#039;, &#039;Special:UserLogin&#039; ) +&lt;br /&gt;
			&#039;?returnto=&#039; + encodeURIComponent( mw.config.get( &#039;wgPageName&#039; ) || &#039;&#039; );&lt;br /&gt;
&lt;br /&gt;
		return el( &#039;div&#039;, { id: &#039;p-discord-signin&#039; }, [&lt;br /&gt;
			el( &#039;a&#039;, { &#039;class&#039;: &#039;pg-discord-signin&#039;, href: href }, [&lt;br /&gt;
				el( &#039;span&#039;, { &#039;class&#039;: &#039;pg-discord-signin-mark&#039;, &#039;aria-hidden&#039;: &#039;true&#039; } ),&lt;br /&gt;
				el( &#039;span&#039;, { text: &#039;Sign in with Discord&#039; } )&lt;br /&gt;
			] )&lt;br /&gt;
		] );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	/* ---------------------------------------------------------------- wire it up */&lt;br /&gt;
&lt;br /&gt;
	function init() {&lt;br /&gt;
		// Restore the saved theme first so the toggle&#039;s label starts out correct.&lt;br /&gt;
		try {&lt;br /&gt;
			if ( localStorage.getItem( THEME_KEY ) === &#039;day&#039; ) {&lt;br /&gt;
				applyTheme( &#039;day&#039; );&lt;br /&gt;
			}&lt;br /&gt;
		} catch ( err ) {}&lt;br /&gt;
&lt;br /&gt;
		var siteNav = document.getElementById( &#039;mw-site-navigation&#039; );&lt;br /&gt;
		var navChunk = document.getElementById( &#039;site-navigation&#039; );&lt;br /&gt;
&lt;br /&gt;
		if ( navChunk ) {&lt;br /&gt;
			var inner = navChunk.querySelector( &#039;.sidebar-inner&#039; );&lt;br /&gt;
			if ( inner ) {&lt;br /&gt;
				inner.insertBefore( buildClockPanel(), inner.firstChild );&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if ( siteNav &amp;amp;&amp;amp; !document.getElementById( &#039;theme-tools&#039; ) ) {&lt;br /&gt;
			siteNav.appendChild( chunk( &#039;theme-tools&#039;, &#039;Appearance&#039;, buildThemeToggle() ) );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		var related = document.getElementById( &#039;mw-related-navigation&#039; );&lt;br /&gt;
		if ( related &amp;amp;&amp;amp; !document.getElementById( &#039;discord-panel&#039; ) ) {&lt;br /&gt;
			related.insertBefore( buildDiscordPanel(), related.firstChild );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		var userTools = document.getElementById( &#039;user-tools&#039; );&lt;br /&gt;
		if ( userTools &amp;amp;&amp;amp; userTools.parentNode &amp;amp;&amp;amp; !document.getElementById( &#039;p-discord-signin&#039; ) ) {&lt;br /&gt;
			var button = buildHeaderButton();&lt;br /&gt;
			if ( button ) {&lt;br /&gt;
				userTools.parentNode.insertBefore( button, userTools );&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if ( document.readyState === &#039;loading&#039; ) {&lt;br /&gt;
		document.addEventListener( &#039;DOMContentLoaded&#039;, init );&lt;br /&gt;
	} else {&lt;br /&gt;
		init();&lt;br /&gt;
	}&lt;br /&gt;
}() );&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Cab._Gauntlets_%2B4_description.png&amp;diff=142074</id>
		<title>File:Cab. Gauntlets +4 description.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Cab._Gauntlets_%2B4_description.png&amp;diff=142074"/>
		<updated>2026-08-30T00:04:48Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Power_Sandals_icon.png&amp;diff=142073</id>
		<title>File:Power Sandals icon.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Power_Sandals_icon.png&amp;diff=142073"/>
		<updated>2026-08-30T00:04:48Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Image07-09-2009_1.jpg&amp;diff=142072</id>
		<title>File:Image07-09-2009 1.jpg</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Image07-09-2009_1.jpg&amp;diff=142072"/>
		<updated>2026-08-30T00:04:48Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:27554_description.png&amp;diff=142071</id>
		<title>File:27554 description.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:27554_description.png&amp;diff=142071"/>
		<updated>2026-08-30T00:04:48Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Sea_Spray_Risotto_description.png&amp;diff=142070</id>
		<title>File:Sea Spray Risotto description.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Sea_Spray_Risotto_description.png&amp;diff=142070"/>
		<updated>2026-08-30T00:04:48Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Arke_Cosciales_description.png&amp;diff=142069</id>
		<title>File:Arke Cosciales description.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Arke_Cosciales_description.png&amp;diff=142069"/>
		<updated>2026-08-30T00:04:48Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Scorpion_Claw_icon.png&amp;diff=142068</id>
		<title>File:Scorpion Claw icon.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Scorpion_Claw_icon.png&amp;diff=142068"/>
		<updated>2026-08-30T00:04:48Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Iron_Giant_Head.jpg&amp;diff=142067</id>
		<title>File:Iron Giant Head.jpg</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Iron_Giant_Head.jpg&amp;diff=142067"/>
		<updated>2026-08-30T00:04:48Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Spharai_(Level_75)_description.png&amp;diff=142066</id>
		<title>File:Spharai (Level 75) description.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Spharai_(Level_75)_description.png&amp;diff=142066"/>
		<updated>2026-08-30T00:04:47Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Baking_Soda_description.png&amp;diff=142065</id>
		<title>File:Baking Soda description.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Baking_Soda_description.png&amp;diff=142065"/>
		<updated>2026-08-30T00:04:47Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Dia_(Scroll)_description.png&amp;diff=142064</id>
		<title>File:Dia (Scroll) description.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Dia_(Scroll)_description.png&amp;diff=142064"/>
		<updated>2026-08-30T00:04:47Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:61501_icon.png&amp;diff=142063</id>
		<title>File:61501 icon.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:61501_icon.png&amp;diff=142063"/>
		<updated>2026-08-30T00:04:47Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:SoftBank_Dorimaga_Vol349_ABM.jpg&amp;diff=142062</id>
		<title>File:SoftBank Dorimaga Vol349 ABM.jpg</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:SoftBank_Dorimaga_Vol349_ABM.jpg&amp;diff=142062"/>
		<updated>2026-08-30T00:04:47Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Odd_Postcard_description.png&amp;diff=142061</id>
		<title>File:Odd Postcard description.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Odd_Postcard_description.png&amp;diff=142061"/>
		<updated>2026-08-30T00:04:47Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Niobium_Ore_description.png&amp;diff=142060</id>
		<title>File:Niobium Ore description.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Niobium_Ore_description.png&amp;diff=142060"/>
		<updated>2026-08-30T00:04:47Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Hepatizon_Ingot_icon.png&amp;diff=142059</id>
		<title>File:Hepatizon Ingot icon.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Hepatizon_Ingot_icon.png&amp;diff=142059"/>
		<updated>2026-08-30T00:04:47Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Fotia_Gorget_icon.png&amp;diff=142058</id>
		<title>File:Fotia Gorget icon.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Fotia_Gorget_icon.png&amp;diff=142058"/>
		<updated>2026-08-30T00:04:46Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Cab._Leggings_%2B3_description.png&amp;diff=142057</id>
		<title>File:Cab. Leggings +3 description.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Cab._Leggings_%2B3_description.png&amp;diff=142057"/>
		<updated>2026-08-30T00:04:46Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Reikikon_icon.png&amp;diff=142056</id>
		<title>File:Reikikon icon.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Reikikon_icon.png&amp;diff=142056"/>
		<updated>2026-08-30T00:04:46Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Bastokan_flag_appearance.jpg&amp;diff=142055</id>
		<title>File:Bastokan flag appearance.jpg</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Bastokan_flag_appearance.jpg&amp;diff=142055"/>
		<updated>2026-08-30T00:04:46Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Irn.Msk.Gmbsn._%2B2_icon.png&amp;diff=142054</id>
		<title>File:Irn.Msk.Gmbsn. +2 icon.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Irn.Msk.Gmbsn._%2B2_icon.png&amp;diff=142054"/>
		<updated>2026-08-30T00:04:46Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Cook%27s_Pride.jpg&amp;diff=142053</id>
		<title>File:Cook&#039;s Pride.jpg</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Cook%27s_Pride.jpg&amp;diff=142053"/>
		<updated>2026-08-30T00:04:46Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Choco_races_5.jpg&amp;diff=142052</id>
		<title>File:Choco races 5.jpg</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Choco_races_5.jpg&amp;diff=142052"/>
		<updated>2026-08-30T00:04:46Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Shard_of_Cowardice.jpg&amp;diff=142051</id>
		<title>File:Shard of Cowardice.jpg</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Shard_of_Cowardice.jpg&amp;diff=142051"/>
		<updated>2026-08-30T00:04:46Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Regal_Gloves_description.png&amp;diff=142050</id>
		<title>File:Regal Gloves description.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Regal_Gloves_description.png&amp;diff=142050"/>
		<updated>2026-08-30T00:04:46Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Forban_Cape_%2B1_icon.png&amp;diff=142049</id>
		<title>File:Forban Cape +1 icon.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Forban_Cape_%2B1_icon.png&amp;diff=142049"/>
		<updated>2026-08-30T00:04:45Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Lexeme_Blade_III_description.png&amp;diff=142048</id>
		<title>File:Lexeme Blade III description.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Lexeme_Blade_III_description.png&amp;diff=142048"/>
		<updated>2026-08-30T00:04:45Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Update03-26-2012_17.jpg&amp;diff=142047</id>
		<title>File:Update03-26-2012 17.jpg</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Update03-26-2012_17.jpg&amp;diff=142047"/>
		<updated>2026-08-30T00:04:45Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Ore_golem.jpg&amp;diff=142046</id>
		<title>File:Ore golem.jpg</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Ore_golem.jpg&amp;diff=142046"/>
		<updated>2026-08-30T00:04:45Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Frost_(Scroll)_description.png&amp;diff=142045</id>
		<title>File:Frost (Scroll) description.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Frost_(Scroll)_description.png&amp;diff=142045"/>
		<updated>2026-08-30T00:04:43Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:MithraA8.jpg&amp;diff=142044</id>
		<title>File:MithraA8.jpg</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:MithraA8.jpg&amp;diff=142044"/>
		<updated>2026-08-30T00:04:43Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Green_9D_Almirah_description.png&amp;diff=142043</id>
		<title>File:Green 9D Almirah description.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Green_9D_Almirah_description.png&amp;diff=142043"/>
		<updated>2026-08-30T00:04:43Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Ruthenium_Ingot_icon.png&amp;diff=142042</id>
		<title>File:Ruthenium Ingot icon.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Ruthenium_Ingot_icon.png&amp;diff=142042"/>
		<updated>2026-08-30T00:04:43Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:1st_Legion_Aidman.jpg&amp;diff=142041</id>
		<title>File:1st Legion Aidman.jpg</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:1st_Legion_Aidman.jpg&amp;diff=142041"/>
		<updated>2026-08-30T00:04:43Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Ix%27aern_(DRG)_-_The_Garden_of_Ru%27Hmet.jpg&amp;diff=142040</id>
		<title>File:Ix&#039;aern (DRG) - The Garden of Ru&#039;Hmet.jpg</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Ix%27aern_(DRG)_-_The_Garden_of_Ru%27Hmet.jpg&amp;diff=142040"/>
		<updated>2026-08-30T00:04:42Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Ghost_2.png&amp;diff=142039</id>
		<title>File:Ghost 2.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Ghost_2.png&amp;diff=142039"/>
		<updated>2026-08-30T00:04:42Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:26706_icon.png&amp;diff=142038</id>
		<title>File:26706 icon.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:26706_icon.png&amp;diff=142038"/>
		<updated>2026-08-30T00:04:42Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:CatsEyeXI_Tempered_Gorget_icon.png&amp;diff=142037</id>
		<title>File:CatsEyeXI Tempered Gorget icon.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:CatsEyeXI_Tempered_Gorget_icon.png&amp;diff=142037"/>
		<updated>2026-08-30T00:04:42Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Scintillant_strand.jpg&amp;diff=142036</id>
		<title>File:Scintillant strand.jpg</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Scintillant_strand.jpg&amp;diff=142036"/>
		<updated>2026-08-30T00:04:42Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Bale_Flanchard_%2B2_description.png&amp;diff=142035</id>
		<title>File:Bale Flanchard +2 description.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Bale_Flanchard_%2B2_description.png&amp;diff=142035"/>
		<updated>2026-08-30T00:04:41Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Lucerewe_Meat_icon.png&amp;diff=142034</id>
		<title>File:Lucerewe Meat icon.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Lucerewe_Meat_icon.png&amp;diff=142034"/>
		<updated>2026-08-30T00:04:41Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Timepiece_description.png&amp;diff=142033</id>
		<title>File:Timepiece description.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Timepiece_description.png&amp;diff=142033"/>
		<updated>2026-08-30T00:04:41Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Fuel_reservoir.jpg&amp;diff=142032</id>
		<title>File:Fuel reservoir.jpg</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Fuel_reservoir.jpg&amp;diff=142032"/>
		<updated>2026-08-30T00:04:41Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Msk._Sword_%2B1_description.png&amp;diff=142031</id>
		<title>File:Msk. Sword +1 description.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Msk._Sword_%2B1_description.png&amp;diff=142031"/>
		<updated>2026-08-30T00:04:41Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Snowsteel_icon.png&amp;diff=142030</id>
		<title>File:Snowsteel icon.png</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Snowsteel_icon.png&amp;diff=142030"/>
		<updated>2026-08-30T00:04:41Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
	<entry>
		<id>https://pegasusxi.com/w/index.php?title=File:Ullr.jpeg&amp;diff=142029</id>
		<title>File:Ullr.jpeg</title>
		<link rel="alternate" type="text/html" href="https://pegasusxi.com/w/index.php?title=File:Ullr.jpeg&amp;diff=142029"/>
		<updated>2026-08-30T00:04:41Z</updated>

		<summary type="html">&lt;p&gt;PegasusAdmin: == Summary ==
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Imported from the BG-Wiki archive (CC BY-NC-SA 3.0)&lt;/div&gt;</summary>
		<author><name>PegasusAdmin</name></author>
	</entry>
</feed>