Compare commits

...

4 commits

Author SHA1 Message Date
c3bbc5eb8a Host Dreccan Mono theme + fonts for css-include 2026-08-07 15:34:41 -05:00
Elm
de0c724112 Build on server instead of locally to fix ICU library mismatch
The binary was being built locally on Arch (with ICU 78) and then
deployed to Debian, causing shared library errors. Now the deployment
installs Bun on the server and builds the binary there, ensuring
compatibility with the target system's libraries.
2026-03-05 08:39:54 -06:00
Elm
28b94a096a fix: use auto for ansible_python_interpreter
The remote host doesn't have python3.11, so switch to auto detection
2026-03-05 08:11:34 -06:00
64d01a3f40 Remove AI agents post (outdated) 2026-03-05 08:05:40 -06:00
13 changed files with 747 additions and 7 deletions

View file

@ -28,13 +28,54 @@
owner: "{{ app_user }}" owner: "{{ app_user }}"
group: "{{ app_user }}" group: "{{ app_user }}"
- name: Copy binary to target location - name: Install Bun
copy: shell: curl -fsSL https://bun.sh/install | bash
src: ../target/sepiatones_xyz args:
dest: "{{ app_home }}/sepiatones_xyz" creates: /home/{{ app_user }}/.bun/bin/bun
become_user: "{{ app_user }}"
- name: Create source directory
file:
path: "{{ app_home }}/src"
state: directory
owner: "{{ app_user }}" owner: "{{ app_user }}"
group: "{{ app_user }}" group: "{{ app_user }}"
mode: "0755"
- name: Copy package.json
copy:
src: ../package.json
dest: "{{ app_home }}/package.json"
owner: "{{ app_user }}"
group: "{{ app_user }}"
- name: Copy bun.lockb
copy:
src: ../bun.lockb
dest: "{{ app_home }}/bun.lockb"
owner: "{{ app_user }}"
group: "{{ app_user }}"
- name: Copy source files
synchronize:
src: ../src/
dest: "{{ app_home }}/src/"
recursive: yes
owner: no
group: no
archive: yes
delete: yes
- name: Install dependencies
shell: /home/{{ app_user }}/.bun/bin/bun install --frozen-lockfile
args:
chdir: "{{ app_home }}"
become_user: "{{ app_user }}"
- name: Build binary
shell: /home/{{ app_user }}/.bun/bin/bun build --compile --minify --target bun --outfile ./sepiatones_xyz ./src/index.tsx
args:
chdir: "{{ app_home }}"
become_user: "{{ app_user }}"
- name: Copy data directory - name: Copy data directory
synchronize: synchronize:

View file

@ -1,3 +1,3 @@
# It is expected that you have sepiatonesxyz defined in your .ssh/config # It is expected that you have sepiatonesxyz defined in your .ssh/config
[production] [production]
sepiatonesxyz ansible_host=sepiatonesxyz ansible_python_interpreter=/usr/bin/python3.11 sepiatonesxyz ansible_host=sepiatonesxyz ansible_python_interpreter=auto

BIN
bun.lockb Normal file → Executable file

Binary file not shown.

View file

@ -0,0 +1,699 @@
/* ============================================================
DENSE a classless CSS sheet
Flat, squared, information-dense, general-purpose.
Backbone: ress / resset v5.0.2 (inline below, MIT)
Font: Ioskeley Mono <https://github.com/ahatem/IoskeleyMono>
Aesthetic: U.S. Graphics Co. (usgraphics.com) + the-monospace-web
Swap the entire color scheme by editing the two :root token
blocks below (light / dark). Everything is driven by them.
============================================================ */
/* ------- fonts ------------------------------------------------- */
@font-face {
font-family: "Ioskeley Mono";
src: url("fonts/IoskeleyMono-Regular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Ioskeley Mono";
src: url("fonts/IoskeleyMono-Italic.woff2") format("woff2");
font-weight: 400;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "Ioskeley Mono";
src: url("fonts/IoskeleyMono-SemiBold.woff2") format("woff2");
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Ioskeley Mono";
src: url("fonts/IoskeleyMono-Bold.woff2") format("woff2");
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: "Ioskeley Mono";
src: url("fonts/IoskeleyMono-BoldItalic.woff2") format("woff2");
font-weight: 700;
font-style: italic;
font-display: swap;
}
@font-face {
font-family: "Ioskeley Mono";
src: url("fonts/IoskeleyMono-Black.woff2") format("woff2");
font-weight: 900;
font-style: normal;
font-display: swap;
}
/* ------- design tokens (LIGHT theme, default) ----------------
EDIT THIS BLOCK (or the dark block below) to re-skin the sheet.
------------------------------------------------------------ */
:root {
/* base palette */
--bg: #f4f4ef; /* page background */
--bg-alt: #e9e9e1; /* raised / inset panels */
--fg: #16160f; /* main text */
--fg-muted: #5c5c52; /* secondary text */
--border: #16160f; /* hard rules & outlines */
--border-soft: #a6a69a; /* faint inner rules */
/* accent (used sparingly: focus, selection, marks) */
--accent: #0047ff;
--accent-fg: #ffffff;
/* type */
--font-family: "Ioskeley Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
--font-weight-body: 400;
--font-weight-strong: 700;
--font-weight-heading: 700;
--line-height: 1.45;
--scale: 1rem; /* base font size */
--measure: 92ch; /* page column width */
--sides: 4ch; /* horizontal page padding */
/* spacing (1lh = one line of leaded text) */
--space: 0.5lh;
--space-lg: 1lh;
--border-thickness: 2px;
--border-heavy: var(--border-thickness);
--radius: 0; /* squared by design */
font-family: var(--font-family);
font-weight: var(--font-weight-body);
font-style: normal;
font-size: var(--scale);
line-height: var(--line-height);
font-variant-numeric: tabular-nums lining-nums;
font-feature-settings: "liga" 1, "calt" 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: var(--fg);
background-color: var(--bg);
}
/* ------- design tokens (DARK theme) --------------------------
Auto-applied via prefers-color-scheme, or force it by putting
data-theme="dark" / data-theme="light" on the <html> element.
------------------------------------------------------------ */
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--bg: #101010;
--bg-alt: #1b1b1b;
--fg: #ecece5;
--fg-muted: #8c8c82;
--border: #c6c6bb;
--border-soft: #3d3d37;
--accent: #7aa2ff;
--accent-fg: #0a0a12;
}
}
:root[data-theme="dark"] {
--bg: #101010;
--bg-alt: #1b1b1b;
--fg: #ecece5;
--fg-muted: #8c8c82;
--border: #c6c6bb;
--border-soft: #3d3d37;
--accent: #7aa2ff;
--accent-fg: #0a0a12;
}
/* ============================================================
BACKBONE ress / resset v5.0.2 (MIT, inline)
============================================================ */
/* resset.dev • v5.0.2 */
/* ---- Global selectors ---- */
html {
box-sizing: border-box;
-webkit-text-size-adjust: 100%;
word-break: normal;
-moz-tab-size: 4;
tab-size: 4;
}
*,
::before,
::after {
background-repeat: no-repeat;
box-sizing: inherit;
}
::before,
::after {
text-decoration: inherit;
vertical-align: inherit;
}
* {
padding: 0;
margin: 0;
}
/* ---- General elements ---- */
hr { overflow: visible; height: 0; color: inherit; }
details, main { display: block; }
summary { display: list-item; }
small { font-size: 80%; }
[hidden] { display: none; }
abbr[title] {
border-bottom: none;
text-decoration: underline;
text-decoration: underline dotted;
}
a { background-color: transparent; }
a:active, a:hover { outline-width: 0; }
code, kbd, pre, samp { font-family: monospace, monospace; }
pre { font-size: 1em; }
b, strong { font-weight: bolder; }
sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; }
sub { bottom: -0.25em; }
sup { top: -0.5em; }
table { border-color: inherit; text-indent: 0; }
iframe { border-style: none; }
/* ---- Forms ---- */
input { border-radius: 0; }
[type='number']::-webkit-inner-spin-button, [type='number']::-webkit-outer-spin-button { height: auto; }
[type='search'] { -webkit-appearance: textfield; outline-offset: -2px; }
[type='search']::-webkit-search-decoration { -webkit-appearance: none; }
textarea { overflow: auto; resize: vertical; }
button, input, optgroup, select, textarea { font: inherit; }
optgroup { font-weight: bold; }
button { overflow: visible; }
button, select { text-transform: none; }
button, [type='button'], [type='reset'], [type='submit'], [role='button'] { cursor: pointer; }
button::-moz-focus-inner, [type='button']::-moz-focus-inner,
[type='reset']::-moz-focus-inner, [type='submit']::-moz-focus-inner { border-style: none; padding: 0; }
button:-moz-focusring, [type='button']::-moz-focus-inner,
[type='reset']::-moz-focus-inner, [type='submit']::-moz-focus-inner { outline: 1px dotted ButtonText; }
button, html [type='button'], [type='reset'], [type='submit'] { -webkit-appearance: button; }
button, input, select, textarea { background-color: transparent; border-style: none; }
a:focus, button:focus, input:focus, select:focus, textarea:focus { outline-width: 0; }
select { -moz-appearance: none; -webkit-appearance: none; }
select::-ms-expand { display: none; }
select::-ms-value { color: currentColor; }
legend { border: 0; color: inherit; display: table; white-space: normal; }
::-webkit-file-upload-button { -webkit-appearance: button; color: inherit; font: inherit; }
[disabled] { cursor: default; }
/* ---- Media ---- */
img { border-style: none; }
progress { vertical-align: baseline; }
/* ---- Accessibility state ---- */
[aria-busy='true'] { cursor: progress; }
[aria-controls] { cursor: pointer; }
[aria-disabled='true'] { cursor: default; }
/* ============================================================
LAYOUT
============================================================ */
html {
display: flex;
flex-direction: column;
align-items: center;
}
body {
width: 100%;
min-height: 100vh;
max-width: var(--measure);
padding: var(--space-lg) var(--sides);
margin: 0 auto;
display: flex;
flex-direction: column;
}
main { flex: 1 1 auto; }
/* ============================================================
FLOW UTILITIES
Add one of these single classes to any wrapper to force how
its children are laid out.
- .flow-horizontal : children share a row, wrap when tight.
- .flow-vertical : explicit column flow. Default document
flow is already vertical, so this is a
no-op today; it exists as the symmetric
counterpart and marks intent.
The page column is governed by --measure / --sides; override
those tokens per-page to reclaim horizontal space, e.g. a
dashboard.
============================================================ */
.flow-horizontal,
.flow-vertical {
display: flex;
margin: var(--space) 0;
}
.flow-vertical {
flex-direction: column;
}
.flow-horizontal {
flex-flow: row wrap;
align-items: stretch;
gap: var(--space);
}
.flow-horizontal > * {
flex: 1 1 0;
margin-block: 0;
}
/* ============================================================
HEADINGS
============================================================ */
h1, h2, h3, h4, h5, h6 {
font-family: inherit;
font-weight: var(--font-weight-heading);
text-transform: uppercase;
letter-spacing: 0.04em;
line-height: 1.2;
}
h1 {
font-size: clamp(1.7rem, 4.5vw, 2.4rem);
font-weight: 900;
letter-spacing: 0.02em;
margin: var(--space) 0;
padding: 0;
border-bottom: var(--border-heavy) solid var(--border);
}
h2 {
font-size: 1em;
font-weight: 700;
margin: var(--space-lg) 0;
padding-top: var(--space);
border-top: var(--border-heavy) solid var(--border);
}
h3 {
font-size: 0.85em;
font-weight: 600;
margin: var(--space) 0;
}
h4, h5, h6 {
font-size: 1em;
font-weight: 700;
margin: var(--space) 0;
}
/* ============================================================
TEXT
============================================================ */
p { margin: var(--space) 0; }
strong { font-weight: var(--font-weight-strong); }
em { font-style: italic; }
small { font-size: 0.8em; color: var(--fg-muted); }
a {
color: var(--fg);
text-decoration-thickness: var(--border-thickness);
text-underline-offset: 3px;
}
a:hover { color: var(--accent); }
a:visited { color: var(--fg); }
mark {
background: var(--accent);
color: var(--accent-fg);
padding: 0 0.2ch;
}
code {
background: var(--bg-alt);
padding: 0.05em 0.4ch;
font-weight: 500;
}
pre {
border: var(--border-heavy) solid var(--border);
background: var(--bg-alt);
padding: var(--space) 2ch;
margin: var(--space) 0;
overflow-x: auto;
font-size: 0.9em;
line-height: 1.5;
}
pre code { background: none; padding: 0; }
kbd {
border: var(--border-heavy) solid var(--border);
border-bottom-width: 4px;
background: var(--bg-alt);
padding: 0.05em 0.6ch;
font-size: 0.85em;
font-weight: 500;
}
blockquote {
border-left: 4px solid var(--border);
padding: 0 var(--space) 0 2ch;
margin: var(--space) 0;
}
blockquote p { margin: var(--space) 0; }
blockquote footer { color: var(--fg-muted); font-size: 0.85em; }
hr {
border: none;
border-top: 4px double var(--border);
margin: var(--space-lg) 0;
}
address { font-style: normal; color: var(--fg-muted); margin: var(--space) 0; }
/* ============================================================
LISTS
============================================================ */
ul, ol {
margin: var(--space) 0;
padding-left: 3ch;
}
li { margin: 0.15lh 0; }
li::marker { font-weight: 700; }
ol {
list-style: none;
counter-reset: item;
padding-left: 0;
}
ol > li {
counter-increment: item;
}
ol > li::before {
content: counters(item, ".") ". ";
font-weight: 700;
margin-right: 0.5ch;
}
ol ol, ol ul, ul ol, ul ul {
margin: 0.15lh 0;
padding-left: 3ch;
}
dl { margin: var(--space) 0; }
dt { font-weight: 700; text-transform: uppercase; font-size: 0.85em; letter-spacing: 0.05em; }
dd { margin: var(--space) 2ch; }
/* ============================================================
NAVIGATION
============================================================ */
header {
margin: 0 0 var(--space);
}
nav {
margin-top: var(--space);
padding-top: var(--space);
border-top: var(--border-heavy) solid var(--border);
}
nav ul {
list-style: none;
display: flex;
flex-wrap: wrap;
gap: 0.4lh 2.5ch;
margin: 0;
padding: 0;
}
nav a {
text-decoration: none;
font-weight: 600;
text-transform: uppercase;
font-size: 0.85em;
letter-spacing: 0.05em;
}
nav a:hover { text-decoration: underline; text-underline-offset: 3px; }
/* ============================================================
TABLES
============================================================ */
table {
width: 100%;
border-collapse: collapse;
margin: var(--space) 0;
}
th, td {
border: var(--border-heavy) solid var(--border);
padding: 0.3lh 1ch;
text-align: left;
vertical-align: top;
}
th {
font-weight: 700;
text-transform: uppercase;
font-size: 0.8em;
letter-spacing: 0.06em;
}
caption {
caption-side: bottom;
color: var(--fg-muted);
font-size: 0.8em;
text-align: left;
padding-top: var(--space);
}
/* ============================================================
FORMS
============================================================ */
label {
display: block;
font-weight: 600;
text-transform: uppercase;
font-size: 0.8em;
letter-spacing: 0.05em;
margin: var(--space) 0;
}
fieldset {
border: var(--border-heavy) solid var(--border);
padding: var(--space) 2ch;
margin: var(--space) 0;
}
legend {
background: var(--bg);
padding: 0 1ch;
font-weight: 700;
text-transform: uppercase;
font-size: 0.8em;
letter-spacing: 0.06em;
}
input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="button"]):not([type="reset"]):not([type="range"]):not([type="file"]):not([type="color"]),
select,
textarea {
display: block;
width: 100%;
border: var(--border-heavy) solid var(--border);
border-radius: var(--radius);
background: var(--bg);
color: var(--fg);
padding: 0.4lh 1ch;
margin: var(--space) 0;
font: inherit;
-webkit-appearance: none;
appearance: none;
}
:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
input:not([type="checkbox"]):not([type="radio"]):focus,
select:focus,
textarea:focus {
border-color: var(--accent);
outline: none;
}
select {
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23808080'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 1ch center;
background-size: 0.8em;
padding-right: 3ch;
}
textarea { resize: vertical; min-height: 4lh; }
::placeholder { color: var(--fg-muted); opacity: 1; }
button,
input[type="submit"],
input[type="button"],
input[type="reset"],
[role="button"] {
display: inline-block;
border: var(--border-heavy) solid var(--border);
border-radius: var(--radius);
background: var(--bg);
color: var(--fg);
padding: 0.4lh 1.4em;
margin: 0 var(--space) var(--space) 0;
font: inherit;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
line-height: 1;
cursor: pointer;
-webkit-appearance: none;
appearance: none;
}
button:hover,
input[type="submit"]:hover,
input[type="button"]:hover,
input[type="reset"]:hover {
background: var(--fg);
color: var(--bg);
}
button:active,
input[type="submit"]:active,
input[type="button"]:active,
input[type="reset"]:active {
transform: translate(1px, 1px);
}
button:disabled,
input[disabled] {
opacity: 0.5;
cursor: default;
}
/* flat, squared checkboxes & radios */
input[type="checkbox"],
input[type="radio"] {
-webkit-appearance: none;
appearance: none;
width: 1.05em;
height: 1.05em;
border: var(--border-heavy) solid var(--border);
border-radius: var(--radius);
background: var(--bg);
display: inline-block;
vertical-align: -0.2em;
margin: 0 0.6ch 0 0;
cursor: pointer;
position: relative;
}
input[type="checkbox"]:checked::before,
input[type="radio"]:checked::before {
content: "";
position: absolute;
inset: 0;
background: var(--accent);
}
input[type="checkbox"]:focus,
input[type="radio"]:focus { outline: none; border-color: var(--accent); }
/* ============================================================
DISCLOSURE (details / summary)
============================================================ */
details {
border: var(--border-heavy) solid var(--border);
margin: var(--space) 0;
}
summary {
cursor: pointer;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
font-size: 0.85em;
padding: 0.7lh 1.5ch;
list-style: none;
display: flex;
align-items: center;
gap: 1ch;
}
summary::-webkit-details-marker { display: none; }
summary::before {
content: "\25B8"; /* ▸ */
color: var(--accent);
}
details[open] summary::before { content: "\25BE"; } /* ▾ */
details[open] summary { border-bottom: var(--border-heavy) solid var(--border); }
details :is(p, pre, ul, ol, table, blockquote):last-child { margin-bottom: calc(0.7 * var(--space)); }
details > :not(summary) { padding-left: 1.5ch; padding-right: 1.5ch; }
/* ============================================================
MEDIA / MISC
============================================================ */
figure { margin: var(--space) 0; }
figcaption {
color: var(--fg-muted);
font-size: 0.8em;
margin-top: 0.35lh;
}
img, video, iframe {
display: block;
max-width: 100%;
height: auto;
border: var(--border-heavy) solid var(--border);
}
progress {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 1.2em;
border: var(--border-heavy) solid var(--border);
background: var(--bg);
margin: var(--space) 0;
}
progress::-webkit-progress-bar { background: var(--bg); }
progress::-webkit-progress-value { background: var(--accent); }
progress::-moz-progress-bar { background: var(--accent); }
footer {
margin-top: var(--space-lg);
padding-top: var(--space);
border-top: var(--border-heavy) solid var(--border);
color: var(--fg-muted);
font-size: 0.85em;
}
::selection {
background: var(--accent);
color: var(--accent-fg);
}
/* plain, squared scrollbars */
* { scrollbar-width: thin; scrollbar-color: var(--border-soft) transparent; }
*::-webkit-scrollbar { width: 12px; height: 12px; background: var(--bg); }
*::-webkit-scrollbar-thumb {
border: var(--border-thickness) solid var(--border);
background: var(--border-soft);
}
/* accessibility: no animations/transforms on demand */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
transform: none !important;
}
}
/* responsive: tighten the column on small screens */
@media screen and (max-width: 540px) {
:root { font-size: 0.92rem; }
body { padding: var(--space-lg) 2ch; }
}

Binary file not shown.

View file

@ -7,7 +7,7 @@ dev:
build: build:
bun build --compile --minify --target bun --outfile ./target/sepiatones_xyz ./src/index.tsx bun build --compile --minify --target bun --outfile ./target/sepiatones_xyz ./src/index.tsx
deploy: build deploy:
ansible-playbook -i ansible/hosts.ini ansible/deploy.yml ansible-playbook -i ansible/hosts.ini ansible/deploy.yml
test: test: