diff options
56 files changed, 959 insertions, 1482 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 18bcb54..9c5d2e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 11.0 + +* Elaborate pages +* Rework banner (for main page) +* Rework navigation (remove side menu) +* Use PHP for page initialisation (disable SSI) +* Refactor stylesheets +* Update keywords +* Restructure pages +* Update readme +* Update touch styling +* Add anchor link buttons +* Update Linktree link +* Update footer styling + ## 10.2 * Remove *X* link @@ -5,53 +5,3 @@ This repository contains the source code for [Achernar's webservice](https://ach ## Testing Use the installation script at `install.sh` to install the server. - -If using nginx, add the following server to your configuration (assuming installation at `/srv/achernar`): - -``` -server { - listen 8080; - server_name localhost; - - location = / { - return 307 /html/achernar.html; - } - - location = /apple-touch-icon.png { - alias /srv/achernar/apple-touch-icon.png; - } - - location = /favicon.ico { - alias /srv/achernar/favicon.ico; - } - - location /css { - alias /srv/achernar/css; - } - - location /font { - alias /srv/achernar/font; - } - - location /html { - alias /srv/achernar/html; - ssi on; - } - - location /image { - alias /srv/achernar/image; - } - - location /include { - alias /srv/achernar/include; - } - - location /js { - alias /srv/achernar/js; - } - - location /svg { - alias /srv/achernar/svg; - } -} -``` diff --git a/css/fonts.scss b/css/font.scss index 603c9ec..603c9ec 100644 --- a/css/fonts.scss +++ b/css/font.scss diff --git a/css/footer.scss b/css/footer.scss index 159fa10..4095b0f 100644 --- a/css/footer.scss +++ b/css/footer.scss @@ -1,26 +1,38 @@ +@use "sass:list"; + #footer { - background-color: var(--foregroundColour); - border-radius: 1rem; - font-family: $monospaceFont; - margin-top: 1rem; - padding: 1rem; - text-align: center; + background-color: var(--foregroundColour); + border-top-left-radius: $padding; + border-top-right-radius: $padding; + font-family: $monospaceFont; + margin: auto; + max-width: calc($contentWidth + $padding * 2); + padding: $padding; + text-align: center; + width: 100%; - h1 { - font-size: 2em; - font-weight: bold; - text-align: center; + #romanisation { + $width: 31; + $height: 11; - &::after { - background-color: var(--textColour); - border-radius: $separatorWidth; - content: ""; - display: block; - height: $separatorWidth; - margin-bottom: 1rem; - margin-top: $separatorWidth; - width: 100%; - } + $actualHeight: 4em; + + aspect-ratio: list.slash($width, $height); + background-color: var(--textColour); + image-rendering: pixelated; + margin: auto; + mask-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB8AAAALCAQAAAAt4J4/AAAAS0lEQVQoz9WRMQ4AIAgDW+P/v1xHIKIRNhlpuQYgmqWG4nVBqVObL+fGvtHu85YpYDgcnzamB87A3fNCMEFlQXG4cO55vEz1H//VAg5FJOyjOJa9AAAAAElFTkSuQmCC"); + mask-size: cover; + max-height: $actualHeight; + max-width: calc($actualHeight * ($width / $height)); + } + + #systemDescription { + font-style: italic; + margin: $padding auto; + max-width: calc($contentWidth / 2); + text-align: justify; + text-align-last: center; } #cvrNumber { @@ -30,17 +42,20 @@ } #emailAddress { - aspect-ratio: 183/8; + $width: 183; + $height: 8; + + $actualHeight: 1em; + + aspect-ratio: list.slash($width, $height); background-color: var(--textColour); display: block; image-rendering: pixelated; margin: auto; - mask-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALcAAAAICAYAAAC78m4/AAAA80lEQVRYw+1YQQ7EIAiETf//ZfeyJqRRhAFN18rNEhGGAbFMRFRKKXTkyEbCzMyV2MzMUikJ39Pdv0d0VW/RIcXoicF6hgezXgxy7cWlZUNbe3LkiTvCF8uZo33d86yAZ+ruTo++WXQWcLy+oDY9MWTua9mJ5igaewbWHr7I9YV2rV5lZY04kS79L9fmjNg1u2+Ty0pUDTRZNXLUiRB/BqlnJB61iY4GKJ6WYtqtmXyswJafIEkczVyrBI1htU0UT80Pzc8n5Si9c9dZKaMrIA8SbZbyPFieIi08kXkevfFWEbSV9yiXvHzRztt6rj3y7l+BX5T3s/pA2wdgAAAAAElFTkSuQmCC"); - mask-position: center; - mask-repeat: no-repeat; + mask-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALcAAAAICAQAAAAR+6a0AAAA0klEQVRIx+1W0Q4CMQhrzf3/L9eHM25zwJAzi4nyomSw0kLYEYDwtz1GQgD4cE/he4/P0MgDZHh+G+neEkWO+Ozy5dbSIsZfm5GNHesyZ76edXk0C895cP7N3lyOglwv0kbInbWYFaMV+rpqWxcC0BHOVt+tzMrh5tXEN9D5DdvkMEUdSzt7w+HMpqWiUNXIaB1EdbI4TpftZharAFzT7qqY0vRUFELGq2Tf+RlG6ekWmJgF/ymh+Zh4VK+0iOGdMZ5KgjbuK5U8XYa83dv2xz8E70XwbQlyNNhzAAAAAElFTkSuQmCC"); mask-size: cover; - max-height: 1em; - max-width: calc(183em / 8); + max-height: $actualHeight; + max-width: calc($actualHeight * $width / $height); width: 100%; } @@ -49,7 +64,6 @@ font-weight: bold; gap: 1rem; margin: auto; - margin-top: 1rem; width: fit-content; a { diff --git a/css/glyph.scss b/css/glyph.scss deleted file mode 100644 index d547bc2..0000000 --- a/css/glyph.scss +++ /dev/null @@ -1,78 +0,0 @@ -#glyph { - aspect-ratio: 1; - background-position: center; - background-repeat: no-repeat; - background-size: contain; - left: 50vw; - position: absolute; - top: 50vh; - transform: translate(-50%, -50%); - width: 50vmin; - - body[data-page = "achernar"] & { - $pixelWIdth: calc(100vmax / 256); - - --glyphWidth: 154; - --glyphHeight: 42; - - background-image: url("/image/achernarPixelated.webp"); - height: calc($pixelWIdth * var(--glyphHeight)); - width: calc($pixelWIdth * var(--glyphWidth)); - - @media (orientation: portrait) { - --glyphWidth: 90; - --glyphHeight: 74; - - background-image: url("/image/achernarVerticalPixelated.webp"); - } - } - - body[data-page = "agbsum"] & { - background-image: url("/svg/glyph/ax.svg"); - } - - body[data-page = "ax"] & { - background-image: url("/svg/glyph/ax.svg"); - } - - body[data-page = "backspace"] & { - background-image: url("/svg/glyph/backspace.svg"); - } - - body[data-page = "benoit"] & { - background-image: url("/svg/glyph/benoit.svg"); - } - - body[data-page = "bowshock"] & { - background-image: url("/svg/glyph/bowshock.svg"); - } - - body[data-page = "bzipper"] & { - background-image: url("/svg/glyph/bzipper.svg"); - } - - body[data-page = "deltaWorld"] & { - background-image: url("/svg/glyph/deltaWorld.svg"); - filter: drop-shadow(0 0 calc(100vmin / 128) var(--textColour)); - } - - body[data-page = "dux"] & { - background-image: url("/svg/glyph/dux.svg"); - } - - body[data-page = "eas"] & { - background-image: url("/svg/glyph/eas.svg"); - } - - body[data-page = "luma"] & { - background-image: url("/svg/glyph/luma.svg"); - } - - body[data-page = "pollex"] & { - background-image: url("/svg/glyph/pollex.svg"); - } - - body[data-page = "u8c"] & { - background-image: url("/svg/glyph/u8c.svg"); - } -} diff --git a/css/header.scss b/css/header.scss index a90ff5d..49e6597 100644 --- a/css/header.scss +++ b/css/header.scss @@ -1,114 +1,31 @@ #header { - $gap: 1rem; - $hamburgerHeight: 2rem; - $sideMenuToggleDuration: 0.5s; + $gap: 1rem; + --foregroundColour: var(--backgroundColour); + + align-items: center; background-color: var(--backgroundColour); background-size: cover; - border-bottom-left-radius: var(--padding); - border-bottom-right-radius: var(--padding); - height: fit-content; + border-bottom-left-radius: $padding; + border-bottom-right-radius: $padding; + box-shadow: 0 0 calc($padding / 2) $shadowColour; + display: flex; + height: 100vh; image-rendering: pixelated; + justify-content: center; overflow: hidden; position: relative; user-select: none; width: 100%; - body[data-page = "achernar"] & { - --backgroundColour: #007B34; - --textColour: white; - - @media not (prefers-reduced-motion) { - background-image: url("/image/vertex.webp"); - } - } - - body[data-page = "agbsum"] & { - --backgroundColour: #422984; - --textColour: #FFFFFF; - } - - body[data-page = "ax"] & { - --backgroundColour: #422984; - --textColour: #FFFFFF; - } - - body[data-page = "backspace"] & { - --backgroundColour: #000000; - --textColour: #FFFFFF; - } - - body[data-page = "benoit"] & { - --backgroundColour: #BA0035; - --textColour: #FFFFFF; - - background-image: url("/svg/benoitBackground.svg"); - } - - body[data-page = "bowshock"] & { - --backgroundColour: #B61833; - --textColour: #FFEEE0; - } - - body[data-page = "bzipper"] & { - --backgroundColour: #FFFFFF; - --textColour: #B4202D; - } - - body[data-page = "deltaWorld"] & { - --backgroundColour: #000000; - --textColour: #FFFFFF; - } - - body[data-page = "dux"] & { - --backgroundColour: #131313; - --textColour: #06fbb2; - - background-image: url("/image/duxBackground.webp"); - } - - body[data-page = "eas"] & { - --backgroundColour: #01CD93; - --textColour: #00291B; - } - - body[data-page = "luma"] & { - --backgroundColour: #6051B2; - --textColour: #FFFFFF; - } - - body[data-page = "pollex"] & { - --backgroundColour: #4D4084; - --textColour: #FFFFFF; - } - - body[data-page = "u8c"] & { - --backgroundColour: #444747; - --textColour: #A9E13D; - } - - p, a { - @media not (prefers-reduced-motion) { - transition-timing-function: ease-in-out; - } - } - - a { - cursor: pointer; - font-weight: bold; - text-decoration: underline; - text-decoration-color: #00000000; - - @media not (prefers-reduced-motion) { - transition: text-decoration-color 0.125s; - } - - &:hover { - text-decoration-color: var(--textColour); - } + #glyph { + aspect-ratio: 1; + display: block; + image-rendering: pixelated; + object-fit: contain; + pointer-events: none; + width: 50vmin; } @import "navBar"; - @import "sideMenu"; - @import "glyph"; } diff --git a/css/main.scss b/css/main.scss index d408d19..7687479 100644 --- a/css/main.scss +++ b/css/main.scss @@ -1,12 +1,15 @@ -@import "fonts"; - $monospaceFont: "Fira Mono", "monospace"; $contentWidth: 72rem; +$padding: 1rem; $separatorWidth: 0.25rem; +$shadowColour: color-mix(in srgb, black, transparent 50%); + :root { font-family: "Raleway", "sans-serif"; + + scroll-behavior: smooth; } * { @@ -17,19 +20,16 @@ $separatorWidth: 0.25rem; } body { - --backgroundColour: oklch( 20% 0.029600 253.71); - --foregroundColour: oklch( 25% 0.029600 253.71); - --textColour: oklch(100% 0 0); - - --padding: 1rem; + --backgroundColour: oklch( 25% 0 0); + --foregroundColour: oklch( 20% 0 0); + --textColour: oklch(100% 0 0); background-color: var(--backgroundColour); color: var(--textColour); z-index: -1; - @media (pointer: coarse) { - --padding: 2rem; - } + // Default. + //&.dark { } &.light { --backgroundColour: oklch(96.875% 0 0); diff --git a/css/navBar.scss b/css/navBar.scss index 60f4d8d..2ef4d3a 100644 --- a/css/navBar.scss +++ b/css/navBar.scss @@ -2,36 +2,39 @@ align-items: center; display: flex; justify-content: space-between; - padding: var(--padding); + left: 50%; + max-width: calc($contentWidth + $padding * 2); + padding: $padding; position: absolute; top: 0; + transform: translateX(-50%); width: 100%; - body[data-page = "achernar"] & { - a, #home, #hamburger { - mix-blend-mode: difference; - } - } - section { display: flex; - flex: 1; + flex: 1 1 0; gap: $gap; justify-content: center; + text-align: center; &:first-of-type { justify-content: start; + text-align: left; } &:last-of-type { justify-content: end; + text-align: right; } a { + cursor: pointer; + font-weight: bold; position: relative; + text-decoration: underline; text-decoration-color: #00000000; - &[aria-current = "page"]:not(#home)::after { + &[aria-current = "page"]::after { background-color: var(--textColour); border-radius: $separatorWidth; content: ""; @@ -45,11 +48,12 @@ } @media not (prefers-reduced-motion) { - transition: opacity $sideMenuToggleDuration, text-decoration-color 0.125s; + transition: text-decoration-color 0.125s; + transition-timing-function: ease-in-out; } @media (orientation: portrait) or (pointer: coarse) { - &:not(#home, #hamburger) { + &:not(#home, #themeToggler) { display: none; } } @@ -58,22 +62,5 @@ text-decoration-color: var(--textColour); } } - - #home, #hamburger { - aspect-ratio: 1; - background-color: var(--textColour); - cursor: pointer; - height: $hamburgerHeight; - mask-size: cover; - z-index: 255; - } - - #home { - mask-image: url("/svg/icon/home.svg"); - } - - #hamburger { - mask-image: url("/svg/icon/hamburger.svg"); - } } } diff --git a/css/noScript.scss b/css/noScript.scss index c76d9e2..4cc630c 100644 --- a/css/noScript.scss +++ b/css/noScript.scss @@ -1,7 +1,3 @@ -#navBar { - display: none; -} - #themeToggler { display: none; } diff --git a/css/overview.scss b/css/overview.scss new file mode 100644 index 0000000..874ae2b --- /dev/null +++ b/css/overview.scss @@ -0,0 +1,42 @@ +#overview { + $cardsPerRow: 3; + + align-items: center; + background-color: var(--foregroundColour); + border-radius: $padding; + display: flex; + flex-wrap: wrap; + gap: $padding; + padding: $padding; + width: 100%; + + a { + --foregroundColour: var(--backgroundColour); + + align-items: center; + aspect-ratio: 1; + background-color: var(--backgroundColour); + background-size: cover; + border-radius: $padding; + display: flex; + flex-basis: calc((100% - $padding * ($cardsPerRow - 1)) / $cardsPerRow); + justify-content: center; + padding: $padding; + width: $padding; + + @media not (prefers-reduced-motion) { + transition: box-shadow 0.25s; + transition-timing-function: steps(4); + } + + &:hover { + box-shadow: inset 0 0 $padding $shadowColour; + } + + img { + display: block; + pointer-events: none; + width: 50%; + } + } +} diff --git a/css/page.scss b/css/page.scss index 6033f34..3ede81a 100644 --- a/css/page.scss +++ b/css/page.scss @@ -1,8 +1,6 @@ #page { - $padding: var(--padding); - margin: auto; - max-width: $contentWidth; + max-width: calc($contentWidth + $padding * 2); padding: $padding; a, a:visited { @@ -11,10 +9,9 @@ } h1 { - font-size: 2em; - font-weight: bold; - margin-bottom: 1rem; - width: 100%; + font-size: 2em; + font-weight: bold; + width: 100%; &:nth-of-type(even) { text-align: right; @@ -26,42 +23,58 @@ content: ""; display: block; height: $separatorWidth; - margin-bottom: 1rem; margin-top: $separatorWidth; width: 100%; - }; + } + + a.anchor { + aspect-ratio: 1; + background-color: var(--textColour); + display: inline-block; + mask-image: url("/svg/glyph/chain.svg"); + mask-size: cover; + vertical-align: middle; + width: 0.5em; + } } section { - font-family: "Montserrat"; + font-family: "Montserrat", "sans-serif"; line-height: 2em; max-width: calc($contentWidth / 2 - $padding); text-align: justify; width: 100%; - &:nth-of-type(even) { - margin-left: auto; - - ol, ul { - margin-left: auto; - text-align: left - } + &:nth-of-type(even):not(.fullWidth) { + margin-left: auto; + text-align-last: right; } &.fullWidth { - max-width: 100%; + max-width: 100%; + text-align: center; } - & + h1 { - margin-top: 1rem; + p.codeblock { + background-color: var(--textColour); + border-radius: $padding; + color: var(--backgroundColour); + font-family: $monospaceFont; + line-height: normal; + padding: $padding; + text-align: left; + text-align-last: left; + white-space: preserve; } p.note { - font-weight: bold; - text-align: center; + font-style: italic; + font-weight: bold; + text-align: left; + text-align-last: left; &::before { - content: "Note:\0020"; + content: "\2014\0020Note:\0020"; } } @@ -73,60 +86,43 @@ padding: $padding; } - em { - font-style: normal; - font-weight: bold; - } - ul { background-color: var(--foregroundColour); - border-radius: 1rem; - padding: 1rem; + border-radius: $padding; + display: block; + padding: $padding; text-align: left; + text-align-last: left; width: 100%; } - div.codeblock { - background-color: var(--textColour); - border-radius: 1rem; - color: var(--backgroundColour); - font-family: $monospaceFont; - line-height: normal; - padding: 1rem; - white-space: preserve; - } - } - - x-image { - display: block; - position:relative; - width: 100%; - - &:nth-of-type(odd) { - margin-right: auto; - } - - &:nth-of-type(even) { - margin-left: auto; - } - - img.blur { - filter: blur(1rem) saturate(200%); - position: absolute; + div.image { + position: relative; width: 100%; - } - a { - position: relative; + a { + border-radius: $padding; + display: block; + position: relative; + } img { - border-radius: 1rem; - display: block; - image-rendering: pixelated; - width: 100%; + border-radius: $padding; + display: block; + width: 100%; + + &.blur { + filter: blur($padding) saturate(200%); + pointer-events: none; + position: absolute; + } } } } - @import "footer"; + h1 + section, section + h1 { + margin-top: $padding; + } + + @import "overview"; } diff --git a/css/sideMenu.scss b/css/sideMenu.scss deleted file mode 100644 index 7fea9f2..0000000 --- a/css/sideMenu.scss +++ /dev/null @@ -1,75 +0,0 @@ -#sideMenu { - align-items: center; - background-color: var(--backgroundColour); - display: flex; - flex-direction: column; - gap: $separatorWidth; - justify-content: space-between; - left: 100vw; - min-height: calc(100vh + var(--padding)); - padding: 1rem; - padding-bottom: 2rem; - padding-top: calc($hamburgerHeight + var(--padding) * 2); - position: relative; - width: 100%; - z-index: 127; - - body[data-page = "achernar"] & { - background-color: var(--backgroundColour); - } - - @media (pointer: fine) and (not (prefers-reduced-motion)) { - transition: left $sideMenuToggleDuration; - transition-timing-function: ease-in-out; - } - - &.visible { - left: 0; - } - - section.links { - display: flex; - gap: $separatorWidth; - justify-content: end; - max-width: $contentWidth; - padding: 0 1rem; - width: 100%; - - @media (orientation: portrait) { - flex-direction: column; - } - - section { - display: flex; - flex-direction: column; - gap: $separatorWidth; - - p { - background-color: var(--textColour); - border-radius: $separatorWidth; - color: var(--backgroundColour); - font-weight: bold; - padding: $separatorWidth 1rem; - text-align: center; - } - - a { - padding: 0 calc($separatorWidth * 2); - position: relative; - - &[aria-current = "page"]::after { - background-color: var(--textColour); - border-radius: $separatorWidth; - content: ""; - display: block; - height: 100%; - position: absolute; - right: 0; - top: 50%; - transform: translateY(-50%); - width: $separatorWidth; - } - } - } - } -} diff --git a/html/achernar.html b/html/achernar.html deleted file mode 100644 index 11d06ba..0000000 --- a/html/achernar.html +++ /dev/null @@ -1,120 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8"> - <meta name="author" content="Gabriel Bjørnager Jensen"> - <meta name="darkreader-lock"> - <meta name="description" content="Achernar is a Danish indie studio developing video games and open-source software."> - <meta name="keywords" content="achernar, games, software, video games"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - - <link href="/favicon.ico" rel="icon" type="image/vnd.microsoft.icon"> - <link href="/apple-touch-icon.png" rel="apple-touch-icon" type="image/png"> - <title>Achernar</title> - - <link href="/css/main.css" rel="stylesheet"> - <noscript> - <link href="/css/noScript.css" rel="stylesheet"> - </noscript> - - <script src="/js/main.js" type="text/javascript"></script> - </head> - - <body data-page="achernar"> - <!--#include virtual="/include/header.shtml"--> - - <div id="page"> - <h1 id="anchor.about">the future is now</h1> - - <section> - <p><em>Achernar</em> is a European indie development studio based in the Capital Region of Denmark. 🇩🇰</p> - <br> - <p>We aim to develop high-quality video games and software alike, and we believe that all technologies should be for humanity as a whole. We therefore publish all relevant source code for our products.</p> - <br> - <p class="note">See footer for contact information.</p> - </section> - - <h1 id="anchor.vision">vision</h1> - - <section> - <p>The goal of <em>Achernar</em> is to promote modern and robust software for everyone. In other words, our vision is to develop a human and clean industry and community for technology.</p> - <br> - <p>We believe in a society free of patents, and as such we release our scientific software in open-source form. For our games, we try to keep the base engine as open as possible whilst still keeping in mind that they yield our main income.</p> - </section> - - <h1 id="anchor.roadmap">roadmap</h1> - - <section class="fullWidth"> - <p>Currently, our roadmap is as follows:</p> - <br> - <ul> - <li> - <p>Complete the webservice before <em>autumn, 2024</em></p> - </li> - <li> - <p>Release our first game by <em>2024</em>, as well as company merchandise in some form</p> - </li> - <li> - <p>Release early-access for Bowshock (on Steam) by <em>2025</em></p> - </li> - <li> - <p>Restructure to a normal sole proprietorship</p> - </li> - </ul> - <br> - <p>This roadmap is, however, subject to change, altough we do strive to live up to it.</p> - </section> - - <h1 id="anchor.team">team</h1> - - <section> - <p>As <em>Achernar</em> is currently registered as a PMV (lesser sole proprietorship), <em>Gabriel Bjørnager Jensen</em> is currently our only member.</p> - </section> - - <h1 id="anchor.inception">inception</h1> - - <section> - <p><em>Achernar</em> was incorporated on the first july of 2024 by current sole proprietor <em>Gabriel Bjørnager Jensen</em>.</p> - <br> - <p>Our domain – <code>achernar.dk</code> – was already registered in the winter of 2021, at that time being used for hosting on-line source code repositories. This was, however, quickly outsourced to <code><a href="https://mandelbrot.dk">mandelbrot.dk</a></code> after I had managed to secure that domain.</p> - <br> - <p><code>achernar.dk</code> was then, in the mean time, used for hosting a few look-up references. This mostly served as my own notes for school – mainly mathematics, physics, and chemistry – but quickly grew unorganised and unmaintained, although I did have plans to expand the lexicon.</p> - <br> - <p>The idea of a company had started in the spring of 2020. At that time it would've been named after the binary system <em>Sheliak</em> (Bayer: <code>β Lyræ</code>). But it was this idea that evolved into <em>Achernar</em>.</p> - </section> - - <h1 id="anchor.credits">credits</h1> - - <section class="fullWidth"> - <p>Thanks to the following creators for the fonts which we use on our website:</p> - <br> - <ul> - <li> - <p>Carrois Apostrophe: <a href="https://fonts.google.com/specimen/Fira+Mono/"><em style="font-family: Fira Mono;">Fira Mono</em></a></p> - </li> - <li> - <p>Roman Shamin & the “people” at Evil Martians 👽: <a href="https://fonts.google.com/specimen/Martian+Mono/"><em style="font-family: Martian Mono;">Martian Mono</em></a></p> - </li> - <li> - <p>Sorkin Type: <a href="https://fonts.google.com/specimen/Merriweather/"><em style="font-family: Merriweather;">Merriweather</em></a></p> - </li> - <li> - <p>Julieta Ulanovsky, Sol Matas, Juan Pablo del Peral, and Jacques Le Bailly: <a href="https://fonts.google.com/specimen/Montserrat/"><em style="font-family: Montserrat;">Montserrat</em></a></p> - </li> - <li> - <p>Claus Eggers Sørensen: <a href="https://fonts.google.com/specimen/Playfair+Display/"><em style="font-family: Playfair Display;">Playfair Display</em></a></p> - </li> - <li> - <p>Matt McInerney, Pablo Impallari, and Rodrigo Fuenzalida: <a href="https://fonts.google.com/specimen/Raleway/"><em style="font-family: Raleway;">Raleway</em></a></p> - </li> - </ul> - </section> - - <!--#include virtual="/include/footer.shtml"--> - </div> - </body> - - <script type="text/javascript"> - Ach.init(); - </script> -</html> diff --git a/html/agbsum.html b/html/agbsum.html deleted file mode 100644 index f409765..0000000 --- a/html/agbsum.html +++ /dev/null @@ -1,54 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8"> - <meta name="author" content="Gabriel Bjørnager Jensen"> - <meta name="darkreader-lock"> - <meta name="description" content="agbsum is a CLI utility for patching AGB images."> - <meta name="keywords" content="achernar, advance, agb, agbsum, embedded, game"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - - <link href="/favicon.ico" rel="icon" type="image/vnd.microsoft.icon"> - <link href="/apple-touch-icon.png" rel="apple-touch-icon" type="image/png"> - <title>agbsum | Achernar</title> - - <link href="/css/main.css" rel="stylesheet"> - <noscript> - <link href="/css/noScript.css" rel="stylesheet"> - </noscript> - - <script src="/js/main.js" type="text/javascript"></script> - </head> - - <body data-page="agbsum"> - <!--#include virtual="/include/header.shtml"--> - - <div id="page"> - <h1 id="anchor.about">agbsum</h1> - - <section> - <p><em>agbsum</em> is a command line utility for patching AGB images.</p> - </section> - - <h1 id="anchor.compatibility">compatibility</h1> - - <section> - <p><em>agbsum</em> is written in <em>C99</em> and uses makefiles as its build system. It has been tested to compile under <em>Clang</em>, <em>GCC</em>, and the <a href="https://bellard.org/tcc/"><em>Tiny C Compiler</em></a> (altough the latter may have problems with the standard library), as well as both <em>GNU Make</em> and <em>BSD Make</em>.</p> - <br> - <p>In theory, all UNIX-like systems (including older ones) should be supported. Please open a bug report if you experience otherwise.</p> - </section> - - <h1 id="anchor.bugReports">bug reports</h1> - - <section> - <p>Please write an e-mail to me (see footer) for bug reports.</p> - </section> - - <!--#include virtual="/include/footer.shtml"--> - </div> - </body> - - <script type="text/javascript"> - Ach.init(); - </script> -</html> diff --git a/html/ax.html b/html/ax.html deleted file mode 100644 index 626f446..0000000 --- a/html/ax.html +++ /dev/null @@ -1,46 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8"> - <meta name="author" content="Gabriel Bjørnager Jensen"> - <meta name="darkreader-lock"> - <meta name="description" content="AX is a C framework for developing AGB apps."> - <meta name="keywords" content="achernar, advance, agb, arm, assembly, ax, c, c++, cxx, thumb"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - - <link href="/favicon.ico" rel="icon" type="image/vnd.microsoft.icon"> - <link href="/apple-touch-icon.png" rel="apple-touch-icon" type="image/png"> - <title>AX | Achernar</title> - - <link href="/css/main.css" rel="stylesheet"> - <noscript> - <link href="/css/noScript.css" rel="stylesheet"> - </noscript> - - <script src="/js/main.js" type="text/javascript"></script> - </head> - - <body data-page="ax"> - <!--#include virtual="/include/header.shtml"--> - - <div id="page"> - <h1 id ="anchor.about">Advanced X</h1> - - <section> - <p><em>Advanced X</em> (or just <em>AX</em>) is a C library for developing apps for the <em>AGB</em> line of hardware.</p> - </section> - - <h1 id="anchor.status">status</h1> - - <section> - <p>The development of <em>AX</em> is currently on hold to prioritise <a href="/html/luma.html"><em>Luma</em></a> and <a href="/html/eas.html"><em>eAS</em></a>.</p> - </section> - - <!--#include virtual="/include/footer.shtml"--> - </div> - </body> - - <script type="text/javascript"> - Ach.init(); - </script> -</html> diff --git a/html/backspace.html b/html/backspace.html deleted file mode 100644 index ca033ae..0000000 --- a/html/backspace.html +++ /dev/null @@ -1,38 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8"> - <meta name="author" content="Gabriel Bjørnager Jensen"> - <meta name="darkreader-lock"> - <meta name="description" content="About the Backspace game engine."> - <meta name="keywords" content="achernar, backspace, game engine, rust, udp, webgpu"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - - <link href="/favicon.ico" rel="icon" type="image/vnd.microsoft.icon"> - <link href="/apple-touch-icon.png" rel="apple-touch-icon" type="image/png"> - <title>Backspace | Achernar</title> - - <link href="/css/main.css" rel="stylesheet"> - <noscript> - <link href="/css/noScript.css" rel="stylesheet"> - </noscript> - - <script src="/js/main.js" type="text/javascript"></script> - </head> - - <body data-page="backspace"> - <!--#include virtual="/include/header.shtml"--> - - <div id="page"> - <section class="fullWidth"> - <p class="note">coming soon</p> - </section> - - <!--#include virtual="/include/footer.shtml"--> - </div> - </body> - - <script type="text/javascript"> - Ach.init(); - </script> -</html> diff --git a/html/benoit.html b/html/benoit.html deleted file mode 100644 index 083532d..0000000 --- a/html/benoit.html +++ /dev/null @@ -1,74 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8"> - <meta name="author" content="Gabriel Bjørnager Jensen"> - <meta name="darkreader-lock"> - <meta name="description" content="Benoit is a Rust-written fractal renderer."> - <meta name="keywords" content="achernar, benoit, fractal, mandelbrot, rust, webgpu"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - - <link href="/favicon.ico" rel="icon" type="image/vnd.microsoft.icon"> - <link href="/apple-touch-icon.png" rel="apple-touch-icon" type="image/png"> - <title>Benoit | Achernar</title> - - <link href="/css/main.css" rel="stylesheet"> - <noscript> - <link href="/css/noScript.css" rel="stylesheet"> - </noscript> - - <script src="/js/main.js" type="text/javascript"></script> - </head> - - <body data-page="benoit"> - <!--#include virtual="/include/header.shtml"--> - - <div id="page"> - <h1 id="anchor.about">benoit</h1> - - <section> - <p><em>Benoit</em> is a Rust-written programme for visualising complex functions, e.g. <em>the Mandelbrot Set</em> and similar fractals.</p> - <br> - <x-image alt="A render of a single Minibrot on a green background. The render is coloures so that it resembles lightning coming from the Minibrot." data-file="benoit_screenshot0"></x-image> - <br> - <p>The project consists of the core <a href="https://crates.io/crates/benoit/"><code>benoit</code></a> crate, from which the frontends <code>benoit-cli</code> and (in the future) <code>benoit-gui</code> derive.</p> - </section> - - <h1 id="anchor.benoitCli">benoit-cli</h1> - - <section> - <p>The <code>benoit-cli</code> executable can render and animate using <a href="https://en.wikipedia.org/wiki/TOML/">TOML</a> files right from the commandline.</p> - <br> - <x-image alt="A Julia Set centred on a point inside the Mandelbrot Set. The resulting image resembles creeping, black vines with rainbows around." data-file="benoit_screenshot1"></x-image> - <br> - <p>The main use of <code>benoit-cli</code> is to render still images or animations of fractals, e.g. zoom-ins. An example configuration could look like the following:</p> - <br> - <div class="codeblock"># benoit.toml<br><br>[render]<br>count = 24<br>width = 1024<br>height = 1024<br><br>fractal = "mandelbrot"<br>inverse = false<br>julia = false<br><br>[render.start]<br>frame = 0<br><br>max_iter_count = 0x100<br><br>centre = "0.0+1.0i"<br>seed = "0.0+0.0i"<br>zoom = "1.0"<br><br>colour_range = 64.0<br><br>[render.stop]<br>frame = 23<br><br>max_iter_count = 0x100<br><br>centre = "0.0+1.0i"<br>seed = "0.0+0.0i"<br>zoom = "1.0"<br><br>colour_range = 64.0<br><br>[final]<br>palette = "fire"<br><br>[output]<br>directory = "render/"</div> - <br> - <p>Just provide the path to the desired configuration:</p> - <br> - <code>benoit-cli "benoit.toml"</code> - </section> - - <h1 id="anchor.benoitGui">benoit-gui</h1> - - <section> - <p>The <code>benoit-gui</code> executable, on the other hand, allows viewing fractals in realtime. Do note, however, that this frontend is currently unimplemented.</p> - <br> - <x-image alt="An render of an inverse Julia Set of the Mandelbrot. It appears on a grey gradiant background with a warm-coloured pattern resembling smaller minibrots." data-file="benoit_screenshot2"></x-image> - </section> - - <h1 id="anchor.docs">docs</h1> - - <section> - <p>Documentation is written in source. Documentation for the main library is hosted on <a href="https://docs.rs/benoit/latest/benoit/"><code>docs.rs</code></a>.</p> - </section> - - <!--#include virtual="/include/footer.shtml"--> - </div> - </body> - - <script type="text/javascript"> - Ach.init(); - </script> -</html> diff --git a/html/bowshock.html b/html/bowshock.html deleted file mode 100644 index 968fffa..0000000 --- a/html/bowshock.html +++ /dev/null @@ -1,38 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8"> - <meta name="author" content="Gabriel Bjørnager Jensen"> - <meta name="darkreader-lock"> - <meta name="description" content="About Bowshock."> - <meta name="keywords" content="achernar, bowshock, frontier, game, rust, open world, sci-fi, science fiction, space, video game"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - - <link href="/favicon.ico" rel="icon" type="image/vnd.microsoft.icon"> - <link href="/apple-touch-icon.png" rel="apple-touch-icon" type="image/png"> - <title>Bowshock | Achernar</title> - - <link href="/css/main.css" rel="stylesheet"> - <noscript> - <link href="/css/noScript.css" rel="stylesheet"> - </noscript> - - <script src="/js/main.js" type="text/javascript"></script> - </head> - - <body data-page="bowshock"> - <!--#include virtual="/include/header.shtml"--> - - <div id="page"> - <section class="fullWidth"> - <p class="note">Coming soon.</p> - </section> - - <!--#include virtual="/include/footer.shtml"--> - </div> - </body> - - <script type="text/javascript"> - Ach.init(); - </script> -</html> diff --git a/html/bzipper.html b/html/bzipper.html deleted file mode 100644 index 8a58b0e..0000000 --- a/html/bzipper.html +++ /dev/null @@ -1,48 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8"> - <meta name="author" content="Gabriel Bjørnager Jensen"> - <meta name="darkreader-lock"> - <meta name="description" content="bzipper is a Rust crate for serialisation and deserialisation of binary streams."> - <meta name="keywords" content="achernar, binary, bzipper, deserialise, deserialiser, deserialize, deserializer, octet, serialize, serializer, serialize, serializer, tcp, udp"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - - <link href="/favicon.ico" rel="icon" type="image/vnd.microsoft.icon"> - <link href="/apple-touch-icon.png" rel="apple-touch-icon" type="image/png"> - <title>bzipper | Achernar</title> - - <link href="/css/main.css" rel="stylesheet"> - <noscript> - <link href="/css/noScript.css" rel="stylesheet"> - </noscript> - - <script src="/js/main.js" type="text/javascript"></script> - </head> - - <body data-page="bzipper"> - <!--#include virtual="/include/header.shtml"--> - - <div id="page"> - <h1 id ="anchor.about">bzipper</h1> - - <section> - <p><em>bzipper</em> is a Rust crate for serialisation and deserialisation of binary streams.</p> - <br> - <p>See more at <code><a href="https://crates.io/crates/bzipper/">crates.io</a></code>.</p> - </section> - - <h1 id="anchor.docs">docs</h1> - - <section> - <p>See <a href="https://docs.rs/pollex/latest/pollex/"><code>docs.rs</code></a> for documentation.</p> - </section> - - <!--#include virtual="/include/footer.shtml"--> - </div> - </body> - - <script type="text/javascript"> - Ach.init(); - </script> -</html> diff --git a/html/deltaWorld.html b/html/deltaWorld.html deleted file mode 100644 index b0aba84..0000000 --- a/html/deltaWorld.html +++ /dev/null @@ -1,38 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8"> - <meta name="author" content="Gabriel Bjørnager Jensen"> - <meta name="darkreader-lock"> - <meta name="description" content="About Delta World."> - <meta name="keywords" content="achernar, adventure, delta world, open world, rust, webgpu"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - - <link href="/favicon.ico" rel="icon" type="image/vnd.microsoft.icon"> - <link href="/apple-touch-icon.png" rel="apple-touch-icon" type="image/png"> - <title>Delta·World | Achernar</title> - - <link href="/css/main.css" rel="stylesheet"> - <noscript> - <link href="/css/noScript.css" rel="stylesheet"> - </noscript> - - <script src="/js/main.js" type="text/javascript"></script> - </head> - - <body data-page="deltaWorld"> - <!--#include virtual="/include/header.shtml"--> - - <div id="page"> - <section class="fullWidth"> - <p class="note">Coming soon.</p> - </section> - - <!--#include virtual="/include/footer.shtml"--> - </div> - </body> - - <script type="text/javascript"> - Ach.init(); - </script> -</html> diff --git a/html/dux.html b/html/dux.html deleted file mode 100644 index 742bced..0000000 --- a/html/dux.html +++ /dev/null @@ -1,46 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8"> - <meta name="author" content="Gabriel Bjørnager Jensen"> - <meta name="darkreader-lock"> - <meta name="description" content="Dux is a cross-platform widgeting library for developing GUI applications.."> - <meta name="keywords" content="achernar, dux, multimedia, rust, webgpu, widget"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - - <link href="/favicon.ico" rel="icon" type="image/vnd.microsoft.icon"> - <link href="/apple-touch-icon.png" rel="apple-touch-icon" type="image/png"> - <title>Dux | Achernar</title> - - <link href="/css/main.css" rel="stylesheet"> - <noscript> - <link href="/css/noScript.css" rel="stylesheet"> - </noscript> - - <script src="/js/main.js" type="text/javascript"></script> - </head> - - <body data-page="dux"> - <!--#include virtual="/include/header.shtml"--> - - <div id="page"> - <h1 id="anchor.about">Dux</h1> - - <section> - <p><em>Dux</em> is a cross-platform widgeting library for developing GUI applications.</p> - </section> - - <h1 id="anchor.status">status</h1> - - <section> - <p>The <em>Dux</em> library is currently unmaintained.</p> - </section> - - <!--#include virtual="/include/footer.shtml"--> - </div> - </body> - - <script type="text/javascript"> - Ach.init(); - </script> -</html> diff --git a/html/eas.html b/html/eas.html deleted file mode 100644 index 2e52b7a..0000000 --- a/html/eas.html +++ /dev/null @@ -1,47 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8"> - <meta name="author" content="Gabriel Bjørnager Jensen"> - <meta name="darkreader-lock"> - <meta name="description" content="eAS is an assembler for cross-compiling to Arm ISAs."> - <meta name="keywords" content="achernar, agb, arm, eas, thumb"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - - <link href="/favicon.ico" rel="icon" type="image/vnd.microsoft.icon"> - <link href="/apple-touch-icon.png" rel="apple-touch-icon" type="image/png"> - <title>eAS | Achernar</title> - - <link href="/css/main.css" rel="stylesheet"> - <noscript> - <link href="/css/noScript.css" rel="stylesheet"> - </noscript> - - <script src="/js/main.js" type="text/javascript"></script> - </head> - - <body data-page="eas"> - <!--#include virtual="/include/header.shtml"--> - - <div id="page"> - <h1 id ="anchor.about">the embedded assembler</h1> - - <section> - <p>The <em>Embedded Assembler</em> (or simply <em>eAS</em>) is an assembler for cross-compiling to Arm ISAs.</p> - <br> - <p>The assembler is intended as a drop-in replacement for the official <em>armasm</em> assembler by Arm Limited. It therefore imitates the same syntax.</p> - </section> - - <h1 id="anchor.status">docs</h1> - - <section> - <p>Like <a href="/html/luma.html"><em>Luma</em></a>, the development of <em>eAS</em> is currently on hold in favour of the <a href="/html/pollex.html"><em>Pollex</em></a> library, which will be used as a backend.</p> - </section> - <!--#include virtual="/include/footer.shtml"--> - </div> - </body> - - <script type="text/javascript"> - Ach.init(); - </script> -</html> diff --git a/html/luma.html b/html/luma.html deleted file mode 100644 index 7b2ebde..0000000 --- a/html/luma.html +++ /dev/null @@ -1,46 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8"> - <meta name="author" content="Gabriel Bjørnager Jensen"> - <meta name="darkreader-lock"> - <meta name="description" content="Luma is an emulator for the <em>AGB</em> line of game consoles."> - <meta name="keywords" content="achernar, agb, arm, luma, rust, thumb"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - - <link href="/favicon.ico" rel="icon" type="image/vnd.microsoft.icon"> - <link href="/apple-touch-icon.png" rel="apple-touch-icon" type="image/png"> - <title>Luma | Achernar</title> - - <link href="/css/main.css" rel="stylesheet"> - <noscript> - <link href="/css/noScript.css" rel="stylesheet"> - </noscript> - - <script src="/js/main.js" type="text/javascript"></script> - </head> - - <body data-page="luma"> - <!--#include virtual="/include/header.shtml"--> - - <div id="page"> - <h1 id ="anchor.about">luma</h1> - - <section> - <p><em>Luma</em> is an emulator for the <em>AGB</em> line of game consoles by Nintendo Co.</p> - </section> - - <h1 id="anchor.status">status</h1> - - <section> - <p>The development of <em>Luma</em> is currently on hold in favour of <a href="/html/pollex.html"><em>Pollex</em></a>, which is to be used as a backend.</p> - </section> - - <!--#include virtual="/include/footer.shtml"--> - </div> - </body> - - <script type="text/javascript"> - Ach.init(); - </script> -</html> diff --git a/html/pollex.html b/html/pollex.html deleted file mode 100644 index 938df7c..0000000 --- a/html/pollex.html +++ /dev/null @@ -1,56 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8"> - <meta name="author" content="Gabriel Bjørnager Jensen"> - <meta name="darkreader-lock"> - <meta name="description" content="Pollex is a Rust crate for manipulating Arm ISA instructions.."> - <meta name="keywords" content="achernar, agb, arm, pollex, rust, thumb"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - - <link href="/favicon.ico" rel="icon" type="image/vnd.microsoft.icon"> - <link href="/apple-touch-icon.png" rel="apple-touch-icon" type="image/png"> - <title>Pollex | Achernar</title> - - <link href="/css/main.css" rel="stylesheet"> - <noscript> - <link href="/css/noScript.css" rel="stylesheet"> - </noscript> - - <script src="/js/main.js" type="text/javascript"></script> - </head> - - <body data-page="pollex"> - <!--#include virtual="/include/header.shtml"--> - - <div id="page"> - <h1 id ="anchor.about">pollex</h1> - - <section> - <p><em>Pollex</em> is a Rust crate for manipulating Arm ISA instructions. Its goal is to be used as a backend for Arm emulators, assemblers etc.</p> - <br> - <p>Currently, only the legacy 32-bit architectures are supported (if barely), but complete support is planned in the future.</p> - <br> - <p>See more at <code><a href="https://crates.io/crates/pollex/">crates.io</a></code>.</p> - </section> - - <h1 id="anchor.docs">docs</h1> - - <section> - <p>As per usual, documentation for <em>Pollex</em> can be found on <a href="https://docs.rs/pollex/latest/pollex/"><code>docs.rs</code></a>.</p> - </section> - - <h1 id="anchor.bugReports">bug reports</h1> - - <section> - <p>Please write an e-mail to me (see footer) for bug reports.</p> - </section> - - <!--#include virtual="/include/footer.shtml"--> - </div> - </body> - - <script type="text/javascript"> - Ach.init(); - </script> -</html> diff --git a/html/u8c.html b/html/u8c.html deleted file mode 100644 index b653409..0000000 --- a/html/u8c.html +++ /dev/null @@ -1,60 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8"> - <meta name="author" content="Gabriel Bjørnager Jensen"> - <meta name="darkreader-lock"> - <meta name="description" content="u8c is a library for handling Unicode sequences in C."> - <meta name="keywords" content="achernar, u8c, unicode, utf-16, utf-32, utf-8, utf16, utf32, utf8"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - - <link href="/favicon.ico" rel="icon" type="image/vnd.microsoft.icon"> - <link href="/apple-touch-icon.png" rel="apple-touch-icon" type="image/png"> - <title>u8c | Achernar</title> - - <link href="/css/main.css" rel="stylesheet"> - <noscript> - <link href="/css/noScript.css" rel="stylesheet"> - </noscript> - - <script src="/js/main.js" type="text/javascript"></script> - </head> - - <body data-page="u8c"> - <!--#include virtual="/include/header.shtml"--> - - <div id="page"> - <h1 id ="anchor.about">u8c</h1> - - <section> - <p><em>u8c</em> is a library for handling Unicode sequences in C.</p> - </section> - - <h1 id ="anchor.features">features</h1> - - <section class="fullWidth"> - <p><em>u8c</em> supports the following facilities:</p> - <br> - <ul> - <li> - <p>Encoding and decoding <em>UTF-8</em></p> - </li> - <li> - <p>Encoding and decoding <em>UTF-16</em></p> - </li> - <li> - <p>Manipulation of <em>UTF-32</em> strings.</p> - </li> - </ul> - <br> - <p>Note that <em>UTF-32</em> is used as an intermediate format between conversions.</p> - </section> - - <!--#include virtual="/include/footer.shtml"--> - </div> - </body> - - <script type="text/javascript"> - Ach.init(); - </script> -</html> diff --git a/include/achernar.php b/include/achernar.php new file mode 100644 index 0000000..3fbde25 --- /dev/null +++ b/include/achernar.php @@ -0,0 +1,132 @@ +<?php addHeading("The future is now", "about"); ?> + +<section> + <p><em>Achernar</em> is a European indie development studio based in the Capital Region of Denmark. 🇩🇰</p> + <br> + <p>We aim to develop high-quality video games and software alike, and we believe that all technologies should be for humanity as a whole. We therefore publish all relevant source code for our products.</p> + <br> + <p class="note">See footer for contact information.</p> +</section> + +<?php addHeading("Projects", "projects"); ?> + +<section class="fullWidth"> + <p>The following is a list of our current and maintained projects.</p> + <br> + <p class="note">Scroll <a href="#anchor.vision">down</a> for more information about us.</p> + <br> + <div id="overview"> + <?php + function addCard($page, $title) { + [$backgroundColour, $textColour] = pageColours($page); + $backgroundImage = pageBackgroundImage($page); + $glyphAddr = pageGlyph($page); + + if (is_null($backgroundImage)) { + $backgroundImage = "none"; + } else { + $backgroundImage = "url('" . $backgroundImage . "')"; + } + + $cardStyle = "--backgroundColour: $backgroundColour; --textColour: $textColour; background-image: $backgroundImage"; + + echo <<<HTML + <a href="?p=$page" style="$cardStyle" title="$title"> + <img alt="$page" src="$glyphAddr"> + </a> + HTML; + } + + addCard("agbsum", "agbsum"); + //addCard("ax", "AX"); + //addCard("backspace", "Backspace"); + addCard("benoit", "Benoit"); + addCard("bowshock", "Bowshock"); + addCard("bzipper", "Bzipper"); + //addCard("deltaWorld", "Delta·World"); + addCard("dux", "Dux"); + addCard("eas", "eAS"); + addCard("luma", "Luma"); + addCard("pollex", "Pollex"); + addCard("u8c", "u8c"); + ?> + </div> +</section> + +<?php addHeading("Vision", "vision"); ?> + +<section> + <p>The goal of Achernar is to promote modern and robust software for everyone. In other words, our vision is to develop a human and clean industry and community for technology.</p> + <br> + <p>We believe in a society free of patents, and as such we release our scientific software in open-source form. For our games, we try to keep the base engine as open as possible whilst still keeping in mind that they yield our main income.</p> +</section> + +<?php addHeading("Roadmap", "roadmap"); ?> + +<section> + <p>Currently, our roadmap is as follows:</p> + <br> + <ul> + <li> + <p>Complete the webservice before <strong>autumn, 2024</strong></p> + </li> + <li> + <p>Release our first game by <strong>2024</strong>, as well as company merchandise in some form</p> + </li> + <li> + <p>Release early-access for Bowshock (on Steam) by <strong>2025</strong></p> + </li> + <li> + <p>Restructure to a normal sole proprietorship</p> + </li> + </ul> + <br> + <p>This roadmap is, however, also subject to change, altough we do strive to live up to it.</p> +</section> + +<?php addHeading("Team", "team"); ?> + +<section> + <p>As Achernar is currently registered as a PMV (lesser sole proprietorship), Gabriel Bjørnager Jensen is currently our only member.</p> +</section> + +<?php addHeading("Inception", "inception"); ?> + +<section> + <p><em>Achernar</em> was incorporated on the first july of 2024 by current sole proprietor Gabriel Bjørnager Jensen.</p> + <br> + <p>Our first domain – <code>achernar.dk</code> – was already registered in the winter of 2021, at that time being used for hosting on-line source code repositories. This was, however, quickly outsourced to <code><a href="https://mandelbrot.dk">mandelbrot.dk</a></code> after I had managed to secure that domain.</p> + <br> + <p><code>achernar.dk</code> was then, in the mean time, used for hosting a few look-up references. This mostly served as my own notes for school – mainly mathematics, physics, and chemistry – but quickly grew unorganised and unmaintained, although I did have plans to expand the encyclopedia.</p> + <br> + <p>The idea of a company had started in the spring of 2020. At that time it would've been named after the binary system Sheliak (Bayer: <code>β Lyræ</code>). But it was this idea that evolved into “Achernar.”</p> + <br> + <p>Shortly after being incorporated, we registered the domain <code>achernar.io</code>. At that time, we also set up mail services using our domain.</p> +</section> + +<?php addHeading("Credits", "credits"); ?> + +<section class="fullWidth"> + <p>Thanks to <strong>Nicolas Gallagher</strong> for the <a href="https://necolas.github.io/normalize.css/"><code>normalize.css</code></a> stylesheet. Additionally thanks to the following creators for the fonts which we use on our website:</p> + <br> + <ul> + <li> + <p>Carrois Apostrophe: <a href="https://fonts.google.com/specimen/Fira+Mono/"><span style="font-family: Fira Mono;">Fira Mono</span></a></p> + </li> + <li> + <p>Roman Shamin & the “people” at Evil Martians 👽: <a href="https://fonts.google.com/specimen/Martian+Mono/"><span style="font-family: Martian Mono;">Martian Mono</span></a></p> + </li> + <li> + <p>Sorkin Type: <a href="https://fonts.google.com/specimen/Merriweather/"><span style="font-family: Merriweather;">Merriweather</span></a></p> + </li> + <li> + <p>Julieta Ulanovsky, Sol Matas, Juan Pablo del Peral, and Jacques Le Bailly: <a href="https://fonts.google.com/specimen/Montserrat/"><span style="font-family: Montserrat;">Montserrat</span></a></p> + </li> + <li> + <p>Claus Eggers Sørensen: <a href="https://fonts.google.com/specimen/Playfair+Display/"><span style="font-family: Playfair Display;">Playfair Display</span></a></p> + </li> + <li> + <p>Matt McInerney, Pablo Impallari, and Rodrigo Fuenzalida: <a href="https://fonts.google.com/specimen/Raleway/"><span style="font-family: Raleway;">Raleway</span></a></p> + </li> + </ul> +</section> diff --git a/include/agbsum.php b/include/agbsum.php new file mode 100644 index 0000000..afe3ebe --- /dev/null +++ b/include/agbsum.php @@ -0,0 +1,21 @@ +<?php addHeading("agbsum", "about"); ?> + +<section> + <p><em>agbsum</em> is a command line utility for patching AGB images.</p> +</section> + +<?php addHeading("Specs", "specs"); ?> + +<section> + <p>All AGB images have a header at offsets <code>0x00-0xE3</code> (inclusive), of which (29) bytes in <code>0xA0-0xBD</code> denote metadata.</p> + <br> + <p>The first byte after this sequence holds a checksum of the metadata, which if invalid, the device bootloader will usually reject the entire image.</p> +</section> + +<?php addHeading("Compatibility", "compatibility"); ?> + +<section> + <p><em>agbsum</em> is written in <strong>C99</strong> and uses makefiles as its build system. It has been tested to compile under Clang, GCC, and the <a href="https://bellard.org/tcc/">Tiny C Compiler</a> (altough the latter may have problems with the standard library). Both GNU Make (<code>gmake</code>) and BSD Make (<code>bmake</code>).</p> + <br> + <p>In theory, all “UNIX-like” systems (including older ones) should be supported. Please open a bug report if you experience otherwise.</p> +</section> diff --git a/include/ax.php b/include/ax.php new file mode 100644 index 0000000..bc7cdc4 --- /dev/null +++ b/include/ax.php @@ -0,0 +1,11 @@ +<?php addHeading("Advanced X", "about"); ?> + +<section> + <p><em>Advanced X</em> (or just <em>AX</em>) is a C library for developing apps for the <em>AGB</em> line of hardware.</p> +</section> + +<?php addHeading("Status", "status"); ?> + +<section> + <p>The development of <em>AX</em> is currently on hold to prioritise <a href="?p=luma"><em>Luma</em></a> and <a href="?p=eas"><em>eAS</em></a>.</p> +</section> diff --git a/include/backspace.php b/include/backspace.php new file mode 100644 index 0000000..b51e0f7 --- /dev/null +++ b/include/backspace.php @@ -0,0 +1,3 @@ +<section class="fullWidth"> + <p class="note">coming soon</p> +</section> diff --git a/include/benoit.php b/include/benoit.php new file mode 100644 index 0000000..9334ae8 --- /dev/null +++ b/include/benoit.php @@ -0,0 +1,41 @@ +<?php addHeading("Benoit", "about"); ?> + +<section> + <p><em>Benoit</em> is a Rust-written programme for visualising complex functions, e.g. <a href="https://en.wikipedia.org/wiki/Mandelbrot_set/"><em>the Mandelbrot Set</em></a> and similar fractals.</p> + <br> + <?php addImage("benoit_screenshot0", "A render of a single Minibrot on a green background. The render is coloures so that it resembles lightning coming from the Minibrot.") ?> + <br> + <p>The project consists of the core <a href="https://crates.io/crates/benoit/"><code>benoit</code></a> crate, from which the front-ends <code>benoit-cli</code> and (in the future) <code>benoit-gui</code> derive.</p> +</section> + +<?php addHeading("benoit-cli", "benoitCli"); ?> + +<section> + <p>The <code>benoit-cli</code> executable can render and animate using <a href="https://en.wikipedia.org/wiki/TOML/">TOML</a> files right from the commandline.</p> + <br> + <?php addImage("benoit_screenshot1", "A Julia Set centred on a point inside the Mandelbrot Set. The resulting image resembles creeping, black vines with rainbows around.") ?> + <br> + <p>The main use of <code>benoit-cli</code> is to render still images or animations of fractals, e.g. zoom-ins. An example configuration could look like the following:</p> + <br> + <p class="codeblock"># benoit.toml<br><br>[render]<br>count = 24<br>width = 1024<br>height = 1024<br><br>fractal = "mandelbrot"<br>inverse = false<br>julia = false<br><br>[render.start]<br>frame = 0<br><br>max_iter_count = 0x100<br><br>centre = "0.0+1.0i"<br>seed = "0.0+0.0i"<br>zoom = "1.0"<br><br>colour_range = 64.0<br><br>[render.stop]<br>frame = 23<br><br>max_iter_count = 0x100<br><br>centre = "0.0+1.0i"<br>seed = "0.0+0.0i"<br>zoom = "1.0"<br><br>colour_range = 64.0<br><br>[final]<br>palette = "fire"<br><br>[output]<br>directory = "render/"</p> + <br> + <p>Just provide the path to the desired configuration:</p> + <br> + <p class="codeblock">benoit-cli "benoit.toml"</p> +</section> + +<?php addHeading("benoit-gui", "benoitGui"); ?> + +<section> + <p>The <code>benoit-gui</code> executable, on the other hand, allows viewing fractals in realtime. Do note, however, that this front-end is currently unimplemented.</p> + <br> + <?php addImage("benoit_screenshot2", "An render of an inverse Julia Set of the Mandelbrot. It appears on a grey gradiant background with a warm-coloured pattern resembling smaller minibrots.") ?> + <br> + <p>Until this front-end is implemented, please use version <a href="https://mandelbrot.dk/benoit/tag/?h=2.7.1"><code>2.7.1</code></a> of Benoit instead.</p> +</section> + +<?php addHeading("Docs", "docs"); ?> + +<section> + <p>Documentation is written in source. Documentation for the main library is hosted on <a href="https://docs.rs/benoit/latest/benoit/"><code>docs.rs</code></a>.</p> +</section> diff --git a/include/bowshock.php b/include/bowshock.php new file mode 100644 index 0000000..0d2b9bb --- /dev/null +++ b/include/bowshock.php @@ -0,0 +1,3 @@ +<section class="fullWidth"> + <p class="note">Coming soon.</p> +</section> diff --git a/include/bzipper.php b/include/bzipper.php new file mode 100644 index 0000000..8b6b677 --- /dev/null +++ b/include/bzipper.php @@ -0,0 +1,27 @@ +<?php addHeading("bzipper", "about"); ?> + +<section> + <p><em>bzipper</em> is a Rust crate for serialisation and deserialisation of binary streams.</p> + <br> + <p>See more at <code><a href="https://crates.io/crates/bzipper/">crates.io</a></code>.</p> +</section> + +<?php addHeading("rationale", "rationale"); ?> + +<section> + <p>Contrary to <a href="https://crates.io/crates/serde/">Serde</a>/<a href="https://crates.io/crates/bincode/">Bincode</a>, the goal of this crate is to serialise data with a known size limit. Therefore, this crate may be more suited for networking or other cases where a fixed-sized buffer is needed.</p> +</section> + +<?php addHeading("data model", "dataModel"); ?> + +<section> + <p>Most primitive types serialise losslessly, with the exception being <code>usize</code> and <code>isize</code>. These serialise as <code>u16</code> and <code>u32</code>, respectively, for portability reasons.</p> + <br> + <p>Unsized types, such as <code>str</code> and slices, are not supported. Instead, array should be used. For strings, the <code>FixedString</code> type is also provided.</p> +</section> + +<?php addHeading("docs", "docs"); ?> + +<section> + <p>Documentation is written in-source. See <a href="https://docs.rs/pollex/latest/pollex/"><code>docs.rs</code></a> for a rendered instance.</p> +</section> diff --git a/include/deltaWorld.php b/include/deltaWorld.php new file mode 100644 index 0000000..0d2b9bb --- /dev/null +++ b/include/deltaWorld.php @@ -0,0 +1,3 @@ +<section class="fullWidth"> + <p class="note">Coming soon.</p> +</section> diff --git a/include/dux.php b/include/dux.php new file mode 100644 index 0000000..409ae2a --- /dev/null +++ b/include/dux.php @@ -0,0 +1,11 @@ +<?php addHeading("Dux", "about"); ?> + +<section> + <p><em>Dux</em> is a cross-platform widgeting library for developing GUI applications.</p> +</section> + +<?php addHeading("Status", "status"); ?> + +<section> + <p>Dux is currently on standby and therefore unmaintained.</p> +</section> diff --git a/include/eas.php b/include/eas.php new file mode 100644 index 0000000..ef51325 --- /dev/null +++ b/include/eas.php @@ -0,0 +1,13 @@ +<?php addHeading("The Embedded Assembler", "about"); ?> + +<section> + <p>The <em>Embedded Assembler</em> (or simply <em>eAS</em>; as in <i>ease</i>) is an assembler for cross-compiling to Arm ISAs.</p> + <br> + <p>The assembler is intended as a drop-in replacement for the official <em>armasm</em> assembler by Arm Limited. It therefore imitates the same syntax.</p> +</section> + +<?php addHeading("Status", "status"); ?> + +<section> + <p>Like <a href="?p=luma"><em>Luma</em></a>, the development of <em>eAS</em> is currently on hold in favour of the <a href="?p=pollex"><em>Pollex</em></a> library, which will be used as a backend.</p> +</section>
\ No newline at end of file diff --git a/include/footer.shtml b/include/footer.shtml deleted file mode 100644 index d79024c..0000000 --- a/include/footer.shtml +++ /dev/null @@ -1,17 +0,0 @@ -<footer id="footer"> - <h1>achernar</h1> - - <p id="cvrNumber" title="Danish VAT number.">vat: DK44936429</p> - <br> - <p>Communications can be done in English and Danish.</p> - <br> - <p>This webservice may be cloned from <a href="https://mandelbrot.dk/achernar/" rel="noopener noreferrer" target="_blank"><code>mandelbrot.dk</code></a>.</p> - <br> - <div class="center" id="emailAddress" title="Obfuscated email address."></div> - - <div id="socials"> - <a class="instagram" href="https://www.instagram.com/primuseridani/" title="Instagram: primuseridani"></a> - <a class="linktree" href="https://linktr.ee/alphaeridani/" title="Linktree: @alphaeridani"></a> - <a class="threads" href="https://www.threads.net/@primuseridani/" title="Threads: @primuseridani"></a> - </div> -</footer> diff --git a/include/header.shtml b/include/header.shtml deleted file mode 100644 index d6fb1e6..0000000 --- a/include/header.shtml +++ /dev/null @@ -1,60 +0,0 @@ -<header id="header"> - <div id="navBar"> - <section> - <a id="home" href="/html/achernar.html" title="Home"></a> - </section> - - <section> - <a href="/html/benoit.html">BENOIT</a> - <a href="/html/bowshock.html">BOWSHOCK</a> - <a href="/html/deltaWorld.html">DELTA·WORLD</a> - <a href="/html/eas.html">eAS</a> - </section> - - <section> - <a id="hamburger" onclick="Ach.toggleSideMenu();" title="Side menu"></a> - </section> - </div> - - <div id="sideMenu"> - <section class="links"> - <section> - <p>ACHERNAR</p> - - <a href="/html/achernar.html#about">ABOUT</a> - <a href="/html/achernar.html#vision">VISION</a> - <a href="/html/achernar.html#roadmap">ROADMAP</a> - <a href="/html/achernar.html#team">TEAM</a> - <a href="https://mandelbrot.dk" rel="noopener noreferrer" target="_blank">SOURCES</a> - </section> - - <section> - <p>GAMES</p> - - <a href="/html/bowshock.html">BOWSHOCK</a> - <a href="/html/deltaWorld.html">DELTA·WORLD</a> - </section> - - <section> - <p>PROJECTS</p> - - <a href="/html/agbsum.html">AGBSUM</a> - <a href="/html/ax.html">AX</a> - <a href="/html/backspace.html">BACKSPACE</a> - <a href="/html/benoit.html">BENOIT</a> - <a href="/html/bzipper.html">BZIPPER</a> - <a href="/html/dux.html">DUX</a> - <a href="/html/eas.html">eAS</a> - <a href="/html/luma.html">LUMA</a> - <a href="/html/pollex.html">POLLEX</a> - <a href="/html/u8c.html">U8C</a> - </section> - </section> - - <section> - <a id="themeToggler" onclick="Ach.toggleTheme();">TOGGLE THEME</a> - </section> - </div> - - <div id="glyph"></div> -</header> diff --git a/include/luma.php b/include/luma.php new file mode 100644 index 0000000..a8192ea --- /dev/null +++ b/include/luma.php @@ -0,0 +1,11 @@ +<?php addHeading("Luma", "about"); ?> + +<section> + <p><em>Luma</em> is an emulator for the <em>AGB</em> line of game consoles by Nintendo Co.</p> +</section> + +<?php addHeading("Status", "status"); ?> + +<section> + <p>The development of <em>Luma</em> is currently on hold in favour of <a href="?p=pollex"><em>Pollex</em></a>, which is to be used as a backend.</p> +</section> diff --git a/include/pollex.php b/include/pollex.php new file mode 100644 index 0000000..d4b9e69 --- /dev/null +++ b/include/pollex.php @@ -0,0 +1,15 @@ +<?php addHeading("Pollex", "about"); ?> + +<section> + <p><em>Pollex</em> is a Rust crate for manipulating Arm ISA instructions. Its goal is to be used as a backend for Arm emulators, assemblers etc.</p> + <br> + <p>Currently, only the legacy 32-bit architectures are supported (if barely), but complete support is planned in the future.</p> + <br> + <p>See more at <a href="https://crates.io/crates/pollex/"><code>crates.io</code></a>.</p> +</section> + +<?php addHeading("Docs", "docs"); ?> + +<section> + <p>As per usual, documentation for <em>Pollex</em> can be found on <a href="https://docs.rs/pollex/latest/pollex/"><code>docs.rs</code></a>.</p> +</section> diff --git a/include/prelude.php b/include/prelude.php new file mode 100644 index 0000000..e782a48 --- /dev/null +++ b/include/prelude.php @@ -0,0 +1,60 @@ +<?php + function readConfig($key) { + if (isset($_GET[$key])) { + return htmlspecialchars($_GET[$key], ENT_SUBSTITUTE, "UTF-8"); + } else { + return null; + } + } + + function pageColours($page) { + return match ($page) { + "achernar" => ["#007B34", "#FFFFFF"], + "agbsum" => ["#422984", "#FFFFFF"], + "ax" => ["#422984", "#FFFFFF"], + "backspace" => ["#000000", "#FFFFFF"], + "benoit" => ["#BA0035", "#FFFFFF"], + "bowshock" => ["#B61833", "#FFEEE0"], + "bzipper" => ["#FFFFFF", "#B4202D"], + "deltaWorld" => ["#000000", "#FFFFFF"], + "dux" => ["#131313", "#06FBB2"], + "eas" => ["#01CD93", "#00291B"], + "luma" => ["#6051B2", "#FFFFFF"], + "pollex" => ["#4D4084", "#FFFFFF"], + "u8c" => ["#444747", "#A9E13D"], + default => die(), + }; + } + + function pageBackgroundImage($page) { + return match ($page) { + "benoit" => "/svg/benoitBackground.svg", + "dux" => "/image/duxBackground.webp", + default => null, + }; + } + + function pageGlyph($page) { + return "/svg/glyph/" . $page . ".svg"; + } + + function addHeading($title, $anchor) { + $anchor = "anchor." . $anchor; + + echo "<h1 id=\"$anchor\"><a class=\"anchor\" href=\"#$anchor\" title=\"Anchor\"></a> $title</h1>"; + } + + function addImage($image, $alt) { + $sourceAddr = "/image/source/" . $image . ".webp"; + $thumbnailAddr = "/image/thumbnail/" . $image . ".avif"; + + echo <<<HTML + <div class="image"> + <img class="blur" src="$thumbnailAddr"> + <a href="$sourceAddr" rel="noopener noreferrer" target="_blank" title="Click to view image source."> + <img alt="$alt" src="$thumbnailAddr"> + </a> + </div> + HTML; + } +?> diff --git a/include/u8c.php b/include/u8c.php new file mode 100644 index 0000000..c0ded80 --- /dev/null +++ b/include/u8c.php @@ -0,0 +1,25 @@ +<?php addHeading("u8c", "about"); ?> + +<section> + <p><em>u8c</em> is a library for handling Unicode sequences in C.</p> +</section> + +<?php addHeading("Features", "features"); ?> + +<section> + <p><em>u8c</em> supports the following facilities:</p> + <br> + <ul> + <li> + <p>Encoding and decoding <em>UTF-8</em></p> + </li> + <li> + <p>Encoding and decoding <em>UTF-16</em></p> + </li> + <li> + <p>Manipulation of <em>UTF-32</em> strings.</p> + </li> + </ul> + <br> + <p>Note that <em>UTF-32</em> is used as an intermediate format between conversions.</p> +</section> diff --git a/index.php b/index.php new file mode 100644 index 0000000..2ce1170 --- /dev/null +++ b/index.php @@ -0,0 +1,229 @@ +<?php require "include/prelude.php" ?> + +<?php + $currentPage = readConfig("p"); + + // Remember to sanitise unsafe values. + + $currentPage = match ($currentPage) { + "achernar", + "agbsum", + "ax", + "backspace", + "benoit", + "bowshock", + "bzipper", + "deltaWorld", + "dux", + "eas", + "luma", + "pollex", + "u8c", + => $currentPage, + + default => "achernar", + }; +?> + +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8"> + <meta name="author" content="Gabriel Bjørnager Jensen"> + <meta name="darkreader-lock"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + + <?php + [$title, $description, $keywords] = match ($currentPage) { + "achernar" => [ + "Achernar", + "Achernar is a Danish indie studio developing video games and open-source software.", + "achernar, fractals, game, open source, open-source, software, video game", + ], + + "agbsum" => [ + "agbsum | Achernar", + "agbsum is a CLI utility for patching AGB images.", + "achernar, advance, agb, agbsum, cli, console, embedded, game, terminal", + ], + + "ax" => [ + "AX | Achernar", + "AX is a C framework for developing AGB apps.", + "achernar, advance, agb, arm, assembly, ax, c, c++, cpp, cxx, thumb", + ], + + "backspace" => [ + "Backspace | Achernar", + "About the Backspace game engine.", + "achernar, backspace, game engine, rust, udp, webgpu", + ], + + "benoit" => [ + "Benoit | Achernar", + "Benoit is a Rust-written fractal renderer.", + "achernar, benoit, burning ship, cli, console, fractal, julia, mandelbrot, rust, terminal, tricorn, webgpu", + ], + + "bowshock" => [ + "Bowshock | Achernar", + "About Bowshock.", + "achernar, bowshock, dangerous, frontier, game, rust, open world, sci-fi, science fiction, space, video game", + ], + + "bzipper" => [ + "bzipper | Achernar", + "bzipper is a Rust crate for serialisation and deserialisation of binary streams.", + "achernar, binary, bzipper, deserialise, deserialiser, deserialize, deserializer, octet, serialize, serializer, serialize, serializer, tcp, udp", + ], + + "deltaWorld" => [ + "Delta·World | Achernar", + "About Delta World.", + "achernar, adventure, delta world, open world, rust, webgpu", + ], + + "dux" => [ + "Dux | Achernar", + "Dux is a cross-platform widgeting library for developing GUI applications.", + "achernar, dux, multimedia, rust, webgpu, widget", + ], + + "eas" => [ + "eAS | Achernar", + "eAS is an assembler for cross-compiling to Arm ISAs.", + "achernar, agb, arm, assembler, assembly, eas, embedded, risc, thumb", + ], + + "luma" => [ + "Luma | Achernar", + "Luma is an emulator for the AGB line of devices.", + "achernar, agb, arm, luma, rust, thumb", + ], + + "pollex" => [ + "Pollex | Achernar", + "Pollex is a Rust crate for manipulating Arm ISA instructions.", + "achernar, agb, arm, pollex, rust, thumb", + ], + + "u8c" => [ + "u8c | Achernar", + "u8c is a library for handling Unicode sequences in C.", + "achernar, u8c, unicode, utf, utf-16, utf-32, utf-8, utf16, utf32, utf8", + ], + + default => die(), + }; + + echo <<<HTML + <meta name="description" content="$description"> + <meta name="keywords" content="$keywords"> + + <title>$title</title> + HTML; + ?> + + <link href="/favicon.ico" rel="icon" type="image/vnd.microsoft.icon"> + <link href="/apple-touch-icon.png" rel="apple-touch-icon" type="image/png"> + + <link href="/css/normalise.css" rel="stylesheet" type="text/css"> + <link href="/css/font.css" rel="stylesheet" type="text/css"> + <link href="/css/main.css" rel="stylesheet" type="text/css"> + <noscript> + <link href="/css/noScript.css" rel="stylesheet" type="text/css"> + </noscript> + + <script src="/js/main.js" type="text/javascript"></script> + + <style type="text/css"> + #header { + --backgroundColour: <?php echo pageColours($currentPage)[0x0] ?>; + --textColour: <?php echo pageColours($currentPage)[0x1] ?>; + + <?php + $backgroundImage = pageBackgroundImage($currentPage); + + if (!is_null($backgroundImage)) { + echo 'background-image: url("' . $backgroundImage . '");'; + } + ?> + } + </style> + </head> + + <body> + <header id="header"> + <div id="navBar"> + <?php + function addNavBarLink($title, $page) { + global $currentPage; + + $ariaCurrent = "false"; + if ($page == $currentPage) { + $ariaCurrent = "page"; + } + + $id = match ($page) { + "achernar" => "home", + default => "", + }; + + echo "<a aria-current=\"$ariaCurrent\" href=\"?p=$page\" id=\"$id\">$title</a>"; + } + ?> + + <section> + <?php addNavBarLink("ACHERNAR", "achernar"); ?> + </section> + + <section> + <?php + addNavBarLink("BENOIT", "benoit"); + addNavBarLink("BOWSHOCK", "bowshock"); + addNavBarLink("DELTA·WORLD", "deltaWorld"); + addNavBarLink("eAS", "eas"); + ?> + </section> + + <section> + <a id="themeToggler" onclick="Ach.toggleTheme();">TOGGLE THEME</a> + </section> + </div> + + <?php + $glyphAddr = pageGlyph($currentPage); + + echo "<img alt=\"$currentPage\" id=\"glyph\" src=\"$glyphAddr\" />"; + ?> + </header> + + <div id="page"> + <?php require "include/" . $currentPage . ".php" ?> + </div> + + <footer id="footer"> + <div id="romanisation" title="آخِر اَلْنَهَر"></div> + + <p id="systemDescription">ACHERNAR — Type B star; primary component of ALPHA ERIDANI; approx. (6) solar masses, (15) kilokelvin at surface; c. (140) light years from SOL III; no native lifeforms discovered, human life on terraformed ACHERNAR IV.</p> + + <p>Communications can be done in English and Danish.</p> + <br> + <p>This webservice may be cloned from <a href="https://mandelbrot.dk/achernar/" rel="noopener noreferrer" target="_blank"><code>mandelbrot.dk</code></a>.</p> + <br> + <p id="cvrNumber">vat: DK44936429</p> + <br> + <div class="center" id="emailAddress" title="Obfuscated email address."></div> + <br> + <div id="socials"> + <a class="instagram" href="https://www.instagram.com/primuseridani/" title="Instagram: primuseridani"></a> + <a class="linktree" href="https://linktr.ee/PrimusEridani/" title="Linktree: @PrimusEridani"></a> + <a class="threads" href="https://www.threads.net/@primuseridani/" title="Threads: @primuseridani"></a> + </div> + </footer> + </body> + + <script type="text/javascript"> + Ach.init(); + </script> +</html> @@ -18,9 +18,9 @@ mkdir -p "${DESTINATION}" cp "apple-touch-icon.png" "${DESTINATION}" cp "favicon.ico" "${DESTINATION}" +cp "index.php" "${DESTINATION}" cp -r "font" "${DESTINATION}" -cp -r "html" "${DESTINATION}" cp -r "image" "${DESTINATION}" cp -r "include" "${DESTINATION}" cp -r "svg" "${DESTINATION}" @@ -1,31 +1,66 @@ -/// <reference path="navigation.ts" /> -/// <reference path="page.ts" /> /// <reference path="theme.ts" /> namespace Ach { - export async function init() { + export async function init(): Promise<void> { Ach.loadTheme(); - Ach.initImages(); - Ach.initLinks(); - - window.addEventListener("popstate", (_e) => { - location.reload(); - }); - } - - export function currentPage() { - let body = Ach.getFirstElement(document, "body"); - let page = body.getAttribute("data-page"); + let glyph = Ach.getOnlyElementIn(document, "glyph"); + let page = glyph.getAttribute("alt"); if (!page) { - throw new Error("body does not specify page"); + throw new Error('glyph does not specify page in "alt" attribute'); } - return page; + let portrait = matchMedia("(orientation: portrait)"); + let reducedMotion = matchMedia("(prefers-reduced-motion)"); + + let updateDynamicGlyph = (): void => { + console.log("updating dynamic glyph"); + console.log(`note: configuration is { page: "${page}", portrait: ${portrait.matches}, reducedMotion: ${reducedMotion.matches} }`); + + let newGlyphAddr: string | undefined = undefined; + switch (page) { + case "achernar": + switch (true) { + case portrait.matches && reducedMotion.matches: + newGlyphAddr = "/svg/glyph/achernarVertical.svg"; + break; + + case portrait.matches && !reducedMotion.matches: + newGlyphAddr = "/image/achernarVerticalAnimated.webp"; + break; + + case !portrait.matches && reducedMotion.matches: + newGlyphAddr = "/svg/glyph/achernar.svg"; + break; + + case !portrait.matches && !reducedMotion.matches: + newGlyphAddr = "/image/achernarAnimated.webp"; + break; + } + + break; + + default: + break; + } + + if (newGlyphAddr) { + console.log(`note: new glyph is at "${newGlyphAddr}"`); + + glyph.setAttribute("src", newGlyphAddr); + } else { + console.log("note: no new glyph was found suitable"); + } + }; + + updateDynamicGlyph(); + + portrait .addEventListener("change", updateDynamicGlyph); + reducedMotion.addEventListener("change", updateDynamicGlyph); } - export function getFirstElement(dom: Document, tag: string): Element { + export function getFirstElementIn(dom: Document, tag: string): Element { let elements = dom.getElementsByTagName(tag); if (elements.length < 0x0) { @@ -35,7 +70,7 @@ namespace Ach { return elements[0x0]; } - export function getOnlyElement(dom: Document, id: string): Element { + export function getOnlyElementIn(dom: Document, id: string): Element { let element = dom.getElementById(id); if (!element) { diff --git a/js/navigation.ts b/js/navigation.ts deleted file mode 100644 index c1af99f..0000000 --- a/js/navigation.ts +++ /dev/null @@ -1,97 +0,0 @@ -namespace Ach { - export async function loadPage(page_name: string, anchor?: string) { - console.log(`loading page \`${page_name}\``); - - window.scrollTo({ - top: 0.0, - left: undefined, - behavior: "smooth", - }); - - let url = `/html/${page_name}.html`; - console.log(`note: page is at "${url}"`); - - window.history.pushState(page_name, "", url); - - let response = await fetch(url); - - if (!response.ok) { - throw new Error(`unable to load page: \"${response.status}\"`); - } - - let markup = await response.text(); - - let parser = new DOMParser(); - let dom = parser.parseFromString(markup, "text/html"); - - let body = Ach.getFirstElement(document, "body"); - - let title = Ach.getFirstElement(document, "title"); - let page = Ach.getOnlyElement(document, "page"); - - let newTitle = Ach.getFirstElement(dom, "title"); - let newPage = Ach.getOnlyElement(dom, "page"); - - title.replaceWith(newTitle); - body.setAttribute("data-page", page_name); - page.replaceWith(newPage); - - initImages(); - initLinks(); - - if (anchor) { - console.log(`going to anchor \`${anchor}\``); - - anchor = `anchor.${anchor}`; - - console.log(`note: anchor has id "${anchor}"`); - - let anchor_element = document.getElementById(anchor); - - if (!anchor_element) { - throw new Error(`unable to find anchor "${anchor}"`); - } - - anchor_element.scrollIntoView({ - behavior: "smooth", - }); - } - } - - export function toggleSideMenu() { - window.scrollTo({ - top: 0.0, - left: undefined, - behavior: "smooth", - }); - - let sideMenu = Ach.getOnlyElement(document, "sideMenu"); - let navBar = Ach.getOnlyElement(document, "navBar"); - let glyph = Ach.getOnlyElement(document, "glyph"); - - sideMenu.classList.toggle("visible"); - glyph.classList.toggle("hidden"); - - for (let link of navBar.getElementsByTagName("a")) { - link.classList.toggle("hidden"); - } - } - - export function parseInternalLink(address: string): [string, string | undefined] | undefined { - let regex = /\/html\/([A-Za-z0-9]+)\.html(?:#([A-Za-z0-9]+)){0,1}/; - let regex_result = regex.exec(address); - - if (!regex_result) { - return; - } - - let page = regex_result[0x1]; - let anchor = regex_result[0x2]; - - if (!page) { - return; - } - - return [page, anchor]; - } -} diff --git a/js/page.ts b/js/page.ts deleted file mode 100644 index c4f0d71..0000000 --- a/js/page.ts +++ /dev/null @@ -1,104 +0,0 @@ -namespace Ach { - export async function initImages() { - let page = Ach.getOnlyElement(document, "page"); - - let imageList = Array.from(page.getElementsByTagName("x-image")); - - for (let image of imageList) { - let alt = image.getAttribute("alt"); - let file = image.getAttribute("data-file"); - - if (!alt) { - alert("stupid superuser forgot to add image caption, please notify"); - alt = ""; - } - - if (!file) { - throw new Error("file not set for image element"); - } - - console.log("initialising image that links to \"" + file + "\""); - - let sourceUrl = "/image/source/" + file + ".webp"; - let thumbnailUrl = "/image/thumbnail/" + file + ".avif"; - - let blurElement = document.createElement("img"); - blurElement.setAttribute("class", "blur"); - blurElement.setAttribute("src", thumbnailUrl); - - let hyperlinkElement = document.createElement("a"); - hyperlinkElement.setAttribute("href", sourceUrl); - hyperlinkElement.setAttribute("rel", "noopener noreferrer"); - hyperlinkElement.setAttribute("target", "_blank"); - hyperlinkElement.setAttribute("title", "Click to view image source."); - - let image_element = document.createElement("img"); - image_element.setAttribute("alt", alt); - image_element.setAttribute("src", thumbnailUrl); - - hyperlinkElement.appendChild(image_element); - - image.appendChild(blurElement); - image.appendChild(hyperlinkElement); - } - } - - export async function initLinks() { - console.log("initialising links"); - - let stats = { - total: 0x0, - overrideCount: 0x0, - currentCount: 0x0, - }; - - for (let link of document.getElementsByTagName("a")) { - ++stats.total; - - let address = link.getAttribute("href"); - - if (!address) { - console.log("note: skipping override of empty link"); - continue; - } - - let pageAnchor = Ach.parseInternalLink(address); - - if (!pageAnchor) { - console.log(`note: skipping override of link to "${address}"`); - continue; - } - - console.log(`note: overriding link to "${address}"`); - ++stats.overrideCount; - - let command = `Ach.loadPage(${JSON.stringify(pageAnchor[0x0])}, ${JSON.stringify(pageAnchor[0x1])})` - - link.removeAttribute("href"); - link.setAttribute("data-page", pageAnchor[0x0]); - link.setAttribute("onclick", command); - - if (pageAnchor[0x1]) { - link.setAttribute("data-anchor", pageAnchor[0x1]); - } - } - - let currentPage = Ach.currentPage(); - - for (let link of document.getElementsByTagName("a")) { - let page = link.getAttribute("data-page"); - let anchor = link.getAttribute("data-anchor"); - - if (page == currentPage && !anchor) { - ++stats.currentCount; - link.setAttribute("aria-current", "page"); - } else { - link.setAttribute("aria-current", "false"); - } - } - - console.log(`note: initialised (${stats.total}) links`); - console.log(`note: of which (${stats.overrideCount}) overrides were done`); - console.log(`note: and of which (${stats.currentCount}) were marked as current`); - } -} diff --git a/js/theme.ts b/js/theme.ts index 45101da..611f225 100644 --- a/js/theme.ts +++ b/js/theme.ts @@ -1,9 +1,8 @@ namespace Ach { - export function toggleTheme() { - let body = Ach.getFirstElement(document, "body"); + export function toggleTheme(): void { + let body = Ach.getFirstElementIn(document, "body"); let theme = "light"; - if (body.classList.contains("light")) { theme = "dark"; } @@ -14,7 +13,7 @@ namespace Ach { sessionStorage.setItem("theme", theme); } - export function loadTheme() { + export function loadTheme(): void { let theme = sessionStorage.getItem("theme"); if (!theme) { @@ -24,11 +23,11 @@ namespace Ach { switch (theme) { case "dark": - // We assume this theme in our stylesheets. + // We already assume this theme in our stylesheets. break; case "light": - let body = Ach.getFirstElement(document, "body"); + let body = Ach.getFirstElementIn(document, "body"); body.classList.add("light"); break; @@ -53,6 +53,7 @@ render_icon "svg/logo/achernarIcon.svg" 180 "apple-touch-icon.png" make_favicon "svg/favicon.svg" +make_stylesheet "font" make_stylesheet "main" make_stylesheet "noScript" diff --git a/svg/glyph/achernarVertical.svg b/svg/glyph/achernarVertical.svg new file mode 100644 index 0000000..dd37bdd --- /dev/null +++ b/svg/glyph/achernarVertical.svg @@ -0,0 +1,35 @@ +<svg height="176" width="144" xmlns="http://www.w3.org/2000/svg"> + <mask id="wordmark"> + <polygon fill="white" points="8,0 56,0 64,8 64,32 48,32 48,24 16,24 16,32 0,32 0,8" /> + <circle cx="8" cy="8" fill="white" r="8" /> + <circle cx="56" cy="8" fill="white" r="8" /> + <polygon fill="black" points="16,8 48,8 48,16 16,16" />½ + + <polygon fill="white" points="88,0 144,0 144,8 96,8 96,24 144,24 144,32 88,32 80,24 80,8" /> + <circle cx="88" cy="8" fill="white" r="8" /> + <circle cx="88" cy="24" fill="white" r="8" /> + + <polygon fill="white" points="0,48 16,48 16,60 48,60 48,48 64,48 64,80 48,80 48,68 16,68 16,80 0,80" /> + + <polygon fill="white" points="80,48 144,48 144,56 96,56 96,60 112,60 112,68 96,68 96,72 144,72 144,80 80,80" /> + + <polygon fill="white" points="0,96 56,96 64,104 64,112 56,120 48,120 64,128 48,128 32,120 16,120 16,128 0,128" /> + <circle cx="56" cy="104" fill="white" r="8" /> + <circle cx="56" cy="112" fill="white" r="8" /> + <polygon fill="black" points="16,104 48,104 48,112 16,112" /> + + <polygon fill="white" points="80,96 96,96 128,112 128,96 144,96 144,128 128,128 96,112 96,128 80,128" /> + + <polygon fill="white" points="8,144 56,144 64,152 64,176 48,176 48,168 16,168 16,176 0,176 0,152" /> + <circle cx="8" cy="152" fill="white" r="8" /> + <circle cx="56" cy="152" fill="white" r="8" /> + <polygon fill="black" points="16,152 48,152 48,160 16,160" /> + + <polygon fill="white" points="80,144 136,144 144,152 144,160 136,168 128,168 144,176 128,176 112,168 96,168 96,176 80,176" /> + <circle cx="136" cy="152" fill="white" r="8" /> + <circle cx="136" cy="160" fill="white" r="8" /> + <polygon fill="black" points="96,152 128,152 128,160 96,160" /> + </mask> + + <rect fill="#FFFFFF" mask="url(#wordmark)" height="100%" width="100%" x="0" y="0" /> +</svg> diff --git a/svg/glyph/agbsum.svg b/svg/glyph/agbsum.svg new file mode 100644 index 0000000..8e5e921 --- /dev/null +++ b/svg/glyph/agbsum.svg @@ -0,0 +1,11 @@ +<svg height="36" width="84" xmlns="http://www.w3.org/2000/svg"> + <mask id="glyph"> + <polygon fill="white" points="12,0 24,0 36,36 24,36 18,18 12,36 0,36" /> + <polygon fill="black" points="0,0 18,18 0,18" /> + + <polygon fill="white" points="48,0 60,0 66,9 72,0 84,0 72,18 84,36 72,36 66,27 60,36 48,36 60,18" /> + <polygon fill="black" points="48,0 66,18 48,18" /> + </mask> + + <rect fill="#FFFFFF" height="100%" mask="url(#glyph)" width="100%" x="0" y="0" /> +</svg> diff --git a/svg/glyph/chain.svg b/svg/glyph/chain.svg new file mode 100644 index 0000000..6139779 --- /dev/null +++ b/svg/glyph/chain.svg @@ -0,0 +1,23 @@ +<svg height="96" width="96" xmlns="http://www.w3.org/2000/svg"> + <clipPath id="clip0"> + <polygon points="32,0 96,0 96,64" /> + </clipPath> + + <clipPath id="clip1"> + <polygon points="0,32 64,96 0,96" /> + </clipPath> + + <mask id="glyph"> + <circle clip-path="url(#clip0)" cx="64" cy="32" r="26" stroke="white" stroke-width="12" /> + <circle cx="45.615223689" cy="13.615223689" fill="white" r="6" /> + <circle cx="82.384776311" cy="50.384776311" fill="white" r="6" /> + + <circle clip-path="url(#clip1)" cx="32" cy="64" r="26" stroke="white" stroke-width="12" /> + <circle cx="13.615223689" cy="45.615223689" fill="white" r="6" /> + <circle cx="50.384776311" cy="82.384776311" fill="white" r="6" /> + + <polyline points="64,32 32,64" stroke="white" stroke-linecap="round" stroke-width="12" /> + </mask> + + <rect fill="#FFFFFF" mask="url(#glyph)" height="100%" width="100%" x="0" y="0" /> +</svg> diff --git a/svg/glyph/dux.svg b/svg/glyph/dux.svg index 02f65b1..a071490 100644 --- a/svg/glyph/dux.svg +++ b/svg/glyph/dux.svg @@ -33,17 +33,17 @@ <mask id="spiral"> <circle cx="88" cy="88" fill="none" r="84" stroke="white" stroke-width="8" /> - <circle clip-path="url(#semicircle-vertical-left)" cx="88" cy="130" fill="none" r="42" stroke="#000" stroke-width="24" /> - <circle clip-path="url(#semicircle-vertical-right)" cx="88" cy="46" fill="none" r="42" stroke="#000" stroke-width="24" /> - <circle cx="88" cy="88" fill="#000" r="8" /> + <circle clip-path="url(#semicircle-vertical-left)" cx="88" cy="130" fill="none" r="42" stroke="black" stroke-width="24" /> + <circle clip-path="url(#semicircle-vertical-right)" cx="88" cy="46" fill="none" r="42" stroke="black" stroke-width="24" /> + <circle cx="88" cy="88" fill="black" r="12" /> <circle clip-path="url(#semicircle-vertical-left)" cx="88" cy="130" fill="none" r="42" stroke="white" stroke-width="8" /> <circle clip-path="url(#semicircle-vertical-right)" cx="88" cy="46" fill="none" r="42" stroke="white" stroke-width="8" /> <circle cx="88" cy="88" fill="white" r="4" /> - <circle clip-path="url(#semicircle-horizontal-high)" cx="46" cy="88" fill="none" r="42" stroke="#000" stroke-width="24" /> - <circle clip-path="url(#semicircle-horizontal-low)" cx="130" cy="88" fill="none" r="42" stroke="#000" stroke-width="24" /> - <circle cx="88" cy="88" fill="#000" r="8" /> + <circle clip-path="url(#semicircle-horizontal-high)" cx="46" cy="88" fill="none" r="42" stroke="black" stroke-width="24" /> + <circle clip-path="url(#semicircle-horizontal-low)" cx="130" cy="88" fill="none" r="42" stroke="black" stroke-width="24" /> + <circle cx="88" cy="88" fill="black" r="12" /> <circle clip-path="url(#semicircle-horizontal-high)" cx="46" cy="88" fill="none" r="42" stroke="white" stroke-width="8" /> <circle clip-path="url(#semicircle-horizontal-low)" cx="130" cy="88" fill="none" r="42" stroke="white" stroke-width="8" /> diff --git a/svg/icon/hamburger.svg b/svg/icon/hamburger.svg deleted file mode 100644 index fec654b..0000000 --- a/svg/icon/hamburger.svg +++ /dev/null @@ -1,9 +0,0 @@ -<svg height="15" width="15" xmlns="http://www.w3.org/2000/svg"> - <mask id="glyph"> - <rect fill="white" height="3" rx="1" width="100%" x="0" y="0" /> - <rect fill="white" height="3" rx="1" width="100%" x="0" y="6" /> - <rect fill="white" height="3" rx="1" width="100%" x="0" y="12" /> - </mask> - - <rect fill="white" mask="url(#glyph)" height="100%" width="100%" x="0" y="0" /> -</svg> diff --git a/svg/icon/home.svg b/svg/icon/home.svg deleted file mode 100644 index 9d1ed85..0000000 --- a/svg/icon/home.svg +++ /dev/null @@ -1,10 +0,0 @@ -<svg height="64" width="64" xmlns="http://www.w3.org/2000/svg"> - <mask id="glyph"> - <polygon fill="white" points="16,0 48,0 64,16 64,64 48,64 48,48 16,48 16,64 0,64 0,16" /> - <circle cx="16" cy="16" fill="white" r="16" /> - <circle cx="48" cy="16" fill="white" r="16" /> - <polygon fill="black" points="16,16 48,16 48,32 16,32" /> - </mask> - - <rect fill="black" mask="url(#glyph)" height="100%" width="100%" x="0" y="0" /> -</svg> diff --git a/svg/logo/agbsum.svg b/svg/logo/agbsum.svg new file mode 100644 index 0000000..cda6147 --- /dev/null +++ b/svg/logo/agbsum.svg @@ -0,0 +1,12 @@ +<svg height="108" width="108" xmlns="http://www.w3.org/2000/svg"> + <mask id="letters"> + <polygon fill="white" points="24,36 36,36 48,72 36,72 30,54 24,72 12,72" /> + <polygon fill="black" points="12,36 30,54 12,54" /> + + <polygon fill="white" points="60,36 72,36 78,45 84,36 96,36 84,54 96,72 84,72 78,63 72,72 60,72 72,54" /> + <polygon fill="black" points="60,36 78,54 60,54" /> + </mask> + + <rect fill="#422984" height="100%" width="100%" x="0" y="0" /> + <rect fill="#FFFFFF" height="100%" mask="url(#letters)" width="100%" x="0" y="0" /> +</svg> diff --git a/svg/logo/dux.svg b/svg/logo/dux.svg index d8bcf97..ab213c4 100644 --- a/svg/logo/dux.svg +++ b/svg/logo/dux.svg @@ -42,9 +42,9 @@ <circle cx="96" cy="96" fill="none" r="84" stroke="white" stroke-width="8" /> <!-- vertical spiral arm border --> - <circle clip-path="url(#semicircle-vertical-left)" cx="96" cy="138" fill="none" r="42" stroke="#000" stroke-width="24" /> - <circle clip-path="url(#semicircle-vertical-right)" cx="96" cy="54" fill="none" r="42" stroke="#000" stroke-width="24" /> - <circle cx="96" cy="96" fill="#000" r="8" /> + <circle clip-path="url(#semicircle-vertical-left)" cx="96" cy="138" fill="none" r="42" stroke="black" stroke-width="24" /> + <circle clip-path="url(#semicircle-vertical-right)" cx="96" cy="54" fill="none" r="42" stroke="black" stroke-width="24" /> + <circle cx="96" cy="96" fill="black" r="12" /> <!-- vertical spiral arm --> <circle clip-path="url(#semicircle-vertical-left)" cx="96" cy="138" fill="none" r="42" stroke="white" stroke-width="8" /> @@ -52,9 +52,9 @@ <circle cx="96" cy="96" fill="white" r="4" /> <!-- horizontal spiral arm border --> - <circle clip-path="url(#semicircle-horizontal-high)" cx="54" cy="96" fill="none" r="42" stroke="#000" stroke-width="24" /> - <circle clip-path="url(#semicircle-horizontal-low)" cx="138" cy="96" fill="none" r="42" stroke="#000" stroke-width="24" /> - <circle cx="96" cy="96" fill="#000" r="8" /> + <circle clip-path="url(#semicircle-horizontal-high)" cx="54" cy="96" fill="none" r="42" stroke="black" stroke-width="24" /> + <circle clip-path="url(#semicircle-horizontal-low)" cx="138" cy="96" fill="none" r="42" stroke="black" stroke-width="24" /> + <circle cx="96" cy="96" fill="black" r="12" /> <!-- horizontal spiral arm --> <circle clip-path="url(#semicircle-horizontal-high)" cx="54" cy="96" fill="none" r="42" stroke="white" stroke-width="8" /> |