/* ══════════════════════════════════════════════════════════════ custom.css — extends shibui WITHOUT touching the theme. - top bar reads like a terminal prompt: "/ karimgabrielevarano_ ls" - clicking "ls" lists the pages, stacked, inverted on hover - only the work index breaks the narrow column remove this file → original shibui look returns. ══════════════════════════════════════════════════════════════ */ /* plain Iosevka instead of the theme's "Iosevka Curly Slab" — same family, calmer letterforms (straight $, no slab foot on digits). self-hosted, subset to exactly the characters used on the site (fonttools --unicodes), so it's smaller than the curly-slab cut it replaces despite covering en+de plus the shell/tree symbols (→ ← … · ▶ ⛶ ☕ ✕ ❚ box-drawing). overrides the theme's --font-family-* vars below (custom.css loads after site.css). */ @font-face { font-family: "Iosevka"; font-style: normal; font-weight: 400; font-display: swap; src: url("/fonts/iosevka-400.woff2") format("woff2"); } @font-face { font-family: "Iosevka"; font-style: normal; font-weight: 700; font-display: swap; src: url("/fonts/iosevka-700.woff2") format("woff2"); } :root { --font-family-mono: "Iosevka", ui-monospace, monospace; --font-family-sans: "Iosevka", ui-monospace, monospace; } /* self-hosted serif, used only for the project-info side panel */ @font-face { font-family: "Libre Baskerville"; font-style: normal; font-weight: 400; font-display: swap; src: url("/fonts/libre-baskerville-400.woff2") format("woff2"); } @font-face { font-family: "Libre Baskerville"; font-style: italic; font-weight: 400; font-display: swap; src: url("/fonts/libre-baskerville-400italic.woff2") format("woff2"); } @font-face { font-family: "Libre Baskerville"; font-style: normal; font-weight: 700; font-display: swap; src: url("/fonts/libre-baskerville-700.woff2") format("woff2"); } :root { --font-family-serif: "Libre Baskerville", Georgia, serif; } /* ─────────────────────── SHELL PROMPT ─────────────────────── */ .prompt { font-family: var(--font-family-mono); display: flex; /* stay on ONE line: when the command gets too wide, clip the left (older) part terminal-style and keep the caret/right end visible (shell.js scrolls it to the end). previously flex-wrap:wrap dropped to a 2nd line and bugged. */ flex-wrap: nowrap; align-items: flex-start; gap: 0; line-height: 1.5; overflow: hidden; white-space: nowrap; } .prompt__user { font-weight: 700; } .prompt__path { color: var(--color-text-muted); } /* breadcrumb segments look like plain prompt text at rest, fill on hover */ .prompt__seg { margin-left: 0; border-bottom: none; background: none; color: inherit; } .prompt__seg:hover { background: var(--color-text-primary); color: var(--color-bg-primary); border-bottom: none; } /* the typeable command — looks like shell text, native caret hidden so our blinking "_" is the cursor */ .prompt__input { margin-left: 0.6ch; min-width: 0; font-weight: 700; color: var(--color-text-primary); white-space: pre; outline: none; caret-color: transparent; } /* a command "typed" by the file browser on hover/freeze — dimmed so it reads as a hint, not something you committed. any real keystroke discards it. */ .prompt__input.is-ghost { color: var(--color-text-muted); font-weight: 400; } /* blinking terminal cursor, sits right after the typed text */ .caret { font-weight: 700; color: var(--color-text-primary); animation: blink 1.1s steps(1) infinite; } @keyframes blink { 50% { opacity: 0; } } /* always-open file menu (the shell's "ls") — only shown on the home page; on subpages it stays in the DOM (hidden) so typed commands still resolve */ /* stays the file list's normal width even when .topbar is widened (below) to line up the shell/lang-switch with a wider info panel elsewhere — the home menu was never meant to stretch, only the prompt row + toggle were */ .menu { margin: var(--spacing-sm) 0 0; max-width: var(--container-width); } body:not(.home) .menu { display: none; } /* easter-egg output line — takes no space until it actually has output */ .prompt__out { font-family: var(--font-family-mono); color: var(--color-text-muted); margin: 0; white-space: pre-wrap; } .prompt__out:not(:empty) { margin: 0.35rem 0 0; } /* ─────────────────────── HOME IMAGE ─────────────────────── */ /* sits low enough that opening the ls overlay never moves it */ .home .hero__img { margin: 2rem 0 0; text-align: left; } .hero__img img { width: 100%; height: auto; margin: 0; } /* ─────────────────── WORK INDEX (ls -l style) ─────────────────── */ .tui { font-family: var(--font-family-mono); } .tui__cmd { margin: 0; color: var(--color-text-muted); } .tui__sym { color: var(--color-text-muted); } .tui__total { margin: 0 0 0.6rem; } .tui__ls { list-style: none; margin: 0; padding: 0; } .tui__ls li { margin: 0; } .tui__ls a { display: grid; grid-template-columns: 11ch 5ch 1fr; gap: 0 1ch; margin: 0; padding: 0 0.25em; border-bottom: none; color: var(--color-text-primary); white-space: nowrap; } .tui__perm, .tui__date { color: var(--color-text-muted); } .tui__ls a:hover, .tui__ls a.is-selected { background: var(--color-text-primary); color: var(--color-bg-primary); } .tui__ls a:hover .tui__perm, .tui__ls a:hover .tui__date, .tui__ls a:hover .tui__name, .tui__ls a.is-selected .tui__perm, .tui__ls a.is-selected .tui__date, .tui__ls a.is-selected .tui__name { color: var(--color-bg-primary); } /* preview pane (ranger-style) below the listing */ .tui__preview { margin: 1.25rem 0 0; } .tui__preview img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; height: auto; margin: 0; display: block; 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) */ .tui--files .tui__ls a { grid-template-columns: 11ch 1fr; } /* long listings (>8 entries) scroll instead of pushing the preview down */ .tui--files .tui__ls { max-height: 12em; /* ~8 rows at line-height 1.5 */ overflow-y: auto; } .tui--files .tui__preview img { aspect-ratio: auto; /* keep each image's own ratio — no letterboxing */ width: auto; max-width: 100%; max-height: 80vh; height: auto; object-fit: contain; background: none; } /* info.md text preview */ .tui__text { font-size: var(--font-size-base); text-align: left; } .tui__text p { text-align: left; } .tui__text > :first-child { margin-top: 0; } .tui__text > :last-child { margin-bottom: 0; } /* ─────────────────── PROJECT INFO PANEL ─────────────────── */ /* info.md (the project description / about text) lives outside the filebrowser now — on wide viewports it sits to the right of the file list, clearing the "$ ls" / "total N" shell lines up top; on narrow ones there's no room for two columns, so it just stacks below instead. the file list itself always stays exactly where the single centered column normally sits — same position with or without an info panel, so nothing shifts around depending on whether a page has text. */ .proj-layout { display: block; } .proj-info { margin: 1.5rem 0 0; font-family: var(--font-family-serif); font-size: 0.85em; line-height: 1.6; text-align: justify; /* justified needs hyphenation, otherwise a narrow column like this one gets ugly gaps between words; :lang() on (set per-page by Hugo) picks the right hyphenation dictionary for en vs. de automatically. */ hyphens: auto; -webkit-hyphens: auto; text-wrap: pretty; } .proj-info > :first-child { margin-top: 0; } .proj-info > :last-child { margin-bottom: 0; } @media (min-width: 1100px) { .proj-layout { width: 100vw; margin-left: calc(50% - 50vw); /* pushes the file list back out to the same spot the narrow centered column always occupies — this is a fixed position, not a re-centered pair, so it doesn't move depending on whether .proj-info exists */ padding-left: calc((100vw - var(--container-width)) / 2); padding-right: 1.5rem; box-sizing: border-box; display: flex; align-items: flex-start; gap: 1.5rem; } .proj-layout .filebrowser { flex: 0 0 var(--container-width); } .proj-info { flex: 0 0 18rem; margin-top: 3.6rem; /* clear the two shell lines above the file list */ } /* shell line + lang-switch always stretch out to the file-list + info-panel width, on every page — not just ones that happen to render an info panel — so the toggle sits in the same spot site-wide */ .topbar { width: calc(var(--container-width) + 1.5rem + 18rem); } } /* small pinned copy of the locked image — appears at the left edge (same spot the real preview normally sits) once that preview has scrolled out of view, and disappears again once the text next to it runs out. width matches .proj-info so the two columns read as a pair. fixed, not sticky: the file list's own box is only as tall as its own content, not the much taller text next to it, so sticky positioning has nothing to be bounded by — a scroll-driven fixed element sidesteps that entirely. */ .proj-sticky { position: fixed; top: 50%; left: calc((100vw - var(--container-width)) / 2); transform: translateY(-50%); width: 18rem; z-index: 5; opacity: 0; visibility: hidden; transition: opacity 0.25s ease; pointer-events: none; } .proj-sticky.is-visible { opacity: 1; visibility: visible; } .proj-sticky img { width: 100%; height: auto; max-height: 60vh; object-fit: contain; display: block; margin: 0; } @media (max-width: 1099.98px) { .proj-sticky { display: none; } } /* prev/next as shell "cd" commands — same vocabulary as the rest of the site */ .proj-nav { font-family: var(--font-family-mono); margin: 1.5rem 0 0; display: flex; gap: 1.5ch; } .proj-nav a[rel="next"] { margin-left: auto; } /* → always pinned right */ .proj-nav a { border-bottom: none; color: var(--color-text-muted); } .proj-nav a:hover { background: var(--color-text-primary); color: var(--color-bg-primary); border-bottom: none; } /* studies path → clickable taxonomy filters, but indistinguishable from plain text (no underline, no hover, inherits the surrounding colour) */ .proj-path__seg, .proj-path__seg:hover { margin-left: 0; border-bottom: none; background: none; color: inherit; } .proj-path__seg--last { font-weight: 700; } /* secret retro games (doom, simcity, pacman, tetris) — each runs inline as .sh inside a fake . folder, same file-listing chrome as any other page. sized to the classic 4:3 DOS/arcade aspect ratio. */ .game-player { width: 100%; aspect-ratio: 4 / 3; background: #000; } .game-player iframe { width: 100%; height: 100%; border: 0; display: block; } /* about: terminal-style metadata / contact list */ .meta { display: grid; grid-template-columns: max-content 1fr; column-gap: 1.5ch; row-gap: 0.15rem; font-family: var(--font-family-mono); margin: var(--spacing-base) 0 0; } .meta dt { color: var(--color-text-muted); } .meta dd { margin: 0; } .meta a { margin-left: 0; } /* `dark` / `light` theme toggle — overrides the theme's colour variables */ html.dark { color-scheme: dark; --color-bg-primary: hsl(0 0% 8%); --color-bg-secondary: hsl(0 0% 12%); --color-border: hsl(0 0% 22%); --color-selection-bg: hsl(0 0% 32%); --color-text-primary: hsl(0 0% 92%); --color-text-muted: hsl(0 0% 58%); --color-text-code: hsl(0 0% 78%); } /* `sl` — a steam locomotive that rolls across the screen */ .sl { position: fixed; top: 38%; left: 0; margin: 0; z-index: 9998; white-space: pre; font-family: var(--font-family-mono); font-size: 12px; line-height: 1; color: var(--color-text-primary); pointer-events: none; } /* boot splash — systemd/Arch-style log, but in the site's own look: light background, same mono font, constrained to the site column width */ #boot { display: none; } html.booting #boot { display: block; position: fixed; inset: 0; z-index: 10000; overflow-y: auto; scrollbar-width: none; padding: 0.8rem 0; background: var(--color-bg-primary); cursor: none; } html.booting #boot::-webkit-scrollbar { display: none; } .boot__log { width: 100%; max-width: var(--container-width); margin: 0 auto; padding: 0 1.5rem; border: 0; background: none; box-shadow: none; border-radius: 0; font-family: var(--font-family-mono); font-size: var(--font-size-small); line-height: 1.6; color: var(--color-text-primary); white-space: pre-wrap; word-break: break-word; } .boot__line { white-space: pre-wrap; } .boot__ok { font-weight: 700; } .boot__dim { color: var(--color-text-muted); } /* text isn't selectable anywhere — except the command line, which you type in */ body { -webkit-user-select: none; -moz-user-select: none; user-select: none; } .prompt__input { -webkit-user-select: text; -moz-user-select: text; user-select: text; } /* dotted content + footer links: unchanged at rest, invert to black on hover. include the header language switch so en/de behave like every other link. */ main a:hover, footer a:hover, .lang-switch:hover { background: var(--color-text-primary); color: var(--color-bg-primary); border-bottom-color: transparent; } /* self-hosted video — custom terminal-style player (no native controls, so only our own cursor shows; same-origin keeps the pixel cursor working) */ .player { position: relative; width: 100%; aspect-ratio: 16 / 9; background: #000; font-family: var(--font-family-mono); overflow: hidden; } .player video { width: 100%; height: 100%; display: block; object-fit: contain; background: #000; } .player__big { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 2.4rem; color: #fff; background: none; border: 0; } .player.is-playing .player__big { display: none; } .player__bar { position: absolute; left: 0; right: 0; bottom: 0; display: flex; align-items: center; gap: 1ch; padding: 0.5rem 1ch; color: #fff; font-size: var(--font-size-small); background: linear-gradient(transparent, rgba(0, 0, 0, 0.55)); opacity: 0; transition: opacity 0.2s ease; } .player:hover .player__bar, .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:hover { background: #fff; color: #000; } .player__scrub { flex: 1; height: 3px; background: rgba(255, 255, 255, 0.3); position: relative; } .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:fullscreen { width: 100vw; height: 100vh; aspect-ratio: auto; } /* all images always in colour (selection drives what's shown, not hover) */ img { filter: none; } /* make the [hidden] attribute win over .tui__preview img { display:block } */ [hidden] { display: none !important; } .tui__status { font-family: var(--font-family-mono); font-size: var(--font-size-small); color: var(--color-text-muted); margin-top: 0.4rem; } /* frozen/locked preview indicator */ .filebrowser.is-frozen .tui__status { color: var(--color-text-primary); } .filebrowser.is-frozen .tui__status::after { content: " ● locked"; } /* hide every scrollbar site-wide — content still scrolls (wheel/touch/keys), just no native chrome. keeps the clean terminal surface everywhere. */ * { scrollbar-width: none; /* Firefox */ -ms-overflow-style: none; /* old Edge/IE */ } *::-webkit-scrollbar { /* WebKit/Blink */ display: none; } /* ─────────────────── LANGUAGE SWITCH ─────────────────── */ /* top-right toggle on the shell row: shows only the *other* language, click to switch. you never see both at once. */ .topbar { position: relative; } .lang-switch { position: absolute; top: 0; right: 0; margin-left: 0; font-family: var(--font-family-mono); color: var(--color-text-muted); border-bottom: 1px dotted; z-index: 2; } /* hover state comes from the shared "main a:hover, footer a:hover, .lang-switch:hover" rule above (invert to black bg + white text) — a second, later rule here used to override just the text color back to dark, leaving black-on-black on hover. */ /* no static width reservation needed here anymore — syncLangSwitch() (shell.js) already hides the toggle the moment a long line would reach it */