/* longman.me. Tokens and their contrast ratios are listed in the design spec.
   --rule is for lines only, never for text. Minimum text size is 0.75rem. */

@font-face {
    font-family: 'Plex Sans';
    src: url('/assets/fonts/plex-sans-400.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Plex Sans';
    src: url('/assets/fonts/plex-sans-600.woff2') format('woff2');
    font-weight: 600;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: 'Plex Mono';
    src: url('/assets/fonts/plex-mono-400.woff2') format('woff2');
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

:root {
    --bg: #0e0c08;
    --surface: #17130c;
    --border: #2a2418;
    --rule: #6e6353;
    --text: #f0eae0;
    --text-muted: #a79c8b;
    --text-faint: #8a7e6b;
    --accent: #e8b14a;
    --accent-border: #4a3a1c;
    --sans: 'Plex Sans', system-ui, sans-serif;
    --mono: 'Plex Mono', ui-monospace, monospace;
    --measure: 46rem;
    color-scheme: dark;
}

/* Light theme: the same tokens on warm paper. Applies when the system prefers light and no dark choice is
   stored, or when a light choice is stored. Ratios are in the spec's light token table. */
@media (prefers-color-scheme: light) {
    :root:not([data-theme="dark"]) {
        --bg: #faf7f2;
        --surface: #f1ece3;
        --border: #e0d8ca;
        --rule: #b8ad9c;
        --text: #1c1710;
        --text-muted: #5e5447;
        --text-faint: #6e6253;
        --accent: #855a0a;
        --accent-border: #d9be86;
        color-scheme: light;
    }
}
:root[data-theme="light"] {
    --bg: #faf7f2;
    --surface: #f1ece3;
    --border: #e0d8ca;
    --rule: #b8ad9c;
    --text: #1c1710;
    --text-muted: #5e5447;
    --text-faint: #6e6253;
    --accent: #855a0a;
    --accent-border: #d9be86;
    color-scheme: light;
}

*, *::before, *::after { box-sizing: border-box; }

html {
    background: var(--bg);
    color: var(--text);
    font-family: var(--sans);
    line-height: 1.6;
    -webkit-text-size-adjust: 100%;
}

body { margin: 0; }

main, .site-header, .site-footer {
    max-width: var(--measure);
    margin: 0 auto;
    padding-left: 1.25rem;
    padding-right: 1.25rem;
}

a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 0.2em; }
a:hover { text-decoration-thickness: 2px; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

h1, h2, h3 { font-weight: 600; line-height: 1.15; letter-spacing: -0.02em; }
h1 { font-size: clamp(2.25rem, 6vw, 3rem); margin: 0; }
code, kbd, pre, samp { font-family: var(--mono); font-size: 0.9em; }

.eyebrow, .chip-value, .chip-label, .meta, .fingerprint, .figure { font-family: var(--mono); }
.eyebrow, .chip-label {
    font-size: 0.75rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--text-faint);
}

/* Header: the handle at the left, the nav and the theme toggle at the right. Under 480px the handle takes the first
   line and the nav and the toggle share the second. The toggle keeps its box while the script has not revealed it,
   so revealing it moves nothing: the page starts at the same height with or without scripts. */
.site-header {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.75rem 1.25rem;
    padding-top: 1.5rem;
    padding-bottom: 1.5rem;
}
.site-header .handle { font-family: var(--mono); color: var(--text); text-decoration: none; }
.site-header nav { margin-left: auto; }
.site-header ul {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1.25rem;
    list-style: none;
    margin: 0;
    padding: 0;
    font-size: 0.9rem;
}
.theme-toggle {
    font: inherit;
    font-size: 0.85rem;
    color: var(--accent);
    background: none;
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 0.2rem 0.7rem;
    cursor: pointer;
}
.theme-toggle[hidden] { display: inline-block; visibility: hidden; }
@media (max-width: 479px) {
    .site-header { display: grid; grid-template-columns: 1fr auto; }
    .site-header .handle { grid-column: 1 / -1; justify-self: start; }
    .site-header nav { margin-left: 0; }
    .site-header ul { column-gap: 1rem; }
}

/* Hero. The front page's hero carries a full-bleed backdrop (the owner's drone still, dimmed and faded into
   the page), the portrait and a credit; the Street View page keeps the plain hero. */
.hero { padding: 2.5rem 0 3rem; }
.hero .role { color: var(--text-muted); margin: 0.75rem 0 0; }
.hero .lede { font-size: 1.125rem; max-width: 38rem; margin: 1rem 0 0; }
/* The backdrop's width: 100vw includes a classic scrollbar's width, a sliver wider than the page; the front page
   clips it rather than scroll sideways to it. The clip passes to the viewport and hides any sideways overflow on
   the page, so only the front page sets it. */
body.home { overflow-x: clip; }
.home .hero { position: relative; display: flex; flex-direction: column; gap: 1.5rem; padding: 1.5rem 0 3rem; }
.home .hero-text { flex: 1; min-width: 0; }
.home .hero .chips li { background: color-mix(in srgb, var(--surface) 88%, transparent); }
.home .portrait {
    order: -1;
    width: 96px;
    height: 96px;
    border-radius: 50%;
    object-fit: cover;
    border: 2px solid var(--accent-border);
    box-shadow: 0 0 0 10px color-mix(in srgb, var(--bg) 50%, transparent), 0 0 60px color-mix(in srgb, var(--accent) 18%, transparent);
}
.hero-backdrop {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 50%;
    width: 100vw;
    transform: translateX(-50%);
    z-index: -1;
    overflow: hidden;
}
.hero-backdrop img { width: 100%; height: 100%; object-fit: cover; opacity: 0.38; filter: saturate(0.7); }
/* The fade's top stops are as light as AA allows for the faint eyebrow over the photo's brightest part (the snow
   behind it), measured in both themes; re-measure when the photo changes. */
.hero-backdrop::after {
    content: '';
    position: absolute;
    inset: 0;
    background:
        linear-gradient(180deg, color-mix(in srgb, var(--bg) 92%, transparent) 0%, color-mix(in srgb, var(--bg) 85%, transparent) 55%, var(--bg) 100%);
}
/* Two amber discs drift behind the text and the portrait: the ::before is the larger one, top right, and its
   box-shadow the smaller one, below and to the left of it (placed per layout), so one animation moves both and
   readers who prefer reduced motion get both still. The photo's opacity makes it a layer of its own, painted after
   ::before, so the discs glow up through the photo; the fade (::after) lies over both. */
.hero-backdrop::before {
    content: '';
    position: absolute;
    width: 28rem;
    height: 28rem;
    right: -8rem;
    top: -10rem;
    border-radius: 50%;
    background: color-mix(in srgb, var(--accent) 22%, transparent);
    box-shadow: -15rem 18rem 0 -3rem color-mix(in srgb, var(--accent) 18%, transparent);
    filter: blur(80px);
    animation: drift 40s ease-in-out infinite alternate;
}
@keyframes drift {
    from { transform: translate(0, 0) scale(1); }
    to { transform: translate(-6rem, 5rem) scale(1.15); }
}
@media (prefers-reduced-motion: reduce) {
    .hero-backdrop::before { animation: none; }
}
.hero-credit {
    position: absolute;
    right: 0;
    bottom: 0.75rem;
    margin: 0;
    font-family: var(--mono);
    font-size: 0.75rem;
    color: var(--text-faint);
}
@media (min-width: 48rem) {
    .home .hero { flex-direction: row; align-items: center; gap: 2.5rem; min-height: 560px; padding: 2.5rem 0 3.5rem; }
    .home .portrait { order: 0; width: 220px; height: 220px; flex: none; }
    .hero-backdrop::before { box-shadow: -22rem 18rem 0 -3rem color-mix(in srgb, var(--accent) 18%, transparent); }
    .hero-backdrop::after {
        background:
            linear-gradient(90deg, color-mix(in srgb, var(--bg) 95%, transparent) 0%, color-mix(in srgb, var(--bg) 75%, transparent) 45%, color-mix(in srgb, var(--bg) 30%, transparent) 100%),
            linear-gradient(180deg, color-mix(in srgb, var(--bg) 67%, transparent) 0%, color-mix(in srgb, var(--bg) 10%, transparent) 60%, var(--bg) 100%);
    }
}
.chips { display: flex; flex-wrap: wrap; gap: 0.625rem; list-style: none; margin: 1.75rem 0 0; padding: 0; }
.chips li {
    display: flex;
    flex-direction: column;
    gap: 0.125rem;
    padding: 0.625rem 0.875rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
}
.chip-value { font-size: 1.125rem; color: var(--accent); font-variant-numeric: tabular-nums; }
.inline-links {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    list-style: none;
    margin: 1.5rem 0 0;
    padding: 0;
    font-size: 0.9rem;
}

/* Sections and lists: one surface, a hairline and air between sections, a real heading under a mono eyebrow */
main > section { padding: 2rem 0 2.25rem; }
main > section + section { border-top: 1px solid var(--border); }
.eyebrow:has(+ .section-heading) { margin: 0; }
/* A jump to a heading lands it, with the eyebrow above it, below the floating section nav. */
.section-heading { font-size: 1.45rem; margin: 0.15rem 0 1.25rem; scroll-margin-top: 5.5rem; }
.section-heading::after {
    content: '';
    display: block;
    width: 2.5rem;
    height: 2px;
    margin-top: 0.6rem;
    background: var(--accent);
}
.prose .section-heading { margin-top: 2.25rem; }
.list { list-style: none; margin: 0; padding: 0; }
.list > li { padding: 0.625rem 0; border-bottom: 1px solid var(--border); }
.list > li:last-child { border-bottom: 0; }
.row { display: grid; grid-template-columns: 1fr auto; gap: 0.25rem 1rem; align-items: baseline; }
.row .primary { font-weight: 600; }
.row .secondary { grid-column: 1; }
.secondary { color: var(--text-muted); }
.meta { font-size: 0.8125rem; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.row .meta { white-space: nowrap; }
.figure { color: var(--accent); font-variant-numeric: tabular-nums; }
.more { margin: 1rem 0 0; font-size: 0.9rem; }

/* Role rows: a mark beside the text. On a phone the mark, the gaps and the dates leave the title too little room
   (the front page would clip the dates), so the dates take their own line under the title and may wrap. From the
   48rem layout breakpoint up, and in print, they sit at the right of the title as in every other row. */
.role-row { grid-template-columns: 44px 1fr; column-gap: 0.75rem; }
.role-row .mark { grid-row: 1 / span 3; align-self: start; }
.role-row > :not(.mark) { grid-column: 2; }
.role-row .meta { white-space: normal; }
@media (min-width: 48rem), print {
    .role-row { grid-template-columns: 44px 1fr auto; column-gap: 1rem; }
    .role-row .mark { grid-row: 1 / span 2; }
    .role-row .meta { grid-column: 3; white-space: nowrap; }
}
.mark {
    width: 44px;
    height: 44px;
    border-radius: 6px;
    background: var(--surface);
    border: 1px solid var(--border);
    color: var(--text-muted);
    font-family: var(--mono);
    font-size: 0.75rem;
    letter-spacing: 0.05em;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}
.mark svg { width: 32px; height: 32px; }

/* Tabs: panels stack with their headings; the script turns them into a tab list. The headings hide only behind a
   tab list that exists, so a script that stops before building one leaves them in place. */
.tab-heading { font-size: 1rem; margin: 1.25rem 0 0.5rem; }
.tab-panel:first-child .tab-heading { margin-top: 0; }
.tablist ~ .tab-panel .tab-heading { display: none; }
.tablist { display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 0 0 1rem; }
.tablist button {
    font: inherit;
    font-size: 0.85rem;
    padding: 0.3rem 0.8rem;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: none;
    color: var(--text-muted);
    cursor: pointer;
}
.tablist button[aria-selected="true"] { background: var(--accent); border-color: var(--accent); color: var(--bg); }

/* Package cards */
.cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
    gap: 0.75rem;
    list-style: none;
    margin: 0;
    padding: 0;
}
.card { padding: 0.875rem 1rem; background: var(--surface); border: 1px solid var(--border); border-radius: 10px; }
.card h3 { display: flex; align-items: center; gap: 0.5rem; font-size: 1rem; margin: 0 0 0.25rem; letter-spacing: 0; }
/* The registry glyph: accent on bg, not on the border tone, keeps the glyph text AA in both themes. Mono has one
   weight, so the heading's 600 is reset rather than synthesised. */
.glyph {
    flex: none;
    width: 26px;
    height: 26px;
    border-radius: 6px;
    background: var(--bg);
    border: 1px solid var(--border);
    color: var(--accent);
    font-family: var(--mono);
    font-size: 0.75rem;
    font-weight: 400;
    letter-spacing: 0.02em;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.card p { color: var(--text-muted); font-size: 0.875rem; margin: 0 0 0.625rem; }
.card .stats {
    display: flex;
    gap: 1rem;
    margin: 0;
    font-family: var(--mono);
    font-size: 0.8125rem;
    color: var(--text-faint);
}
.card .stats strong { color: var(--accent); font-weight: 400; }

/* Posts with a cover. On the front page (.post-row) the cover spans the row above the title. On the blog index
   it stacks above the text on phones and sits in a column beside it from 48rem up. */
.post-cover-link { display: block; max-width: 18rem; margin-bottom: 0.5rem; }
.post-row .post-cover-link { grid-column: 1 / -1; }
.post-cover { display: block; width: 100%; height: auto; border-radius: 8px; border: 1px solid var(--border); }
@media (min-width: 48rem) {
    .prose .list > li:has(.post-cover-link) {
        display: grid;
        grid-template-columns: 11rem 1fr;
        gap: 1rem;
        align-items: start;
    }
    .prose .post-cover-link { margin-bottom: 0; }
}

/* Footer */
.site-footer {
    margin-top: 2rem;
    padding-top: 2rem;
    padding-bottom: 3rem;
    border-top: 1px solid var(--rule);
    color: var(--text-muted);
    font-size: 0.875rem;
}
.site-footer p { margin: 0.375rem 0; }
.footer-line { color: var(--text-faint); font-style: italic; }
/* The SSH fingerprint is one 50-character word; it may break anywhere rather than widen the page. */
.fingerprint { font-size: 0.8125rem; color: var(--text-faint); overflow-wrap: anywhere; }
.site-footer ul { display: flex; flex-wrap: wrap; gap: 1rem; list-style: none; margin: 0.75rem 0 0; padding: 0; }

/* Prose: posts and simple pages */
.prose { padding: 1rem 0 2rem; }
.prose h1 { margin-bottom: 0.5rem; }
.prose h2:not(.section-heading) { font-size: 1.5rem; margin: 2.25rem 0 0.75rem; }
.prose h3 { font-size: 1.2rem; margin: 1.75rem 0 0.5rem; }
.prose p, .prose ul, .prose ol { margin: 0 0 1rem; }
.prose img { max-width: 100%; height: auto; border-radius: 8px; }
.prose pre {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.875rem 1rem;
    overflow-x: auto;
    line-height: 1.5;
}
.prose :not(pre) > code {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 4px;
    padding: 0.05em 0.3em;
}
.prose blockquote {
    margin: 0 0 1rem;
    padding-left: 1rem;
    border-left: 2px solid var(--rule);
    color: var(--text-muted);
}

/* Terminal layer: /uses and the 404 page */
.terminal main { font-family: var(--mono); font-size: 0.9375rem; }
.terminal .prose h1, .terminal .prose h2 { font-family: var(--mono); letter-spacing: 0; }
.terminal .prompt::before { content: '$ '; color: var(--accent); }

/* Street View page */
.qr-note {
    display: flex;
    gap: 0.75rem;
    align-items: center;
    margin: 1.25rem 0 0;
    padding: 0.625rem 0.75rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    color: var(--text-muted);
    font-size: 0.875rem;
}
.qr-note img { flex: none; border-radius: 4px; }
.gallery { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.75rem; list-style: none; margin: 0; padding: 0; }
@media (min-width: 40rem) {
    .gallery { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.gallery p { margin: 0.25rem 0 0; }
.gallery .primary { font-weight: 600; font-size: 0.9rem; }
.gallery img, .preview-placeholder { display: block; width: 100%; height: auto; border-radius: 8px; }
.preview-placeholder { background: var(--surface); border: 1px dashed var(--border); }
.gallery-wide .preview-placeholder { aspect-ratio: 16 / 9; }
.gallery-square .preview-placeholder { aspect-ratio: 1; }

/* Street View strip and the lightbox */
.strip { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.6rem; list-style: none; margin: 0; padding: 0; }
@media (min-width: 40rem) { .strip { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.strip img { display: block; width: 100%; height: auto; border-radius: 6px; border: 1px solid var(--border); }
.strip .meta { margin: 0.35rem 0 0; }
.lightbox {
    max-width: min(90vw, 70rem);
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg);
    color: var(--text);
}
.lightbox::backdrop { background: color-mix(in srgb, #000 80%, transparent); }
.lightbox img { display: block; max-width: 100%; max-height: 80vh; }
.lightbox-bar { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 0.6rem 0.9rem; }
.lightbox-close { font: inherit; background: none; border: 0; color: var(--text-muted); cursor: pointer; font-size: 1.1rem; }

/* Floating section nav: a pill with the portrait and the section links, shown by the script past the hero. A fixed
   box at left: 50% would shrink to half the viewport and wrap its links, so the pill takes its one-line width, kept
   inside the viewport; under 640px the gaps tighten so it fits a 480px screen. Its place between the hero and the
   first section breaks `section + section`, so the rule after the link colours gives that section its hairline.
   The pill is opaque: a see-through one lets the text under it pull the marked link below AA in the light theme. */
.section-nav {
    position: fixed;
    top: 0.75rem;
    left: 50%;
    transform: translateX(-50%);
    z-index: 10;
    display: flex;
    align-items: center;
    gap: 0.75rem;
    width: max-content;
    max-width: calc(100% - 2rem);
    padding: 0.35rem 0.9rem 0.35rem 0.4rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 999px;
    box-shadow: 0 6px 24px color-mix(in srgb, #000 40%, transparent);
    font-size: 0.85rem;
}
.section-nav[hidden] { display: none; }
.section-nav-home img { display: block; width: 28px; height: 28px; border-radius: 50%; }
.section-nav ul { display: flex; gap: 0.9rem; list-style: none; margin: 0; padding: 0; }
.section-nav a { color: var(--text); text-decoration: none; }
.section-nav a[aria-current="true"] { color: var(--accent); }
.section-nav + section { border-top: 1px solid var(--border); }
@media (max-width: 639px) {
    .section-nav { gap: 0.5rem; padding-right: 0.75rem; }
    .section-nav ul { gap: 0.6rem; }
}
@media (max-width: 479px) { .section-nav { display: none; } }

/* About length control, added by the script */
.about-length { display: flex; align-items: center; gap: 0.6rem; margin: 0 0 1rem; font-family: var(--mono); font-size: 0.75rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-faint); }
.about-length input { flex: 0 1 12rem; accent-color: var(--accent); }

/* Copy buttons, added by the script at the end of anything with data-copy */
.copy {
    font: inherit;
    font-family: var(--mono);
    font-size: 0.75rem;
    margin-left: 0.5rem;
    padding: 0.1rem 0.5rem;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--surface);
    color: var(--accent);
    cursor: pointer;
    vertical-align: middle;
    /* A hand selection of the target leaves the label out, so a copied fingerprint does not end in "Copy". */
    -webkit-user-select: none;
    user-select: none;
}
/* In a code block the button ends the row and the command scrolls beside it, so the button covers no character at
   any width. Only a block that holds a button changes; without the script it looks as it always did. */
pre:has(> .copy) { display: flex; align-items: center; }
pre:has(> .copy) > code { flex: 1; min-width: 0; overflow-x: auto; }
pre > .copy { flex: none; }

/* Social preview card, rendered to og.png at build time */
.og {
    width: 1200px;
    height: 630px;
    margin: 0;
    padding: 72px 80px;
    display: flex;
    flex-direction: column;
    justify-content: center;
}
.og h1 { font-size: 88px; }
.og .role { font-size: 32px; color: var(--text-muted); margin: 20px 0 0; }
.og .chips { margin-top: 44px; }
.og .chip-value { font-size: 34px; }
.og .chip-label { font-size: 16px; }

/* Plex has no Georgian glyphs. Kept after the class rules so it wins ties with them. */
:lang(ka) { font-family: system-ui, sans-serif; }

/* Print: the CV sheet, also used to generate cv.pdf */
@media print {
    html { background: #fff; color: #000; font-size: 10.5pt; }
    /* 9pt is the 12px text floor; these rem sizes would fall below it at the 10.5pt root. */
    /* Section headings print as the mono labels they were, so the PDF keeps its upper-case section lines. */
    .section-heading { font-family: var(--mono); font-size: 9pt; font-weight: 400; letter-spacing: 0.12em; text-transform: uppercase; }
    .section-heading::after { display: none; }
    .eyebrow, .chip-label, .meta, .fingerprint, .card .stats { font-size: 9pt; }
    /* 0.9em compounds when nested (pre > code, code in a label), so code text is clamped to the floor too. */
    code, kbd, pre, samp { font-size: max(0.9em, 9pt); }
    .site-header, .site-footer, .section-nav, .theme-toggle, .no-print { display: none; }
    main { max-width: none; padding: 0; }
    a { color: #000; text-decoration: none; }
    .section-heading, .meta, .eyebrow, .figure, .secondary { color: #333; }
    .list > li { border-bottom-color: #ddd; break-inside: avoid; }
    .mark { color: #000; border-color: #ddd; font-size: 9pt; }
    @page { margin: 16mm 16mm 18mm; }
}
