Compare commits
22 Commits
fa7232cd1e
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 247ca6ac9a | |||
| 55d37ecd92 | |||
| 666681be7c | |||
| 11660ac63d | |||
| 46aff56577 | |||
| dd915e1797 | |||
| b665293922 | |||
| aa33ce56bf | |||
| ffe9fa11fc | |||
| 6b59b58082 | |||
| 892dfe600d | |||
| 4aae68d836 | |||
| 7f6bedebdc | |||
| 55926a84f2 | |||
| 932d7a7653 | |||
| 84ac7ae562 | |||
| 37fd39f488 | |||
| 0469e9dab5 | |||
| 113a954b16 | |||
| 5c5b267a7f | |||
| f318ef9a2f | |||
| 68474c5940 |
+87
-3
@@ -147,8 +147,13 @@ body:not(.home) .menu { display: none; }
|
|||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
/* name + tags share the grid's last column but need their own row to put
|
||||||
|
the tag flush right without wrapping onto a second line — two grid items
|
||||||
|
left in the same cell just stack instead of sitting side by side */
|
||||||
|
.tui__row3 { display: flex; justify-content: space-between; align-items: baseline; gap: 1ch; min-width: 0; }
|
||||||
.tui__perm,
|
.tui__perm,
|
||||||
.tui__date { color: var(--color-text-muted); }
|
.tui__date,
|
||||||
|
.tui__tags { color: var(--color-text-muted); }
|
||||||
.tui__ls a:hover,
|
.tui__ls a:hover,
|
||||||
.tui__ls a.is-selected {
|
.tui__ls a.is-selected {
|
||||||
background: var(--color-text-primary);
|
background: var(--color-text-primary);
|
||||||
@@ -157,9 +162,11 @@ body:not(.home) .menu { display: none; }
|
|||||||
.tui__ls a:hover .tui__perm,
|
.tui__ls a:hover .tui__perm,
|
||||||
.tui__ls a:hover .tui__date,
|
.tui__ls a:hover .tui__date,
|
||||||
.tui__ls a:hover .tui__name,
|
.tui__ls a:hover .tui__name,
|
||||||
|
.tui__ls a:hover .tui__tags,
|
||||||
.tui__ls a.is-selected .tui__perm,
|
.tui__ls a.is-selected .tui__perm,
|
||||||
.tui__ls a.is-selected .tui__date,
|
.tui__ls a.is-selected .tui__date,
|
||||||
.tui__ls a.is-selected .tui__name { color: var(--color-bg-primary); }
|
.tui__ls a.is-selected .tui__name,
|
||||||
|
.tui__ls a.is-selected .tui__tags { color: var(--color-bg-primary); }
|
||||||
|
|
||||||
/* preview pane (ranger-style) below the listing */
|
/* preview pane (ranger-style) below the listing */
|
||||||
.tui__preview { margin: 1.25rem 0 0; }
|
.tui__preview { margin: 1.25rem 0 0; }
|
||||||
@@ -172,6 +179,10 @@ body:not(.home) .menu { display: none; }
|
|||||||
display: block;
|
display: block;
|
||||||
transition: opacity 0.25s ease;
|
transition: opacity 0.25s ease;
|
||||||
}
|
}
|
||||||
|
/* portfolio index only (not project pages): the big preview is itself a
|
||||||
|
second way into the already-previewed project, next to clicking its row
|
||||||
|
again */
|
||||||
|
.filebrowser:not(.tui--files) .tui__preview img { cursor: pointer; }
|
||||||
|
|
||||||
/* project page: image files (no date column), show full image (contain) */
|
/* project page: image files (no date column), show full image (contain) */
|
||||||
.tui--files .tui__ls a { grid-template-columns: 11ch 1fr; }
|
.tui--files .tui__ls a { grid-template-columns: 11ch 1fr; }
|
||||||
@@ -247,6 +258,43 @@ body:not(.home) .menu { display: none; }
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* small peek column — mirrors .proj-info's spot and width on the OTHER side
|
||||||
|
of the file list, showing whichever image is under the mouse while
|
||||||
|
something is locked. sits inside the space .proj-layout already reserves
|
||||||
|
to keep the file list anchored (see above), so it needs that reserved
|
||||||
|
padding to be at least 18rem + the gap wide to have room; 1300px keeps a
|
||||||
|
safety margin above the ~1168px point where that stops being true. plain
|
||||||
|
absolute positioning against .filebrowser — no scroll-tracking needed,
|
||||||
|
it just scrolls together with the file list it's attached to. */
|
||||||
|
.proj-peek { display: none; }
|
||||||
|
@media (min-width: 1300px) {
|
||||||
|
.filebrowser { position: relative; }
|
||||||
|
.proj-peek {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
top: 3.6rem; /* same offset as .proj-info, clearing the shell lines */
|
||||||
|
right: 100%;
|
||||||
|
margin-right: 1.5rem;
|
||||||
|
width: 18rem;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
transition: opacity 0.15s ease;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.proj-peek.is-visible { opacity: 1; visibility: visible; }
|
||||||
|
/* same fixed shape as the portfolio-index preview (.tui__preview img above)
|
||||||
|
— a plan and a photo have wildly different natural ratios, so without
|
||||||
|
this the peek box would jump size on every hover */
|
||||||
|
.proj-peek img {
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 3 / 2;
|
||||||
|
object-fit: cover;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* prev/next as shell "cd" commands — same vocabulary as the rest of the site */
|
/* prev/next as shell "cd" commands — same vocabulary as the rest of the site */
|
||||||
.proj-nav {
|
.proj-nav {
|
||||||
font-family: var(--font-family-mono);
|
font-family: var(--font-family-mono);
|
||||||
@@ -428,9 +476,45 @@ footer a:hover,
|
|||||||
.player:not(.is-playing) .player__bar { opacity: 1; }
|
.player:not(.is-playing) .player__bar { opacity: 1; }
|
||||||
.player__bar button { font: inherit; color: #fff; background: none; border: 0; padding: 0 0.4ch; line-height: 1; }
|
.player__bar button { font: inherit; color: #fff; background: none; border: 0; padding: 0 0.4ch; line-height: 1; }
|
||||||
.player__bar button:hover { background: #fff; color: #000; }
|
.player__bar button:hover { background: #fff; color: #000; }
|
||||||
.player__scrub { flex: 1; height: 3px; background: rgba(255, 255, 255, 0.3); position: relative; }
|
/* fixed width + tightened tracking so ▶ and ❚❚ render as the same size
|
||||||
|
button instead of the pause glyph's two characters (with the font's
|
||||||
|
normal letter-spacing between them) widening it */
|
||||||
|
/* pause icon is two CSS bars, not a "❚❚" glyph — a monospace font's own
|
||||||
|
per-character cell width left a gap between the glyphs that no amount of
|
||||||
|
letter-spacing closed without also looking wrong at other sizes */
|
||||||
|
.player__play { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 2.4ch; }
|
||||||
|
.player.is-playing .player__playicon { visibility: hidden; }
|
||||||
|
.player__play::before,
|
||||||
|
.player__play::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
width: 2px;
|
||||||
|
height: 0.8em;
|
||||||
|
background: currentColor;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
.player.is-playing .player__play::before { opacity: 1; left: calc(50% - 3px); }
|
||||||
|
.player.is-playing .player__play::after { opacity: 1; left: calc(50% + 1px); }
|
||||||
|
.player__scrub { flex: 1; height: 3px; background: rgba(255, 255, 255, 0.3); position: relative; cursor: pointer; }
|
||||||
.player__fill { position: absolute; left: 0; top: 0; bottom: 0; width: 0; background: #fff; }
|
.player__fill { position: absolute; left: 0; top: 0; bottom: 0; width: 0; background: #fff; }
|
||||||
.player__time { white-space: nowrap; font-variant-numeric: tabular-nums; color: #fff; }
|
.player__time { white-space: nowrap; font-variant-numeric: tabular-nums; color: #fff; }
|
||||||
|
.player__vol { width: 4ch; height: 3px; background: rgba(255, 255, 255, 0.3); position: relative; cursor: pointer; }
|
||||||
|
.player__volfill { position: absolute; left: 0; top: 0; bottom: 0; width: 100%; background: #fff; }
|
||||||
|
/* invisible hit-area extending past the thin visual bar — the bar itself
|
||||||
|
stays 3px, but clicking/dragging within 5px above or below it still
|
||||||
|
counts (rects used for the seek/volume math are still the element's own,
|
||||||
|
unaffected by this) */
|
||||||
|
.player__scrub::before,
|
||||||
|
.player__vol::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -5px;
|
||||||
|
bottom: -5px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
.player:fullscreen { width: 100vw; height: 100vh; aspect-ratio: auto; }
|
.player:fullscreen { width: 100vw; height: 100vh; aspect-ratio: auto; }
|
||||||
|
|
||||||
/* all images always in colour (selection drives what's shown, not hover) */
|
/* all images always in colour (selection drives what's shown, not hover) */
|
||||||
|
|||||||
+84
-20
@@ -1,15 +1,14 @@
|
|||||||
/* tui file browser — preview pane (image OR text panes), hover + freeze +
|
/* tui file browser — preview pane (image OR text panes), hover + lock +
|
||||||
keyboard. re-binds itself after pjax navigation (document listeners are added
|
keyboard. re-binds itself after pjax navigation (document listeners are added
|
||||||
once; rows/preview are re-queried per page). */
|
once; rows/preview are re-queried per page). */
|
||||||
(function () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
var rows = [], img = null, texts = [], cap = null, cur = 0, frozen = false, container = null, player = null, pv = null;
|
var rows = [], img = null, texts = [], cap = null, cur = 0, locked = -1, container = null, player = null, pv = null;
|
||||||
|
|
||||||
function hideTexts() { texts.forEach(function (t) { t.hidden = true; }); }
|
function hideTexts() { texts.forEach(function (t) { t.hidden = true; }); }
|
||||||
function showText(k) { texts.forEach(function (t) { t.hidden = (t.getAttribute("data-text") !== k); }); }
|
function showText(k) { texts.forEach(function (t) { t.hidden = (t.getAttribute("data-text") !== k); }); }
|
||||||
function hidePlayer() { if (player) { player.hidden = true; if (pv && !pv.paused) pv.pause(); } }
|
function hidePlayer() { if (player) { player.hidden = true; if (pv && !pv.paused) pv.pause(); } }
|
||||||
function showPlayer() { if (player) player.hidden = false; }
|
function showPlayer() { if (player) player.hidden = false; }
|
||||||
function setFrozen(v) { frozen = v; if (container) container.classList.toggle("is-frozen", v); }
|
|
||||||
|
|
||||||
// tell the shell to "type" the matching command. navigable rows (those with an
|
// tell the shell to "type" the matching command. navigable rows (those with an
|
||||||
// href: ".." and the portfolio project folders) are directories → `cd`; the
|
// href: ".." and the portfolio project folders) are directories → `cd`; the
|
||||||
@@ -31,16 +30,16 @@
|
|||||||
if (img.complete) img.style.opacity = "1"; // already cached → no fade
|
if (img.complete) img.style.opacity = "1"; // already cached → no fade
|
||||||
}
|
}
|
||||||
|
|
||||||
function select(i, silent, scroll) {
|
// renders row i into the MAIN preview pane. driven by lock (click) and
|
||||||
|
// keyboard nav only — hovering never touches this, it drives the small
|
||||||
|
// peek column instead (see bind()), so a locked file stays put while you
|
||||||
|
// browse the others.
|
||||||
|
function render(i, silent, scroll) {
|
||||||
if (!rows.length) return;
|
if (!rows.length) return;
|
||||||
if (i < 0) i = 0;
|
if (i < 0) i = 0;
|
||||||
if (i >= rows.length) i = rows.length - 1;
|
if (i >= rows.length) i = rows.length - 1;
|
||||||
cur = i;
|
cur = i;
|
||||||
for (var r = 0; r < rows.length; r++) rows[r].classList.remove("is-selected");
|
|
||||||
var a = rows[i];
|
var a = rows[i];
|
||||||
a.classList.add("is-selected");
|
|
||||||
// only scroll for keyboard nav — scrolling on hover nudges edge rows out from
|
|
||||||
// under the cursor, so the top/bottom row "sometimes" can't be clicked.
|
|
||||||
if (scroll && a.scrollIntoView) a.scrollIntoView({ block: "nearest" });
|
if (scroll && a.scrollIntoView) a.scrollIntoView({ block: "nearest" });
|
||||||
|
|
||||||
var src = a.getAttribute("data-img");
|
var src = a.getAttribute("data-img");
|
||||||
@@ -49,7 +48,8 @@
|
|||||||
if (src) { showImg(src); hideTexts(); hidePlayer(); }
|
if (src) { showImg(src); hideTexts(); hidePlayer(); }
|
||||||
else if (vid) { if (img) img.hidden = true; hideTexts(); showPlayer(); }
|
else if (vid) { if (img) img.hidden = true; hideTexts(); showPlayer(); }
|
||||||
else if (tkey) { if (img) img.hidden = true; hidePlayer(); showText(tkey); }
|
else if (tkey) { if (img) img.hidden = true; hidePlayer(); showText(tkey); }
|
||||||
else { if (img) img.hidden = true; hideTexts(); hidePlayer(); }
|
// rows with no preview of their own (".." above all) leave whatever was
|
||||||
|
// last shown in place instead of blanking the pane out
|
||||||
|
|
||||||
if (cap) {
|
if (cap) {
|
||||||
var n = a.querySelector(".tui__name");
|
var n = a.querySelector(".tui__name");
|
||||||
@@ -57,9 +57,17 @@
|
|||||||
cap.textContent = (n ? n.textContent.trim() : a.getAttribute("data-name")) + (y ? " " + y.textContent.trim() : "");
|
cap.textContent = (n ? n.textContent.trim() : a.getAttribute("data-name")) + (y ? " " + y.textContent.trim() : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!silent) ghostCmd(a); // echo into the shell, except on the initial load
|
if (!silent) ghostCmd(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function lock(i) {
|
||||||
|
locked = i;
|
||||||
|
for (var r = 0; r < rows.length; r++) rows[r].classList.remove("is-selected");
|
||||||
|
if (rows[i]) rows[i].classList.add("is-selected");
|
||||||
|
if (container) container.classList.toggle("is-frozen", locked >= 0);
|
||||||
|
}
|
||||||
|
function unlock() { lock(-1); }
|
||||||
|
|
||||||
function bind() {
|
function bind() {
|
||||||
var list = document.querySelector(".filebrowser .tui__ls");
|
var list = document.querySelector(".filebrowser .tui__ls");
|
||||||
container = list ? list.closest(".filebrowser") : null;
|
container = list ? list.closest(".filebrowser") : null;
|
||||||
@@ -69,21 +77,73 @@
|
|||||||
player = document.querySelector(".tui__preview .player");
|
player = document.querySelector(".tui__preview .player");
|
||||||
pv = player ? player.querySelector("video") : null;
|
pv = player ? player.querySelector("video") : null;
|
||||||
cap = document.querySelector(".tui__status");
|
cap = document.querySelector(".tui__status");
|
||||||
cur = 0; frozen = false;
|
cur = 0; locked = -1;
|
||||||
if (!rows.length) return;
|
if (!rows.length) return;
|
||||||
|
|
||||||
|
// small side-by-side peek — sits to the left of the file list (mirroring
|
||||||
|
// the info text's spot on the right, same 18rem width) and shows
|
||||||
|
// whichever OTHER image is under the mouse while something is locked,
|
||||||
|
// without disturbing the main preview.
|
||||||
|
var peek = null, peekImg = null;
|
||||||
|
if (container) {
|
||||||
|
peek = document.createElement("div");
|
||||||
|
peek.className = "proj-peek";
|
||||||
|
peekImg = document.createElement("img");
|
||||||
|
peekImg.alt = "";
|
||||||
|
peek.appendChild(peekImg);
|
||||||
|
container.insertBefore(peek, container.firstChild);
|
||||||
|
}
|
||||||
|
function showPeek(a) {
|
||||||
|
var src = a.getAttribute("data-img");
|
||||||
|
if (!peek || !src) { if (peek) peek.classList.remove("is-visible"); return; }
|
||||||
|
peekImg.src = src;
|
||||||
|
peek.classList.add("is-visible");
|
||||||
|
}
|
||||||
|
function hidePeek() { if (peek) peek.classList.remove("is-visible"); }
|
||||||
|
|
||||||
rows.forEach(function (a, i) {
|
rows.forEach(function (a, i) {
|
||||||
a.addEventListener("mouseenter", function () { if (!frozen) select(i); });
|
// portfolio-index rows are real links (data-img + href together) — a
|
||||||
|
// single click there used to jump straight into the project, which
|
||||||
|
// meant hovering was the only way to preview a different one first.
|
||||||
|
// now a click just locks/unlocks it like any other row; the preview
|
||||||
|
// image is the dedicated way to actually navigate in.
|
||||||
|
var isProjectLink = a.hasAttribute("href") && a.hasAttribute("data-img");
|
||||||
|
a.addEventListener("mouseenter", function () {
|
||||||
|
// the peek column only exists for images — text/video rows (like on
|
||||||
|
// about) have no equivalent, so they always drive the main preview
|
||||||
|
// directly, lock or no lock, same as before locking existed
|
||||||
|
if (locked < 0 || !a.hasAttribute("data-img")) { hidePeek(); render(i); }
|
||||||
|
else if (i !== locked) showPeek(a);
|
||||||
|
else hidePeek();
|
||||||
|
});
|
||||||
a.addEventListener("click", function (e) {
|
a.addEventListener("click", function (e) {
|
||||||
if (a.getAttribute("href")) return; // navigation rows behave normally
|
if (isProjectLink) e.preventDefault(); // never navigate from the row itself
|
||||||
e.preventDefault();
|
else if (a.getAttribute("href")) return; // ".." navigates normally
|
||||||
if (frozen && cur === i) { setFrozen(false); ghostClear(); }
|
else e.preventDefault();
|
||||||
else { select(i); setFrozen(true); }
|
if (locked === i) { unlock(); ghostClear(); }
|
||||||
|
else { lock(i); render(i, true); }
|
||||||
|
hidePeek();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (list) list.addEventListener("mouseleave", hidePeek);
|
||||||
|
|
||||||
var start = rows.findIndex(function (a) { return a.classList.contains("is-selected"); });
|
var start = rows.findIndex(function (a) { return a.classList.contains("is-selected"); });
|
||||||
select(start < 0 ? 0 : start, true); // initial selection: don't echo into the shell yet
|
start = start < 0 ? 0 : start;
|
||||||
|
render(start, true); // initial preview: don't echo into the shell yet
|
||||||
|
// portfolio/about listings start locked on their first file — games stay
|
||||||
|
// unlocked, there's nothing else to browse there
|
||||||
|
if (container && !container.classList.contains("game-embed")) lock(start);
|
||||||
|
|
||||||
|
if (img) {
|
||||||
|
img.onclick = function () {
|
||||||
|
var a = rows[cur];
|
||||||
|
if (a && a.hasAttribute("href") && a.hasAttribute("data-img")) {
|
||||||
|
var href = a.getAttribute("href");
|
||||||
|
if (window.pjax) window.pjax(href); else window.location.href = href;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// warm the cache so switching between gallery images is instant
|
// warm the cache so switching between gallery images is instant
|
||||||
var srcs = rows.map(function (a) { return a.getAttribute("data-img"); }).filter(Boolean);
|
var srcs = rows.map(function (a) { return a.getAttribute("data-img"); }).filter(Boolean);
|
||||||
@@ -94,16 +154,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// one-time document listeners (reference the current bound state)
|
// one-time document listeners (reference the current bound state)
|
||||||
|
// clicking elsewhere IN THE WIDGET (the preview image, blank space in the
|
||||||
|
// list) releases the lock; clicks out in the text/page don't — a click to
|
||||||
|
// read or select a word shouldn't unlock the currently selected file.
|
||||||
document.addEventListener("click", function (e) {
|
document.addEventListener("click", function (e) {
|
||||||
if (e.target.closest && e.target.closest(".tui__ls a")) return;
|
if (e.target.closest && e.target.closest(".tui__ls a")) return;
|
||||||
if (frozen) { setFrozen(false); ghostClear(); }
|
if (!(e.target.closest && e.target.closest(".filebrowser"))) return;
|
||||||
|
if (locked >= 0) { unlock(); ghostClear(); }
|
||||||
});
|
});
|
||||||
document.addEventListener("keydown", function (e) {
|
document.addEventListener("keydown", function (e) {
|
||||||
var t = e.target;
|
var t = e.target;
|
||||||
if (t && (t.isContentEditable || /^(input|textarea|select)$/i.test(t.tagName))) return;
|
if (t && (t.isContentEditable || /^(input|textarea|select)$/i.test(t.tagName))) return;
|
||||||
if (!rows.length) return;
|
if (!rows.length) return;
|
||||||
if (e.key === "ArrowDown") { e.preventDefault(); select(cur + 1, false, true); }
|
if (e.key === "ArrowDown") { e.preventDefault(); render(cur + 1, false, true); }
|
||||||
else if (e.key === "ArrowUp") { e.preventDefault(); select(cur - 1, false, true); }
|
else if (e.key === "ArrowUp") { e.preventDefault(); render(cur - 1, false, true); }
|
||||||
else if (e.key === "Enter") {
|
else if (e.key === "Enter") {
|
||||||
var href = rows[cur] && rows[cur].getAttribute("href");
|
var href = rows[cur] && rows[cur].getAttribute("href");
|
||||||
if (href) { if (window.pjax) window.pjax(href); else window.location.href = href; }
|
if (href) { if (window.pjax) window.pjax(href); else window.location.href = href; }
|
||||||
|
|||||||
+53
-5
@@ -15,6 +15,8 @@
|
|||||||
var fill = root.querySelector(".player__fill");
|
var fill = root.querySelector(".player__fill");
|
||||||
var time = root.querySelector(".player__time");
|
var time = root.querySelector(".player__time");
|
||||||
var mute = root.querySelector(".player__mute");
|
var mute = root.querySelector(".player__mute");
|
||||||
|
var vol = root.querySelector(".player__vol");
|
||||||
|
var volFill = root.querySelector(".player__volfill");
|
||||||
var full = root.querySelector(".player__full");
|
var full = root.querySelector(".player__full");
|
||||||
if (!v) return;
|
if (!v) return;
|
||||||
|
|
||||||
@@ -23,19 +25,65 @@
|
|||||||
if (big) big.addEventListener("click", toggle);
|
if (big) big.addEventListener("click", toggle);
|
||||||
v.addEventListener("click", toggle);
|
v.addEventListener("click", toggle);
|
||||||
|
|
||||||
v.addEventListener("play", function () { root.classList.add("is-playing"); if (play) play.textContent = "❚❚"; });
|
// drive the scrub bar off rAF instead of `timeupdate` — timeupdate only
|
||||||
v.addEventListener("pause", function () { root.classList.remove("is-playing"); if (play) play.textContent = "▶"; });
|
// fires a handful of times a second (browser-dependent), which made the
|
||||||
v.addEventListener("loadedmetadata", function () { if (time) time.textContent = "00:00 / " + fmt(v.duration); });
|
// fill visibly step instead of glide; rAF ties it to the display's own
|
||||||
v.addEventListener("timeupdate", function () {
|
// refresh rate, only running while actually playing.
|
||||||
|
var raf = null;
|
||||||
|
function sync() {
|
||||||
var p = v.duration ? v.currentTime / v.duration : 0;
|
var p = v.duration ? v.currentTime / v.duration : 0;
|
||||||
if (fill) fill.style.width = (p * 100) + "%";
|
if (fill) fill.style.width = (p * 100) + "%";
|
||||||
if (time) time.textContent = fmt(v.currentTime) + " / " + fmt(v.duration || 0);
|
if (time) time.textContent = fmt(v.currentTime) + " / " + fmt(v.duration || 0);
|
||||||
|
}
|
||||||
|
function tick() { sync(); raf = requestAnimationFrame(tick); }
|
||||||
|
|
||||||
|
// the pause icon is drawn in CSS (two bars via ::before/::after on
|
||||||
|
// .is-playing), not a "❚❚" glyph — a monospace font's letter-spacing
|
||||||
|
// between two heavy-bar characters left a gap no amount of
|
||||||
|
// letter-spacing closed cleanly. the ▶ span just hides/shows.
|
||||||
|
v.addEventListener("play", function () {
|
||||||
|
root.classList.add("is-playing");
|
||||||
|
if (!raf) raf = requestAnimationFrame(tick);
|
||||||
});
|
});
|
||||||
|
v.addEventListener("pause", function () {
|
||||||
|
root.classList.remove("is-playing");
|
||||||
|
if (raf) { cancelAnimationFrame(raf); raf = null; }
|
||||||
|
sync(); // one last sync so the bar lands exactly on the paused frame
|
||||||
|
});
|
||||||
|
v.addEventListener("ended", function () { if (raf) { cancelAnimationFrame(raf); raf = null; } });
|
||||||
|
v.addEventListener("loadedmetadata", function () { if (time) time.textContent = "00:00 / " + fmt(v.duration); });
|
||||||
|
v.addEventListener("seeked", sync); // scrubbing while paused still needs to reflect immediately
|
||||||
if (scrub) scrub.addEventListener("click", function (e) {
|
if (scrub) scrub.addEventListener("click", function (e) {
|
||||||
var r = scrub.getBoundingClientRect();
|
var r = scrub.getBoundingClientRect();
|
||||||
if (v.duration) v.currentTime = ((e.clientX - r.left) / r.width) * v.duration;
|
if (v.duration) v.currentTime = ((e.clientX - r.left) / r.width) * v.duration;
|
||||||
});
|
});
|
||||||
if (mute) mute.addEventListener("click", function () { v.muted = !v.muted; mute.textContent = v.muted ? "muted" : "vol"; });
|
// volume level, not just mute — click or drag the little bar next to
|
||||||
|
// the mute button, same interaction as the scrub bar
|
||||||
|
function syncVol() {
|
||||||
|
var level = v.muted ? 0 : v.volume;
|
||||||
|
if (volFill) volFill.style.width = (level * 100) + "%";
|
||||||
|
if (mute) mute.textContent = (v.muted || v.volume === 0) ? "muted" : "vol";
|
||||||
|
}
|
||||||
|
function setVolFromEvent(e) {
|
||||||
|
var r = vol.getBoundingClientRect();
|
||||||
|
var level = Math.min(1, Math.max(0, (e.clientX - r.left) / r.width));
|
||||||
|
v.volume = level;
|
||||||
|
v.muted = level <= 0;
|
||||||
|
}
|
||||||
|
if (vol) {
|
||||||
|
vol.addEventListener("click", setVolFromEvent);
|
||||||
|
vol.addEventListener("mousedown", function (e) {
|
||||||
|
setVolFromEvent(e);
|
||||||
|
function onMove(ev) { setVolFromEvent(ev); }
|
||||||
|
function onUp() { document.removeEventListener("mousemove", onMove); document.removeEventListener("mouseup", onUp); }
|
||||||
|
document.addEventListener("mousemove", onMove);
|
||||||
|
document.addEventListener("mouseup", onUp);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
v.addEventListener("volumechange", syncVol);
|
||||||
|
syncVol();
|
||||||
|
|
||||||
|
if (mute) mute.addEventListener("click", function () { v.muted = !v.muted; syncVol(); });
|
||||||
if (full) full.addEventListener("click", function () {
|
if (full) full.addEventListener("click", function () {
|
||||||
if (document.fullscreenElement) document.exitFullscreen();
|
if (document.fullscreenElement) document.exitFullscreen();
|
||||||
else if (root.requestFullscreen) root.requestFullscreen();
|
else if (root.requestFullscreen) root.requestFullscreen();
|
||||||
|
|||||||
+3
-1
@@ -4,6 +4,8 @@ description: "über karim gabriele varano"
|
|||||||
layout: "about"
|
layout: "about"
|
||||||
---
|
---
|
||||||
|
|
||||||
mein name ist karim gabriele varano. ich bin im rheintal im kanton st. gallen aufgewachsen. vor der architektur habe ich in der elektrotechnik gearbeitet, auf der baustelle und in der planung. ich bin durch die gestalterische vorbildung gegangen und von dort ins architekturstudium an der ost und der hslu. ich habe einen bachelor in architektur und arbeite zurzeit auf den master hin. was mich an der architektur fesselt, ist ihre tiefe. sie bewegt sich zwischen dem gewicht eines materials und dem gewicht einer gesellschaftlichen krise. sie borgt sich aus dem ingenieurwesen, der soziologie, der kunst, der politik. sie ist keine wissenschaft. sie ist der ort, an dem die wissenschaften sich treffen, und an dem dieses treffen in etwas realem und bewohnbarem enden muss.
|
mein name ist karim gabriele varano. ich bin im rheintal im kanton st. gallen aufgewachsen. bevor ich zur architektur kam, habe ich in der elektrotechnik gearbeitet, auf der baustelle und in der planung. nach der gestalterischen bms habe ich architektur studiert, an der ost und später an der hslu. heute habe ich einen bachelor in architektur und studiere im master.
|
||||||
|
|
||||||
|
an der architektur interessiert mich vor allem ihre vielschichtigkeit. sie bedient sich am ingenieurwesen, an der soziologie, an der kunst, an der politik. eine eigene wissenschaft ist sie nicht, sondern der ort, an dem sich diese bereiche treffen müssen. und wo am ende etwas gebautes, bewohnbares daraus werden muss.
|
||||||
|
|
||||||
ich bin offen für [zusammenarbeit](mailto:hi@karimgabrielevarano.xyz).
|
ich bin offen für [zusammenarbeit](mailto:hi@karimgabrielevarano.xyz).
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ build:
|
|||||||
**DOSSIER**\
|
**DOSSIER**\
|
||||||
2026
|
2026
|
||||||
|
|
||||||
ein rhino-plugin für die frühe phase des architektonischen entwurfs. wände, türen, fenster, decken, treppen, geschosse und eine 2d-zeichenebene, die sich wie eine 2d-zeichenebene anfühlt. native rhino-objekte, keine parallele datenbank zum abgleichen. was es gibt, ist entweder teure proprietäre software oder vollständige bim-systeme, gebaut für die dokumentationsphasen im büromassstab, die nicht dazu passen, wie man während einer skizze tatsächlich denkt. quelloffene bim-werkzeuge leben in blender oder freecad und folgen einer 3d-zuerst-logik, nützlich, aber nicht so, wie man einen plan in 2d zeichnet. studierende und kleine büros zahlen am ende, raubkopieren oder behelfen sich im blanken rhino. dossier ist das werkzeug, das ich während meines masters und meiner eigenen arbeit selbst nutzen wollte. warum rhino und nicht ein vollständig quelloffenes cad? rhino verkauft dauerlizenzen, die man wirklich besitzt, statt abonnements, die man mietet, und seine dateiformate (3dm, opennurbs) und apis (rhinocommon, eto.forms) sind gut dokumentiert und auch ausserhalb der anwendung lesbar. das ist keine freie software, aber eine gangbare position zu eigentum und offenen daten, und dort, wo manche architekten ohnehin schon arbeiten. freecad oder librecad wären philosophisch offener, aber ihre verbreitung in der architektur ist marginal. dossier trifft die disziplin dort, wo sie steht. offene daten, eine etablierte gemeinschaft, genug infrastruktur, um darauf aufzubauen.
|
ein cad für die frühe phase des architektonischen entwurfs. wände, türen, fenster, decken, treppen, dächer, geschosse und eine 2d-zeichenebene, die sich wie eine 2d-zeichenebene anfühlt. ein semantisches modell als einzige quelle der wahrheit — grundriss, schnitt, ansicht und 3d-ansicht werden alle daraus abgeleitet, nicht als separate zeichnungen von hand synchron gehalten. begonnen als rhino-plugin, mittlerweile eine eigenständige desktop-anwendung: eigener geometrie-kernel, eigene rendering-engine, eigenes dateiformat, keine abhängigkeit von software, die man nicht besitzt. was es gibt, ist entweder teure proprietäre software oder vollständige bim-systeme, gebaut für die dokumentationsphasen im büromassstab, die nicht dazu passen, wie man während einer skizze tatsächlich denkt. quelloffene bim-werkzeuge leben in blender oder freecad und folgen einer 3d-zuerst-logik, nützlich, aber nicht so, wie man einen plan in 2d zeichnet. dossier ist das werkzeug, das ich während meines masters und meiner eigenen arbeit selbst nutzen wollte. offene daten, agpl-lizenziert, teil der openbureau-suite.
|
||||||
|
|
||||||
[repo](https://dossier.openbureau.ch)
|
[repo](https://git.openbureau.ch/karim/DOSSIER-STANDALONE)
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ build:
|
|||||||
**DOSSIER**\
|
**DOSSIER**\
|
||||||
2026
|
2026
|
||||||
|
|
||||||
a rhino plugin for early-phase architectural design. walls, doors, windows, slabs, stairs, levels, and a 2d drawing layer that feels like a 2d drawing layer. native rhino objects, no parallel database to sync. what exists is either expensive proprietary software, or full bim systems built for office-scale documentation phases that don't fit how you actually think during a sketch. open source bim tools live in blender or freecad and follow a 3d-first logic — useful, but not how you 2d draw a plan. students and small offices end up either paying, pirating, or working around in plain rhino. dossier is the tool i wanted to use myself during my master's and my own work. why rhino and not a fully open source cad? rhino sells perpetual licences you actually own instead of subscriptions you rent, and its file formats (3dm, opennurbs) and apis (rhinocommon, eto.forms) are well documented and readable outside the application. that's not free software, but it's a workable position on ownership and open data and where some architects already work. freecad or librecad would be more philosophically open, but adoption in architecture is marginal. dossier meets the discipline where it is. open data, established community, enough infrastructure to build on.
|
a cad for early-phase architectural design. walls, doors, windows, slabs, stairs, roofs, levels, and a 2d drawing layer that feels like a 2d drawing layer. one semantic model as the single source of truth — plan, section, elevation, and 3d view are all derived from it, not separate drawings kept in sync by hand. it started as a rhino plugin; it's now its own standalone desktop app, with its own geometry kernel, rendering engine, and file format — no dependency on software you don't own. what exists is either expensive proprietary software, or full bim systems built for office-scale documentation that don't fit how you actually think during a sketch. open source bim tools live in blender or freecad and follow a 3d-first logic — useful, but not how you draw a plan in 2d. dossier is the tool i wanted to use myself, during my master's and in my own work. open data, agpl-licensed, part of the openbureau suite.
|
||||||
|
|
||||||
[repo](https://dossier.openbureau.ch)
|
[repo](https://git.openbureau.ch/karim/DOSSIER-STANDALONE)
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ build:
|
|||||||
**RAPPORT**\
|
**RAPPORT**\
|
||||||
2026
|
2026
|
||||||
|
|
||||||
ein verwaltungswerkzeug für kleine architekturbüros. projektverwaltung, zeiterfassung, offerten und rechnungen, buchhaltung, personaldaten, lohn, sitzungsprotokolle. alles an einem ort statt fünf zusammengeflickter abonnements. was es gibt, ist entweder zu klein (einzweck-apps, die nicht miteinander reden) oder zu gross (unternehmenssuiten für büros mit hundert leuten, mit passendem budget dazu). kleine büros zahlen am ende für software, die für jemand anderen entworfen wurde, und füllen die lücken mit tabellen. rapport ist das werkzeug, das ich selbst nutzen wollte. freie software, selbst hostbar, keine lizenz pro arbeitsplatz. deine daten bleiben auf deinen geräten.
|
ein verwaltungswerkzeug für kleine architekturbüros: projektverwaltung, zeiterfassung, offerten und rechnungen, buchhaltung, personaldaten, lohn, sitzungsprotokolle — alles an einem ort statt fünf zusammengeflickter abonnements. freie software, selbst hostbar, keine lizenz pro arbeitsplatz. deine daten bleiben auf deinen eigenen geräten.
|
||||||
|
|
||||||
[repo](https://rapport.openbureau.ch)
|
[repo](https://git.openbureau.ch/karim/RAPPORT)
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ build:
|
|||||||
**RAPPORT**\
|
**RAPPORT**\
|
||||||
2026
|
2026
|
||||||
|
|
||||||
a studio management tool for small architecture offices. project management, time tracking, quotes and invoicing, bookkeeping, employee records, payroll, meeting minutes. all in one place instead of five subscriptions patched together. what exists is either too small (single-purpose apps that don't talk to each other) or too big (enterprise suites built for offices of a hundred people, with a budget to match). small practices end up paying for software designed for someone else and filling the gaps with spreadsheets. rapport is the tool i wanted to use myself. free software, self-hostable, no per-seat licensing. your data stays on your devices.
|
a management tool for small architecture offices: project management, time tracking, quotes and invoicing, bookkeeping, employee records, payroll, meeting minutes — one place instead of five subscriptions stitched together. free software, self-hostable, no per-seat licensing. your data stays on your own devices.
|
||||||
|
|
||||||
[repo](https://rapport.openbureau.ch)
|
[repo](https://git.openbureau.ch/karim/RAPPORT)
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ architektur läuft auf werkzeugen. die werkzeuge formen, was gedacht, gezeichnet
|
|||||||
|
|
||||||
freie und quelloffene software ist die sauberste antwort, aber nicht die einzige. worum es mir geht, ist eigentum und offenheit. werkzeuge, die du besitzt, statt sie zu mieten. dateiformate, die jeder lesen kann. daten, die nicht hinter einer bezahlschranke liegen. code als gemeingut, wo möglich. wo nicht, wenigstens offene daten.
|
freie und quelloffene software ist die sauberste antwort, aber nicht die einzige. worum es mir geht, ist eigentum und offenheit. werkzeuge, die du besitzt, statt sie zu mieten. dateiformate, die jeder lesen kann. daten, die nicht hinter einer bezahlschranke liegen. code als gemeingut, wo möglich. wo nicht, wenigstens offene daten.
|
||||||
|
|
||||||
openbureau ist der ort, an dem ich sammle, was ich in dieser richtung baue. kleine werkzeuge, für die eigene praxis geschrieben, veröffentlicht, damit andere sie nutzen oder forken können.
|
openbureau ist diese haltung, zu einer plattform geworden — noch im entstehen. dossier und rapport sind die ersten stücke, die darauf aufbauen: werkzeuge für die eigene praxis geschrieben, veröffentlicht, damit andere sie nutzen oder forken können.
|
||||||
|
|
||||||
über das hinaus, was ich baue, ist openbureau auch eine dienstleistung: ich kuratiere quelloffene software, die keine fixkosten und keine abhängigkeit erzeugt, richte sie ein und betreue sie, damit ein studio oder ein kleines büro auf werkzeugen läuft, die es besitzt, statt auf abonnements, die es mietet.
|
über das hinaus, was ich baue, ist openbureau auch eine dienstleistung: ich kuratiere quelloffene software, die keine fixkosten und keine abhängigkeit erzeugt, richte sie ein und betreue sie, damit ein studio oder ein kleines büro auf werkzeugen läuft, die es besitzt, statt auf abonnements, die es mietet.
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ architecture runs on tools. the tools shape what gets thought, drawn, and built.
|
|||||||
|
|
||||||
free and open source software is the cleanest answer, but not the only one. what i'm after is ownership and openness. tools you own instead of rent. file formats anyone can read. data that doesn't sit behind a paywall. code as a commons when possible. when it isn't, at least open data.
|
free and open source software is the cleanest answer, but not the only one. what i'm after is ownership and openness. tools you own instead of rent. file formats anyone can read. data that doesn't sit behind a paywall. code as a commons when possible. when it isn't, at least open data.
|
||||||
|
|
||||||
openbureau is where i collect what i build along these lines. small tools, written for my own practice, released so other people can use or fork them.
|
openbureau is that position turned into a platform — still taking shape. dossier and rapport are the first pieces built on it: tools written for my own practice, released so other people can use or fork them.
|
||||||
|
|
||||||
beyond what i build, openbureau is also a service: i curate open-source software that carries no fixed costs and no lock-in, set it up, and support it, so a studio or small practice can run on tools it owns instead of subscriptions it rents.
|
beyond what i build, openbureau is also a service: i curate open-source software that carries no fixed costs and no lock-in, set it up, and support it, so a studio or small practice can run on tools it owns instead of subscriptions it rents.
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ images:
|
|||||||
- { src: "/img/sem02/04.webp", name: "front-elevation.webp" }
|
- { src: "/img/sem02/04.webp", name: "front-elevation.webp" }
|
||||||
- { src: "/img/sem02/05.webp", name: "side-elevation.webp" }
|
- { src: "/img/sem02/05.webp", name: "side-elevation.webp" }
|
||||||
- { src: "/img/sem02/06.webp", name: "joint-axonometric.webp" }
|
- { src: "/img/sem02/06.webp", name: "joint-axonometric.webp" }
|
||||||
|
categories: ["design"]
|
||||||
---
|
---
|
||||||
|
|
||||||
im zentrum dieses entwurfs steht die konstruktive auseinandersetzung mit dem holzknoten – nicht als statisches detail, sondern als räumlich wirksames system. ausgehend von einem grundknoten, der ohne diagonale aussteifungen auskommt und ausschliesslich mit horizontalen und vertikalen balken funktioniert, wurde ein räumliches prinzip entwickelt, das zugleich rational und atmosphärisch ist.
|
im zentrum dieses entwurfs steht die konstruktive auseinandersetzung mit dem holzknoten – nicht als statisches detail, sondern als räumlich wirksames system. ausgehend von einem grundknoten, der ohne diagonale aussteifungen auskommt und ausschliesslich mit horizontalen und vertikalen balken funktioniert, wurde ein räumliches prinzip entwickelt, das zugleich rational und atmosphärisch ist.
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ images:
|
|||||||
- { src: "/img/sem02/04.webp", name: "front-elevation.webp" }
|
- { src: "/img/sem02/04.webp", name: "front-elevation.webp" }
|
||||||
- { src: "/img/sem02/05.webp", name: "side-elevation.webp" }
|
- { src: "/img/sem02/05.webp", name: "side-elevation.webp" }
|
||||||
- { src: "/img/sem02/06.webp", name: "joint-axonometric.webp" }
|
- { src: "/img/sem02/06.webp", name: "joint-axonometric.webp" }
|
||||||
|
categories: ["design"]
|
||||||
---
|
---
|
||||||
|
|
||||||
at the centre of this design is the constructive exploration of the timber joint — not as a static detail, but as a spatially effective system. starting from a basic joint that works without diagonal bracing, relying solely on horizontal and vertical beams, a spatial principle was developed that is both rational and atmospheric.
|
at the centre of this design is the constructive exploration of the timber joint — not as a static detail, but as a spatially effective system. starting from a basic joint that works without diagonal bracing, relying solely on horizontal and vertical beams, a spatial principle was developed that is both rational and atmospheric.
|
||||||
|
|||||||
@@ -7,10 +7,15 @@ schools: ["hslu"]
|
|||||||
degrees: ["ma"]
|
degrees: ["ma"]
|
||||||
images:
|
images:
|
||||||
- { src: "/img/hotel-national/entrance-model.webp", name: "entrance-model.webp" }
|
- { src: "/img/hotel-national/entrance-model.webp", name: "entrance-model.webp" }
|
||||||
- { src: "/img/hotel-national/plan-ground-floor.webp", name: "plan-ground-floor.webp" }
|
- { src: "/img/hotel-national/staircase-model.webp", name: "staircase-model.webp" }
|
||||||
|
- { src: "/img/hotel-national/reading-room-model.webp", name: "reading-room-model.webp" }
|
||||||
|
- { src: "/img/hotel-national/plan-full-ground-floor.webp", name: "plan-full-ground-floor.webp" }
|
||||||
|
- { src: "/img/hotel-national/plan-intervention-near.webp", name: "plan-intervention-near.webp" }
|
||||||
|
- { src: "/img/hotel-national/plan-intervention-detail.webp", name: "plan-intervention-detail.webp" }
|
||||||
|
categories: ["design"]
|
||||||
---
|
---
|
||||||
|
|
||||||
die zusammenführung des hotel national mit der lotte bibliothek basiert auf einem bewusst gesetzten widerspruch. ein grandhotel mit klassischer repräsentationsarchitektur trifft auf eine linksautonome bibliothek. statt diesen gegensatz zu nivellieren, wird er räumlich inszeniert – als produktive koexistenz zweier haltungen innerhalb desselben gebäudes.
|
in zusammenarbeit mit tamara grabovska basiert die zusammenführung des hotel national mit der lotte bibliothek auf einem bewusst gesetzten widerspruch. ein grandhotel mit klassischer repräsentationsarchitektur trifft auf eine linksautonome bibliothek. statt diesen gegensatz zu nivellieren, wird er räumlich inszeniert – als produktive koexistenz zweier haltungen innerhalb desselben gebäudes.
|
||||||
|
|
||||||
der eingriff konzentriert sich auf eine ecke des historischen, mit pilastern gegliederten baukörpers. durch das freistellen einer säule entsteht eine präzise gesetzte leerstelle. zwei wände werden entfernt, das hochparterre im bereich des einschnitts auf terrainhöhe abgesenkt. so bildet sich ein überdachter aussenraum – getragen von einer einzelnen säule –, der als schwelle, durchgang und adresse zugleich fungiert.
|
der eingriff konzentriert sich auf eine ecke des historischen, mit pilastern gegliederten baukörpers. durch das freistellen einer säule entsteht eine präzise gesetzte leerstelle. zwei wände werden entfernt, das hochparterre im bereich des einschnitts auf terrainhöhe abgesenkt. so bildet sich ein überdachter aussenraum – getragen von einer einzelnen säule –, der als schwelle, durchgang und adresse zugleich fungiert.
|
||||||
|
|
||||||
|
|||||||
@@ -7,10 +7,15 @@ schools: ["hslu"]
|
|||||||
degrees: ["ma"]
|
degrees: ["ma"]
|
||||||
images:
|
images:
|
||||||
- { src: "/img/hotel-national/entrance-model.webp", name: "entrance-model.webp" }
|
- { src: "/img/hotel-national/entrance-model.webp", name: "entrance-model.webp" }
|
||||||
- { src: "/img/hotel-national/plan-ground-floor.webp", name: "plan-ground-floor.webp" }
|
- { src: "/img/hotel-national/staircase-model.webp", name: "staircase-model.webp" }
|
||||||
|
- { src: "/img/hotel-national/reading-room-model.webp", name: "reading-room-model.webp" }
|
||||||
|
- { src: "/img/hotel-national/plan-full-ground-floor.webp", name: "plan-full-ground-floor.webp" }
|
||||||
|
- { src: "/img/hotel-national/plan-intervention-near.webp", name: "plan-intervention-near.webp" }
|
||||||
|
- { src: "/img/hotel-national/plan-intervention-detail.webp", name: "plan-intervention-detail.webp" }
|
||||||
|
categories: ["design"]
|
||||||
---
|
---
|
||||||
|
|
||||||
merging the hotel national with the lotte library is based on a deliberately set contradiction. a grand hotel with classical representational architecture meets a left-autonomous library. rather than levelling this opposition, it is staged spatially — as a productive coexistence of two attitudes within the same building.
|
in collaboration with tamara grabovska, merging the hotel national with the lotte library is based on a deliberately set contradiction. a grand hotel with classical representational architecture meets a left-autonomous library. rather than levelling this opposition, it is staged spatially — as a productive coexistence of two attitudes within the same building.
|
||||||
|
|
||||||
the intervention concentrates on one corner of the historic, pilaster-articulated volume. freeing a column creates a precisely placed void. two walls are removed, and the raised ground floor is lowered to grade in the area of the incision. this forms a covered outdoor space — carried by a single column — that functions simultaneously as threshold, passage, and address.
|
the intervention concentrates on one corner of the historic, pilaster-articulated volume. freeing a column creates a precisely placed void. two walls are removed, and the raised ground floor is lowered to grade in the area of the incision. this forms a covered outdoor space — carried by a single column — that functions simultaneously as threshold, passage, and address.
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ images:
|
|||||||
- { src: "/img/sem01/05.webp", name: "cutaway-model-angle.webp" }
|
- { src: "/img/sem01/05.webp", name: "cutaway-model-angle.webp" }
|
||||||
- { src: "/img/sem01/06.webp", name: "site-plan.webp" }
|
- { src: "/img/sem01/06.webp", name: "site-plan.webp" }
|
||||||
- { src: "/img/sem01/07.webp", name: "street-linocut.webp" }
|
- { src: "/img/sem01/07.webp", name: "street-linocut.webp" }
|
||||||
|
categories: ["design"]
|
||||||
---
|
---
|
||||||
|
|
||||||
tonhalle museo ist ein spekulativer entwurf an der schnittstelle von architektur, klang und theorie. zwei scheinbar gegensätzliche programme – konzertsaal und museum – treffen in einem gemeinsamen baukörper aufeinander. dieses aufeinandertreffen wird nicht harmonisiert, sondern bewusst zugespitzt.
|
tonhalle museo ist ein spekulativer entwurf an der schnittstelle von architektur, klang und theorie. zwei scheinbar gegensätzliche programme – konzertsaal und museum – treffen in einem gemeinsamen baukörper aufeinander. dieses aufeinandertreffen wird nicht harmonisiert, sondern bewusst zugespitzt.
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ images:
|
|||||||
- { src: "/img/sem01/05.webp", name: "cutaway-model-angle.webp" }
|
- { src: "/img/sem01/05.webp", name: "cutaway-model-angle.webp" }
|
||||||
- { src: "/img/sem01/06.webp", name: "site-plan.webp" }
|
- { src: "/img/sem01/06.webp", name: "site-plan.webp" }
|
||||||
- { src: "/img/sem01/07.webp", name: "street-linocut.webp" }
|
- { src: "/img/sem01/07.webp", name: "street-linocut.webp" }
|
||||||
|
categories: ["design"]
|
||||||
---
|
---
|
||||||
|
|
||||||
tonhalle museo is a speculative design at the intersection of architecture, sound, and theory. two seemingly contradictory programmes — concert hall and museum — meet within a single building. this convergence is not harmonised but deliberately sharpened.
|
tonhalle museo is a speculative design at the intersection of architecture, sound, and theory. two seemingly contradictory programmes — concert hall and museum — meet within a single building. this convergence is not harmonised but deliberately sharpened.
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ slug: "object-oriented-ontology"
|
|||||||
date: 2026-01-15
|
date: 2026-01-15
|
||||||
thumbnail: "/img/ooo/thumbnail.webp"
|
thumbnail: "/img/ooo/thumbnail.webp"
|
||||||
video: "/media/ooo.mp4"
|
video: "/media/ooo.mp4"
|
||||||
|
categories: ["film"]
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ slug: "object-oriented-ontology"
|
|||||||
date: 2026-01-15
|
date: 2026-01-15
|
||||||
thumbnail: "/img/ooo/thumbnail.webp"
|
thumbnail: "/img/ooo/thumbnail.webp"
|
||||||
video: "/media/ooo.mp4"
|
video: "/media/ooo.mp4"
|
||||||
|
categories: ["film"]
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -26,4 +26,5 @@ images:
|
|||||||
- { src: "/img/studytrip-trieste/Triest_Rozzol_Melara-04.webp", name: "rozzol-melara-04.webp" }
|
- { src: "/img/studytrip-trieste/Triest_Rozzol_Melara-04.webp", name: "rozzol-melara-04.webp" }
|
||||||
- { src: "/img/studytrip-trieste/Triest_Rozzol_Melara-05.webp", name: "rozzol-melara-05.webp" }
|
- { src: "/img/studytrip-trieste/Triest_Rozzol_Melara-05.webp", name: "rozzol-melara-05.webp" }
|
||||||
- { src: "/img/studytrip-trieste/Triest_Stanjel-01.webp", name: "stanjel.webp" }
|
- { src: "/img/studytrip-trieste/Triest_Stanjel-01.webp", name: "stanjel.webp" }
|
||||||
|
categories: ["photo"]
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -26,4 +26,5 @@ images:
|
|||||||
- { src: "/img/studytrip-trieste/Triest_Rozzol_Melara-04.webp", name: "rozzol-melara-04.webp" }
|
- { src: "/img/studytrip-trieste/Triest_Rozzol_Melara-04.webp", name: "rozzol-melara-04.webp" }
|
||||||
- { src: "/img/studytrip-trieste/Triest_Rozzol_Melara-05.webp", name: "rozzol-melara-05.webp" }
|
- { src: "/img/studytrip-trieste/Triest_Rozzol_Melara-05.webp", name: "rozzol-melara-05.webp" }
|
||||||
- { src: "/img/studytrip-trieste/Triest_Stanjel-01.webp", name: "stanjel.webp" }
|
- { src: "/img/studytrip-trieste/Triest_Stanjel-01.webp", name: "stanjel.webp" }
|
||||||
|
categories: ["photo"]
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ images:
|
|||||||
- { src: "/img/sem05/05.webp", name: "dwelling-interior.webp" }
|
- { src: "/img/sem05/05.webp", name: "dwelling-interior.webp" }
|
||||||
- { src: "/img/sem05/section01.webp", name: "long-section.webp" }
|
- { src: "/img/sem05/section01.webp", name: "long-section.webp" }
|
||||||
- { src: "/img/sem05/section02.webp", name: "hall-section.webp" }
|
- { src: "/img/sem05/section02.webp", name: "hall-section.webp" }
|
||||||
|
categories: ["design"]
|
||||||
---
|
---
|
||||||
|
|
||||||
die séchoir-halle, ein ehemaliger industriebau mit ursprung in der lokalen textilgeschichte, wurde in ihrer identität bewahrt und zugleich neu programmiert. wo früher viskosestoffe zum trocknen aufgehängt wurden, entstehen heute neue bausteine aus recycelten textilien. aus alten stoffen werden im stampfverfahren tragende elemente gefertigt – ein konstruktives system auf basis von kreislaufprinzipien.
|
die séchoir-halle, ein ehemaliger industriebau mit ursprung in der lokalen textilgeschichte, wurde in ihrer identität bewahrt und zugleich neu programmiert. wo früher viskosestoffe zum trocknen aufgehängt wurden, entstehen heute neue bausteine aus recycelten textilien. aus alten stoffen werden im stampfverfahren tragende elemente gefertigt – ein konstruktives system auf basis von kreislaufprinzipien.
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ images:
|
|||||||
- { src: "/img/sem05/05.webp", name: "dwelling-interior.webp" }
|
- { src: "/img/sem05/05.webp", name: "dwelling-interior.webp" }
|
||||||
- { src: "/img/sem05/section01.webp", name: "long-section.webp" }
|
- { src: "/img/sem05/section01.webp", name: "long-section.webp" }
|
||||||
- { src: "/img/sem05/section02.webp", name: "hall-section.webp" }
|
- { src: "/img/sem05/section02.webp", name: "hall-section.webp" }
|
||||||
|
categories: ["design"]
|
||||||
---
|
---
|
||||||
|
|
||||||
the séchoir-halle, a former industrial building rooted in the local textile history, was preserved in its identity while being given a new programme. where viscose fabrics were once hung to dry, new building blocks made from recycled textiles are produced today — load-bearing elements crafted from old fabrics through a ramming process, a constructive system based on circular principles.
|
the séchoir-halle, a former industrial building rooted in the local textile history, was preserved in its identity while being given a new programme. where viscose fabrics were once hung to dry, new building blocks made from recycled textiles are produced today — load-bearing elements crafted from old fabrics through a ramming process, a constructive system based on circular principles.
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ slug: "the-shadow-over-emmen"
|
|||||||
date: 2025-01-10T09:00:00
|
date: 2025-01-10T09:00:00
|
||||||
thumbnail: "/img/shadowofemmen/thumbnail.webp"
|
thumbnail: "/img/shadowofemmen/thumbnail.webp"
|
||||||
video: "/media/shadow.mp4"
|
video: "/media/shadow.mp4"
|
||||||
|
categories: ["film"]
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ slug: "the-shadow-over-emmen"
|
|||||||
date: 2025-01-10T09:00:00
|
date: 2025-01-10T09:00:00
|
||||||
thumbnail: "/img/shadowofemmen/thumbnail.webp"
|
thumbnail: "/img/shadowofemmen/thumbnail.webp"
|
||||||
video: "/media/shadow.mp4"
|
video: "/media/shadow.mp4"
|
||||||
|
categories: ["film"]
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ schools: ["hslu"]
|
|||||||
degrees: ["ma"]
|
degrees: ["ma"]
|
||||||
images:
|
images:
|
||||||
- { src: "/img/translation/grid-collage.webp", name: "grid-collage.webp" }
|
- { src: "/img/translation/grid-collage.webp", name: "grid-collage.webp" }
|
||||||
|
categories: ["design", "text"]
|
||||||
---
|
---
|
||||||
|
|
||||||
in der zusammenarbeit mit tim schwander und jérôme arnold entsteht eine auseinandersetzung mit architektonischen denkweisen, die weniger zitiert als vielmehr übersetzt werden. die arbeit versteht sich als experimentierfeld, in dem ordnungssysteme, landschaft und fragment sich gegenseitig verschieben.
|
in der zusammenarbeit mit tim schwander und jérôme arnold entsteht eine auseinandersetzung mit architektonischen denkweisen, die weniger zitiert als vielmehr übersetzt werden. die arbeit versteht sich als experimentierfeld, in dem ordnungssysteme, landschaft und fragment sich gegenseitig verschieben.
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ schools: ["hslu"]
|
|||||||
degrees: ["ma"]
|
degrees: ["ma"]
|
||||||
images:
|
images:
|
||||||
- { src: "/img/translation/grid-collage.webp", name: "grid-collage.webp" }
|
- { src: "/img/translation/grid-collage.webp", name: "grid-collage.webp" }
|
||||||
|
categories: ["design", "text"]
|
||||||
---
|
---
|
||||||
|
|
||||||
in collaboration with tim schwander and jérôme arnold, this work engages with architectural ways of thinking that are less quoted than translated. it understands itself as a field of experimentation in which systems of order, landscape, and fragment shift against one another.
|
in collaboration with tim schwander and jérôme arnold, this work engages with architectural ways of thinking that are less quoted than translated. it understands itself as a field of experimentation in which systems of order, landscape, and fragment shift against one another.
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ images:
|
|||||||
- { src: "/img/utopie-der-koexistenz/facade-detail-model.webp", name: "facade-detail-model.webp" }
|
- { src: "/img/utopie-der-koexistenz/facade-detail-model.webp", name: "facade-detail-model.webp" }
|
||||||
- { src: "/img/utopie-der-koexistenz/courtyard-overview-render.webp", name: "courtyard-overview-render.webp" }
|
- { src: "/img/utopie-der-koexistenz/courtyard-overview-render.webp", name: "courtyard-overview-render.webp" }
|
||||||
- { src: "/img/utopie-der-koexistenz/site-plan-ground-floor.webp", name: "site-plan-ground-floor.webp" }
|
- { src: "/img/utopie-der-koexistenz/site-plan-ground-floor.webp", name: "site-plan-ground-floor.webp" }
|
||||||
|
categories: ["design"]
|
||||||
---
|
---
|
||||||
|
|
||||||
vor dem hintergrund der funktionalen krise vieler spitäler aus den 1960er- und 70er-jahren setzt diese bachelorarbeit auf eine weiterentwicklung des bestehenden kantonsspitals baden anstelle eines radikalen abrisses. der ursprünglich vorgesehene komplettneubau wird infrage gestellt – zugunsten einer strategie, die brüche, übergänge und reibungen nicht glättet, sondern produktiv macht.
|
vor dem hintergrund der funktionalen krise vieler spitäler aus den 1960er- und 70er-jahren setzt diese bachelorarbeit auf eine weiterentwicklung des bestehenden kantonsspitals baden anstelle eines radikalen abrisses. der ursprünglich vorgesehene komplettneubau wird infrage gestellt – zugunsten einer strategie, die brüche, übergänge und reibungen nicht glättet, sondern produktiv macht.
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ images:
|
|||||||
- { src: "/img/utopie-der-koexistenz/facade-detail-model.webp", name: "facade-detail-model.webp" }
|
- { src: "/img/utopie-der-koexistenz/facade-detail-model.webp", name: "facade-detail-model.webp" }
|
||||||
- { src: "/img/utopie-der-koexistenz/courtyard-overview-render.webp", name: "courtyard-overview-render.webp" }
|
- { src: "/img/utopie-der-koexistenz/courtyard-overview-render.webp", name: "courtyard-overview-render.webp" }
|
||||||
- { src: "/img/utopie-der-koexistenz/site-plan-ground-floor.webp", name: "site-plan-ground-floor.webp" }
|
- { src: "/img/utopie-der-koexistenz/site-plan-ground-floor.webp", name: "site-plan-ground-floor.webp" }
|
||||||
|
categories: ["design"]
|
||||||
---
|
---
|
||||||
|
|
||||||
against the backdrop of the functional crisis affecting many hospitals built in the 1960s and 70s, this bachelor thesis pursues a development of the existing kantonsspital baden rather than a radical demolition. the originally planned complete new build is called into question — in favour of a strategy that does not smooth over ruptures, transitions, and frictions, but makes them productive.
|
against the backdrop of the functional crisis affecting many hospitals built in the 1960s and 70s, this bachelor thesis pursues a development of the existing kantonsspital baden rather than a radical demolition. the originally planned complete new build is called into question — in favour of a strategy that does not smooth over ruptures, transitions, and frictions, but makes them productive.
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ enableRobotsTXT = true
|
|||||||
school = "schools"
|
school = "schools"
|
||||||
degree = "degrees"
|
degree = "degrees"
|
||||||
semester = "semesters"
|
semester = "semesters"
|
||||||
|
category = "categories"
|
||||||
|
|
||||||
[params]
|
[params]
|
||||||
description = "architecture studio"
|
description = "architecture studio"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
data-img="{{ with $p.Params.thumbnail }}{{ . | relURL }}{{ end }}"
|
data-img="{{ with $p.Params.thumbnail }}{{ . | relURL }}{{ end }}"
|
||||||
data-name="{{ $p.Slug }}"
|
data-name="{{ $p.Slug }}"
|
||||||
{{ if eq $i 0 }}class="is-selected"{{ end }}>
|
{{ if eq $i 0 }}class="is-selected"{{ end }}>
|
||||||
<span class="tui__perm">drwxr-xr-x</span><span class="tui__date">{{ $p.Date.Format "2006" }}</span><span class="tui__name">{{ $p.Slug }}/</span>
|
<span class="tui__perm">drwxr-xr-x</span><span class="tui__date">{{ $p.Date.Format "2006" }}</span><span class="tui__row3"><span class="tui__name">{{ $p.Slug }}/</span>{{ with $p.Params.categories }}<span class="tui__tags">{{ range . }}[{{ . }}]{{ end }}</span>{{ end }}</span>
|
||||||
</a></li>
|
</a></li>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -30,15 +30,16 @@
|
|||||||
<figure class="tui__preview">
|
<figure class="tui__preview">
|
||||||
{{ if $video }}
|
{{ if $video }}
|
||||||
<div class="player">
|
<div class="player">
|
||||||
<video preload="none" playsinline{{ with .Params.thumbnail }} poster="{{ . }}"{{ end }}>
|
<video preload="metadata" playsinline{{ with .Params.thumbnail }} poster="{{ . }}"{{ end }}>
|
||||||
<source src="{{ $video }}" type="video/mp4">
|
<source src="{{ $video }}" type="video/mp4">
|
||||||
</video>
|
</video>
|
||||||
<button class="player__big" type="button" aria-label="play">▶</button>
|
<button class="player__big" type="button" aria-label="play">▶</button>
|
||||||
<div class="player__bar">
|
<div class="player__bar">
|
||||||
<button class="player__play" type="button" aria-label="play/pause">▶</button>
|
<button class="player__play" type="button" aria-label="play/pause"><span class="player__playicon">▶</span></button>
|
||||||
<div class="player__scrub"><div class="player__fill"></div></div>
|
<div class="player__scrub"><div class="player__fill"></div></div>
|
||||||
<span class="player__time">00:00 / 00:00</span>
|
<span class="player__time">00:00 / 00:00</span>
|
||||||
<button class="player__mute" type="button" aria-label="mute">vol</button>
|
<button class="player__mute" type="button" aria-label="mute">vol</button>
|
||||||
|
<div class="player__vol" aria-label="volume"><div class="player__volfill"></div></div>
|
||||||
<button class="player__full" type="button" aria-label="fullscreen">⛶</button>
|
<button class="player__full" type="button" aria-label="fullscreen">⛶</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 29 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 640 KiB After Width: | Height: | Size: 256 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
Reference in New Issue
Block a user