/* Harry Park — personal site
   Bright, flat, bold. No external fonts, no build step.

   PALETTE A — electric violet.
   Colours are declared once as --l-* (light) and --d-* (dark). The
   mapping block below wires them to the names the rest of the sheet
   uses, and is palette-agnostic — never edit it when swapping.
   Alternatives live in palettes/. */

:root {
    /* ---- light ---- */
    --l-bg:        #ffffff;
    --l-surface:   #faf9ff;
    --l-fg:        #0f0d1a;
    --l-muted:     #5c5570;
    --l-faint:     #e7e3f5;
    --l-accent:    #7c3aed;
    --l-accent-2:  #6d28d9;
    --l-on-accent: #ffffff;
    --l-tag-bg:    #f0ebff;
    --l-tag-fg:    #6d28d9;
    --l-linkedin:  #0a66c2;

    /* ---- dark. Accent is lightened and desaturated; the light-mode
            violet halates against a near-black ground. ---- */
    --d-bg:        #0f0d18;
    --d-surface:   #171327;
    --d-fg:        #f0edfa;
    --d-muted:     #948dad;
    --d-faint:     #282040;
    --d-accent:    #a78bfa;
    --d-accent-2:  #c4b5fd;
    --d-on-accent: #150d29;
    --d-tag-bg:    #241c3d;
    --d-tag-fg:    #c4b5fd;
    --d-linkedin:  #70b5f9;

    --measure:   46rem;
    --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
    --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

    --ease: cubic-bezier(0.16, 1, 0.3, 1);
}

/* ---------- theme mapping ----------
   Default is light. The media query applies dark unless the reader has
   explicitly chosen light; the [data-theme] rules come last so an
   explicit choice always beats the system preference. */

:root {
    --bg:        var(--l-bg);
    --surface:   var(--l-surface);
    --fg:        var(--l-fg);
    --muted:     var(--l-muted);
    --faint:     var(--l-faint);
    --accent:    var(--l-accent);
    --accent-2:  var(--l-accent-2);
    --on-accent: var(--l-on-accent);
    --tag-bg:    var(--l-tag-bg);
    --tag-fg:    var(--l-tag-fg);
    --linkedin:  var(--l-linkedin);
}

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --bg:        var(--d-bg);
        --surface:   var(--d-surface);
        --fg:        var(--d-fg);
        --muted:     var(--d-muted);
        --faint:     var(--d-faint);
        --accent:    var(--d-accent);
        --accent-2:  var(--d-accent-2);
        --on-accent: var(--d-on-accent);
        --tag-bg:    var(--d-tag-bg);
        --tag-fg:    var(--d-tag-fg);
        --linkedin:  var(--d-linkedin);
    }
}

:root[data-theme="dark"] {
    --bg:        var(--d-bg);
    --surface:   var(--d-surface);
    --fg:        var(--d-fg);
    --muted:     var(--d-muted);
    --faint:     var(--d-faint);
    --accent:    var(--d-accent);
    --accent-2:  var(--d-accent-2);
    --on-accent: var(--d-on-accent);
    --tag-bg:    var(--d-tag-bg);
    --tag-fg:    var(--d-tag-fg);
    --linkedin:  var(--d-linkedin);
}

:root[data-theme="light"] {
    --bg:        var(--l-bg);
    --surface:   var(--l-surface);
    --fg:        var(--l-fg);
    --muted:     var(--l-muted);
    --faint:     var(--l-faint);
    --accent:    var(--l-accent);
    --accent-2:  var(--l-accent-2);
    --on-accent: var(--l-on-accent);
    --tag-bg:    var(--l-tag-bg);
    --tag-fg:    var(--l-tag-fg);
    --linkedin:  var(--l-linkedin);
}

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

html { -webkit-text-size-adjust: 100%; }

@media (prefers-reduced-motion: no-preference) {
    html { scroll-behavior: smooth; }
}

body {
    margin: 0;
    background: var(--bg);
    color: var(--fg);
    font-family: var(--sans);
    font-size: 17px;
    line-height: 1.65;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    /* No overflow-x:hidden here — it computes overflow-y to auto, which
       makes body a scroll container and silently kills the sticky rail. */
}

/* Two-column: identity pinned left, content scrolling right.
   align-items:start is what lets the rail actually stick — a stretched
   grid item is full-height and has nothing to stick within. */
.page {
    position: relative;
    max-width: 72rem;
    margin: 0 auto;
    padding: 5.5rem 2rem 5rem;
    display: grid;
    grid-template-columns: 16rem minmax(0, 1fr);
    gap: 5rem;
    align-items: start;
}

.rail {
    position: sticky;
    top: 5.5rem;
    /* On a short window the rail would otherwise overflow the viewport and
       strand the links below the fold, unreachable because it's pinned. */
    max-height: calc(100vh - 7rem);
    overflow-y: auto;
}

.content { min-width: 0; }

.content > section { scroll-margin-top: 5rem; }

/* Theme changes fade; page load does not. The class is added by the
   toggle only for the duration of the crossfade, so it never fires on
   first paint and never competes with the hover transitions. */
@media (prefers-reduced-motion: no-preference) {
    :root.theming,
    :root.theming *,
    :root.theming *::before,
    :root.theming *::after {
        transition: background-color 0.32s var(--ease),
                    border-color     0.32s var(--ease),
                    color            0.32s var(--ease),
                    fill             0.32s var(--ease) !important;
    }
}

/* ---------- theme toggle ---------- */

.theme-toggle {
    position: fixed;
    top: 1rem;
    right: 1rem;
    z-index: 10;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    padding: 0;
    border: 2px solid var(--faint);
    border-radius: 8px;
    background: var(--surface);
    color: var(--fg);
    cursor: pointer;
    transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}

.theme-toggle:hover,
.theme-toggle:focus-visible {
    border-color: var(--accent);
    color: var(--accent);
}

.theme-toggle svg {
    width: 16px;
    height: 16px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* Show the icon for the mode you'd switch TO. Order matters: the
   explicit [data-theme] rules must come after the media query. */
.ico-sun  { display: none; }
.ico-moon { display: block; }

@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .ico-sun  { display: block; }
    :root:not([data-theme="light"]) .ico-moon { display: none; }
}

:root[data-theme="dark"] .ico-sun  { display: block; }
:root[data-theme="dark"] .ico-moon { display: none; }
:root[data-theme="light"] .ico-sun  { display: none; }
:root[data-theme="light"] .ico-moon { display: block; }

/* ---------- entrance ---------- */

/* The rail and the first screenful arrive together. */
.rail,
.content > .timeline,
.content > section {
    animation: rise 0.6s var(--ease) backwards;
}

.content > .timeline { animation-delay: 60ms; }
.content > section:nth-of-type(1) { animation-delay: 120ms; }
.content > section:nth-of-type(2) { animation-delay: 180ms; }
.content > section:nth-of-type(3) { animation-delay: 240ms; }

@keyframes rise {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
}

/* ---------- rail ---------- */

.avatar {
    display: block;
    /* Fills the rail so its edges line up with the name and nav beneath.
       max-width stops it ballooning once the rail goes full-width on
       narrow screens. */
    width: 100%;
    max-width: 16rem;
    height: auto;
    aspect-ratio: 1;
    margin: 0 0 1.75rem;
    border: 2px solid var(--faint);
    border-radius: 50%;
    object-fit: cover;
}

h1 {
    margin: 0;
    font-size: clamp(2.5rem, 4.4vw, 3.4rem);
    font-weight: 800;
    letter-spacing: -0.045em;
    line-height: 0.95;
}

.role {
    margin: 1rem 0 0;
    font-size: 0.9375rem;
    line-height: 1.5;
    color: var(--muted);
}

.nav {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    margin: 2.5rem 0 0;
}

.nav a {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.35rem 0;
    color: var(--muted);
    font-size: 0.75rem;
    font-weight: 800;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    text-decoration: none;
    transition: color 0.2s var(--ease);
}

/* The rule grows and takes the accent for the section you're reading. */
.nav .dash {
    display: block;
    width: 1.5rem;
    height: 2px;
    background: var(--faint);
    transition: width 0.25s var(--ease), background-color 0.25s var(--ease);
}

.nav a:hover,
.nav a:focus-visible { color: var(--fg); }

.nav a:hover .dash,
.nav a:focus-visible .dash { width: 2.5rem; }

.nav a.active { color: var(--fg); }

.nav a.active .dash {
    width: 3.25rem;
    background: var(--accent);
}

/* ---------- career timeline ---------- */

.timeline {
    display: flex;
    align-items: stretch;
    gap: 0.5rem;
    margin: 0 0 4.5rem;
}

.tl-track {
    display: flex;
    flex: 1;
    gap: 3px;
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Width is proportional to time served — --span is duration in years.
   min-width keeps a one-year stint wide enough to still read. */
.tl-seg {
    flex: var(--span) 1 0;
    min-width: 4.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.tl-co,
.tl-role {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tl-co {
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: -0.01em;
}

.tl-role {
    font-size: 0.6875rem;
    color: var(--muted);
    margin-top: -0.15rem;
}

.tl-bar {
    height: 7px;
    background: var(--faint);
    margin-top: 0.15rem;
}

/* Education is a starting point, not a job — hollow, and not to scale. */
.tl-edu {
    flex: none;
    width: 5.5rem;
}

.tl-edu .tl-bar {
    background: none;
    border: 2px dashed var(--faint);
    height: 7px;
}

.tl-edu .tl-co { color: var(--muted); }

.tl-seg.current .tl-bar { background: var(--accent); }
.tl-seg.current .tl-co  { color: var(--accent); }

.tl-year {
    font-family: var(--mono);
    font-size: 0.6875rem;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
}

.tl-end {
    align-self: flex-end;
    flex: none;
    font-family: var(--mono);
    font-size: 0.6875rem;
    color: var(--muted);
    line-height: 1.6;
}

/* ---------- sections ---------- */

section { margin-bottom: 4.5rem; }

h2 {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin: 0 0 2rem;
    font-size: 0.75rem;
    font-weight: 800;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--fg);
}

h2::after {
    content: "";
    flex: 1;
    height: 2px;
    background: var(--faint);
}

h2 .num {
    font-family: var(--mono);
    color: var(--accent);
    letter-spacing: 0.04em;
}

.about p { margin: 0 0 1.25rem; color: var(--fg); }
.about p:last-child { margin-bottom: 0; }

.about .greeting {
    margin-bottom: 0.7rem;
    font-size: 1.35rem;
    font-weight: 750;
    letter-spacing: -0.025em;
}

/* ---------- projects ---------- */

.project-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 1rem;
}

.project {
    display: flex;
    align-items: flex-start;
    gap: 1.35rem;
    padding: 1.75rem;
    border: 2px solid var(--faint);
    border-radius: 10px;
    background: var(--surface);
    transition: transform 0.25s var(--ease),
                border-color 0.25s var(--ease);
}

/* Flat: the hover cue is the border, not a shadow. */
.project:hover {
    transform: translateY(-3px);
    border-color: var(--accent);
}

/* Thumbnail. The <img> deletes this figure if the file is missing, so a
   card with no screenshot simply reflows to full width. */
.shot {
    flex: none;
    width: 11rem;
    margin: 0;
    border: 2px solid var(--faint);
    border-radius: 6px;
    background: var(--bg);
    aspect-ratio: 16 / 9;
    overflow: hidden;
}

.shot img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* Content sits in the upper half of both screenshots. */
    object-position: top center;
}

.project-body { flex: 1; min-width: 0; }

.project-head {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    margin-bottom: 0.6rem;
}

.project-name {
    margin: 0;
    font-size: 1.1875rem;
    font-weight: 750;
    letter-spacing: -0.025em;
}

.project-year {
    margin-left: auto;
    flex: none;
    font-family: var(--mono);
    font-size: 0.75rem;
    color: var(--muted);
    font-variant-numeric: tabular-nums;
}

.project-desc {
    margin: 0;
    color: var(--muted);
    font-size: 0.9375rem;
}

.tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    list-style: none;
    margin: 1rem 0 0;
    padding: 0;
}

.tags li {
    padding: 0.22rem 0.55rem;
    border-radius: 4px;
    background: var(--tag-bg);
    color: var(--tag-fg);
    font-family: var(--mono);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.02em;
}

/* ---------- buttons ---------- */

.project-links {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    margin-top: 1.25rem;
}

.btn {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem 0.95rem;
    border: 2px solid var(--faint);
    border-radius: 7px;
    color: var(--fg);
    font-size: 0.875rem;
    font-weight: 700;
    text-decoration: none;
    transition: border-color 0.2s var(--ease),
                background-color 0.2s var(--ease),
                color 0.2s var(--ease);
}

.btn:hover,
.btn:focus-visible { border-color: var(--accent); }

.btn.primary {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
}

.btn.primary:hover,
.btn.primary:focus-visible {
    background: var(--accent-2);
    border-color: var(--accent-2);
    color: var(--on-accent);
}

.arrow { transition: transform 0.2s var(--ease); }
.btn:hover .arrow { transform: translateX(3px); }

/* ---------- stack ---------- */

.stack-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: 1.5rem 2rem;
    margin: 0;
}

.stack-grid dt {
    font-family: var(--mono);
    font-size: 0.6875rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--accent);
    margin-bottom: 0.35rem;
}

.stack-grid dd {
    margin: 0;
    font-size: 0.9375rem;
    color: var(--muted);
}

/* ---------- availability note + contact ---------- */

.note {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    margin: 2.5rem 0 0;
    padding: 0.6rem 0.7rem;
    border-radius: 6px;
    background: var(--tag-bg);
    color: var(--tag-fg);
    font-size: 0.75rem;
    font-weight: 650;
    line-height: 1.45;
}

.note .dot {
    flex: none;
    width: 7px;
    height: 7px;
    margin-top: 0.32rem;
    border-radius: 50%;
    background: currentColor;
}

@media (prefers-reduced-motion: no-preference) {
    .note .dot { animation: pulse 2.4s ease-in-out infinite; }
}

@keyframes pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.35; }
}

.social {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    list-style: none;
    margin: 0.75rem 0 0;
    padding: 0;
}

.social a {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.7rem 0.95rem;
    border: 2px solid var(--faint);
    border-radius: 7px;
    color: var(--fg);
    font-size: 0.875rem;
    font-weight: 700;
    /* Collapse the line box to the glyphs so the icon centres on the
       letters rather than on the descender space below them. */
    line-height: 1;
    text-decoration: none;
    transition: border-color 0.2s var(--ease), color 0.2s var(--ease);
}

.social a:hover,
.social a:focus-visible {
    border-color: var(--accent);
    color: var(--accent);
}

.ico {
    width: 14px;
    height: 14px;
    flex: none;
    fill: currentColor;
}

/* Brand fill, so it stays LinkedIn blue through hover and dark mode. */
.ico-li { fill: var(--linkedin); }

/* ---------- links ---------- */

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

/* ---------- footer ---------- */

footer {
    margin-top: 5rem;
    padding-top: 1.5rem;
    border-top: 2px solid var(--faint);
}

footer p {
    margin: 0;
    font-family: var(--mono);
    font-size: 0.75rem;
    color: var(--muted);
}

/* ---------- collapse to one column ---------- */

@media (max-width: 900px) {
    .page {
        grid-template-columns: minmax(0, 1fr);
        gap: 3.5rem;
        padding: 4.5rem 1.5rem 4rem;
    }

    /* Nothing to stick to once the rail is just the page header. */
    .rail { position: static; }

    .nav { display: none; }

    .note { margin-top: 1.75rem; }
}

/* ---------- small screens ---------- */

@media (max-width: 560px) {
    body { font-size: 16px; }
    .page { padding: 4rem 1.25rem 3.5rem; }
    section { margin-bottom: 3.5rem; }
    .shot { width: 6.5rem; }
    .avatar { max-width: 9rem; margin-bottom: 1.25rem; }
    .project { padding: 1.35rem; gap: 1rem; }
    .project-links .btn { flex: 1 1 auto; justify-content: center; }
    .timeline { margin-bottom: 3rem; }
    .tl-track { gap: 2px; }
    .tl-seg   { min-width: 3.5rem; }
    .tl-edu   { width: 4.25rem; }
    .tl-co    { font-size: 0.6875rem; }
    .tl-role  { font-size: 0.5625rem; }
    .tl-year  { font-size: 0.625rem; }
    .tl-end   { font-size: 0.625rem; }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation: none !important;
        transition: none !important;
    }
    .reveal { opacity: 1; transform: none; }
}
