:root {
    --bg: #0a0a0b;
    --surface: #18181b;
    --surface2: #232327;
    --border: #27272a;
    --text: #f4f4f5;
    --muted: #a1a1aa;
    --primary: #ee72f1;
    --primary2: #ec4899;
    --primary-glow: rgba(238, 114, 241, 0.18);
    --wip: #eab308;
    --wip-soft: rgba(234, 179, 8, 0.12);
    --danger: #f85149;
    --danger2: #da3633;
    --ok: #3fb950;

    /* Syntax highlighting. Few colours, each standing for one idea, so a
       block reads as code and not as a ransom note. */
    --code-comment: #8a8a94;
    --code-keyword: var(--primary);
    --code-type: #7dd3fc;
    --code-function: #c4b5fd;
    --code-string: #7ee787;
    --code-number: #fbbf24;
    --code-preproc: #a5b4fc;
}

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

body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
    font-size: clamp(18px, 1.25vw, 24px);
    line-height: 1.6;
    background: var(--bg);
    color: var(--text);
    min-height: 100vh;
}

/* One rule for every link on the site: the accent colour, never underlined.
   A link sits inside prose that already leans on the accent for emphasis, so
   the colour is the affordance and the hover deepens it. */
a {
    color: var(--primary);
    text-decoration: none;
    transition: color 0.1s;
}

a:hover {
    color: var(--primary2);
}

/* ── Navbar ─────────────────────────────────────────────────────────── */

.navbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    /* Everything in the bar grows with the viewport, so a wide screen gets a
       bar in proportion instead of the same small one. */
    height: clamp(70px, 6vw, 92px);
    padding: 0 clamp(24px, 4vw, 64px);
    gap: 16px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    box-shadow: 0 1px 24px var(--primary-glow);
    position: sticky;
    top: 0;
    z-index: 50;
}

.navbar-brand {
    display: flex;
    align-items: center;
    gap: 12px;
    text-decoration: none;
    color: var(--text);
}

.navbar-brand:hover {
    color: var(--primary);
}

.nav-logo {
    width: clamp(40px, 3.2vw, 54px);
    height: clamp(40px, 3.2vw, 54px);
    object-fit: contain;
    flex-shrink: 0;
}

.brand-name {
    font-size: clamp(20px, 1.7vw, 30px);
    font-weight: 600;
    color: var(--text);
}

/* Brand and account links take equal shares of the bar, which leaves the
   three sections sitting in the true middle of it. */
.navbar-brand,
.navbar-links {
    flex: 1 1 0;
    min-width: 0;
}

.navbar-links {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: clamp(18px, 2vw, 40px);
}

/* ── Navbar sections ────────────────────────────────────────────────── */

/* Linguaggi, Progetti and Scenari are what the site is: bigger than the rest
   of the bar, in the centre of it, each with an icon. */
.navbar-sections {
    display: flex;
    align-items: center;
    gap: clamp(20px, 2.4vw, 52px);
    flex-shrink: 0;
}

.nav-section {
    position: relative;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 2px;
    color: var(--muted);
    text-decoration: none;
    font-size: clamp(19px, 1.7vw, 30px);
    font-weight: 600;
    line-height: 1;
    transition: color 0.15s;
}

.nav-section-icon {
    width: 1.15em;
    height: 1.15em;
    flex-shrink: 0;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.7;
    stroke-linecap: round;
    stroke-linejoin: round;
    transition: transform 0.18s ease;
}

/* One rule that grows from the middle: the whole of the hover effect. */
.nav-section::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: 0;
    width: 0;
    height: 2px;
    border-radius: 2px;
    background: linear-gradient(90deg, var(--primary), var(--primary2));
    translate: -50% 0;
    transition: width 0.2s ease;
}

.nav-section:hover {
    color: var(--text);
}

.nav-section:hover::after {
    width: 100%;
}

.nav-section:hover .nav-section-icon {
    color: var(--primary);
    transform: translateY(-2px);
}

/* Too narrow for three labels next to the brand and the account links: the
   icons carry the sections on their own. */
@media (max-width: 1000px) {
    .nav-section span {
        display: none;
    }

    .nav-section-icon {
        width: 1.5em;
        height: 1.5em;
    }
}

/* Phones: the brand mark alone, so the three sections keep the middle. */
@media (max-width: 560px) {
    .navbar {
        padding: 0 16px;
        gap: 8px;
    }

    .brand-name {
        display: none;
    }

    .navbar-sections {
        gap: 22px;
    }
}

.navbar-links a {
    color: var(--muted);
    text-decoration: none;
    font-size: clamp(17px, 1.4vw, 25px);
    line-height: 1;
    transition: color 0.1s;
}

.navbar-links a:hover,
.navbar-links .nav-link-btn:hover {
    color: var(--primary);
}

.nav-logout-form {
    display: flex;
    align-items: center;
    margin: 0;
    padding: 0;
}

.navbar-links .nav-link-btn {
    background: none;
    border: none;
    cursor: pointer;
    color: var(--muted);
    font: inherit;
    font-size: clamp(17px, 1.4vw, 25px);
    padding: 0;
    line-height: 1;
    transition: color 0.1s;
}

/* ── Layout ─────────────────────────────────────────────────────────── */

.container {
    max-width: 100%;
    margin: 0 auto;
    padding: 48px clamp(32px, 4vw, 80px);
    min-height: calc(100vh - 70px - 320px);
}

.section {
    margin-top: 64px;
}

.section h2 {
    margin-bottom: 24px;
}

.muted {
    color: var(--muted);
}

.accent {
    color: var(--primary);
}

/* ── Intro ──────────────────────────────────────────────────────────── */

.intro {
    max-width: 1400px;
    padding: 24px 0 16px;
}

.intro h1 {
    font-size: clamp(32px, 3.5vw, 52px);
    line-height: 1.2;
    margin-bottom: 20px;
}

.intro p {
    color: var(--muted);
    margin-bottom: 16px;
}

.intro strong {
    color: var(--text);
}

/* ── Home backdrop ─────────────────────────────────────── */

/* Slow-drifting glows behind the page: enough movement to feel alive, slow
   enough that nothing pulls the eye off the three choices. */
.home-bg {
    position: fixed;
    inset: 0;
    z-index: -1;
    overflow: hidden;
    pointer-events: none;
}

.home-blob {
    position: absolute;
    width: 46vw;
    height: 46vw;
    min-width: 320px;
    min-height: 320px;
    border-radius: 50%;
    filter: blur(90px);
    opacity: 0.35;
    will-change: transform;
}

.home-blob-1 {
    top: -12vw;
    left: -8vw;
    background: radial-gradient(circle, var(--primary), transparent 68%);
    animation: home-drift 26s ease-in-out infinite;
}

.home-blob-2 {
    top: 20vh;
    right: -14vw;
    background: radial-gradient(circle, var(--primary2), transparent 68%);
    animation: home-drift 34s ease-in-out infinite reverse;
}

.home-blob-3 {
    bottom: -18vw;
    left: 30vw;
    opacity: 0.22;
    background: radial-gradient(circle, var(--primary), transparent 70%);
    animation: home-drift 42s ease-in-out infinite;
    animation-delay: -12s;
}

@keyframes home-drift {
    0%, 100% { transform: translate(0, 0) scale(1); }
    33%      { transform: translate(4vw, 3vh) scale(1.06); }
    66%      { transform: translate(-3vw, 5vh) scale(0.96); }
}

/* ── Home hero ─────────────────────────────────────────── */

/* Text carries the idea on the left, the stack shows it on the right. The
   hero claims exactly the first screen — navbar and the container's own
   padding taken out — so both columns sit centred in it with no dead space
   under them. */
/* Title and subtitle run the full width; under them the paragraph and the
   stack split the row, so the heading covers both columns. */
.home-hero {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr);
    align-items: center;
    column-gap: clamp(32px, 5vw, 80px);
    row-gap: clamp(8px, 1.2vh, 16px);
    width: 100%;
    max-width: 1400px;
    margin: 0 auto;
}

.home-title,
.home-subtitle {
    grid-column: 1 / -1;
}

.home-title {
    font-size: clamp(38px, 5vw, 76px);
    line-height: 1.1;
    letter-spacing: -0.01em;
}

/* The home page is meant to be seen whole: navbar, hero and footer share one
   screen with nothing below the fold. Only on a screen tall and wide enough to
   hold it — anywhere else the page scrolls normally rather than clipping. */
@media (min-height: 760px) and (min-width: 1001px) {
    body.home-page {
        height: 100vh;
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }

    body.home-page .container {
        flex: 1;
        min-height: 0;
        display: flex;
        align-items: center;
        padding-block: clamp(20px, 3vh, 48px);
    }

    body.home-page .footer {
        margin-top: 0;
        padding-block: clamp(24px, 3.5vh, 48px);
    }
}

.home-subtitle {
    font-size: clamp(20px, 1.8vw, 30px);
    color: var(--text);
}

/* The heading block stays tight; the row under it is pushed well clear of it,
   so title and subtitle read as one unit and the choices as another. */
.home-copy,
.home-choices {
    margin-top: clamp(28px, 6vh, 84px);
}

.home-copy p {
    color: var(--muted);
    font-size: 1.12em;
    line-height: 1.65;
    margin-bottom: 14px;
}

.home-copy p:last-child {
    margin-bottom: 0;
}

.home-copy strong {
    color: var(--text);
}

/* ── Home choices ──────────────────────────────────────── */

/* The three ways in, as three identical panels: same width, same height, same
   spacing — nothing about the shape says one comes before another.

   One parent grid, four columns: empty, count, name, empty. Each panel is a
   subgrid of that, so the three numbers share a column and line up even
   though the labels are different lengths. The two 1fr columns keep the
   count+name pair centred as a group. */
.home-choices {
    display: grid;
    grid-template-columns: 1fr auto auto 1fr;
    row-gap: clamp(20px, 2.2vw, 30px);
    /* Wide enough that the drift below never closes the gap between panels.
       The same gap sits between the count and the name. */
    column-gap: clamp(12px, 1.4vw, 22px);
    width: 100%;
}

.home-choice {
    position: relative;
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: subgrid;
    align-items: center;
    height: clamp(80px, 8vw, 116px);
    padding: 0 24px;
    background: linear-gradient(135deg, var(--surface2), var(--surface));
    border: 1px solid var(--border);
    border-radius: 14px;
    overflow: hidden;
    text-decoration: none;
    color: var(--text);
    text-align: center;
    transition: transform 0.2s ease, border-color 0.15s, box-shadow 0.2s;
    animation: home-drift-y 6s ease-in-out infinite;
    will-change: transform;
}

/* How far the logged-in user has got: a quiet tint across the panel. */
.home-choice-fill {
    position: absolute;
    inset: 0 auto 0 0;
    z-index: 0;
    pointer-events: none;
    background: var(--primary-glow);
}

.home-choice-fill-done {
    background: rgba(63, 185, 80, 0.16);
}

/* One accent stroke down the left edge: the whole of the decoration. */
.home-choice::before {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    z-index: 1;
    width: 4px;
    height: 34%;
    translate: 0 -50%;
    border-radius: 0 4px 4px 0;
    background: linear-gradient(180deg, var(--primary), var(--primary2));
    opacity: 0.55;
    transition: height 0.2s ease, opacity 0.2s ease;
}

.home-choice:nth-child(2) { animation-delay: -2s; }
.home-choice:nth-child(3) { animation-delay: -4s; }

.home-choice:hover {
    border-color: var(--primary);
    transform: translateY(-6px);
    box-shadow: 0 0 36px var(--primary-glow);
    animation-play-state: paused;
}

.home-choice:hover::before {
    height: 72%;
    opacity: 1;
}

/* Enough movement to read as movement, slow enough to stay calm. */
@keyframes home-drift-y {
    0%, 100% { transform: translateY(-8px); }
    50%      { transform: translateY(8px); }
}

.home-choice-name {
    grid-column: 3;
    position: relative;
    z-index: 1;
    font-size: clamp(22px, 2.1vw, 34px);
    font-weight: 700;
    line-height: 1.2;
    letter-spacing: 0.01em;
    text-align: left;
}

/* How many of this kind there are, read as part of the label: "4 Linguaggi".
   Logged in it becomes a score: "1/4 Linguaggi". */
.home-choice-count {
    grid-column: 2;
    position: relative;
    z-index: 1;
    font-size: clamp(26px, 2.6vw, 40px);
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.03em;
    color: var(--primary);
    font-variant-numeric: tabular-nums;
    text-align: right;
}

/* One column: the heading, the text, then the panels under it. Two columns
   below this width leave both of them too narrow to read. */
@media (max-width: 1000px) {
    .home-hero {
        grid-template-columns: 1fr;
        row-gap: 0;
        margin-bottom: 56px;
    }

    /* The margins below carry the spacing, so the row gap goes to zero above
       rather than adding to them. */
    .home-copy,
    .home-choices {
        margin-top: clamp(22px, 4vh, 44px);
    }

    .home-title {
        font-size: clamp(30px, 6.4vw, 48px);
    }

    .home-subtitle {
        font-size: clamp(18px, 3.2vw, 24px);
    }

    .home-copy p {
        font-size: 1em;
    }
}

/* Phones: shorter panels, tighter everything, and a cheaper blur behind. */
@media (max-width: 560px) {
    .home-choices {
        row-gap: 14px;
    }

    .home-choice {
        height: clamp(64px, 16vw, 84px);
    }

    .home-choice-name {
        font-size: clamp(19px, 5.2vw, 26px);
    }

    .home-choice-count {
        font-size: clamp(22px, 6vw, 30px);
    }

    .home-blob {
        filter: blur(60px);
        width: 70vw;
        height: 70vw;
    }
}

@media (prefers-reduced-motion: reduce) {
    .home-bg *,
    .home-choice {
        animation: none;
    }
}

/* ── Buttons ────────────────────────────────────────────────────────── */

.btn {
    display: inline-block;
    padding: 12px 28px;
    border-radius: 8px;
    text-decoration: none;
    font-weight: 600;
    border: none;
    cursor: pointer;
    transition: filter 0.1s;
}

.btn-primary {
    background: linear-gradient(135deg, var(--primary), var(--primary2));
    color: #fff;
}

.btn-primary:hover {
    filter: brightness(1.1);
}

.empty-state {
    color: var(--muted);
    margin-top: 24px;
}

/* ── Tags and filtering ─────────────────────────────────────────────── */

.tag-filter {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
    max-width: 1900px;
    margin-top: 12px;
    padding-bottom: 4px;
}

/* The first row sits under the intro; the second follows it closely, so the
   two read as one control. */
.tag-filter-languages {
    margin-top: 32px;
}

.tag-chip {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 9px 18px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface);
    color: var(--muted);
    font-size: 0.85em;
    text-decoration: none;
    white-space: nowrap;
    transition: border-color 0.12s, color 0.12s, background 0.12s;
}

.tag-chip-logo {
    width: 26px;
    height: 26px;
}

a.tag-chip:hover {
    border-color: var(--primary);
    color: var(--text);
}

.tag-chip.active {
    border-color: var(--primary);
    background: var(--primary-glow);
    color: var(--primary);
    font-weight: 600;
}

/* A combination that would leave nothing: still clickable, visibly pointless. */
.tag-chip-empty {
    opacity: 0.4;
}

/* On a card the tags are labels, not links. */
.tag-chip-static {
    padding: 4px 12px;
    background: var(--surface2);
    font-size: 0.65em;
    gap: 6px;
}

.project-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

/* ── Language cards ─────────────────────────────────────────────────── */

/* As many columns as fit: the row fills the width instead of leaving a gap on
   the right, and the cards never stretch beyond a comfortable size. */
/* The same grid the projects use, so a language card and a project card are
   the same size on the same screen. */
.language-grid-cards {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 32px;
    margin-top: 24px;
}

@media (max-width: 1400px) {
    .language-grid-cards {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 900px) {
    .language-grid-cards {
        grid-template-columns: 1fr;
    }
}

.language-card {
    display: flex;
    flex-direction: column;
    gap: 16px;
    padding: 26px 28px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    text-decoration: none;
    color: var(--text);
    transition: border-color 0.12s, transform 0.12s;
}

.language-card:hover {
    border-color: var(--primary);
    transform: translateY(-2px);
}

.language-card-head {
    display: flex;
    align-items: center;
    gap: 18px;
}

.language-card-logo {
    width: 52px;
    height: 52px;
    flex-shrink: 0;
}

.language-card-name {
    font-size: 1.1em;
    line-height: 1.2;
}

.language-card .project-meta {
    margin-top: auto;
}

.language-page-logo {
    width: clamp(44px, 4vw, 64px);
    height: clamp(44px, 4vw, 64px);
}

.language-page-tagline {
    color: var(--primary);
    font-size: 0.9em;
}

.language-step-head {
    display: flex;
    align-items: center;
    gap: 14px;
}

.language-step-logo {
    width: 32px;
    height: 32px;
}

/* ── Project sections ───────────────────────────────────────────────── */

/* Wider than the prose columns: three cards a row need the room. */
.projects-section {
    max-width: 1900px;
    margin-top: 96px;
}

.projects-section:first-of-type {
    margin-top: 48px;
}

.section-header {
    display: flex;
    align-items: center;
    gap: 20px;
    margin-bottom: 24px;
}

.section-title {
    display: flex;
    align-items: center;
    gap: 14px;
    font-size: clamp(22px, 2vw, 30px);
    line-height: 1.2;
    white-space: nowrap;
}

.section-title::before {
    content: "";
    width: 5px;
    height: 1.1em;
    border-radius: 99px;
    background: linear-gradient(180deg, var(--primary), var(--primary2));
    box-shadow: 0 0 16px var(--primary-glow);
}

.section-rule {
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg, var(--border), transparent);
}

/* A section header that folds. The <summary> is the header, so the chevron
   tracks the open state with no script; hiding the native marker is what the
   list-style and the -webkit rule are for. */
.section-summary {
    cursor: pointer;
    list-style: none;
    user-select: none;
}

.section-summary::-webkit-details-marker {
    display: none;
}

.section-chevron {
    width: 10px;
    height: 10px;
    border-right: 2px solid var(--muted);
    border-bottom: 2px solid var(--muted);
    transform: rotate(45deg);
    transition: transform 0.15s, border-color 0.15s;
    flex-shrink: 0;
}

details[open] > .section-summary .section-chevron {
    transform: rotate(-135deg);
}

.section-summary .section-title {
    transition: color 0.15s;
}

/* The house signal for text you can click: the same tint the navbar brand
   and the step titles take. Keyboard focus gets it too, or the heading would
   be the one control on the page with no visible state. */
.section-summary:hover .section-title,
.section-summary:focus-visible .section-title {
    color: var(--primary);
}

.section-summary:hover .section-chevron,
.section-summary:focus-visible .section-chevron {
    border-color: var(--primary);
}

/* Closed, the header is the whole section: it has no content left to clear. */
details:not([open]) > .section-summary {
    margin-bottom: 0;
}

@media (max-width: 680px) {
    .projects-section {
        margin-top: 64px;
    }
}

/* ── Project grid ───────────────────────────────────────────────────── */

.project-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 32px;
    margin-top: 24px;
}

@media (max-width: 1400px) {
    .project-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 900px) {
    .project-grid {
        grid-template-columns: 1fr;
    }
}

.project-card {
    display: flex;
    flex-direction: column;
    gap: 14px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 24px;
    color: var(--text);
    text-decoration: none;
    transition: border-color 0.1s, box-shadow 0.1s;
}

.project-card:hover {
    border-color: var(--primary);
    box-shadow: 0 0 24px var(--primary-glow);
}

/* A finished run: the card stops advertising the project and starts
   reporting a result, so it goes green from edge to title. */
.project-card-done {
    border-color: rgba(63, 185, 80, 0.45);
}

.project-card-done:hover {
    border-color: var(--ok);
    box-shadow: 0 0 24px rgba(63, 185, 80, 0.22);
}

.project-card-done .project-title {
    color: var(--ok);
}

.project-card-done .project-progress-label {
    color: var(--ok);
}

.project-card-done .progress-fill {
    background: var(--ok);
    box-shadow: 0 0 16px rgba(63, 185, 80, 0.28);
}

.project-card-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
}

.project-title {
    font-size: clamp(20px, 1.5vw, 26px);
    line-height: 1.3;
    color: var(--primary);
}

.project-summary {
    color: var(--muted);
    font-size: 0.85em;
    flex-grow: 1;
}

.project-progress {
    margin-top: auto;
}

.project-progress-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 8px;
    font-size: 0.7em;
}

.project-progress-label {
    color: var(--primary);
    font-weight: 600;
}

.project-progress-count {
    color: var(--muted);
}

.project-progress .progress-bar {
    height: 6px;
}

/* A scenario has nothing to fill up: it is solved or it is not. The mark goes
   where a project card keeps its progress bar. */
.scenario-solved {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: auto;
    padding-top: 18px;
    color: var(--ok);
    font-size: 0.7em;
    font-weight: 600;
}

.scenario-solved::before {
    content: "";
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--ok);
}

.project-meta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    border-top: 1px solid var(--border);
    padding-top: 14px;
}

.project-languages {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.tag {
    background: var(--surface2);
    border: 1px solid var(--border);
    border-radius: 999px;
    padding: 2px 12px;
    font-size: 0.7em;
    color: var(--muted);
}

.tag-lang {
    border-color: var(--primary);
    color: var(--primary);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

.lang-logo {
    height: 32px;
    width: auto;
}

/* Once the project is under way the other languages are not choices any
   more: they step back so the one being used reads as the current one. */
.lang-logo-faded {
    filter: grayscale(1);
    opacity: 0.3;
}

.tag-lang-faded {
    border-color: var(--border);
    color: var(--muted);
}

/* A card that stands for one run of a project says which language it was
   done in: "Sqrt" alone would not tell two finished runs apart. */
.project-title-lang {
    margin-left: 10px;
    padding: 3px 10px;
    border: 1px solid var(--border);
    border-radius: 99px;
    background: var(--surface2);
    color: var(--muted);
    font-size: 0.6em;
    font-weight: 600;
    vertical-align: middle;
    white-space: nowrap;
}

.project-stats {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--muted);
    font-size: 0.75em;
    white-space: nowrap;
}

/* ── Difficulty dots ────────────────────────────────────────────────── */

.difficulty-dots {
    display: flex;
    gap: 5px;
    flex-shrink: 0;
    padding-top: 8px;
}

.difficulty-dots .dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
}

.difficulty-dots.difficulty-principiante .dot {
    background: #3fb950;
}

.difficulty-dots.difficulty-intermedio .dot {
    background: #d29922;
}

.difficulty-dots.difficulty-avanzato .dot {
    background: #f85149;
}

/* ── Repository statistics ──────────────────────────────────────────── */

.project-outline,
.stats-section {
    margin-top: 64px;
}

.stat-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
}

@media (max-width: 700px) {
    .stat-grid {
        grid-template-columns: 1fr;
    }
}

.stat-card {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 22px 24px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
}

.stat-value {
    font-size: 2em;
    font-weight: 700;
    line-height: 1;
    color: var(--primary);
}

.stat-label {
    color: var(--muted);
    font-size: 0.75em;
}

.lang-bar {
    display: flex;
    height: 10px;
    margin-top: 28px;
    border-radius: 999px;
    overflow: hidden;
    background: var(--surface2);
}

.lang-slice {
    height: 100%;
}

/* Four accent shades, enough to tell a handful of languages apart. */
.lang-slice-0 { background: var(--primary); }
.lang-slice-1 { background: var(--primary2); }
.lang-slice-2 { background: #7c6cf0; }
.lang-slice-3 { background: #4fb3d9; }
/* Not a language: everything the extension map could not put a name to. */
.lang-slice-other { background: var(--border); }

.lang-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 12px 28px;
    list-style: none;
    margin-top: 16px;
}

.lang-legend-item {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 0.8em;
}

.lang-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
}

.lang-logo-small {
    width: 26px;
    height: 26px;
}

.lang-percent {
    color: var(--muted);
}

.stat-last-commit {
    margin-top: 24px;
    color: var(--muted);
    font-size: 0.75em;
}

.stat-last-commit code {
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    color: var(--text);
}

/* ── Choosing a language ────────────────────────────────────────────── */

.language-dialog {
    /* The global reset zeroes margins, which also kills the auto margins a
       modal dialog centres itself with. */
    margin: auto;
    width: min(820px, calc(100vw - 32px));
    padding: 28px 30px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--surface);
    color: var(--text);
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
}

.language-dialog::backdrop {
    background: rgba(10, 10, 11, 0.72);
}

/* Shown once, the moment the last step of a project turns green. */
.celebration {
    /* The global reset zeroes margins, and a modal centres itself with the
       auto ones: same fix as the language dialog. */
    margin: auto;
    width: min(520px, calc(100vw - 32px));
    padding: 36px 34px 30px;
    border: 1px solid var(--border);
    border-top: 3px solid var(--ok);
    border-radius: 14px;
    background: var(--surface);
    color: var(--text);
    text-align: center;
    box-shadow: 0 24px 60px rgba(0, 0, 0, 0.5);
}

.celebration::backdrop {
    background: rgba(10, 10, 11, 0.72);
}

.celebration-mark {
    font-size: 44px;
    line-height: 1;
    margin-bottom: 18px;
}

.celebration-title {
    font-size: 24px;
    font-weight: 600;
    line-height: 1.3;
    margin-bottom: 10px;
}

.celebration-text {
    color: var(--muted);
    font-size: 0.8em;
    margin-bottom: 26px;
}

.celebration-actions {
    display: flex;
    justify-content: center;
    gap: 12px;
}

/* .btn leaves font-size to the browser: pin both to the same box, as the
   other dialog does. */
.celebration-actions .btn {
    padding: 11px 24px;
    font-size: 0.8em;
    line-height: 1.4;
    text-decoration: none;
}

.language-dialog-title {
    font-size: 24px;
    font-weight: 600;
    margin-bottom: 10px;
}

.language-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 14px;
    margin: 24px 0 26px;
}

@media (max-width: 720px) {
    .language-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

/* A card that highlights when picked: the radio itself is hidden, the border
   and background do the talking. */
.language-option {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 22px 10px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--bg);
    cursor: pointer;
    transition: border-color 0.12s, background 0.12s, box-shadow 0.12s;
}

.language-option input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.language-option:hover {
    border-color: var(--primary);
}

.language-option:has(input:checked) {
    border-color: var(--primary);
    background: var(--primary-glow);
    box-shadow: 0 0 24px var(--primary-glow);
}

/* Keyboard users still need to see where they are. */
.language-option:has(input:focus-visible) {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

.language-logo {
    width: 46px;
    height: 46px;
    flex-shrink: 0;
}

.language-logo-blank {
    display: block;
    border: 1px dashed var(--border);
    border-radius: 8px;
}

.language-name {
    font-size: 0.75em;
    font-weight: 600;
    text-align: center;
    /* Long names like "Common Lisp" must not wrap into two lines. */
    white-space: nowrap;
}

.language-option:has(input:checked) .language-name {
    color: var(--primary);
}

/* Offered everywhere, available only where the content exists. */
.language-option-off {
    opacity: 0.4;
    cursor: not-allowed;
}

.language-option-off:hover {
    border-color: var(--border);
}

.language-soon {
    color: var(--muted);
    font-size: 0.6em;
}

.language-dialog-actions {
    display: flex;
    align-items: stretch;
    justify-content: flex-end;
    gap: 12px;
}

/* .btn leaves font-size to the browser, so buttons of different variants end
   up different sizes: pin both of these to the same box. */
.language-dialog-actions .btn {
    padding: 11px 27px;
    font-size: 0.8em;
    line-height: 1.4;
}

.start-fallback {
    display: flex;
    align-items: center;
    gap: 16px;
    justify-content: flex-end;
    margin-top: 16px;
}

.eval-message-right {
    text-align: right;
}

.step-language {
    padding: 2px 10px;
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--muted);
}

/* ── Danger zone ────────────────────────────────────────────────────── */

.manage-section {
    margin-top: 64px;
}

/* A tool that only exists on a developer's machine, marked in the colour the
   site already uses for "not for real": the same yellow as a work in progress,
   so it never reads as something a student is meant to press. */
.dev-panel {
    margin-bottom: 20px;
    padding: 24px 26px;
    border: 1px solid var(--border);
    border-left: 3px solid var(--wip);
    border-radius: 12px;
    background: var(--wip-soft);
}

.dev-title {
    margin-bottom: 10px;
    font-size: 20px;
    font-weight: 600;
    color: var(--wip);
}

.dev-panel form {
    margin-top: 16px;
}

.danger-panel {
    padding: 24px 26px;
    border: 1px solid var(--border);
    border-left: 3px solid var(--danger);
    border-radius: 12px;
    background: var(--surface);
}

.danger-title {
    margin-bottom: 10px;
    font-size: 20px;
    font-weight: 600;
    color: var(--danger);
}

.danger-form {
    margin-top: 20px;
}

.danger-label {
    display: block;
    margin-bottom: 10px;
    color: var(--muted);
    font-size: 0.75em;
}

.danger-label code {
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    color: var(--text);
}

.danger-row {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
}

.danger-input {
    flex: 1 1 240px;
    max-width: 320px;
    padding: 11px 16px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--bg);
    color: var(--text);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.8em;
}

.danger-input:focus {
    outline: none;
    border-color: var(--danger);
}

.btn-danger {
    padding: 11px 27px;
    border: 1px solid var(--danger);
    background: transparent;
    color: var(--danger);
    font-size: 0.8em;
    line-height: 1.4;
    transition: background 0.1s, color 0.1s;
}

.btn-danger:hover {
    background: var(--danger);
    color: #fff;
}

/* ── Project page ───────────────────────────────────────────────────── */

.project-page {
    max-width: 1400px;
    margin: 0 auto;
}

/* The first section on the page, so it sits closer to the title than the
   64px the later ones keep between each other. */
.intro-section {
    margin-top: 40px;
}

.intro-section .project-description {
    margin-bottom: 0;
}

.project-page-title {
    display: flex;
    /* Centred on the title's line box: the badge lands around the middle of
       the text rather than sitting down on its baseline. */
    align-items: center;
    flex-wrap: wrap;
    gap: 28px;
    margin-bottom: 16px;
}

.project-page-title h1 {
    margin-bottom: 0;
}

/* The line box has descender room under the baseline, so its centre sits
   lower than the centre of the visible text: lift the badge to match. */
.project-page-title .project-started-badge {
    transform: translateY(-6px);
}

.project-page-header h1 {
    font-size: clamp(32px, 3.5vw, 52px);
    line-height: 1.2;
    margin-bottom: 16px;
}

.project-page-meta {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-bottom: 32px;
}

.project-page-meta .difficulty-dots {
    padding-top: 0;
}

.project-page-meta .project-stat {
    color: var(--muted);
    font-size: 0.8em;
}

/* The other runs of this project: the same project in another language,
   finished or in hand. A tick marks the ones that are done. */
.other-runs {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin-bottom: 32px;
}

.other-run {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 6px 14px;
    border: 1px solid var(--border);
    border-radius: 99px;
    background: var(--surface);
    color: var(--muted);
    font-size: 0.75em;
    text-decoration: none;
    transition: border-color 0.12s, color 0.12s;
}

.other-run:hover {
    border-color: var(--primary);
    color: var(--text);
}

.other-run-done {
    border-color: rgba(63, 185, 80, 0.45);
    color: var(--ok);
}

.other-run-done:hover {
    border-color: var(--ok);
    color: var(--ok);
}

/* The run the page is showing: filled, so the switch reads as "you are here". */
.other-run-current,
.other-run-current:hover {
    border-color: var(--primary);
    background: var(--surface2);
    color: var(--text);
}

.other-run-done.other-run-current,
.other-run-done.other-run-current:hover {
    border-color: var(--ok);
    color: var(--ok);
}

.project-page-actions {
    display: flex;
    justify-content: flex-end;
    margin-bottom: 48px;
}

/* .btn leaves font-size to the browser: pin it, like the dialog's buttons. */
.project-page-actions .btn {
    padding: 11px 27px;
    font-size: 0.8em;
    line-height: 1.4;
}

.project-started-badge {
    display: inline-block;
    padding: 6px 16px;
    border: 1px solid var(--primary);
    border-radius: 999px;
    color: var(--primary);
    font-size: 0.7em;
    font-weight: 600;
    line-height: 1.4;
    white-space: nowrap;
}

.project-description {
    color: var(--muted);
    margin-bottom: 24px;
}

.project-description p {
    margin-bottom: 16px;
}

.project-description img {
    display: block;
    max-width: 100%;
    height: auto;
    margin: 0 0 24px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--bg);
}

/* A lone image in its own paragraph is a figure: give it room to breathe. */
.project-description p > img:only-child {
    margin: 8px auto 28px;
}

.step-description img {
    display: block;
    max-width: 100%;
    height: auto;
    margin: 8px 0 24px;
    border: 1px solid var(--border);
    border-radius: 12px;
}

/* An image alone in its paragraph is rendered as a <figure>: centred, with
   room above and below so it reads as a break in the prose rather than as
   part of the paragraph it follows. */
.project-description figure,
.step-description figure {
    margin: 28px 0 36px;
    text-align: center;
}

/* The figure owns the spacing now, so the image inside it only has to sit in
   the middle: the margins above would otherwise add to the figure's own. */
.project-description figure img,
.step-description figure img {
    margin: 0 auto;
}

.project-description figcaption,
.step-description figcaption {
    margin-top: 10px;
    font-size: 0.75em;
    line-height: 1.5;
    color: var(--muted);
    text-align: center;
}

.project-description strong {
    color: var(--text);
}

.project-description code {
    background: var(--surface2);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 1px 6px;
    font-size: 0.85em;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

/* A fenced block had no rule of its own, so it inherited the inline-code pill:
   a border and a background around every line. */
.project-description pre {
    margin-bottom: 16px;
    padding: 16px 20px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    overflow-x: auto;
}

.project-description pre code {
    background: none;
    border: none;
    padding: 0;
}

/* ── Protocol transcripts ───────────────────────────────────────────── */

/* A ```session block: what the client types keeps the full colour and carries
   a mark in the gutter, what the server answers stays quiet. The mark is drawn
   by CSS and is not part of the text, so copying the block gives back commands
   ready to be pasted. */
pre.session code {
    display: block;
    padding: 0;
    background: none;
    border: none;
    line-height: 1.75;
    font-size: 0.85em;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

.session-line {
    display: block;
    padding-left: 1.6em;
}

.session-line::before {
    content: "";
    display: inline-block;
    width: 1.6em;
    margin-left: -1.6em;
}

.session-sent {
    color: var(--text);
}

.session-sent::before {
    content: "\203A";
    color: var(--primary);
}

.session-recv {
    color: var(--muted);
}

/* ── Syntax highlighting ────────────────────────────────────────────── */

/* Pygments class names, kept short by Pygments itself: c comment, k keyword,
   s string, m number, n name, o operator, p punctuation. Only the fences
   tagged with a language the site teaches get them; everything else stays
   plain text, and punctuation keeps the foreground colour on purpose. */
.syntax .c,
.syntax .ch,
.syntax .cm,
.syntax .c1,
.syntax .cs {
    color: var(--code-comment);
    font-style: italic;
}

.syntax .cp,
.syntax .cpf {
    color: var(--code-preproc);
}

.syntax .k,
.syntax .kc,
.syntax .kd,
.syntax .kn,
.syntax .kp,
.syntax .kr,
.syntax .ow {
    color: var(--code-keyword);
}

.syntax .kt,
.syntax .nb,
.syntax .bp,
.syntax .nc,
.syntax .no {
    color: var(--code-type);
}

.syntax .nf,
.syntax .fm,
.syntax .nd {
    color: var(--code-function);
}

.syntax .s,
.syntax .sa,
.syntax .sb,
.syntax .sc,
.syntax .dl,
.syntax .sd,
.syntax .s2,
.syntax .sh,
.syntax .si,
.syntax .sx,
.syntax .s1,
.syntax .ss {
    color: var(--code-string);
}

/* An escape inside a string is still a string, only worth noticing. */
.syntax .se {
    color: var(--code-string);
    font-weight: 600;
}

.syntax .m,
.syntax .mb,
.syntax .mf,
.syntax .mh,
.syntax .mi,
.syntax .mo,
.syntax .il {
    color: var(--code-number);
}

.syntax .err {
    color: var(--danger);
}

/* An interpreter session: the prompt is furniture and what the interpreter
   answered is not source, so neither competes with the code typed at it. */
.syntax .gp {
    color: var(--muted);
    user-select: none;
}

.syntax .go {
    color: var(--muted);
}

/* A variable is what a makefile is mostly made of, so CC and $(BIN) are worth
   a colour. The rule is scoped: in Common Lisp the same class marks every
   symbol in the file, and colouring all of them would say nothing. */
.language-make .nv,
.language-makefile .nv {
    color: var(--code-type);
}

/* ── Git panel ──────────────────────────────────────────────────────── */

.git-panel {
    margin-top: 64px;
}

.git-hint {
    color: var(--muted);
    font-size: 0.8em;
}

/* The hint shown in place of the submit form, when the project has not
   been started yet: on its own in the section, it reads better centred. */
.git-hint-center {
    text-align: center;
}

.git-panel .copy-box {
    margin-top: 16px;
}

/* A box you click to copy: the whole thing is the button. */
.copy-box {
    display: flex;
    align-items: center;
    gap: 16px;
    width: 100%;
    margin: 16px 0;
    padding: 14px 18px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    text-align: left;
    font: inherit;
    color: var(--text);
    cursor: pointer;
    overflow-x: auto;
    transition: border-color 0.12s, background 0.12s;
}

.copy-box code {
    flex-grow: 1;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.8em;
    white-space: nowrap;
}

/* A command block: every line carries a prompt, drawn by CSS so that it is
   not part of the text. Selecting the block and copying it gives back the
   commands alone, the way the protocol transcripts already do -- and the
   button copies data-copy, which never had a prompt in it. */
.copy-box code .cmd-line {
    display: block;
    padding-left: 1.4em;
}

.copy-box code .cmd-line::before {
    content: "$";
    display: inline-block;
    width: 1.4em;
    margin-left: -1.4em;
    color: var(--primary);
    /* Belt and braces: a browser that would copy generated content still
       cannot select this one. */
    user-select: none;
    -webkit-user-select: none;
}

/* A line that is input to the command above it, not a command of its own:
   it keeps the gutter, so the block stays aligned, but takes no prompt. */
.copy-box code .cmd-cont::before {
    content: "";
}

/* Multi-line payloads keep their line breaks. */
.copy-box-block {
    align-items: flex-start;
}

.copy-box-block code {
    white-space: pre;
    line-height: 1.8;
}

.copy-hint {
    flex-shrink: 0;
    align-self: center;
    color: var(--muted);
    font-size: 0.7em;
    font-weight: 600;
    opacity: 0;
    transition: opacity 0.12s, color 0.12s;
}

.copy-box:hover,
.copy-box:focus-visible {
    border-color: var(--primary);
    outline: none;
}

.copy-box:hover .copy-hint,
.copy-box:focus-visible .copy-hint {
    opacity: 1;
}

/* The moment it lands in the clipboard. */
.copy-box.copied {
    border-color: var(--primary);
    animation: copy-flash 0.5s ease-out;
}

.copy-box.copied .copy-hint {
    opacity: 1;
    color: var(--primary);
}

@keyframes copy-flash {
    from { background: var(--primary-glow); }
    to { background: var(--surface); }
}

@media (prefers-reduced-motion: reduce) {
    .copy-box.copied {
        animation: none;
    }
}

.token-box {
    margin: 16px 0;
    padding: 14px 18px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    overflow-x: auto;
}

.token-box.git-creds {
    display: flex;
    align-items: center;
}

.git-creds-rows {
    display: grid;
    grid-template-columns: max-content auto 1fr;
    column-gap: 10px;
    row-gap: 10px;
    align-items: center;
    flex-grow: 1;
    min-width: 0;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.85em;
}

.copy-box .git-cred-key,
.token-box .git-cred-key {
    flex-grow: 0;
    background: var(--surface2);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 2px 8px;
    color: var(--primary);
    font-size: 1em;
    white-space: nowrap;
}

.git-cred-sep {
    color: var(--muted);
}

.git-cred-val {
    min-width: 0;
    overflow-x: auto;
    white-space: nowrap;
}

.profile-row-block {
    flex-direction: column;
    align-items: stretch;
}

.token-hidden {
    color: var(--muted);
    letter-spacing: 1px;
}

.git-token-warning {
    color: var(--primary);
    font-size: 18px;
}

.git-token-actions {
    display: flex;
    align-items: stretch;
    gap: 12px;
    margin-top: 16px;
}

.git-token-actions .btn {
    font-size: 20px;
}

/* Outline buttons carry their own border, so they need the same box as the
   filled ones to line up next to them. */
.btn-outline,
.btn-quiet {
    padding: 11px 27px;
    background: transparent;
    border: 1px solid var(--border);
    color: var(--muted);
    font-size: 1em;
    line-height: 1.4;
    transition: border-color 0.1s, color 0.1s;
}

/* The evaluate action: the accent colour the rest of the page uses, filled
   only on hover so it does not shout over the results. */
.btn-accent {
    padding: 11px 27px;
    border: 1px solid var(--primary);
    background: var(--primary-glow);
    color: var(--primary);
    line-height: 1.4;
    transition: background 0.1s, color 0.1s;
}

.btn-accent:hover:not([disabled]) {
    background: var(--primary);
    color: #fff;
}

.btn-outline:hover {
    border-color: var(--primary);
    color: var(--primary);
}

.btn-quiet:hover {
    border-color: var(--danger);
    color: var(--danger);
}

/* ── Outline ────────────────────────────────────────────────────────── */

.outline-stats {
    display: flex;
    align-items: center;
    gap: 20px;
    margin-bottom: 32px;
}

.outline-stat {
    display: flex;
    align-items: baseline;
    gap: 10px;
}

.outline-stat-value {
    font-size: 1.6em;
    font-weight: 700;
    color: var(--primary);
}

.outline-stat-label {
    color: var(--muted);
}

.outline-stat-separator {
    color: var(--border);
    font-size: 1.2em;
}

.milestone-list {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.milestone {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    overflow: hidden;
    transition: border-color 0.1s;
}

.milestone[open],
.milestone:hover {
    border-color: var(--primary);
}

.milestone-summary {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 18px 24px;
    cursor: pointer;
    list-style: none;
    user-select: none;
}

.milestone-summary::-webkit-details-marker {
    display: none;
}

.milestone-index {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: var(--surface2);
    border: 1px solid var(--border);
    color: var(--primary);
    font-weight: 700;
    flex-shrink: 0;
}

.milestone-title {
    font-weight: 600;
    flex-grow: 1;
}

.milestone-count {
    color: var(--muted);
    font-size: 0.75em;
    white-space: nowrap;
}

.milestone-chevron {
    width: 10px;
    height: 10px;
    border-right: 2px solid var(--muted);
    border-bottom: 2px solid var(--muted);
    transform: rotate(45deg);
    transition: transform 0.15s;
    flex-shrink: 0;
}

.milestone[open] .milestone-chevron {
    transform: rotate(-135deg);
}

.step-list {
    list-style: none;
    border-top: 1px solid var(--border);
    padding: 8px 24px 16px;
}

.step {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 12px 0 12px 56px;
    border-bottom: 1px solid var(--border);
    position: relative;
}

.step-body {
    display: flex;
    flex-direction: column;
    flex-grow: 1;
}

.step:last-child {
    border-bottom: none;
}

.step::before {
    content: "";
    position: absolute;
    left: 17px;
    top: 22px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--primary);
}

.step-title {
    font-weight: 500;
}

.step-summary {
    color: var(--muted);
    font-size: 0.8em;
}

/* ── Progress tracking ──────────────────────────────────────────────── */

.progress-panel {
    margin-bottom: 32px;
}

.project-outline .progress-panel,
.project-outline .outline-stats {
    margin-top: 24px;
}

/* With the progress panel under them the stats read as its heading rather
   than a block of their own, so the two sit closer together. Both sides are
   set: adjacent margins collapse to the larger of the two. */
.outline-stats:has(+ .progress-panel) {
    margin-bottom: 0;
}

.outline-stats + .progress-panel {
    margin-top: 16px;
}

.progress-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    margin-bottom: 12px;
}

.progress-label {
    font-weight: 600;
}

.progress-value {
    color: var(--muted);
    font-size: 0.8em;
}

.progress-bar {
    height: 8px;
    border-radius: 999px;
    background: var(--surface2);
    border: 1px solid var(--border);
    overflow: hidden;
}

.progress-fill {
    display: block;
    height: 100%;
    border-radius: 999px;
    background: linear-gradient(90deg, var(--primary), var(--primary2));
    box-shadow: 0 0 16px var(--primary-glow);
    transition: width 0.2s;
}

/* Full marks: the bar stops being progress and becomes the result. */
.progress-fill-done {
    background: var(--ok);
    box-shadow: 0 0 16px rgba(63, 185, 80, 0.28);
}

/* A check mark drawn with two borders. */
.check {
    width: 7px;
    height: 13px;
    border-right: 2.5px solid currentColor;
    border-bottom: 2.5px solid currentColor;
    transform: translateY(-2px) rotate(45deg);
}

/* Once the project is started the outline goes quiet: only the step the user
   has to do next stays coloured. */
.milestone-list-tracked .milestone {
    border-color: var(--border);
}

.milestone-list-tracked .milestone-title,
.milestone-list-tracked .step-title {
    color: var(--muted);
}

.milestone-list-tracked .milestone-index {
    color: var(--muted);
}

.milestone-list-tracked .step::before {
    background: transparent;
    border: 2px solid var(--border);
    width: 14px;
    height: 14px;
    left: 14px;
    top: 50%;
    margin-top: -9px;
    box-sizing: border-box;
}

.milestone-done .milestone-index {
    display: flex;
    color: var(--primary);
    border-color: var(--primary);
    background: var(--primary-glow);
}

.milestone-current {
    border-color: var(--primary) !important;
    box-shadow: 0 0 24px var(--primary-glow);
}

.milestone-current .milestone-index {
    color: var(--primary);
    border-color: var(--primary);
}

.milestone-current .milestone-title {
    color: var(--text) !important;
}

.milestone-list-tracked .step-done .step-title {
    color: var(--muted);
}

/* Completed steps say so with a bigger, filled mark — the title is left
   readable, a struck-through one reads as cancelled rather than done. */
.milestone-list-tracked .step-done::before {
    background: var(--primary-glow);
    border-color: var(--primary);
    width: 22px;
    height: 22px;
    left: 10px;
    margin-top: -13px;
    box-shadow: 0 0 12px var(--primary-glow);
}

/* The tick inside a completed step's dot. */
.milestone-list-tracked .step-done::after {
    content: "";
    position: absolute;
    left: 17px;
    top: 50%;
    width: 6px;
    height: 11px;
    margin-top: -9px;
    border-right: 2.5px solid var(--primary);
    border-bottom: 2.5px solid var(--primary);
    transform: rotate(45deg);
}

.milestone-list-tracked .step-current .step-title {
    color: var(--text);
    font-weight: 600;
}

.milestone-list-tracked .step-current .step-summary {
    color: var(--muted);
}

.milestone-list-tracked .step-current::before {
    border-color: var(--primary);
    box-shadow: 0 0 12px var(--primary-glow);
}

.milestone-list-tracked .step-todo {
    opacity: 0.55;
}

.step-body {
    text-decoration: none;
    color: inherit;
}

a.step-body:hover .step-title {
    color: var(--primary) !important;
}

/* ── Evaluation results ────────────────────────────────────────────── */

.step-submit {
    margin: 40px 0;
    padding-top: 32px;
    border-top: 1px solid var(--border);
}

/* Reads along the left margin like the rest of the section; the button below
   it is the one thing centred. */
.eval-hint {
    margin: 28px 0 0;
    color: var(--muted);
    font-size: 0.8em;
}

.eval-actions {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    margin-top: 16px;
}

.eval-actions .btn {
    padding: 18px 56px;
    font-size: 1.05em;
}

.eval-actions .btn[disabled] {
    opacity: 0.5;
    cursor: default;
}

/* The evaluation is a subsection of the submission: it needs separating from
   the git commands above it. */
.eval-subsection {
    margin-top: 56px;
}

/* The one line that says whether there is anything to evaluate: read at a
   glance, so it is bigger than the hints around it. */
.eval-target {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 24px;
    color: var(--text);
    font-size: 0.9em;
}

.eval-target code {
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    color: var(--text);
}

/* Green when there is something to evaluate, red when there is not. */
.eval-target-icon {
    flex-shrink: 0;
    width: 12px;
    height: 12px;
    border-radius: 50%;
}

.eval-target-found .eval-target-icon {
    background: var(--ok);
    box-shadow: 0 0 10px rgba(63, 185, 80, 0.5);
}

.eval-target-missing .eval-target-icon {
    background: var(--danger);
    box-shadow: 0 0 10px rgba(248, 81, 73, 0.4);
}

.eval-target-found code {
    color: var(--ok);
}

.eval-target-missing code {
    color: var(--danger);
}

.eval-message {
    margin-top: 16px;
    color: var(--danger);
    font-size: 0.8em;
}

.eval-panel {
    margin-top: 24px;
    padding: 18px 22px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface);
}

.eval-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}

.eval-status {
    font-weight: 600;
}

.eval-commit {
    color: var(--muted);
    font-size: 0.7em;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

.eval-detail {
    margin-top: 8px;
}

.eval-elapsed {
    color: var(--muted);
    font-size: 0.7em;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    min-width: 4ch;
}

.eval-checks {
    list-style: none;
    margin-top: 16px;
}

.eval-check {
    display: grid;
    grid-template-columns: 24px 1fr auto;
    gap: 4px 8px;
    align-items: baseline;
    padding: 8px 0;
    border-top: 1px solid var(--border);
    font-size: 0.8em;
}

.eval-check-mark {
    font-weight: 700;
    text-align: center;
}

/* A check reads as its own result: green when it passed, red when it did
   not. The name carries the colour too, not just the mark. */
.eval-check-passed .eval-check-mark,
.eval-check-passed .eval-check-name {
    color: var(--ok);
}

.eval-check-failed .eval-check-mark,
.eval-check-failed .eval-check-name {
    color: var(--danger);
}

/* A failed check is the only one that opens, so the hover keeps its colour
   and underlines instead: the pink hover would read as a third state. */
.eval-check-failed button.eval-check-name:hover,
.eval-check-failed button.eval-check-name:focus-visible {
    color: var(--danger);
    text-decoration: underline;
}

.eval-check-pending {
    opacity: 0.5;
}

.eval-check-pending .eval-check-mark {
    color: var(--muted);
}

/* The check being run right now: a small spinner in place of the mark. */
.eval-check-running .eval-check-mark {
    display: inline-block;
    width: 12px;
    height: 12px;
    border: 2px solid var(--border);
    border-top-color: var(--primary);
    border-radius: 50%;
    font-size: 0;
    animation: eval-spin 0.7s linear infinite;
}

.eval-check-running .eval-check-name {
    color: var(--text);
    text-align: left;
}

/* A check with output is a button: click the name to read it. */
button.eval-check-name {
    padding: 0;
    background: none;
    border: none;
    font: inherit;
    color: var(--text);
    cursor: pointer;
}

button.eval-check-name::after {
    content: "›";
    display: inline-block;
    margin-left: 8px;
    color: var(--muted);
    transition: transform 0.15s;
}

button.eval-check-name[aria-expanded="true"]::after {
    transform: rotate(90deg);
}

button.eval-check-name:hover {
    color: var(--primary);
}

@keyframes eval-spin {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    .eval-check-running .eval-check-mark {
        animation-duration: 2.4s;
    }
}

.eval-check-name {
    color: var(--text);
    text-align: left;
}

/* A check with output is a button: click the name to read it. */
button.eval-check-name {
    padding: 0;
    background: none;
    border: none;
    font: inherit;
    color: var(--text);
    cursor: pointer;
}

button.eval-check-name::after {
    content: "›";
    display: inline-block;
    margin-left: 8px;
    color: var(--muted);
    transition: transform 0.15s;
}

button.eval-check-name[aria-expanded="true"]::after {
    transform: rotate(90deg);
}

button.eval-check-name:hover {
    color: var(--primary);
}

.eval-check-optional {
    color: var(--muted);
    font-size: 0.8em;
    margin-left: 8px;
}

.eval-check-time {
    color: var(--muted);
    font-size: 0.85em;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    white-space: nowrap;
}

/* How many of the checks that gate the step are passing, above the button
   that runs them. */
.eval-progress {
    margin-bottom: 24px;
}

.eval-progress .progress-head {
    margin-bottom: 8px;
}

/* The step is done: say so, and point at what comes next. */
.eval-done[hidden] {
    display: none;
}

.eval-done {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px 20px;
    margin-top: 24px;
    padding: 20px 24px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-left: 3px solid var(--ok);
    border-radius: 12px;
}

.eval-done-mark {
    flex-shrink: 0;
    color: var(--ok);
}

.eval-done-text {
    display: flex;
    flex-direction: column;
    flex-grow: 1;
    gap: 2px;
}

.eval-done-text strong {
    color: var(--ok);
}

.eval-done-text span {
    color: var(--muted);
    font-size: 0.8em;
}

/* .btn leaves font-size to the browser: pin it, like the other panels do. */
.eval-done-next {
    flex-shrink: 0;
    padding: 11px 24px;
    font-size: 0.8em;
    line-height: 1.4;
    text-decoration: none;
}

.eval-check-output[hidden] {
    display: none;
}

.eval-check-output {
    grid-column: 2 / -1;
    margin-top: 6px;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
}

/* Each block is a copy-box, so the whole rectangle is the button: it keeps
   the cursor, the hover border, the hint and the flash from .copy-box, and
   overrides the parts sized for a one-line command. */
.eval-check-detail,
.eval-check-expected {
    align-self: stretch;
    align-items: flex-start;
    width: 100%;
    margin: 0;
    padding: 12px 14px;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: 8px;
    color: var(--muted);
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.85em;
    max-height: 320px;
    overflow-y: auto;
}

.eval-check-detail code,
.eval-check-expected code {
    font-size: 1em;
    color: inherit;
    white-space: pre-wrap;
    /* Long single tokens -- a path, a compiler line -- wrap instead of
       pushing the block sideways. */
    overflow-wrap: anywhere;
}

.eval-check-detail .copy-hint,
.eval-check-expected .copy-hint {
    align-self: flex-start;
}

/* copy.js flashes back to --surface; these sit on --bg. */
.eval-check-detail.copied,
.eval-check-expected.copied {
    animation-name: eval-copy-flash;
}

@keyframes eval-copy-flash {
    from { background: var(--primary-glow); }
    to { background: var(--bg); }
}

/* When the check wanted a particular output, the two blocks are read against
   each other: what came out in red, what should have in green. Plain output
   with nothing to compare it to stays muted. */
.eval-check-output-diff .eval-check-detail,
.eval-check-expected {
    border-left-width: 3px;
}

.eval-check-output-diff .eval-check-detail {
    border-left-color: var(--danger);
    color: var(--danger);
}

.eval-check-expected {
    border-left-color: var(--ok);
    color: var(--ok);
}

/* Hover and focus paint the whole border: keep the edge that says which of
   the two blocks this is. */
.eval-check-output-diff .eval-check-detail:hover,
.eval-check-output-diff .eval-check-detail:focus-visible,
.eval-check-output-diff .eval-check-detail.copied {
    border-left-color: var(--danger);
}

.eval-check-expected:hover,
.eval-check-expected:focus-visible,
.eval-check-expected.copied {
    border-left-color: var(--ok);
}

/* The label rides on the block rather than inside its text, so selecting the
   output or copying it never picks the word up. */
.eval-check-output-diff .eval-check-detail code::before,
.eval-check-expected code::before {
    display: block;
    margin-bottom: 6px;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
    font-size: 0.85em;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    opacity: 0.75;
}

.eval-check-output-diff .eval-check-detail code::before {
    content: "ottenuto";
}

.eval-check-expected code::before {
    content: "atteso";
}


/* ── Step page ──────────────────────────────────────────────────────── */

.step-page {
    max-width: 1400px;
    margin: 0 auto;
}

.step-project {
    padding-bottom: 24px;
    border-bottom: 1px solid var(--border);
    margin-bottom: 32px;
}

.step-back {
    display: inline-block;
    color: var(--muted);
    text-decoration: none;
    font-size: 0.75em;
    margin-bottom: 12px;
}

.step-back:hover {
    color: var(--primary);
}

.step-project-title {
    font-size: 1.1em;
    margin-bottom: 12px;
}

.step-project .project-page-meta {
    margin-bottom: 0;
}

/* Previous / next, inside the progress panel: the two ways out of a step
   that are not the outline. The end of the path keeps its slot, greyed and
   inert, so the button one is aiming for never moves sideways. */
.step-nav {
    display: flex;
    gap: 12px;
    margin-top: 16px;
}

.step-nav-link {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 16px;
    border: 1px solid var(--border);
    border-radius: 8px;
    text-decoration: none;
    color: var(--text);
    transition: border-color 0.1s, background 0.1s;
}

.step-nav-next {
    justify-content: flex-end;
    text-align: right;
}

.step-nav-link:not(.step-nav-end):hover {
    border-color: var(--primary);
    background: var(--primary-glow);
}

.step-nav-end {
    color: var(--muted);
    opacity: 0.55;
    cursor: default;
}

.step-nav-arrow {
    color: var(--primary);
    font-size: 1.1em;
    line-height: 1;
}

.step-nav-end .step-nav-arrow {
    color: inherit;
}

.step-nav-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.step-nav-label {
    font-size: 0.7em;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.step-nav-title {
    font-size: 0.85em;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Standing outside the progress panel — a step read without having started —
   the navigation needs the margin the panel would have given it. */
.step-nav-standalone {
    margin-top: 0;
    margin-bottom: 32px;
}

/* The same pair again at the foot of the page, where the step has been read
   and the next one is the likely destination. The rule separates it from
   whatever section ended above it. */
.step-nav-bottom {
    margin-top: 48px;
    padding-top: 32px;
    border-top: 1px solid var(--border);
}

.step-header {
    margin: 40px 0 24px;
}

.step-locator {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 14px;
    margin-bottom: 12px;
    font-size: 0.75em;
    color: var(--muted);
}

.step-milestone {
    color: var(--primary);
    font-weight: 600;
}

.step-state {
    padding: 3px 12px;
    border: 1px solid var(--border);
    border-radius: 999px;
}

.step-state-done {
    border-color: var(--primary);
    background: var(--primary-glow);
    color: var(--primary);
}

.step-state-current {
    border-color: var(--primary);
    color: var(--primary);
}

.step-header h1 {
    font-size: clamp(28px, 3vw, 44px);
    line-height: 1.2;
}

.step-description {
    color: var(--muted);
}

.step-description p,
.step-description pre {
    margin-bottom: 16px;
}

.step-description h2,
.step-description h3 {
    color: var(--text);
    margin: 32px 0 12px;
}

.step-description strong {
    color: var(--text);
}

.step-description code {
    background: var(--surface2);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 1px 6px;
    font-size: 0.85em;
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

.step-description pre {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 16px 20px;
    overflow-x: auto;
}

.step-description pre code {
    background: none;
    border: none;
    padding: 0;
}

/* ── Elenchi nel markdown ───────────────────────────────────────────── */

/* The reset zeroes padding on everything, so a list kept nothing to hang its
   markers in: with list-style outside, the bullet ended up left of the text
   column instead of under it. The indent is in em, so it grows with the body
   text like the rest of the page, and it is wide enough that the list reads as
   a block set in from the paragraph above rather than as prose with dots. */
.project-description ul,
.project-description ol,
.step-description ul,
.step-description ol {
    padding-left: 1.7em;
    margin-bottom: 16px;
}

.project-description li,
.step-description li {
    margin-bottom: 8px;
}

/* An item that holds a code block is a paragraph plus a block, and the last of
   them brings its own bottom margin: without this the gap under it is counted
   twice and the items drift apart. */
.project-description li > :last-child,
.step-description li > :last-child {
    margin-bottom: 0;
}

/* A nested list sits under its parent item, not adrift from it. */
.project-description li > ul,
.project-description li > ol,
.step-description li > ul,
.step-description li > ol {
    margin: 8px 0 0;
}

/* The marker is punctuation, not text: it points at the item without
   competing with it for attention. */
.project-description li::marker,
.step-description li::marker {
    color: var(--primary);
}

/* ── Tabelle nel markdown ───────────────────────────────────────────── */

/* The renderer emits a bare <table>, so the whole look comes from here. Both
   markdown containers are covered: lessons and long-form descriptions alike. */
.project-description table,
.step-description table {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 16px;
    font-size: 0.95em;
}

.project-description th,
.step-description th {
    text-align: left;
    padding: 10px 14px;
    color: var(--text);
    font-weight: 600;
    background: var(--surface2);
    border-bottom: 1px solid var(--border);
}

.project-description td,
.step-description td {
    padding: 10px 14px;
    vertical-align: top;
    border-bottom: 1px solid var(--border);
}

.project-description tbody tr:last-child td,
.step-description tbody tr:last-child td {
    border-bottom: none;
}

/* Narrow screens: a wide table scrolls on its own rather than pushing the
   page sideways. */
@media (max-width: 700px) {
    .project-description table,
    .step-description table {
        display: block;
        overflow-x: auto;
    }
}

@media (max-width: 600px) {
    .step-nav {
        flex-direction: column;
    }

    .step-nav-next {
        justify-content: space-between;
    }
}

/* ── Note e riferimenti ─────────────────────────────────────────────── */

/* `[^nota]` in the prose leaves a progressive number where it is written and
   its definition at the end of the page. Both halves come from the markdown
   footnotes extension, so this styles them wherever markdown is rendered —
   lessons, step and project descriptions alike. */
.footnote-ref {
    color: var(--primary);
    text-decoration: none;
    padding: 0 1px 0 2px;
}

.footnote {
    margin-top: 40px;
    font-size: 0.92em;
    color: var(--muted);
}

.footnote hr {
    border: 0;
    border-top: 1px solid var(--border);
    margin-bottom: 16px;
}

.footnote ol {
    padding-left: 24px;
}

.footnote li {
    margin-bottom: 6px;
}

.footnote p {
    margin-bottom: 0;
}

/* Jumping to a note, or back from one, must not tuck the target under the
   sticky navbar. The sections a link can land on need the same room: starting
   a project sends the reader to the repository, and a heading hidden behind
   the bar reads as the wrong place. */
.footnote li,
sup[id^="fnref:"],
.git-panel,
.project-outline,
.manage-section {
    scroll-margin-top: clamp(86px, 6vw + 16px, 108px);
}

/* ── Git ────────────────────────────────────────────────────────────── */

.page-title {
    font-size: 38px;
    font-weight: 600;
    margin-bottom: 20px;
    max-width: 1000px;
    margin-left: auto;
    margin-right: auto;
}

.profile-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    overflow: hidden;
    margin: 0 auto 20px;
    max-width: 1000px;
}

.profile-row {
    display: flex;
    padding: 18px 24px;
    border-bottom: 1px solid var(--border);
}

.profile-row:last-child {
    border-bottom: none;
}

.profile-label {
    width: 240px;
    color: var(--muted);
    font-size: 24px;
    flex-shrink: 0;
}

.profile-value {
    font-size: 24px;
}

.badge-admin {
    background: var(--primary-glow);
    color: var(--primary);
    border: 1px solid var(--primary);
    padding: 3px 10px;
    border-radius: 4px;
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
}

.profile-note {
    color: var(--muted);
    font-size: 20px;
    max-width: 1000px;
    margin: 0 auto;
}

/* The account tabs share one column width. */
.account-body {
    max-width: 1000px;
    margin: 0 auto;
}

.account-body .profile-card,
.account-body .profile-note {
    max-width: none;
}

.account-body > .profile-note {
    margin-bottom: 24px;
}

.git-guide {
    margin-top: 56px;
}

.git-guide-title {
    margin: 32px 0 10px;
    font-size: 20px;
    font-weight: 600;
}

.git-guide-title:first-of-type {
    margin-top: 0;
}

.git-guide .git-hint {
    font-size: 20px;
}

.git-guide code {
    font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.9em;
    color: var(--text);
}

.profile-note + .profile-note {
    margin-top: 6px;
}

@media (max-width: 600px) {
    .profile-row {
        padding: 12px 16px;
    }

    .profile-label {
        width: 100px;
        font-size: 14px;
    }

    .profile-value {
        font-size: 14px;
    }
}

/* ── Error page ─────────────────────────────────────────────────────── */

.error-page {
    text-align: center;
    padding: 80px 0;
}

.error-page h1 {
    font-size: clamp(32px, 4vw, 56px);
    margin-bottom: 16px;
}

.error-page p {
    color: var(--muted);
    margin-bottom: 32px;
}

/* ── Footer ─────────────────────────────────────────────────────────── */

.footer {
    border-top: 1px solid var(--border);
    padding: 48px clamp(32px, 4vw, 80px);
    margin-top: 80px;
    background: var(--surface);
    /* Above the landing-page glows, which sit at z-index -1. */
    position: relative;
    z-index: 1;
}

.footer-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 40px;
    max-width: 100%;
    margin: 0 auto;
    justify-items: center;
}

.footer-col {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.footer-col h4 {
    font-size: 20px;
    font-weight: 600;
    margin-bottom: 8px;
}

.footer-col p,
.footer-col a {
    font-size: 14px;
    color: var(--muted);
    text-decoration: none;
    line-height: 1.6;
}

.footer-col a:hover {
    color: var(--primary);
}

.footer-email {
    color: var(--primary) !important;
}

.footer-credit {
    text-align: center;
    color: var(--muted);
    font-size: 13px;
    margin-top: 40px;
    max-width: 1200px;
    margin-left: auto;
    margin-right: auto;
}

@media (max-width: 600px) {
    .footer-grid {
        grid-template-columns: 1fr;
        gap: 32px;
        justify-items: start;
    }
}


/* ── Work in progress ───────────────────────────────────────────────── */

/* Something that exists but is not open yet. It stays on the page, at full
   size, so the offering is visible — only the way in is missing. Yellow does
   the talking: it is the one colour on the site that is neither the accent
   (available) nor the danger red (broken). */

.wip-badge {
    display: inline-flex;
    align-items: center;
    align-self: flex-start;
    gap: 7px;
    padding: 4px 12px 4px 10px;
    border: 1px solid var(--wip);
    border-radius: 999px;
    background: var(--wip-soft);
    color: var(--wip);
    font-size: 0.7em;
    font-weight: 600;
    letter-spacing: 0.03em;
    white-space: nowrap;
}

.wip-badge-icon {
    width: 17px;
    height: 17px;
    fill: currentColor;
}

/* The card keeps its shape and loses its affordances: dashed edge, no hover,
   no pointer. The content dims a little, the badge does not. */
.card-wip {
    position: relative;
    overflow: hidden;
    border-style: dashed;
    border-color: rgba(234, 179, 8, 0.35);
    cursor: default;
}

.card-wip:hover {
    border-color: rgba(234, 179, 8, 0.35);
    transform: none;
    box-shadow: none;
}

/* In development a WIP card is a link: the dashed edge and the badge still
   say it is unfinished, but it can be opened. */
a.card-wip {
    cursor: pointer;
}

a.card-wip:hover {
    border-color: rgba(234, 179, 8, 0.7);
    box-shadow: 0 0 24px var(--wip-soft);
}

/* A run of hazard tape along the top edge, and it moves: the card is not
   merely closed, something is going on behind it. The stripes are drawn at 45
   degrees, so one tile is 16px along the gradient and 16 / cos(45°) = 22.63px
   across — shifting by exactly that much loops seamlessly. */
.card-wip::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    background: repeating-linear-gradient(
        45deg,
        var(--wip) 0 8px,
        rgba(234, 179, 8, 0.15) 8px 16px
    );
    box-shadow: 0 0 12px rgba(234, 179, 8, 0.45);
    opacity: 0.8;
    animation: wip-tape 1.6s linear infinite;
}

@keyframes wip-tape {
    from { background-position: 0 0; }
    to   { background-position: 22.63px 0; }
}

/* Motion is decoration here: the dashed border and the badge already say it. */
@media (prefers-reduced-motion: reduce) {
    .card-wip::before {
        animation: none;
    }
}

.card-wip > *:not(.wip-badge) {
    opacity: 0.55;
}

/* The same mark where there are no cards to put it on: a section that is
   entirely still to come. */
.wip-note {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-top: 24px;
    color: var(--muted);
}
