Compare commits

...

20 Commits

Author SHA1 Message Date
kgva CMS 247ca6ac9a content: repo links point to the actual git repos, not the product sites
rapport -> git.openbureau.ch/karim/RAPPORT, dossier -> git.openbureau.ch/karim/DOSSIER-STANDALONE (the standalone rewrite, matches the text already describing it as such).
2026-08-15 11:53:04 +00:00
kgva CMS 55d37ecd92 fix: wider click tolerance on the scrub and volume bars
both bars are only 3px tall visually, which made them fiddly to hit precisely. added a 5px invisible hit-area above and below via ::before on each — the visual bar stays thin, but clicking near it still registers. the seek/volume math is unaffected since it reads the real elements own rect, not the pseudo-elements.
2026-08-15 11:50:37 +00:00
kgva CMS 666681be7c fix: pause icon drawn in css instead of a glyph, no more gap
letter-spacing on the ❚❚ glyph could tighten the character cells but not the visible gap between them on a monospace font — each character keeps its own cell regardless. now two bars drawn via ::before/::after on .is-playing, with exact pixel control over the gap; the ▶ span just hides instead of getting replaced.
2026-08-15 11:49:27 +00:00
kgva CMS 11660ac63d fix: play/pause button size consistency, add a real volume slider
the play/pause button had no fixed width, so swapping the single-character play glyph for the two-character pause glyph (with the fonts normal spacing between them) visibly widened the button. now a fixed 2.4ch width with tightened tracking on the pause state.\n\nmute only ever toggled — there was no way to set a level. added a small click/drag bar next to it (same interaction as the scrub bar), synced with the video elements own volume/muted state via the volumechange event so it stays correct regardless of how its changed.
2026-08-15 11:44:14 +00:00
kgva CMS 46aff56577 perf: smoother video player — rAF-driven scrub bar, less cold-start stutter
scrub fill/time were driven off timeupdate, which browsers fire irregularly (a handful of times a second) — visible as the bar stepping instead of gliding. now driven by requestAnimationFrame while playing (display refresh rate), with a direct sync on pause/seeked so it still tracks immediately when not running.\n\npreload=none meant zero data was fetched until the play button was pressed, causing a visible stall right at playback start. preload=metadata lets the browser prime the connection and read the moov atom ahead of time without fetching the full video for visitors who never press play.
2026-08-15 00:24:17 +00:00
kgva CMS dd915e1797 content: revise german about text again, fix bms detail
split into two paragraphs, dropped the remaining dash, and corrected the schooling detail: gestalterische bms (not vorbildung/foundation year) came before architecture studies at ost and hslu.
2026-08-15 00:22:18 +00:00
kgva CMS b665293922 fix: about-page text preview responds to hover again while a file is locked
the peek column only exists for images (a side-by-side thumbnail makes sense there, not for text) — but the mouseenter handler routed ALL rows through the locked/peek branch once something was locked, so on about (all text rows, locked by default on load) hovering dossier.md/openbureau.md/rapport.md while cv.md was locked did nothing at all. rows without data-img now always drive the main preview directly on hover, lock or not.
2026-08-15 00:19:05 +00:00
kgva CMS aa33ce56bf content: less cheesy, less literal german about text
dropped the weight-of-a-material-vs-weight-of-a-social-crisis parallel and the sciences-meet-and-must-become-real ending — too directly translated from the english original, reads try-hard in german. keeps the same facts and the core point about architecture sitting between disciplines, phrased more plainly.
2026-08-15 00:11:37 +00:00
kgva CMS ffe9fa11fc fix: category tags flush right instead of wrapping onto their own line
grid-column:3 + justify-self:end put name and tags in the same cell but grid stacks co-located items instead of placing them side by side, so the tag wrapped to a second line. wrapped both in a flex row (justify-content:space-between) inside that grid cell instead.
2026-08-15 00:10:02 +00:00
kgva CMS 6b59b58082 feat: category tags (design/film/photo/text) on the portfolio index
new categories taxonomy, multi-value per project. shown inline in each ls row as [tag] after the slug, muted like the other row metadata and inverting with it on hover/select. reuses the existing shared taxonomy template, so /categories/ and /categories/<tag>/ come for free as browsable archives in the same shell style already used for schools/degrees/semesters.\n\nassigned by medium: design for anything with plans/models, film for the two video projects, photo for the trieste study trip, text alongside design for translation (collage + accompanying theoretical text).
2026-08-14 23:50:54 +00:00
kgva CMS 892dfe600d fix: portfolio index rows can be unlocked again
clicking the already-locked row navigated (the old second-click-to-open flow), which meant there was no way back to the unlocked hover-browsing state once something got locked. row clicks now toggle lock/unlock uniformly everywhere; the preview image is the one dedicated way to actually open a project, on the index same as project pages.
2026-08-14 23:46:38 +00:00
kgva CMS 4aae68d836 fix: hover drives the main preview again when nothing is locked
the peek column only makes sense as an alternative to disturbing an existing lock — with nothing locked, hover had stopped updating anything at all, leaving whatever was last locked stuck on screen while browsing other rows. now: locked -> hover drives the peek column only; unlocked -> hover drives the main preview directly, same as before locking existed.
2026-08-14 23:15:43 +00:00
kgva CMS 7f6bedebdc content: rewrite rapport, dossier, and openbureau texts
rapport: trimmed the competitor-positioning paragraph and the tools-i-wanted framing, just the feature list and the free/self-hostable facts now.\n\ndossier: reflects that it left the rhino plugin behind and is now its own standalone desktop app (own geometry kernel, own rust/wasm rendering engine, own file format), instead of the old rhino-vs-open-source-cad justification.\n\nopenbureau: reframed as the platform the other two are built on rather than just a collection point for finished tools, and named as still taking shape.
2026-08-12 00:10:24 +00:00
kgva CMS 55926a84f2 fix: peek column images now use a fixed 3:2 crop, matching the portfolio-index preview
plans and photos have wildly different natural aspect ratios (a 46x31in A0 detail plan next to a 3:2 photo), so the peek box was jumping size on every hover. now uses the same aspect-ratio:3/2 + object-fit:cover treatment as the portfolio-index preview it already mirrors in position/width — stays a fixed size regardless of what is hovered.
2026-08-11 23:09:17 +00:00
kgva CMS 932d7a7653 content: replace hotel national plans with the official CAD exports, add a third detail view
plan-full-ground-floor and plan-intervention-detail now come from the actual Vectorworks plan PDFs (GRUNDRISS-100.pdf, 1_20.pdf) rather than the typst-export renders — crisper linework, smaller files despite higher resolution. added plan-intervention-near.webp (from 100-NAH.pdf) as a third, medium-detail view between the full floor plan and the 1:20 intervention detail.
2026-08-11 23:01:04 +00:00
kgva CMS 84ac7ae562 fix: peek image starts at the same top offset as the info text
was flush with the shell lines (top:0); now matches .proj-infos 3.6rem margin-top so both side columns start at the same height.
2026-08-11 22:01:55 +00:00
kgva CMS 37fd39f488 feat: peek column to the left of the file list, mirroring the info text on the right
replaces the last commits attempt (hover updating the main preview directly) with what was actually asked for: the main preview now stays on the locked file, untouched by hover; a separate 18rem-wide column to the left of the file list — same width and spot as .proj-info on the right — shows whichever OTHER image is under the mouse while something is locked, and disappears again once the mouse leaves the list or lands back on the locked row itself.\n\npositioned via plain absolute positioning against .filebrowser (not scroll-tracked — it scrolls together with the list naturally), sitting inside the padding .proj-layout already reserves to keep the file list anchored. only enabled above 1300px, with a safety margin over the ~1168px point where that reserved space stops being wide enough; caught and fixed a bug in the same pass where .proj-peek had no display:none fallback below that breakpoint and would otherwise render as a bare block in-flow.
2026-08-11 19:41:06 +00:00
kgva CMS 0469e9dab5 fix: hover now peeks the preview even while a file is locked; remove the scroll-triggered sticky thumbnail
the scroll-triggered mini-preview from the last commit solved the wrong problem — it never showed in practice (images are generally as tall or taller than the short project texts, so the gap it needed never opened), and wasnt what was actually wanted: the existing left-column preview pane should keep responding to hover even once a file is locked, only reverting to the locked file once the mouse leaves the list. lock now only gates the is-selected styling, the ● locked indicator, and the click-to-navigate semantics on the portfolio index — not what the pane displays.
2026-08-11 19:34:11 +00:00
kgva CMS 113a954b16 feat: sticky locked-image thumbnail while scrolling; two-click preview-then-open on the portfolio index
the locked image now gets a small pinned copy (18rem, same width as the text column) that appears once the real preview scrolls out of view and disappears again once the text runs out — fixed-position + scroll-driven rather than CSS sticky, since the filebrowser box is only ever as tall as its own content, not the much taller text next to it.\n\nalso scoped the existing click-outside-unfreezes-the-lock listener to clicks inside .filebrowser only, since with listings now starting locked, a click to read or select a word in the text would otherwise silently kill the sticky preview.\n\nportfolio index: rows there are real links (href + data-img together), so a single click used to jump straight into the project — the only way to preview a different one first was hovering, which does nothing for touch and is now also blocked while something is locked. first click on a different project now just previews it (like hover used to); a second click on the same row, or clicking the preview image itself, opens it.
2026-08-11 19:12:43 +00:00
kgva CMS 5c5b267a7f feat: portfolio/about listings start locked on their first file
same end state as clicking the first row by hand — hovering other rows no longer steals the preview until you actually click one. clicking a different row still jumps straight to it and relocks there; clicking the locked row again releases it back to hover-browsing. games are excluded (nothing to browse), matched via the game-embed class rather than page context so it stays correct no matter which page a game was launched from.
2026-08-11 19:03:17 +00:00
34 changed files with 260 additions and 41 deletions
+87 -3
View File
@@ -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) */
+82 -19
View File
@@ -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");
@@ -58,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;
@@ -70,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);
@@ -95,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
View File
@@ -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
View File
@@ -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).
+2 -2
View File
@@ -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)
+2 -2
View File
@@ -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)
+2 -2
View File
@@ -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)
+2 -2
View File
@@ -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)
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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.
+1
View File
@@ -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.
+1
View File
@@ -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.
+2
View File
@@ -10,7 +10,9 @@ images:
- { src: "/img/hotel-national/staircase-model.webp", name: "staircase-model.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/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-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" } - { src: "/img/hotel-national/plan-intervention-detail.webp", name: "plan-intervention-detail.webp" }
categories: ["design"]
--- ---
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. 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.
+2
View File
@@ -10,7 +10,9 @@ images:
- { src: "/img/hotel-national/staircase-model.webp", name: "staircase-model.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/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-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" } - { src: "/img/hotel-national/plan-intervention-detail.webp", name: "plan-intervention-detail.webp" }
categories: ["design"]
--- ---
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. 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.
+1
View File
@@ -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.
+1
View File
@@ -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"]
--- ---
+1
View File
@@ -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"]
--- ---
+1
View File
@@ -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.
+1
View File
@@ -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"]
--- ---
+1
View File
@@ -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.
+1
View File
@@ -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.
+1
View File
@@ -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"
+1 -1
View File
@@ -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>
+3 -2
View File
@@ -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