/* Base styles, tokens and shared patterns (ADR 0001). Page-specific rules go in the pages layer. */
@layer reset, base, layout, patterns, pages;

/* Dark only for now: light mode is one @media (prefers-color-scheme: light) block that redefines the colour tokens. */
:root {
    color-scheme: dark;

    --bg: oklch(0.185 0.004 20);
    --surface: oklch(0.23 0.005 20);
    --line: oklch(0.32 0.006 20);
    --control: oklch(0.53 0.008 20);
    --text: oklch(0.96 0.004 20);
    --muted: oklch(0.70 0.008 20);
    --accent: oklch(0.55 0.19 18);
    /* Darker, not lighter, so --on-accent text keeps AA contrast on hover. */
    --accent-hover: oklch(0.49 0.19 18);
    --accent-text: oklch(0.74 0.15 18);
    --on-accent: oklch(0.99 0 0);
    --error: oklch(0.78 0.13 45);
    --focus: oklch(0.80 0.12 18);

    --font: system-ui, -apple-system, "Segoe UI Variable Text", "Segoe UI", Roboto, sans-serif;
    --text-s: 0.9375rem;
    --text-m: 1.0625rem;
    --text-l: 1.25rem;
    --text-xl: clamp(1.875rem, 1.5rem + 1.8vw, 2.5rem);
    --text-hero: clamp(3rem, 2rem + 7vw, 5rem);

    --space-2xs: 0.25rem;
    --space-xs: 0.5rem;
    --space-s: 0.75rem;
    --space-m: 1rem;
    --space-l: 1.5rem;
    --space-xl: 1.75rem;
    --gutter: clamp(1rem, 4vw, 2rem);
    --column: 36rem;

    --radius: 0.75rem;
    --control-size: 2.75rem;
    --ease: cubic-bezier(0.2, 0, 0, 1);
    --duration: 150ms;
}

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

    * {
        margin: 0;
    }

    html {
        -webkit-text-size-adjust: none;
        text-size-adjust: none;
    }

    body {
        min-block-size: 100dvh;
    }

    img, svg {
        display: block;
        max-inline-size: 100%;
    }

    input, button, textarea, select {
        font: inherit;
        color: inherit;
    }

    ul, ol, menu {
        padding: 0;
        list-style: none;
    }

    h1, h2, h3 {
        text-wrap: balance;
    }

    p {
        text-wrap: pretty;
    }

    [hidden] {
        display: none !important;
    }

    @media (prefers-reduced-motion: reduce) {
        *, *::before, *::after {
            animation-duration: 1ms !important;
            animation-delay: 0s !important;
            animation-iteration-count: 1 !important;
            transition-duration: 1ms !important;
            transition-delay: 0s !important;
        }
    }
}

@layer base {
    body {
        background: var(--bg);
        color: var(--text);
        font-family: var(--font);
        font-size: var(--text-m);
        line-height: 1.5;
        -webkit-font-smoothing: antialiased;
    }

    h1 {
        font-size: var(--text-xl);
        font-weight: 750;
        line-height: 1.1;
        letter-spacing: -0.025em;
    }

    h2 {
        font-size: var(--text-l);
        font-weight: 650;
        line-height: 1.25;
        letter-spacing: -0.01em;
    }

    a {
        color: var(--accent-text);
        text-underline-offset: 0.2em;
    }

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

    label {
        display: block;
        font-weight: 600;
        margin-block-end: var(--space-xs);
    }

    input {
        inline-size: 100%;
        min-block-size: var(--control-size);
        padding-inline: var(--space-s);
        /* At least 16px, or iOS zooms in on focus. */
        font-size: max(1rem, var(--text-m));
        background: var(--surface);
        border: 1px solid var(--control);
        border-radius: var(--radius);
        transition: border-color var(--duration) var(--ease);

        &::placeholder {
            color: var(--muted);
            opacity: 1;
        }

        &:hover {
            border-color: var(--muted);
        }

        &:focus-visible {
            outline-offset: 0;
            border-color: var(--focus);
        }

        &:user-invalid, &[aria-invalid="true"] {
            border-color: var(--error);
        }

        &[readonly] {
            color: var(--muted);
            text-overflow: ellipsis;
        }
    }

    button {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: var(--space-xs);
        min-block-size: var(--control-size);
        padding-inline: var(--space-l);
        font-weight: 600;
        white-space: nowrap;
        background: var(--surface);
        border: 1px solid var(--control);
        border-radius: 999px;
        cursor: pointer;
        transition: background-color var(--duration) var(--ease), border-color var(--duration) var(--ease);

        &:hover {
            background: var(--line);
        }

        &:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

        & svg {
            inline-size: 1.25rem;
            block-size: 1.25rem;
            flex: none;
        }
    }

    /* The primary action: a form's submit, and the buttons that act as one without a form (lock, confirming the unlock,
       reloading after an error). */
    button[type="submit"], #lock, #confirm-unlock, #blazor-error-ui .reload {
        color: var(--on-accent);
        background: var(--accent);
        border-color: var(--accent);

        &:hover:not(:disabled) {
            background: var(--accent-hover);
            border-color: var(--accent-hover);
        }
    }

    /* A card over the page, faded in: popovers, and the dialog Blazor shows while the connection is down. */
    [popover], dialog {
        margin: auto;
        inline-size: min(100% - 2 * var(--gutter), 24rem);
        padding: var(--space-l);
        color: var(--text);
        background: var(--surface);
        border: 1px solid var(--control);
        border-radius: calc(var(--radius) * 1.5);
        opacity: 0;
        translate: 0 0.5rem;
        transition: opacity 200ms var(--ease), translate 200ms var(--ease), overlay 200ms allow-discrete, display 200ms allow-discrete;

        &:popover-open, &[open] {
            opacity: 1;
            translate: 0 0;

            @starting-style {
                opacity: 0;
                translate: 0 0.5rem;
            }
        }

        &::backdrop {
            background: oklch(0 0 0 / 0.6);
        }

        & > * + * {
            margin-block-start: var(--space-m);
        }

        & menu {
            margin-block-start: var(--space-l);
        }
    }

    /* A row of actions, the main one last and on the right, and on top once they wrap. */
    menu {
        display: flex;
        flex-wrap: wrap-reverse;
        justify-content: flex-end;
        gap: var(--space-xs);
    }

    .visually-hidden {
        position: absolute;
        inline-size: 1px;
        block-size: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }
}

@layer layout {
    /* One centred column, no breakpoints. */
    body {
        display: flex;
        flex-direction: column;
        padding-inline: max(var(--gutter), env(safe-area-inset-left)) max(var(--gutter), env(safe-area-inset-right));
        padding-block-end: max(var(--space-xl), env(safe-area-inset-bottom));
    }

    body > header, body > main {
        inline-size: 100%;
        max-inline-size: var(--column);
        margin-inline: auto;
    }

    /* The mark small in the top left, next to the wordmark. */
    body > header {
        padding-block: max(var(--space-m), env(safe-area-inset-top)) var(--space-l);

        & a {
            display: inline-flex;
            align-items: center;
            gap: var(--space-xs);
            min-block-size: var(--control-size);
            color: var(--text);
            font-weight: 700;
            letter-spacing: -0.01em;
            text-decoration: none;
        }

        /* The mark's three colour channels: outlines, ribbon and bow, pupils and the lid's face. */
        & svg {
            inline-size: 2.25rem;
            block-size: 2.25rem;
            stroke: var(--text);
            color: var(--accent);
            fill: var(--bg);
        }
    }

    /* The pages people land on first (create, and the share page before the reveal) introduce the app with a big mark.
       The share page keeps its reveal form, hidden, once there is nothing left to pick. */
    body:has(#create-group, #reveal:not([hidden]), #waiting, #all-revealed) {
        & > header {
            display: flex;
            justify-content: center;
            padding-block-start: max(var(--space-xl), env(safe-area-inset-top));

            & a {
                flex-direction: column;
                gap: var(--space-xs);
                font-size: var(--text-l);
            }

            & svg {
                inline-size: clamp(7rem, 5rem + 10vw, 9rem);
                block-size: clamp(7rem, 5rem + 10vw, 9rem);
            }
        }

        & main > hgroup {
            text-align: center;
        }
    }

    main {
        display: flex;
        flex-direction: column;
        gap: var(--space-xl);

        & > hgroup p {
            margin-block-start: var(--space-xs);
            color: var(--muted);
            font-size: clamp(var(--text-m), 0.9rem + 0.6vw, var(--text-l));
            line-height: 1.4;
        }
    }

    section {
        display: flex;
        flex-direction: column;
        gap: var(--space-s);

        & > p {
            color: var(--muted);
        }
    }

    /* A form row: input and button on one line, the button wraps under the input only when there is no room. */
    form > div {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-xs);

        & > input {
            flex: 999 1 9rem;
            inline-size: auto;
            min-inline-size: 0;
        }

        & > button {
            flex: 1 0 auto;
        }
    }

    .validation-message {
        margin-block: calc(-1 * var(--space-2xs)) var(--space-xs);
        color: var(--error);
        font-size: var(--text-s);
    }

    /* Blazor's reconnect overlay: Blazor sets the connection's state as a class on the dialog, which shows the
       matching text and buttons. */
    #components-reconnect-modal {
        /* Focused when it opens with no button to show; the dialog itself is nothing to operate. */
        &:focus-visible {
            outline: none;
        }

        & > p, & li {
            display: none;
        }

        &.components-reconnect-show:not(.components-reconnect-retrying) #reconnect-first-attempt,
        &.components-reconnect-retrying #reconnect-repeated-attempt,
        &.components-reconnect-failed #reconnect-failed,
        &.components-reconnect-paused #reconnect-paused,
        &.components-reconnect-resume-failed #reconnect-resume-failed {
            display: block;
        }

        &.components-reconnect-failed li:has(#components-reconnect-button),
        &:is(.components-reconnect-paused, .components-reconnect-resume-failed) li:has(#components-resume-button) {
            display: list-item;
        }

        &:not(.components-reconnect-failed, .components-reconnect-paused, .components-reconnect-resume-failed) menu {
            display: none;
        }

        & > p {
            color: var(--muted);
        }
    }

    /* Blazor's error bar: a card at the bottom of the screen. Blazor shows it by setting display: block on it. */
    #blazor-error-ui {
        display: none;
        position: fixed;
        inset-inline: 0;
        inset-block-end: 0;
        inline-size: min(100% - 2 * var(--gutter), var(--column));
        margin: 0 auto max(var(--gutter), env(safe-area-inset-bottom));
        padding: var(--space-m) var(--space-l);
        background: var(--surface);
        border: 1px solid var(--control);
        border-radius: calc(var(--radius) * 1.5);

        & menu {
            margin-block-start: var(--space-s);
        }
    }
}

@layer patterns {
    /* A link the organizer copies or shares: the share link and the manage link. One row at every width. */
    .copyable-link {
        display: flex;
        gap: var(--space-xs);

        & > input {
            flex: 1;
            min-inline-size: 0;
        }

        & > button {
            inline-size: var(--control-size);
            padding: 0;
        }

        /* A copy button holds the copy icon, then the check it swaps to while [data-copied] is set. */
        & [data-copy] > svg + svg,
        & [data-copied] > svg:first-child {
            display: none;
        }

        & [data-copied] {
            color: var(--accent-text);

            & > svg + svg {
                display: block;
            }
        }
    }
}

@layer pages {
    /* Create */
    #steps {
        counter-reset: step;
        display: flex;
        flex-direction: column;
        gap: var(--space-m);

        & li {
            counter-increment: step;
            display: grid;
            grid-template-columns: 2rem 1fr;
            gap: var(--space-s);
            color: var(--muted);

            &::before {
                content: counter(step);
                display: grid;
                place-items: center;
                inline-size: 2rem;
                block-size: 2rem;
                font-size: var(--text-s);
                font-weight: 700;
                color: var(--accent-text);
                border: 1px solid var(--line);
                border-radius: 50%;
            }
        }

        & strong {
            display: block;
            color: var(--text);
            font-weight: 600;
        }
    }

    /* Manage */
    #manage-link {
        gap: var(--space-xs);
        padding: var(--space-m);
        border: 1px solid var(--line);
        border-radius: calc(var(--radius) * 1.5);

        & h2 {
            font-size: var(--text-m);
        }

        & p {
            font-size: var(--text-s);
        }

        & input {
            background: var(--bg);
        }
    }

    /* The label's own margin spaces it from the link row, as in a form. */
    #share-link {
        gap: 0;
    }

    #participants > ul {
        border: 1px solid var(--line);
        border-radius: var(--radius);

        & li {
            display: flex;
            align-items: center;
            gap: var(--space-s);
            min-block-size: 3rem;
            padding-inline: var(--space-m);

            & + li {
                border-block-start: 1px solid var(--line);
            }

            & > span:first-child {
                flex: 1;
                font-weight: 500;
            }

            & svg {
                inline-size: 1.25rem;
                block-size: 1.25rem;
            }

            & [data-revealed="true"] {
                color: var(--accent-text);
            }

            & [data-revealed="false"] {
                color: var(--muted);
            }
        }
    }

    #lock, #unlock {
        align-self: flex-start;
    }

    #lock-hint {
        font-size: var(--text-s);
    }

    /* Share: each name opens to confirm before the reveal. */
    #reveal ul {
        display: flex;
        flex-direction: column;
        gap: var(--space-xs);
    }

    #reveal details {
        background: var(--surface);
        border: 1px solid var(--control);
        border-radius: var(--radius);
        transition: border-color var(--duration) var(--ease);

        &:hover {
            border-color: var(--muted);
        }

        &[open] {
            border-color: var(--accent);
        }

        & summary {
            display: flex;
            align-items: center;
            min-block-size: 3.25rem;
            padding-inline: var(--space-m);
            font-weight: 600;
            list-style: none;
            cursor: pointer;
            border-radius: var(--radius);

            &::-webkit-details-marker {
                display: none;
            }
        }

        /* The confirmation and its submit button. */
        & > div {
            display: flex;
            flex-direction: column;
            gap: var(--space-m);
            padding: 0 var(--space-m) var(--space-m);
            animation: rise 200ms var(--ease);

            & p {
                color: var(--muted);
            }

            & button {
                align-self: stretch;
            }
        }
    }

    #waiting, #all-revealed {
        padding: var(--space-xl) var(--space-l);
        text-align: center;
        color: var(--muted);
        border: 1px dashed var(--line);
        border-radius: calc(var(--radius) * 1.5);
    }

    /* The reveal: the mark large above the name, which un-blurs once. */
    #assignment {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: var(--space-l);
        padding-block: var(--space-xl);
        text-align: center;

        & svg {
            inline-size: 7rem;
            block-size: 7rem;
            stroke: var(--text);
            color: var(--accent);
            fill: var(--bg);
        }

        & h1 {
            font-size: var(--text-l);
            font-weight: 500;
            color: var(--muted);
            letter-spacing: 0;
        }

        & strong {
            display: block;
            margin-block-start: var(--space-xs);
            font-size: var(--text-hero);
            font-weight: 800;
            line-height: 1;
            letter-spacing: -0.04em;
            color: var(--accent-text);
            overflow-wrap: anywhere;
            animation: unmask 900ms var(--ease) 150ms both;
        }

        & p {
            color: var(--muted);
            max-inline-size: 22rem;
        }
    }

    @keyframes rise {
        from {
            opacity: 0;
            translate: 0 -0.25rem;
        }
    }

    @keyframes unmask {
        from {
            opacity: 0;
            filter: blur(12px);
            scale: 0.94;
        }
    }
}
