/* ==========================================================================
   galapfa.ro — style.css
   --------------------------------------------------------------------------
   Colour model
     Every colour in the UI is derived from two values: the hue (--h) and the
     saturation (--s) of the chosen theme colour. Those two values are printed
     by themes.php::theme_color_css() into the <head> of every page, one rule
     per colour, e.g.  :root[data-color="indigo"]{--h:244;--s:68%}

     16 colours x 2 modes (light / dark) = 32 combinations.
     Neither the light nor the dark palette uses pure black (#000) or pure
     white (#fff); the surfaces are tinted with the theme hue instead.

   Contents
     1.  Tokens
     2.  Reset & base
     3.  Buttons
     4.  Header & navigation
     5.  Theme controls (swatches, picker, light/dark button)
     6.  Home page (hero, about, skills map)
     7.  Login page
     8.  Private area (menu tiles + panel)
     9.  Footer
     10. Utilities, motion & responsive
   ========================================================================== */

/* ==========================================================================
   1. Tokens
   ========================================================================== */

:root {
    /* --h and --s are injected per colour; these are only fallbacks. */
    --h: 244;
    --s: 68%;
    /* Swatch preview values: also injected per swatch, via inline style. */
    --sw-h: 244;
    --sw-s: 68%;

    --radius-sm: 8px;
    --radius: 14px;
    --radius-lg: 22px;
    --radius-pill: 999px;

    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 24px;
    --space-6: 32px;
    --space-7: 48px;
    --space-8: 72px;

    --wrap: 1140px;
    --speed: 180ms;

    --font: "Inter", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
    --font-mono: "JetBrains Mono", "SFMono-Regular", "Consolas", "Liberation Mono", monospace;

    --shadow-sm: 0 1px 2px hsl(var(--h) 24% 12% / 0.07);
    --shadow: 0 8px 24px -12px hsl(var(--h) 30% 14% / 0.28), 0 2px 6px hsl(var(--h) 30% 14% / 0.06);
    --shadow-lg: 0 26px 60px -28px hsl(var(--h) 34% 12% / 0.45), 0 6px 18px hsl(var(--h) 30% 14% / 0.10);
}

/* ---- light palette (default, no pure white) --------------------------- */

:root,
:root[data-mode="light"] {
    --bg: hsl(var(--h) 24% 97.5%);
    --bg-tint: hsl(var(--h) 32% 94.5%);
    --surface: hsl(var(--h) 30% 99.2%);
    --surface-2: hsl(var(--h) 26% 96.5%);
    --border: hsl(var(--h) 22% 88%);
    --border-strong: hsl(var(--h) 24% 78%);

    --text: hsl(var(--h) 20% 16%);
    --text-soft: hsl(var(--h) 14% 40%);
    --text-faint: hsl(var(--h) 12% 55%);

    --primary: hsl(var(--h) var(--s) 45%);
    --primary-hover: hsl(var(--h) var(--s) 39%);
    --primary-soft: hsl(var(--h) var(--s) 93%);
    --primary-line: hsl(var(--h) var(--s) 78%);
    /* --on-primary-ink is overridden per colour by themes.php for the lightest
       hues (amber / yellow) so text on the primary colour stays readable. */
    --on-primary-ink: hsl(var(--h) 44% 99%);
    --on-primary: var(--on-primary-ink);

    --success: hsl(158 62% 36%);
    --danger: hsl(2 66% 48%);
    --info: hsl(206 78% 44%);
    --warning: hsl(38 88% 42%);

    color-scheme: light;
}

/* ---- dark palette (no pure black) ------------------------------------- */

:root[data-mode="dark"] {
    --bg: hsl(var(--h) 20% 9%);
    --bg-tint: hsl(var(--h) 20% 11.5%);
    --surface: hsl(var(--h) 18% 13.5%);
    --surface-2: hsl(var(--h) 17% 16.5%);
    --border: hsl(var(--h) 16% 24%);
    --border-strong: hsl(var(--h) 16% 34%);

    --text: hsl(var(--h) 24% 95%);
    --text-soft: hsl(var(--h) 14% 72%);
    --text-faint: hsl(var(--h) 12% 58%);

    --primary: hsl(var(--h) var(--s) 62%);
    --primary-hover: hsl(var(--h) var(--s) 70%);
    --primary-soft: hsl(var(--h) var(--s) 17%);
    --primary-line: hsl(var(--h) var(--s) 34%);
    --on-primary: hsl(var(--h) 40% 9%);

    --success: hsl(158 58% 48%);
    --danger: hsl(2 74% 62%);
    --info: hsl(206 80% 62%);
    --warning: hsl(38 90% 58%);

    color-scheme: dark;
}

/* ==========================================================================
   2. Reset & base
   ========================================================================== */

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

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

body {
    margin: 0;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    font-family: var(--font);
    font-size: 16px;
    line-height: 1.6;
    color: var(--text);
    background-color: var(--bg);
    background-image:
        radial-gradient(1100px 520px at 12% -8%, hsl(var(--h) var(--s) 55% / 0.16), transparent 68%),
        radial-gradient(900px 460px at 92% 4%, hsl(var(--h) var(--s) 60% / 0.11), transparent 70%);
    background-repeat: no-repeat;
    transition: background-color var(--speed) ease, color var(--speed) ease;
}

h1, h2, h3, h4 {
    margin: 0 0 var(--space-3);
    line-height: 1.2;
    letter-spacing: -0.02em;
    font-weight: 700;
}

h1 { font-size: clamp(2rem, 4.4vw, 3.1rem); }
h2 { font-size: clamp(1.35rem, 2.4vw, 1.9rem); }
h3 { font-size: 1.1rem; }

p { margin: 0 0 var(--space-4); }

a {
    color: var(--primary);
    text-decoration: none;
}
a:hover { text-decoration: underline; }

ul { margin: 0; padding: 0; list-style: none; }

img { display: block; max-width: 100%; }

/* Icons are inline SVGs so they can sit inside a line of text; inside a flex
   or grid container they are blockified automatically. */
.icon {
    display: inline-block;
    vertical-align: middle;
    max-width: 100%;
    flex: 0 0 auto;
    stroke-width: 2;
}

/* Flex/grid containers blockify the icons again. */
.btn .icon,
.nav-link .icon,
.tile__icon .icon,
.panel__icon .icon,
.field__peek .icon,
.site-footer__line .icon,
.brand__mark .icon {
    display: block;
}

code, kbd, pre {
    font-family: var(--font-mono);
    font-size: 0.88em;
}

code {
    padding: 2px 6px;
    border-radius: var(--radius-sm);
    background: var(--primary-soft);
    color: var(--text);
    border: 1px solid var(--primary-line);
}

kbd {
    padding: 1px 6px;
    border-radius: 6px;
    border: 1px solid var(--border-strong);
    border-bottom-width: 2px;
    background: var(--surface-2);
    white-space: nowrap;
}

.icon {
    flex: 0 0 auto;
    stroke-width: 2;
}

.wrap {
    width: 100%;
    max-width: var(--wrap);
    margin-inline: auto;
    padding-inline: var(--space-5);
}

.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 200;
    padding: var(--space-3) var(--space-4);
    background: var(--primary);
    color: var(--on-primary);
    border-radius: 0 0 var(--radius) 0;
}
.skip-link:focus { left: 0; }

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

::selection { background: hsl(var(--h) var(--s) 60% / 0.28); }

/* ==========================================================================
   3. Buttons
   ========================================================================== */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: 11px 18px;
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    font: inherit;
    font-weight: 600;
    font-size: 0.95rem;
    line-height: 1.2;
    cursor: pointer;
    text-decoration: none;
    white-space: nowrap;
    transition: background-color var(--speed) ease, border-color var(--speed) ease,
                color var(--speed) ease, transform var(--speed) ease, box-shadow var(--speed) ease;
}
.btn:hover { text-decoration: none; }
.btn:active { transform: translateY(1px); }

.btn__icon { margin-inline-start: -2px; }

.btn--primary {
    background-color: var(--primary);
    color: var(--on-primary);
    box-shadow: var(--shadow-sm);
}
.btn--primary:hover {
    background-color: var(--primary-hover);
    color: var(--on-primary);
    box-shadow: var(--shadow);
}

.btn--secondary {
    background-color: var(--surface);
    color: var(--text);
    border-color: var(--border-strong);
}
.btn--secondary:hover {
    background-color: var(--surface-2);
    color: var(--text);
    border-color: var(--primary);
}

.btn--ghost {
    background-color: transparent;
    color: var(--text-soft);
    border-color: var(--border);
}
.btn--ghost:hover {
    background-color: var(--surface-2);
    color: var(--text);
    border-color: var(--primary-line);
}

.btn--soft {
    background-color: var(--primary-soft);
    color: var(--primary);
    border-color: var(--primary-line);
}
.btn--soft:hover {
    background-color: var(--primary);
    color: var(--on-primary);
}

.btn--lg { padding: 14px 26px; font-size: 1.02rem; }
.btn--sm { padding: 8px 14px; font-size: 0.87rem; }
/* Icon-only button: equal padding on both sides makes it a circle. */
.btn--icon { padding: 8px; }
.btn--lg.btn--icon { padding: 13px; }
.btn--block { width: 100%; }

.btn[aria-disabled="true"],
.btn:disabled {
    opacity: 0.55;
    cursor: not-allowed;
    pointer-events: none;
}

.inline-form { display: inline-flex; margin: 0; }

/* ==========================================================================
   4. Header & navigation
   ========================================================================== */

.site-header {
    position: sticky;
    top: 0;
    z-index: 60;
    background-color: hsl(var(--h) 26% 98% / 0.82);
    backdrop-filter: blur(12px);
    border-bottom: 1px solid var(--border);
}
:root[data-mode="dark"] .site-header {
    background-color: hsl(var(--h) 20% 11% / 0.82);
}

.site-header__inner {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    min-height: 68px;
    flex-wrap: wrap;
    padding-block: var(--space-2);
}

.brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-3);
    color: var(--text);
    font-weight: 700;
    text-decoration: none;
    margin-inline-end: var(--space-2);
}
.brand:hover { text-decoration: none; }

.brand__mark {
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    border-radius: 12px;
    color: var(--on-primary);
    background-image: linear-gradient(140deg, var(--primary), hsl(var(--h) var(--s) 62%));
    box-shadow: var(--shadow-sm);
}

.brand__text { display: flex; flex-direction: column; line-height: 1.15; }
.brand__name { font-size: 1.06rem; letter-spacing: -0.01em; }
.brand__tag {
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--text-faint);
}

.site-nav {
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

.nav-link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: 9px 14px;
    border-radius: var(--radius-pill);
    color: var(--text-soft);
    font-weight: 600;
    font-size: 0.93rem;
    transition: background-color var(--speed) ease, color var(--speed) ease;
}
.nav-link:hover {
    background-color: var(--surface-2);
    color: var(--text);
    text-decoration: none;
}
.nav-link.is-current {
    background-color: var(--primary-soft);
    color: var(--primary);
}

.site-header__actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
    /* Pins the actions to the far right. It used to be the nav's
       "margin-inline-end: auto" that did this, but the nav is only rendered
       while somebody is signed in — without this the controls would sit right
       next to the brand on the public pages. */
    margin-inline-start: auto;
}

/* ==========================================================================
   5. Theme controls
   ========================================================================== */

/* ---- swatches --------------------------------------------------------- */

.swatches {
    display: grid;
    grid-template-columns: repeat(8, 1fr);
    gap: 6px;
}
.swatches--labelled {
    grid-template-columns: repeat(auto-fill, minmax(74px, 1fr));
    gap: 10px;
}

.swatch {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 3px;
    border-radius: 10px;
    border: 1px solid transparent;
    transition: border-color var(--speed) ease, background-color var(--speed) ease,
                transform var(--speed) ease;
}
.swatch:hover {
    border-color: var(--border-strong);
    background-color: var(--surface-2);
    transform: translateY(-1px);
    text-decoration: none;
}
.swatch.is-active {
    border-color: var(--primary);
    background-color: var(--primary-soft);
}

.swatch__chip {
    position: relative;
    display: block;
    width: 100%;
    aspect-ratio: 1;
    border-radius: 7px;
    background-image: linear-gradient(
        145deg,
        hsl(var(--sw-h) var(--sw-s) 58%),
        hsl(var(--sw-h) var(--sw-s) 42%)
    );
    box-shadow: inset 0 0 0 1px hsl(var(--h) 30% 20% / 0.18);
    transition: transform var(--speed) ease;
}
.swatch:hover .swatch__chip { transform: scale(1.06); }

.swatch__check {
    position: absolute;
    inset: 0;
    margin: auto;
    color: hsl(var(--sw-h) 40% 99%);
    stroke-width: 3.4;
    filter: drop-shadow(0 1px 2px hsl(var(--sw-h) 40% 10% / 0.8));
    pointer-events: none;
}

.swatch__name {
    display: block;
    width: 100%;
    margin-top: 5px;
    text-align: center;
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--text-faint);
}
.swatch.is-active .swatch__name { color: var(--primary); }

.swatches--labelled .swatch { flex-direction: column; padding: 4px; }

/* ---- compact header picker (<details>) -------------------------------- */

.theme-picker { position: relative; }

.theme-picker__summary {
    list-style: none;
    cursor: pointer;
    user-select: none;
}
.theme-picker__summary::-webkit-details-marker { display: none; }
.theme-picker__caret { transition: transform var(--speed) ease; opacity: 0.7; }
.theme-picker[open] .theme-picker__summary {
    background-color: var(--surface-2);
    color: var(--text);
    border-color: var(--primary-line);
}
.theme-picker[open] .theme-picker__caret { transform: rotate(180deg); }

.theme-panel {
    position: absolute;
    right: 0;
    top: calc(100% + 10px);
    z-index: 70;
    width: min(330px, calc(100vw - 40px));
    padding: var(--space-4);
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background-color: var(--surface);
    box-shadow: var(--shadow-lg);
    animation: pop-in 140ms ease-out;
}

/* ---- full picker (home page card) ------------------------------------- */

.theme-card {
    padding: var(--space-5);
    border-radius: var(--radius-lg);
    border: 1px solid var(--border);
    background-color: var(--surface);
    box-shadow: var(--shadow);
}
.theme-card__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
    margin-bottom: var(--space-4);
}
.theme-card__title {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0;
    font-size: 1.05rem;
}
.theme-card__title .icon { color: var(--primary); }
.theme-card__meta {
    margin: 0;
    font-size: 0.83rem;
    color: var(--text-faint);
}
.theme-card__meta strong { color: var(--primary); }

.theme-card__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
    margin-top: var(--space-5);
    padding-top: var(--space-4);
    border-top: 1px dashed var(--border);
}
.theme-card__current { font-size: 0.86rem; color: var(--text-soft); }

/* The light/dark button tints its icon with the mode it leads to. */
:root[data-mode="dark"] .js-mode-icon { color: var(--warning); }

/* ==========================================================================
   6. Home page
   ========================================================================== */

main { flex: 1 0 auto; }

.section { padding-block: var(--space-7); }

.section__head {
    max-width: 640px;
    margin-bottom: var(--space-5);
}
.section__head :is(h1, h2) { margin-bottom: var(--space-2); }
.section__head :is(h1, h2) .icon { vertical-align: -0.18em; margin-right: 4px; }
.section__head p { color: var(--text-soft); margin: 0; }

/* ---- hero -------------------------------------------------------------- */

.hero {
    position: relative;
    padding-block: var(--space-8) var(--space-7);
    overflow: hidden;
}

.hero__grid {
    display: grid;
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
    align-items: center;
    gap: var(--space-7);
}

/* The word is the accent of the page: the theme colour plus a soft,
   theme tinted shadow that lifts it off the background in both modes. */
.hero__title {
    margin-bottom: var(--space-4);
    color: var(--primary);
    text-shadow:
        0 1px 0 hsl(var(--h) var(--s) 96% / 0.4),
        0 10px 24px hsl(var(--h) var(--s) 45% / 0.22);
}

:root[data-mode="dark"] .hero__title {
    text-shadow:
        0 0 26px hsl(var(--h) var(--s) 62% / 0.3),
        0 2px 8px hsl(var(--h) 40% 3% / 0.55);
}

/* The one line that says what this is: bigger than the body, softer than the
   name above it, and kept short on purpose. No bottom margin: the gap to
   whatever follows it is the next element's own margin-top, which keeps the
   spacing right whether or not the profile buttons are switched on. */
.hero__tagline {
    max-width: 34ch;
    font-size: 1.12rem;
    line-height: 1.5;
    color: var(--text-soft);
}

/* The profile buttons: one row while they fit, one per line when they do not.
   The hero column is a grid item that shrinks with the viewport, so wrapping
   here tracks the space the column actually has rather than the screen width —
   which is why this needs no media query. */
.hero__social {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    margin-top: var(--space-5);
}

/* A third party logo keeps its own colour: recolouring it stops it being that
   logo. Only the glyph is tinted — the button itself stays on the theme, so a
   row of brand buttons still belongs to the page. */
.brand-mark { flex: 0 0 auto; }
.btn--brand--github .brand-mark { color: #1a1a1a; }
.btn--brand--linkedin .brand-mark { color: #0a66c2; }

/* In dark mode the GitHub mark is near black on a dark surface, which is the
   one place its official colour genuinely does not work: inverted to the
   GitHub light mark's grey so it stays visible. LinkedIn blue is light enough
   to be left alone. */
:root[data-mode="dark"] .btn--brand--github .brand-mark { color: #e6edf3; }

/* ---- about -------------------------------------------------------------- */

/* The About block sits directly under the hero, and both bring their own
   padding-block: the hero ends on space-7 (48px) and .section starts on the
   same 48px, so an unmodified .section left 96px of air above the rule against
   only its own 32px below. That lopsided 3:1 split is what read as a gap.

   The top padding is cut here rather than in .section, which the cookies page
   and the error pages also use and which keeps its own rhythm, so this touches
   the home page only. space-5 (24px) brings the whitespace above the rule to
   72px against 32px below: still generous, but the divider now reads as a
   divider rather than as the far edge of a gap. */
.about {
    padding-block: var(--space-5) var(--space-7);
}

/* The section carries no heading on purpose, so the line is what separates it
   from the hero. It spans the whole content width, ending flush with the
   hero__grid above it, and stays vague rather than structural: it fades out
   only in its last few percent at each end, and is blurred just enough to read
   as a shadow on the page rather than a rule drawn across it. The earlier
   50/50 fade suited a short line; stretched over 1140px it would leave the
   middle as the only part that reads, so the stops now hold instead.
   Decorative only, hence aria-hidden on the <hr>. */
.about__rule {
    width: 100%;
    height: 1px;
    margin: 0 0 var(--space-6);
    border: 0;
    background: linear-gradient(90deg,
        transparent 0%,
        var(--border-strong) 12%,
        var(--border-strong) 88%,
        transparent 100%);
    box-shadow: 0 4px 14px -6px hsl(var(--h) 30% 18% / 0.55);
    filter: blur(0.5px);
}

:root[data-mode="dark"] .about__rule {
    background: linear-gradient(90deg,
        transparent 0%,
        var(--primary-line) 12%,
        var(--primary-line) 88%,
        transparent 100%);
    box-shadow: 0 6px 18px -8px hsl(var(--h) 50% 2% / 0.9);
}

/* One of these per paragraph in HOME_HERO_LEAD. Full width like the rule above
   it: no measure cap, since a capped column beside a full-bleed rule would read
   as a mistake rather than a choice. */
.about__text {
    margin: 0;
    color: var(--text-soft);
}

/* Space between the paragraphs, not after them: the last one needs no trailing
   margin because .about's own padding-block closes the block. The adjacent
   selector means a one-paragraph HOME_HERO_LEAD is completely unaffected. */
.about__text + .about__text {
    margin-top: var(--space-5);
}

/* ---- skills map (the hero illustration) -------------------------------- */

.skills-map {
    width: 100%;
    max-width: 460px;
    height: auto;
    margin-inline: auto;
    overflow: visible;
}

.skills-map__orbit {
    fill: none;
    stroke: var(--border);
    stroke-width: 1.5;
    stroke-dasharray: 3 7;
    stroke-linecap: round;
}

.skills-map__link {
    stroke: var(--primary-line);
    stroke-width: 1.5;
    opacity: 0.75;
}

.skills-map__core {
    fill: var(--primary-soft);
    stroke: var(--primary);
    stroke-width: 2.5;
}

.skills-map__core-icon {
    fill: none;
    stroke: var(--primary);
    stroke-width: 1.9;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.skills-map__node circle {
    fill: var(--surface);
    stroke: var(--border-strong);
    stroke-width: 1.6;
    transition: fill var(--speed) ease, stroke var(--speed) ease;
}

.skills-map__node-icon {
    fill: none;
    stroke: var(--text);
    stroke-width: 1.9;
    stroke-linecap: round;
    stroke-linejoin: round;
    transition: stroke var(--speed) ease;
}

.skills-map__node:hover circle,
.skills-map__node:focus-visible circle {
    fill: var(--primary-soft);
    stroke: var(--primary);
}

.skills-map__node:hover .skills-map__node-icon {
    stroke: var(--primary);
}

/* ---- feature cards ----------------------------------------------------- */

/* ---- cookie notice ---------------------------------------------------- */

.notice {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 60;
    /* Plain padding, NOT var(--wrap): this bar spans the whole viewport, so a
       1140px padding here would squeeze the content box to nothing on any
       normal screen. The width is capped by .notice__inner below instead. */
    padding: var(--space-3) var(--space-4);
    padding-bottom: calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
    background-color: var(--surface);
    border-top: 1px solid var(--border);
    box-shadow: 0 -8px 28px -18px hsl(var(--h) 30% 14% / 0.4);
}

.notice__inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
        "lead   actions"
        "cats   actions";
    align-items: center;
    gap: var(--space-3) var(--space-5);
    max-width: var(--wrap);
    margin-inline: auto;
}

.notice__lead {
    grid-area: lead;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-width: 0;
}

.notice__badge {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 34px;
    height: 34px;
    border-radius: 11px;
    color: var(--on-primary);
    background-image: linear-gradient(140deg, var(--primary), hsl(var(--h) var(--s) 62%));
}

.notice__text { min-width: 0; }
.notice__title { margin: 0; font-size: 0.92rem; }
.notice__body { margin: 0; font-size: 0.81rem; color: var(--text-soft); }

/* The category chips: the shape a consent banner keeps as more categories are
   added. "is-off" ones are shown as unused rather than hidden. */
.notice__cats {
    grid-area: cats;
    align-self: start;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.notice__cat {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 9px;
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    background-color: var(--surface-2);
    font-size: 0.74rem;
    line-height: 1.35;
}

.notice__cat-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background-color: var(--text-soft);
    opacity: 0.45;
}

.notice__cat.is-on { border-color: var(--primary-line); }
.notice__cat.is-on .notice__cat-dot { background-color: var(--primary); opacity: 1; }

.notice__cat-label { font-weight: 600; }
.notice__cat-state { color: var(--text-soft); }
.notice__cat.is-off { opacity: 0.62; }

.notice__actions {
    grid-area: actions;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
}

.notice__link { font-size: 0.85rem; color: var(--text-soft); text-decoration: underline; }
.notice__link:hover { color: var(--primary); }
.notice__form { margin: 0; }
.notice__btn { flex: 0 0 auto; }

:root[data-mode="dark"] .notice {
    box-shadow: 0 -8px 28px -12px hsl(var(--h) 40% 2% / 0.7);
}

/* The footer sits under the notice while it is on screen. */
body:has(.notice) .site-footer { padding-bottom: var(--space-5); }

/* ---- cookie policy (cookies.php) --------------------------------------- */

.policy {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
    gap: var(--space-4);
}

.policy__cat {
    padding: var(--space-4);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background-color: var(--surface);
    box-shadow: var(--shadow-sm);
}
.policy__cat.is-off { opacity: 0.7; }

.policy__cat-title {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin: 0 0 var(--space-2);
    font-size: 0.95rem;
}

.policy__flag {
    padding: 2px 8px;
    border-radius: var(--radius-pill);
    background-color: var(--primary-soft);
    color: var(--primary);
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}

.policy__cat-text { margin: 0; font-size: 0.86rem; color: var(--text-soft); }
.policy__heading { margin: var(--space-7) 0 var(--space-4); font-size: 1.1rem; }

.policy__choice { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-4); }
.policy__choice p { margin: 0; color: var(--text-soft); flex: 1 1 20rem; }
.policy__choice form { margin: 0; }

/* ---- error pages (errors/render.php) ----------------------------------- */

.errorpage {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
    max-width: 34rem;
    margin-inline: auto;
    padding-block: var(--space-7);
    text-align: center;
}

/* The status code itself is the decoration, so it is hidden from the screen
   reader: the <h1> below carries the same information in words. */
.errorpage__code {
    margin: 0;
    font-family: var(--font-mono);
    font-size: clamp(3.75rem, 16vw, 6.5rem);
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.04em;
    color: var(--primary);
    text-shadow:
        0 1px 0 hsl(var(--h) var(--s) 96% / 0.4),
        0 12px 28px hsl(var(--h) var(--s) 45% / 0.2);
}

.errorpage__badge {
    display: grid;
    place-items: center;
    width: 54px;
    height: 54px;
    margin: 0;
    border-radius: 50%;
    color: var(--on-primary);
    background-image: linear-gradient(140deg, var(--primary), hsl(var(--h) var(--s) 62%));
    box-shadow: var(--shadow-sm);
}

.errorpage__title { margin: 0; font-size: clamp(1.3rem, 4vw, 1.75rem); }
.errorpage__text { margin: 0; color: var(--text-soft); }

.errorpage__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-3);
    margin: var(--space-2) 0 0;
}

:root[data-mode="dark"] .errorpage__code {
    text-shadow:
        0 0 28px hsl(var(--h) var(--s) 62% / 0.3),
        0 2px 8px hsl(var(--h) 40% 3% / 0.55);
}

/* ---- private menu preview --------------------------------------------- */

/* ==========================================================================
   7. Login page
   ========================================================================== */

.auth {
    display: flex;
    align-items: center;
    justify-content: center;
    padding-block: var(--space-7) var(--space-8);
    flex: 1 0 auto;
}

.auth__card {
    width: 100%;
    /* Just wide enough for the three fields and the button: a login form has no
       business being wider than that. */
    max-width: 440px;
    border-radius: var(--radius-lg);
    border: 1px solid var(--border);
    background-color: var(--surface);
    box-shadow: var(--shadow-lg);
}

.auth__form-wrap { padding: var(--space-6); display: flex; flex-direction: column; }
.auth__form { display: grid; gap: var(--space-4); }

.field { display: grid; gap: var(--space-2); }
.field__label {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-soft);
}
.field__label .icon { color: var(--primary); }

.field__control { position: relative; display: flex; align-items: center; }

.input {
    width: 100%;
    padding: 11px 14px;
    font: inherit;
    font-size: 0.95rem;
    color: var(--text);
    background-color: var(--bg);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius);
    transition: border-color var(--speed) ease, box-shadow var(--speed) ease,
                background-color var(--speed) ease;
}
.input::placeholder { color: var(--text-faint); }
.input:hover { border-color: var(--primary-line); }
.input:focus {
    outline: none;
    border-color: var(--primary);
    background-color: var(--surface);
    box-shadow: 0 0 0 4px hsl(var(--h) var(--s) 50% / 0.16);
}
.input--password { padding-inline-end: 46px; }

.field__peek {
    position: absolute;
    right: 6px;
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    border: 0;
    border-radius: 9px;
    background: transparent;
    color: var(--text-faint);
    cursor: pointer;
    transition: background-color var(--speed) ease, color var(--speed) ease;
}
.field__peek:hover { background-color: var(--surface-2); color: var(--primary); }

/* The eye button holds both icons; CSS reveals one based on aria-pressed
   (toggled by assets/app.js). Without JS the "eye" icon stays visible. */
.field__peek .js-eye-off { display: none; }
.field__peek .js-eye { display: block; }
.field__peek[aria-pressed="true"] .js-eye { display: none; }
.field__peek[aria-pressed="true"] .js-eye-off { display: block; }

/* ---- captcha ---------------------------------------------------------- */

/* The field takes what is left of the card, the drawing keeps its own size.
   min-width:0 lets the input shrink instead of pushing the image out. */
.captcha { display: flex; align-items: center; gap: var(--space-3); }
.captcha .field__control { flex: 1 1 auto; min-width: 0; }

.captcha__img {
    flex: 0 0 auto;
    display: block;
    /* The size the drawing is composed for (captcha_size()); declaring it here
       keeps the row from reflowing before the image has loaded. */
    width: 132px;
    height: 44px;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background-color: var(--surface-2);
}

/* The row under the captcha field. It carries the "New code" control alone, at
   the right end of the line; the size is the control's, inherited from here. */
.field__hint {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    font-size: 0.78rem;
}
.captcha__new {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: var(--primary);
    font: inherit;
    font-weight: 600;
    white-space: nowrap;
}
.captcha__new:hover { color: var(--primary-hover); text-decoration: underline; }

/* While the server is holding a drawing back the control is a disabled button
   that looks like the link, dimmed, and counts the seconds down. It is not a
   button the site ever presses: assets/app.js replaces it with the link when
   the wait is over, and without JavaScript it stays dimmed until a reload. */
.captcha__new.is-waiting,
.captcha__new.is-waiting:hover {
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--text-faint);
    cursor: default;
    text-decoration: none;
}

.alert {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius);
    border: 1px solid var(--border);
    background-color: var(--surface-2);
    font-size: 0.88rem;
    animation: shake-in 320ms ease-out;
}
.alert .icon { margin-top: 2px; }
.alert__body { display: grid; gap: 2px; }
.alert__title { font-weight: 700; }
.alert__text { color: var(--text-soft); font-size: 0.85rem; }

.alert--error {
    border-color: hsl(2 66% 48% / 0.38);
    background-color: hsl(2 66% 52% / 0.10);
}
.alert--error .icon,
.alert--error .alert__title { color: var(--danger); }

.alert--warning {
    border-color: hsl(38 88% 46% / 0.42);
    background-color: hsl(38 88% 52% / 0.14);
}
.alert--warning .icon,
.alert--warning .alert__title { color: var(--warning); }

/* ==========================================================================
   8. Private area
   ========================================================================== */

.private {
    padding-block: var(--space-6) var(--space-8);
    flex: 1 0 auto;
}

.private__layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--space-6);
    align-items: start;
}

/* ---- the private menu, as tiles ---------------------------------------- */

.tiles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(248px, 1fr));
    gap: var(--space-4);
}

.tile {
    display: grid;
    gap: var(--space-2);
    align-content: start;
    padding: var(--space-5);
    border-radius: var(--radius-lg);
    border: 1px solid var(--border);
    background-color: var(--surface);
    box-shadow: var(--shadow-sm);
    color: var(--text);
    text-decoration: none;
    transition: transform var(--speed) ease, border-color var(--speed) ease,
                box-shadow var(--speed) ease;
}
.tile:hover {
    transform: translateY(-2px);
    border-color: var(--border-strong);
    box-shadow: var(--shadow);
}
.tile__icon {
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    margin-bottom: var(--space-2);
    border-radius: var(--radius);
    color: var(--on-primary);
    background-image: linear-gradient(140deg, var(--primary), hsl(var(--h) var(--s) 60%));
}
.tile__name { font-weight: 700; }
.tile__about {
    font-size: 0.86rem;
    line-height: 1.5;
    color: var(--text-soft);
}
.tile__foot {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: var(--space-2);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--text-faint);
}

/* ---- content panel ---------------------------------------------------- */

.panel {
    border-radius: var(--radius-lg);
    border: 1px solid var(--border);
    background-color: var(--surface);
    box-shadow: var(--shadow);
    overflow: hidden;
}

.panel__head {
    display: flex;
    align-items: flex-start;
    gap: var(--space-4);
    padding: var(--space-5);
    border-bottom: 1px solid var(--border);
    background-image: linear-gradient(135deg, var(--primary-soft), transparent 62%);
}
.panel__icon {
    display: grid;
    place-items: center;
    width: 52px;
    height: 52px;
    flex: 0 0 52px;
    border-radius: 15px;
    color: var(--on-primary);
    background-image: linear-gradient(140deg, var(--primary), hsl(var(--h) var(--s) 60%));
    box-shadow: var(--shadow-sm);
}
.panel__title { margin: 0 0 var(--space-1); font-size: 1.4rem; }
.panel__about { margin: 0; color: var(--text-soft); font-size: 0.92rem; max-width: 66ch; }
.panel__meta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-3);
}

.badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 11px;
    border-radius: var(--radius-pill);
    border: 1px solid var(--border);
    background-color: var(--surface-2);
    color: var(--text-soft);
    font-size: 0.74rem;
    font-weight: 700;
    letter-spacing: 0.03em;
}

.badge--success {
    background-color: hsl(158 62% 46% / 0.14);
    border-color: hsl(158 62% 40% / 0.4);
    color: var(--success);
}
.badge--info {
    background-color: hsl(206 78% 48% / 0.14);
    border-color: hsl(206 78% 44% / 0.4);
    color: var(--info);
}

/* ---- breadcrumb / user strip ------------------------------------------ */

.crumbs {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
    margin-bottom: var(--space-4);
    font-size: 0.82rem;
    color: var(--text-faint);
}
.crumbs .icon { opacity: 0.6; }
.crumbs a { color: var(--text-soft); }
.crumbs strong { color: var(--primary); }

/* ==========================================================================
   9. Footer
   ========================================================================== */

.site-footer {
    margin-top: auto;
    padding-block: var(--space-5);
    border-top: 1px solid var(--border);
    background-color: hsl(var(--h) 26% 98% / 0.6);
}
:root[data-mode="dark"] .site-footer {
    background-color: hsl(var(--h) 20% 11% / 0.6);
}

/* Column, so the small site description sits on top and the IP / cookies /
   icons line sits directly below it. This used to be a row with
   "justify-content: space-between", which pushed the two lines to opposite
   ends of the footer instead. */
.site-footer__inner {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
}
.site-footer__line {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0;
    font-weight: 600;
    font-size: 0.9rem;
}
.site-footer__line .icon { color: var(--primary); }
.site-footer__meta {
    margin: 0;
    font-size: 0.8rem;
    color: var(--text-faint);
}

/* ==========================================================================
   10. Utilities, motion & responsive
   ========================================================================== */

@keyframes pop-in {
    from { opacity: 0; transform: translateY(-6px) scale(0.98); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes shake-in {
    0%   { transform: translateX(-6px); }
    55%  { transform: translateX(4px); }
    100% { transform: translateX(0); }
}

/* ---- responsive ------------------------------------------------------- */

@media (max-width: 1020px) {
    .hero__grid { gap: var(--space-6); }
}

@media (max-width: 900px) {
    .auth { padding-block: var(--space-6) var(--space-6); }

    .hero__grid { grid-template-columns: minmax(0, 1fr); }
    .hero { padding-block: var(--space-7) var(--space-6); }
    .skills-map { max-width: 380px; }
}

@media (max-width: 760px) {
    .wrap { padding-inline: var(--space-4); }

    /* The notice stacks: text, then chips, then the actions full width. */
    .notice__inner {
        grid-template-columns: minmax(0, 1fr);
        grid-template-areas:
            "lead"
            "cats"
            "actions";
        gap: var(--space-2);
    }
    .notice__actions { gap: var(--space-2); }
    .notice__btn { width: 100%; justify-content: center; }
    .notice__form { flex: 1 1 auto; }

    .site-header__inner { min-height: 0; }
    .site-nav { order: 3; width: 100%; }
    .brand__tag { display: none; }

    .panel__head { flex-direction: column; }
    .theme-panel { right: auto; left: 0; }
}

@media (max-width: 560px) {
    .btn__text { display: none; }
    .btn--primary .btn__text,
    .btn--lg .btn__text { display: inline; }

    /* Narrow phones: the card is the width of the screen minus the page
       padding, so its own padding has to shrink with it. */
    .auth { padding-block: var(--space-5) var(--space-5); }
    .auth__form-wrap { padding: var(--space-4); }

    /* The captcha row stops trying to fit on one line: the drawing keeps its
       size, the field takes the width of the card underneath it. */
    .captcha { flex-wrap: wrap; }
    .captcha__img { order: -1; }

    .swatches { grid-template-columns: repeat(6, 1fr); }
    .swatches--labelled { grid-template-columns: repeat(4, 1fr); }

}

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *,
    *::before,
    *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
    }
}

@media print {
    .site-header,
    .site-footer,
    .theme-card { display: none; }
    body { background: none; }
}