/* ==========================================================================
   Ibomeno Basiekanem — portfolio
   A TUI. Not a "cyber" theme — this borrows from tools people actually use:
   tmux status lines, lazygit panes, vim gutters, k9s tables.

   Palette shares its ground and accent with DataBites, so the two sites feel
   like one place. Type is IBM Plex Mono throughout, because a terminal has
   one font and committing to that is the whole point.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens — dark
   -------------------------------------------------------------------------- */
:root {
    /* Tells the browser which way round native scrollbars and form controls
       should be drawn. Without it Firefox kept them light in the dark theme. */
    color-scheme: dark;
    /* Ground shared with DataBites (thelordbass.github.io/databites), so
       moving between the two sites feels like one place. Same warm paper and
       ink; the structure here stays a TUI. Hues below map one-to-one onto
       six of that app's track inks, the original six. */
    --bg:         #16130f;
    --bg-1:       #1e1a15;
    --bg-2:       #241f19;
    --bg-3:       #2b251e;
    --bg-sel:     #3a322a;

    --fg:         #f4eee4;
    --fg-dim:     #a89f92;
    --fg-faint:   #8f8579;  /* clears 4.5:1 on bg, bg-1 and bg-2, not just bg */

    --accent:     #f07050;
    --accent-2:   #d9573c;
    --yellow:     #d9a874;
    --green:      #adbd84;
    --aqua:       #83c39c;
    --blue:       #7cb2cc;
    --purple:     #dd94ab;
    --red:        #ef7a63;

    --line:       #302a23;
    --line-2:     #453d34;

    --on-accent:  #16130f;

    --mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

    --wrap: 1140px;
    --gutter: clamp(1rem, 3.5vw, 2.25rem);
    --section-y: clamp(3.5rem, 8vw, 6rem);

    --cell: 1.7;        /* line height ~ one terminal cell */
    --t: 120ms linear;  /* terminals don't ease */
}

[data-theme="light"] {
    color-scheme: light;
    --bg:         #faf7f0;
    --bg-1:       #fffdf8;
    --bg-2:       #f1ebdf;
    --bg-3:       #ebe4d6;
    --bg-sel:     #ddd5c5;

    --fg:         #17140f;
    --fg-dim:     #524b44;
    --fg-faint:   #6e655a;  /* clears 4.5:1 on bg through bg-3, not just bg */

    --accent:     #b23a26;
    --accent-2:   #8e2d1e;
    /* These are used as text, so
       every one is darkened until it clears 4.5:1 on --bg while staying the
       same hue as its DataBites counterpart. Verified, not eyeballed. */
    --yellow:     #6b4f34;
    --green:      #4a5537;
    --aqua:       #375043;
    --blue:       #2e4d5d;
    --purple:     #734556;
    --red:        #8e3529;

    --line:       #ddd5c5;
    --line-2:     #c9c0ae;

    --on-accent:  #fdf8f0;
}

/* --------------------------------------------------------------------------
   2. Base
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html {
    scroll-behavior: smooth;
    scroll-padding-top: 4rem;
    -webkit-text-size-adjust: 100%;
}

body {
    background-color: var(--bg);
    /* Dot grid paper: one dot every 24px, quieter behind text than ruled
       lines. Fixed to the viewport so the cursor's lit dots line up with it. */
    background-image: radial-gradient(circle, var(--line-2) .8px, transparent 1.3px);
    background-size: 24px 24px;
    background-attachment: fixed;
    color: var(--fg);
    font-family: var(--mono);
    font-size: 15px;
    line-height: var(--cell);
    padding-bottom: 2.5rem;   /* clears the fixed status line */
    -webkit-font-smoothing: antialiased;
    overflow-x: hidden;
}
[data-theme="light"] body { background-blend-mode: multiply; }

/* Paper grain, shared with DataBites. Sits over the character-cell grid
   rather than replacing it — the grid is structure, this is texture. */
body::after {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 9999;
    pointer-events: none;
    opacity: .05;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}
[data-theme="light"] body::after { opacity: .035; }

/* Cursor spotlight ---------------------------------------------------------
   Two layers that follow the pointer: a soft glow in the accent colour, and
   the dot grid redrawn in the accent and masked to a circle, so the dots
   nearest the pointer light up. Fixed to the viewport, like the dots it sits
   on, so they stay lined up. It sits behind <main>, so it shows in the gaps
   between panes, not over the text. */
.cursor-glow {
    --gx: 50vw;
    --gy: 30vh;
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    opacity: 0;
    transition: opacity .35s ease;
    background:
        radial-gradient(560px circle at var(--gx) var(--gy),
            color-mix(in srgb, var(--accent) 15%, transparent), transparent 70%);
}
.cursor-glow::before {
    content: "";
    position: absolute;
    inset: 0;
    background-image: radial-gradient(circle, var(--accent) 1px, transparent 1.5px);
    background-size: 24px 24px;
    opacity: .75;
    -webkit-mask-image: radial-gradient(300px circle at var(--gx) var(--gy), #000, transparent 75%);
            mask-image: radial-gradient(300px circle at var(--gx) var(--gy), #000, transparent 75%);
}
.cursor-glow.is-on { opacity: 1; }
/* Over text you are reading (hero, section intros, the About prose), the
   glow backs right off so the lit grid lines stop running through letters.
   It comes back as soon as the pointer moves to empty space or a card. */
.cursor-glow.is-on.is-dim { opacity: .3; }
[data-theme="light"] .cursor-glow {
    background:
        radial-gradient(560px circle at var(--gx) var(--gy),
            color-mix(in srgb, var(--accent) 10%, transparent), transparent 70%);
}
[data-theme="light"] .cursor-glow::before { opacity: .55; }
/* Mouse and trackpad only: no pointer to follow on a touch screen. */
@media (hover: none), (pointer: coarse) { .cursor-glow { display: none; } }

img, svg { display: block; max-width: 100%; }
img { height: auto; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

button, input, textarea, select { font: inherit; color: inherit; }
button { cursor: pointer; background: none; border: none; }

h1, h2, h3, h4 { font-weight: 600; line-height: 1.25; }

kbd {
    display: inline-block;
    min-width: 1.35em;
    padding: 0 .3em;
    border: 1px solid var(--line-2);
    background: var(--bg-3);
    color: var(--fg-dim);
    font-family: inherit;
    font-size: .85em;
    text-align: center;
}

::selection { background: var(--accent); color: var(--on-accent); }

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.sr-only {
    position: absolute; width: 1px; height: 1px;
    padding: 0; overflow: hidden; clip: rect(0,0,0,0);
    white-space: nowrap; border: 0;
}

.skip-link {
    position: absolute; top: -100px; left: var(--gutter);
    z-index: 300;
    background: var(--accent); color: var(--on-accent);
    padding: .5rem 1rem; font-weight: 600;
    transition: top var(--t);
}
.skip-link:focus { top: 0; text-decoration: none; }

::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: var(--bg-1); }
::-webkit-scrollbar-thumb { background: var(--bg-sel); border: 3px solid var(--bg-1); }
::-webkit-scrollbar-thumb:hover { background: var(--fg-faint); }

/* --------------------------------------------------------------------------
   3. Layout
   -------------------------------------------------------------------------- */
.wrap {
    width: 100%;
    max-width: var(--wrap);
    margin-inline: auto;
    padding-inline: var(--gutter);
}

main { position: relative; z-index: 1; }

.section { padding-block: var(--section-y); }
.section-alt { background: color-mix(in srgb, var(--bg-1) 72%, transparent); }

.section-head { margin-bottom: clamp(1.75rem, 3.5vw, 2.75rem); }

/* `┌─ label ────────────────────` — a pane header rule that fills its width */
.eyebrow {
    display: flex;
    align-items: center;
    gap: .55rem;
    font-size: .82rem;
    letter-spacing: .04em;
    text-transform: lowercase;
    color: var(--accent);
    margin-bottom: .9rem;
}
.eyebrow::before { content: "┌─"; color: var(--line-2); }
.eyebrow::after {
    content: "";
    flex: 1;
    height: 0;
    border-top: 1px solid var(--line-2);
}

.section-title {
    font-size: clamp(1.4rem, 3vw, 2rem);
    letter-spacing: -0.01em;
    margin-bottom: .6rem;
}
.section-title::before { content: "$ "; color: var(--fg-faint); font-weight: 400; }

.section-lede { color: var(--fg-dim); max-width: 74ch; }

.prose { color: var(--fg-dim); max-width: 74ch; }
.prose p + p { margin-top: 1rem; }

/* Panes ------------------------------------------------------------------ */
.card {
    background: var(--bg-1);
    border: 1px solid var(--line-2);
    padding: clamp(1rem, 2vw, 1.4rem);
    transition: border-color var(--t);
}
/* Only on cards you can act on; a hover on a skill card or the sidebar read
   as though it could be clicked. */
.project-card:hover { border-color: var(--fg-faint); }

.pane-title {
    font-size: .8rem;
    letter-spacing: .04em;
    text-transform: lowercase;
    color: var(--accent);
    margin-bottom: .9rem;
}
.pane-title::before { content: "── "; color: var(--line-2); }

/* --------------------------------------------------------------------------
   4. Buttons
   -------------------------------------------------------------------------- */
.btn {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    padding: .5rem 1rem;
    border: 1px solid var(--line-2);
    background: var(--bg-2);
    color: var(--fg);
    font-size: .9rem;
    transition: background-color var(--t), color var(--t), border-color var(--t);
}
.btn:hover { background: var(--accent); border-color: var(--accent); color: var(--on-accent); text-decoration: none; }
.btn::before { content: "["; color: var(--fg-faint); }
.btn::after  { content: "]"; color: var(--fg-faint); }
.btn:hover::before, .btn:hover::after { color: var(--on-accent); }

.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 600; }
.btn-primary::before, .btn-primary::after { color: var(--on-accent); }
.btn-primary:hover { background: var(--bg-2); border-color: var(--fg-faint); color: var(--fg); }
.btn-primary:hover::before, .btn-primary:hover::after { color: var(--fg-faint); }

.btn-small { padding: .3rem .7rem; font-size: .82rem; }
.btn-block { width: 100%; justify-content: center; }

.linkish { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }

/* --------------------------------------------------------------------------
   5. Tab bar (tmux/vim window list)
   -------------------------------------------------------------------------- */
.tuibar {
    position: sticky;
    top: 0;
    z-index: 100;
    background: var(--bg-1);
    border-bottom: 1px solid var(--line-2);
}

.tuibar-inner {
    display: flex;
    align-items: center;
    gap: 1rem;
    height: 2.9rem;
}

.host { font-size: .85rem; white-space: nowrap; color: var(--fg-dim); }
.host:hover { text-decoration: none; }
.host-user    { color: var(--green); font-weight: 600; }
.host-at      { color: var(--fg-faint); }
.host-machine { color: var(--aqua); }
.host-path    { color: var(--fg-faint); }

.tabs { margin-left: auto; }
.tab-list { display: flex; gap: 0; list-style: none; padding: 0; }

.tab {
    display: block;
    padding: .25rem .7rem;
    font-size: .85rem;
    color: var(--fg-faint);
    border: 1px solid transparent;
    transition: color var(--t), background-color var(--t);
}
.tab b { color: var(--fg-faint); font-weight: 400; margin-right: .35rem; }
.tab:hover { color: var(--fg); background: var(--bg-3); text-decoration: none; }
.tab:hover b { color: var(--accent); }

/* Active pane = inverse video, the way a terminal shows selection */
.tab.is-active {
    background: var(--accent);
    color: var(--on-accent);
    font-weight: 600;
}
.tab.is-active b { color: var(--on-accent); }
.tab.is-active:hover { background: var(--accent); color: var(--on-accent); }

.tuibar-actions { display: flex; align-items: center; gap: .4rem; }

.keycap {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .25rem .55rem;
    border: 1px solid var(--line-2);
    background: var(--bg-2);
    font-size: .8rem;
    color: var(--fg-dim);
    transition: border-color var(--t), color var(--t);
}
.keycap:hover { border-color: var(--accent); color: var(--accent); }
.keycap-key { color: var(--accent); font-weight: 600; }
.keycap:hover .keycap-key { color: var(--accent); }
.keycap-label { text-transform: lowercase; }

.menu-toggle {
    display: none;
    flex-direction: column;
    gap: 4px;
    padding: .5rem .45rem;
    border: 1px solid var(--line-2);
}
.menu-toggle span { display: block; width: 16px; height: 1.5px; background: var(--fg-dim); }
.menu-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(5.5px) rotate(45deg); }
.menu-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.menu-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-5.5px) rotate(-45deg); }

/* --------------------------------------------------------------------------
   6. Status line (fixed, tmux-style)
   -------------------------------------------------------------------------- */
.statusline {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 120;
    display: flex;
    align-items: center;
    gap: .75rem;
    height: 2.1rem;
    padding-inline: .75rem;
    background: var(--bg-3);
    border-top: 1px solid var(--line-2);
    font-size: .78rem;
    color: var(--fg-dim);
    white-space: nowrap;
    overflow: hidden;
}
.sl-mode {
    background: var(--accent);
    color: var(--on-accent);
    font-weight: 700;
    padding: .12rem .6rem;
    letter-spacing: .06em;
}
.sl-file { color: var(--aqua); }
.sl-keys { margin-left: auto; display: flex; align-items: center; gap: .3rem; }
.sl-keys kbd { background: var(--bg-2); }
.sl-sep { color: var(--fg-faint); margin-inline: .25rem; }
.sl-pos {
    background: var(--bg-sel);
    color: var(--fg);
    padding: .12rem .55rem;
    min-width: 3.4rem;
    text-align: right;
}

/* --------------------------------------------------------------------------
   7. Hero
   -------------------------------------------------------------------------- */
.hero { padding-block: clamp(2.5rem, 6vw, 4.5rem) clamp(2.5rem, 6vw, 4rem); }

.hero-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, .92fr);
    gap: clamp(1.5rem, 4vw, 3rem);
    align-items: start;
}

/* Sized so "Data hides things." (18 mono characters, about 0.58em each)
   always fits its column on one line; the second sentence sits underneath
   as a smaller line of its own. The mobile size is set in section 15. */
.hero-title {
    font-size: clamp(2rem, 4vw, 3rem);
    letter-spacing: -0.02em;
    margin-bottom: 1.1rem;
    text-wrap: balance;
}
.hero-sub {
    display: block;
    margin-top: .7rem;
    font-size: clamp(1.1rem, .5em, 1.5rem);
    font-weight: 500;
    letter-spacing: 0;
    line-height: 1.4;
    color: var(--fg-dim);
    text-wrap: balance;
}

.hero-lede { color: var(--fg-dim); max-width: 62ch; margin-bottom: 1.75rem; }

.hero-actions { display: flex; flex-wrap: wrap; gap: .6rem; margin-bottom: 2rem; }

/* Terminal --------------------------------------------------------------- */
.terminal {
    background: var(--bg-1);
    border: 1px solid var(--line-2);
}

.terminal-bar {
    display: flex;
    align-items: center;
    gap: .45rem;
    padding: .4rem .7rem;
    background: var(--bg-3);
    border-bottom: 1px solid var(--line-2);
}
.terminal-bar .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--line-2); }
.terminal-bar .dot:first-child   { background: var(--red); }
.terminal-bar .dot:nth-child(2)  { background: var(--yellow); }
.terminal-bar .dot:nth-child(3)  { background: var(--green); }
.terminal-title { margin-left: auto; font-size: .74rem; color: var(--fg-faint); }

.terminal-body {
    padding: .8rem .9rem;
    font-size: .78rem;
    line-height: 1.65;
    height: 33rem;   /* holds the whole script at rest, so nothing is clipped */
    overflow: auto;
    scrollbar-width: thin;
}
.terminal-body::-webkit-scrollbar { width: 8px; }
.terminal-body::-webkit-scrollbar-track { background: transparent; }
.terminal-body::-webkit-scrollbar-thumb { background: var(--bg-sel); border: 0; }

/* Terminals don't reflow. Wrapping would break the column alignment that
   makes these read as query output, so lines stay on one line and the pane
   scrolls sideways if it has to. */
.t-line { white-space: pre; }
.t-cmd  { color: var(--green); }
.t-out  { color: var(--fg-dim); }
.t-dim  { color: var(--fg-faint); }
.t-ok   { color: var(--aqua); }
.t-head { color: var(--yellow); font-weight: 600; }

.t-caret::after {
    content: "█";
    color: var(--accent);
    animation: blink 1.1s steps(2, start) infinite;
}
@keyframes blink { to { visibility: hidden; } }

/* --------------------------------------------------------------------------
   8. About
   -------------------------------------------------------------------------- */
.about-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
    gap: clamp(1.5rem, 3.5vw, 3rem);
    align-items: start;
}

/* Much shorter than the prose beside it, so it stays in view while you read. */
.about-side { background: var(--bg-1); border: 1px solid var(--line-2); padding: clamp(1rem, 2vw, 1.4rem); position: sticky; top: 4rem; }

.side-title {
    font-size: .8rem;
    text-transform: lowercase;
    letter-spacing: .04em;
    color: var(--accent);
    margin-bottom: 1rem;
}
.side-title::before { content: "── "; color: var(--line-2); }

.detail-list { display: grid; gap: .85rem; font-size: .87rem; }
.detail-list > div { display: grid; gap: .1rem; }
.detail-list dt { color: var(--fg-faint); text-transform: lowercase; }
.detail-list dt::before { content: "▸ "; color: var(--accent); }
.detail-list dd { margin: 0; color: var(--fg); padding-left: 1rem; }

/* --------------------------------------------------------------------------
   9. Skills
   -------------------------------------------------------------------------- */
.skill-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
    gap: 1rem;
    margin-bottom: 2rem;
}

/* Icon and title share the first row; the text and tags run full width
   underneath. The icon sits in a small bordered cell, like a key on the
   status line, so it stays in the TUI's language. */
.skill-card {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    column-gap: .65rem;
    align-content: start;
}
.skill-card > p, .skill-card > .tag-list { grid-column: 1 / -1; }
.skill-icon {
    display: grid;
    place-items: center;
    width: 2rem;
    height: 2rem;
    margin-bottom: .6rem;
    border: 1px solid var(--line-2);
    background: var(--bg-2);
    color: var(--accent);
}
.skill-icon svg { width: 1.05rem; height: 1.05rem; }

/* A seventh card would leave two empty cells at three columns, so it runs
   the full width under the others instead; the text is capped so the line
   length stays readable. */
.skill-card.is-wide { grid-column: 1 / -1; }
.skill-card.is-wide p { max-width: 90ch; }

.skill-card h3 { font-size: .98rem; color: var(--yellow); margin-bottom: .6rem; align-self: center; }
.skill-card p { color: var(--fg-dim); font-size: .87rem; margin-bottom: .9rem; }

.tag-list { display: flex; flex-wrap: wrap; gap: .3rem .5rem; list-style: none; padding: 0; font-size: .76rem; }
.tag-list li { color: var(--fg-faint); }
.tag-list li::before { content: "["; color: var(--line-2); }
.tag-list li::after  { content: "]"; color: var(--line-2); }

/* Sandbox ---------------------------------------------------------------- */
.sandbox { padding: clamp(1rem, 2.5vw, 1.6rem); }
.sandbox-head { margin-bottom: 1rem; }
.sandbox-head h3 { font-size: 1rem; color: var(--yellow); margin-bottom: .3rem; }
.sandbox-head h3::before { content: "» "; color: var(--accent); }
.sandbox-head p { color: var(--fg-dim); font-size: .85rem; }

.chip-row { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: .9rem; }

.chip {
    padding: .3rem .6rem;
    border: 1px solid var(--line-2);
    background: var(--bg-2);
    color: var(--fg-dim);
    font-size: .76rem;
    transition: background-color var(--t), color var(--t), border-color var(--t);
}
.chip:hover { border-color: var(--accent); color: var(--accent); }
.chip.is-active { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 600; }

.sandbox-out {
    background: var(--bg);
    border: 1px solid var(--line-2);
    padding: .8rem .9rem;
    font-size: .78rem;
    min-height: 15rem;
    overflow-x: auto;
}

.res-table { width: 100%; border-collapse: collapse; margin-top: .6rem; font-size: .76rem; }
.res-table th, .res-table td { text-align: left; padding: .28rem .8rem .28rem 0; white-space: nowrap; }
.res-table th { color: var(--yellow); font-weight: 600; border-bottom: 1px solid var(--line-2); padding-bottom: .35rem; }
.res-table td { color: var(--fg-dim); }
.res-table tbody tr:hover td { background: var(--bg-3); color: var(--fg); }

/* --------------------------------------------------------------------------
   10. Projects
   -------------------------------------------------------------------------- */
.filter-row { display: flex; flex-wrap: wrap; gap: .4rem; margin-bottom: 1.4rem; }

.filter-btn {
    padding: .3rem .8rem;
    border: 1px solid var(--line-2);
    background: var(--bg-2);
    color: var(--fg-dim);
    font-size: .82rem;
    transition: background-color var(--t), color var(--t), border-color var(--t);
}
.filter-btn:hover { border-color: var(--accent); color: var(--accent); }
.filter-btn.is-active { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 600; }

/* Column count is fixed per breakpoint, not auto-fill. The featured/normal
   pairing only works if a row is always exactly three wide: with auto-fill,
   widths around 1000px fell to two columns while featured cards still
   spanned two, which left a hole beside every normal card. */
.project-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1rem;
    align-items: start;
}

.project-card { display: flex; flex-direction: column; height: 100%; padding: 0; }
.project-card.is-featured { grid-column: span 2; }
.project-card[hidden] { display: none; }

/* Once a filter is on, the pairing is gone (the Power BI filter is all
   featured cards), so every card takes one column and rows fill normally. */
.project-grid.is-filtered .project-card.is-featured { grid-column: span 1; }

.project-shot { display: block; border-bottom: 1px solid var(--line-2); overflow: hidden; aspect-ratio: 16/9; background: var(--bg); }
.project-shot img { width: 100%; height: 100%; object-fit: cover; object-position: top left; }
.project-shot:hover { text-decoration: none; }

.project-top,
.project-card > .project-desc,
.project-card > .tag-list,
.project-actions { padding-inline: clamp(.9rem, 2vw, 1.3rem); }

.project-top { padding-top: clamp(.9rem, 2vw, 1.3rem); margin-bottom: .55rem; }

.project-kind { display: block; font-size: .74rem; color: var(--accent); margin-bottom: .3rem; text-transform: lowercase; }
.project-kind::before { content: "── "; color: var(--line-2); }

.project-title { font-size: 1rem; color: var(--yellow); }

.project-desc { color: var(--fg-dim); font-size: .85rem; margin-bottom: .9rem; flex: 1 0 auto; max-width: 72ch; }
.project-card > .tag-list { margin-bottom: 1rem; }

.project-actions {
    display: flex;
    align-items: center;
    gap: .8rem;
    padding-bottom: clamp(.9rem, 2vw, 1.3rem);
    margin-top: auto;
    border-top: 1px dashed var(--line);
    padding-top: .8rem;
}

.project-open { font-size: .82rem; color: var(--accent); }
.project-open::before { content: "<enter> "; color: var(--fg-faint); }
.project-open:hover { text-decoration: underline; text-underline-offset: 3px; }

.project-repo, .project-viz {
    margin-left: auto;
    font-size: .78rem;
    color: var(--fg-faint);
    padding: .15rem .5rem;
    border: 1px solid var(--line-2);
    transition: color var(--t), border-color var(--t);
}
.project-repo::before { content: "</> "; color: var(--line-2); }
.project-viz::before  { content: "● ";   color: var(--green); }
.project-repo:hover, .project-viz:hover { color: var(--accent); border-color: var(--accent); text-decoration: none; }

/* fg-dim, not fg-faint: this sits on the open background, where the cursor
   glow can pass behind it, and fg-faint dips under 4.5:1 at the glow's centre. */
.grid-empty { text-align: center; color: var(--fg-dim); padding: 2.5rem 0; }
.grid-empty::before { content: "-- "; }
.grid-empty::after  { content: " --"; }

.grid-more { display: flex; flex-wrap: wrap; gap: .6rem; justify-content: center; margin-top: 1.75rem; }

/* --------------------------------------------------------------------------
   11. Timeline — a file tree, not a decorative rail
   -------------------------------------------------------------------------- */
.timeline { list-style: none; padding: 0; display: grid; gap: 1rem; }
.timeline-item { position: relative; padding-left: 1.6rem; }
.timeline-item::before {
    content: "├─";
    position: absolute;
    left: 0; top: .9rem;
    color: var(--line-2);
}
.timeline-item:last-child::before { content: "└─"; }
.timeline-marker { display: none; }

.timeline-date { font-size: .78rem; color: var(--accent); margin-bottom: .3rem; }
.timeline-role { font-size: 1.05rem; color: var(--yellow); margin-bottom: .15rem; }
.timeline-org  { font-size: .85rem; color: var(--fg-faint); margin-bottom: .9rem; }
.timeline-note { font-size: .85rem; color: var(--fg-dim); margin: -.4rem 0 .9rem; max-width: 72ch; }

.timeline-points { list-style: none; padding: 0; display: grid; gap: .55rem; max-width: 74ch; margin-bottom: .3rem; }
.timeline-points li { color: var(--fg-dim); font-size: .87rem; padding-left: 1.2rem; text-indent: -1.2rem; }
.timeline-points li::before { content: "▸ "; color: var(--accent); }

/* --------------------------------------------------------------------------
   12. Contact
   -------------------------------------------------------------------------- */
.contact-grid {
    display: grid;
    grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr);
    gap: 1rem;
    align-items: start;
}
.contact-side { display: grid; gap: 1rem; }
.contact-card { display: grid; gap: .3rem; }

.contact-label { font-size: .76rem; text-transform: lowercase; color: var(--accent); }
.contact-label::before { content: "── "; color: var(--line-2); }
.contact-value { font-size: .9rem; word-break: break-word; }
.contact-note  { font-size: .8rem; color: var(--fg-faint); }
.contact-card .btn-small { justify-self: start; margin-top: .4rem; }

.social-list { list-style: none; padding: 0; display: grid; gap: .45rem; font-size: .85rem; }
.social-list a { display: inline-flex; align-items: center; gap: .5rem; color: var(--fg); }
.social-list a:hover { color: var(--accent); }
.social-list svg { flex: none; }

/* Form ------------------------------------------------------------------- */
.form-card { padding: clamp(1.1rem, 2.5vw, 1.6rem); }

.form-intro {
    font-size: .82rem;
    color: var(--fg-faint);
    margin-bottom: 1.2rem;
    padding-bottom: 1rem;
    border-bottom: 1px dashed var(--line-2);
}

.field { margin-bottom: .9rem; }
.field label { display: block; font-size: .82rem; color: var(--fg-dim); margin-bottom: .3rem; }
.field label::before { content: "› "; color: var(--accent); }

.field input, .field textarea {
    width: 100%;
    padding: .5rem .65rem;
    background: var(--bg);
    border: 1px solid var(--line-2);
    color: var(--fg);
    font-size: .88rem;
    resize: vertical;
    transition: border-color var(--t);
}
/* Keeps the site-wide focus ring as well; a 1px border colour change on
   its own was too faint to find when tabbing through the form. */
.field input:focus, .field textarea:focus { border-color: var(--accent); }
.field input[aria-invalid="true"], .field textarea[aria-invalid="true"] { border-color: var(--red); }

.field-error { font-size: .78rem; color: var(--red); margin-top: .25rem; }
.field-error::before { content: "!! "; }

.form-status { margin-top: .8rem; font-size: .82rem; min-height: 1.2rem; }
.form-status.is-ok  { color: var(--green); }
.form-status.is-err { color: var(--red); }

/* --------------------------------------------------------------------------
   13. Modal
   -------------------------------------------------------------------------- */
.modal, .keys {
    position: fixed; inset: 0; z-index: 200;
    display: grid; place-items: center;
    padding: var(--gutter);
}
.modal[hidden], .keys[hidden] { display: none; }

.modal-backdrop, .keys-backdrop {
    position: absolute; inset: 0;
    background: color-mix(in srgb, var(--bg) 82%, transparent);
}

.modal-panel {
    position: relative;
    width: min(780px, 100%);
    max-height: min(86vh, 900px);
    overflow-y: auto;
    overscroll-behavior: contain;
    background: var(--bg-1);
    border: 1px solid var(--accent);
    padding: clamp(1.2rem, 3vw, 2rem);
}

.modal-close {
    position: absolute; top: .6rem; right: .6rem;
    width: 1.9rem; height: 1.9rem;
    display: grid; place-items: center;
    border: 1px solid var(--line-2);
    color: var(--fg-dim);
    background: var(--bg-2);
    z-index: 2;
}
.modal-close:hover { border-color: var(--red); color: var(--red); }

.m-kind  { font-size: .78rem; color: var(--accent); text-transform: lowercase; margin-bottom: .4rem; }
.m-kind::before { content: "── "; color: var(--line-2); }
.m-title { font-size: clamp(1.15rem, 2.6vw, 1.5rem); color: var(--yellow); margin-bottom: 1.3rem; padding-right: 2.5rem; }

.m-gallery { display: grid; gap: 1rem; margin-bottom: 1.6rem; }
.m-shot { margin: 0; border: 1px solid var(--line-2); background: var(--bg); }
.m-shot img { width: 100%; }
.m-shot figcaption { padding: .45rem .7rem; border-top: 1px solid var(--line-2); font-size: .78rem; color: var(--fg-faint); }

.m-block { margin-bottom: 1.3rem; }
.m-block:last-child { margin-bottom: 0; }
.m-h { font-size: .8rem; text-transform: lowercase; color: var(--accent); margin-bottom: .5rem; }
.m-h::before { content: "── "; color: var(--line-2); }
.m-block p { color: var(--fg-dim); font-size: .88rem; max-width: 74ch; }
.m-block p + p { margin-top: .7rem; }

.m-list { list-style: none; padding: 0; display: grid; gap: .45rem; max-width: 74ch; }
.m-list li { color: var(--fg-dim); font-size: .88rem; padding-left: 1.2rem; text-indent: -1.2rem; }
.m-list li::before { content: "▸ "; color: var(--accent); }
.m-list strong { color: var(--fg); }

.m-code {
    background: var(--bg);
    border: 1px solid var(--line-2);
    border-left: 2px solid var(--accent);
    padding: .8rem .9rem;
    font-size: .76rem;
    line-height: 1.65;
    color: var(--fg-dim);
    overflow-x: auto;
    white-space: pre;
}

.m-foot { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: 1.75rem; padding-top: 1.2rem; border-top: 1px dashed var(--line-2); }

/* Keybinding overlay ----------------------------------------------------- */
.keys-panel {
    position: relative;
    width: min(440px, 100%);
    background: var(--bg-1);
    border: 1px solid var(--accent);
    padding: 1.4rem;
}
.keys-list { display: grid; gap: .5rem; margin-bottom: 1.2rem; }
.keys-list > div { display: grid; grid-template-columns: 7.5rem 1fr; align-items: baseline; gap: .5rem; }
.keys-list dt { display: flex; gap: .2rem; align-items: center; }
.keys-list dd { margin: 0; color: var(--fg-dim); font-size: .85rem; }
.keys-foot { font-size: .8rem; color: var(--fg-faint); padding-top: .9rem; border-top: 1px dashed var(--line-2); }
.keys-switch { display: flex; align-items: center; gap: .5rem; font-size: .85rem; color: var(--fg-dim); margin-bottom: 1rem; cursor: pointer; }
.keys-switch input { accent-color: var(--accent); width: 1rem; height: 1rem; }
/* Shortcuts switched off: the status line stops advertising them. */
.keys-off .sl-keys { display: none; }

/* --------------------------------------------------------------------------
   14. Footer
   -------------------------------------------------------------------------- */
.site-footer { position: relative; z-index: 1; border-top: 1px solid var(--line-2); background: var(--bg-1); padding-block: 1.5rem; }
.footer-inner { display: flex; flex-wrap: wrap; gap: .3rem 1.5rem; justify-content: space-between; font-size: .8rem; color: var(--fg-faint); }

/* --------------------------------------------------------------------------
   15. Responsive
   -------------------------------------------------------------------------- */
@media (max-width: 980px) {
    .hero-grid { grid-template-columns: minmax(0, 1fr); }
    /* One column: the name and intro come first, the terminal follows. */
    .hero-title { font-size: clamp(1.5rem, 8vw, 3rem); }
    .terminal-body { height: 16rem; }
    .about-grid, .contact-grid { grid-template-columns: 1fr; }
    .about-side { position: static; }
    .project-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .project-card.is-featured { grid-column: span 1; }
}

@media (max-width: 780px) {
    .menu-toggle { display: flex; }
    .tabs {
        position: fixed;
        inset: 2.9rem 0 auto 0;
        background: var(--bg-1);
        border-bottom: 1px solid var(--accent);
        padding: .4rem var(--gutter) .8rem;
        display: none;
    }
    .tabs.is-open { display: block; }
    .tab-list { flex-direction: column; }
    .tab { padding: .55rem .5rem; font-size: .95rem; }

    .keycap-label { display: none; }
    .sl-keys { display: none; }
    .host-path { display: none; }

    .project-grid { grid-template-columns: 1fr; }
    .keys-list > div { grid-template-columns: 6rem 1fr; }
}

@media (max-width: 460px) {
    .host-machine { display: none; }
    .host-at { display: none; }
    /* A card carrying both a Live and a Code badge runs ~8px over at 375px.
       The `<enter>` prefix is decoration, so it goes first and the labels stay.
       Wrap is kept as the fallback for anything narrower still. */
    .project-actions { flex-wrap: wrap; gap: .45rem; }
    .project-repo, .project-viz { margin-left: 0; }
    .project-open { margin-right: auto; }
    .project-open::before { content: none; }
}

/* --------------------------------------------------------------------------
   16. Motion & print
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
    }
    .t-caret::after { animation: none; }
    .cursor-glow { display: none; }
}

@media print {
    .tuibar, .statusline, .modal, .keys, .sandbox, .filter-row,
    .hero-actions, .form-card, .skip-link, .terminal, .cursor-glow { display: none !important; }
    body { background: #fff; color: #000; padding-bottom: 0; }
    .section { padding-block: 1.25rem; page-break-inside: avoid; }
    .card { border: 1px solid #ccc; }
    /* Real addresses only: in-page links printed as "(#)" and "(#about)". */
    a[href^="http"]::after, a[href^="mailto:"]::after { content: " (" attr(href) ")"; font-size: .75em; color: #555; }
}
