/*
 * Everything every page shares: the palette, the base elements, the header, and the
 * few form pieces more than one feature uses. A feature's own rules live beside its
 * page in css/<feature>.css, linked from that page's head.
 */


/* --- tokens ----------------------------------------------------------------- */

/* WaveFront's dark appearance, by the colorset each value comes from. Dark only:
   the app's light side is a different palette, not a lighter one, and the pages
   would need their own to match it. */
:root {
    color-scheme: dark;

    --accent: #ff0035;                /* AccentColor, LogoBar */
    --accent-soft: rgb(255 0 53 / .14);
    --bg: #141414;                    /* ListBackground */
    --card: #1c1c1e;                  /* ListRowBackground */
    --bar: #222222;                   /* NavigationBackground */
    --inset: #0e0e0e;                 /* TimelineBackground */
    --ruler: #161616;                 /* RulerBackground */
    --track: #3a3a3c;                 /* ModulationPlaceholder */
    --edge: #2c2c2e;                  /* between TextTertiary and the card */
    --text: #d1d1d8;                  /* TextPrimary */
    --muted: #98989d;                 /* LogoText. TextSecondary fails contrast on a card */
    --faint: #68686a;                 /* TextSecondary: decoration and disabled, never copy */
    --ok: #30d158;
    --warn: #ff9f0a;

    --font: -apple-system, BlinkMacSystemFont, system-ui, "Segoe UI", sans-serif;
    --mono: ui-monospace, "SF Mono", Menlo, monospace;
    --radius: 4px;
    --measure: 46rem;
    --frame: 80rem;
    --frame-padding: 2rem;
    --gutter: 1rem;
    --header: 56px;
}


/* --- base ------------------------------------------------------------------- */

* {
    box-sizing: border-box;
}

/* The attribute wins over any display rule: the scripts hide and show by it. */
[hidden] {
    display: none !important;
}

/* A page can be long or short, so the scrollbar comes and goes between them and
   between a library's tabs; its room is kept everywhere, so nothing centred on the
   page shifts sideways when it does, the account head least of all. */
html {
    height: 100%;
    scrollbar-gutter: stable;
}

body {
    min-height: 100%;
    margin: 0;
    font: 15px/1.5 var(--font);
    color: var(--text);
    background: var(--bg);
    -webkit-font-smoothing: antialiased;
}

/* Every page shares one frame, so its edges stand in the same place on each and
   moving between them changes only what is inside. Reading inside it keeps to the
   measure; a page that is only a form or prose narrows main itself. */
main {
    max-width: var(--frame);
    margin: 0 auto;
    padding: 2rem var(--frame-padding) 4rem;

    @media (width < 52rem) {
        padding-left: var(--gutter);
        padding-right: var(--gutter);
    }
}

/* Prose, read top to bottom: the measure, not the frame. */
main.terms {
    max-width: var(--measure);
}

h1 {
    font-size: 1.4rem;
    margin: 0 0 .25rem;
}

h2 {
    font-size: 1.05rem;
    margin: 2rem 0 .5rem;
}

h3 {
    font-size: .95rem;
    margin: 1.25rem 0 .5rem;
}

a {
    color: var(--text);

    &:hover {
        color: #ffffff;
    }
}

.lede {
    color: var(--muted);
    margin-top: 0;
}

.hint {
    color: var(--muted);
    font-size: .85rem;
}

.warning {
    color: var(--warn);
}

.error {
    color: var(--accent);
    border-left: 3px solid var(--accent);
    padding-left: .7rem;
}

/* A status line a script fills only when something goes wrong. */
.error:empty {
    display: none;
}

/* A page's status line, empty until a request has something to say. Out of the flow
   while empty, so it puts no margins or gap of its own between what is either side;
   not hidden, because a live region has to be there before the words arrive. */
#status:empty {
    position: absolute;
    margin: 0;
}

/* The element whose request is in flight, until the answer is swapped in. */
.htmx-request {
    cursor: progress;
}

output {
    font-variant-numeric: tabular-nums;
}


/* --- controls --------------------------------------------------------------- */

button,
a.button,
label.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .5rem;
    height: 40px;
    padding: 0 1rem;
    font: inherit;
    font-weight: 500;
    color: var(--text);
    background: var(--card);
    border: 1px solid var(--edge);
    border-radius: var(--radius);
    text-decoration: none;
    cursor: pointer;

    &:hover:not(:disabled) {
        border-color: var(--track);
        color: #ffffff;
    }

    &:disabled {
        opacity: .4;
        cursor: default;
    }

    /* Red with near-black text: white on the accent fails contrast, this passes. */
    &.primary {
        background: var(--accent);
        border-color: var(--accent);
        color: var(--bg);
        font-weight: 600;
    }

    &.small {
        height: 32px;
        padding: 0 .75rem;
        font-size: .85rem;
    }

    &.danger {
        color: var(--accent);
    }
}

input[type=text],
input[type=number],
input[type=password],
select {
    height: 40px;
    padding: 0 .65rem;
    font: inherit;
    color: var(--text);
    background: var(--inset);
    border: 1px solid var(--edge);
    border-radius: var(--radius);

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

/* The browser's chevron sits flush against the edge; this one keeps its distance.
   A data URI rather than a file: same origin, and one fewer request. */
select {
    appearance: none;
    padding-right: 2.25rem;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2398989d' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right .7rem center;
    background-size: 14px;
}

input[type=number] {
    font-family: var(--mono);
    font-variant-numeric: tabular-nums;
    text-align: right;
}

input[type=checkbox] {
    width: 18px;
    height: 18px;
    margin: 0;
    accent-color: var(--accent);
}

/* The fill left of the thumb is --fill, set by whoever moves the value. Without it
   the track is plain, which is still a slider. */
input[type=range] {
    appearance: none;
    width: 100%;
    height: 4px;
    margin: 8px 0;
    border-radius: 2px;
    background: linear-gradient(to right, var(--accent) var(--fill, 0%), var(--track) var(--fill, 0%));

    &::-webkit-slider-thumb {
        appearance: none;
        width: 18px;
        height: 18px;
        border: 0;
        border-radius: 50%;
        background: #ffffff;
        box-shadow: 0 1px 3px rgb(0 0 0 / .6);
    }

    &::-moz-range-thumb {
        width: 18px;
        height: 18px;
        border: 0;
        border-radius: 50%;
        background: #ffffff;
        box-shadow: 0 1px 3px rgb(0 0 0 / .6);
    }

    &::-moz-range-track {
        background: transparent;
    }

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

.controls {
    display: flex;
    gap: .5rem;
    align-items: center;
    flex-wrap: wrap;
}

/* Pushes whatever follows it to the far end of a flex row. */
.spacer {
    margin-left: auto;
}

/* A short form: one column of labelled fields. */
.stack {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    max-width: 26rem;

    label {
        display: flex;
        flex-direction: column;
        gap: .3rem;
    }
}

.badge {
    display: inline-flex;
    align-items: center;
    height: 20px;
    padding: 0 .45rem;
    font-size: .7rem;
    font-weight: 600;
    letter-spacing: .02em;
    color: var(--muted);
    background: var(--inset);
    border: 1px solid var(--edge);
    border-radius: var(--radius);

    &.accent {
        color: var(--accent);
        background: var(--accent-soft);
        border-color: transparent;
    }
}

h2.danger {
    color: var(--accent);
}


/* --- header ----------------------------------------------------------------- */

/* Named for the transition between pages, like the account head: it holds still
   while the page under it fades. */
header.site {
    view-transition-name: site-header;
    position: sticky;
    top: 0;
    z-index: 20;
    display: flex;
    align-items: center;
    gap: 1.5rem;
    height: var(--header);
    padding: 0 2rem;
    background: var(--bar);
    border-bottom: 1px solid var(--edge);

    .brand {
        display: flex;
        align-items: center;
        gap: .6rem;
        font-weight: 700;
        letter-spacing: -.01em;
        text-decoration: none;

        .mark {
            width: 22px;
            height: 22px;
            fill: var(--accent);
        }
    }

    nav {
        display: flex;
        align-self: stretch;
    }

    /* Sign in sits apart from the nav but is marked the same way on its own page. */
    nav a,
    .signin {
        position: relative;
        display: inline-flex;
        align-items: center;
        padding: 0 .9rem;
        font-size: .9rem;
        font-weight: 500;
        color: var(--muted);
        text-decoration: none;

        &:hover {
            color: var(--text);
        }

        &[aria-current=page] {
            color: #ffffff;
            font-weight: 600;
        }

        /* Its own element rather than the link's shadow, so the transition between
           pages can carry it from one link to another. */
        .underline {
            position: absolute;
            inset: auto 0 0;
            height: 2px;
            background: var(--accent);
            view-transition-name: nav-underline;
        }
    }

    .signin {
        align-self: stretch;
        margin-left: auto;
    }

    .signin + .menu,
    .admin + .menu {
        margin-left: 0;
    }

    .menu {
        margin-left: auto;
    }

    @media (width < 52rem) {
        padding: 0 .75rem 0 var(--gutter);

        nav,
        .signin {
            display: none;
        }

        /* With the sign-in link gone, the menu is what sits at the far end. */
        .signin + .menu {
            margin-left: auto;
        }

        /* Anchored to the header rather than the button: the button sits at the
           right edge and a dropdown hung from it would leave the screen. */
        .menu .items {
            position: fixed;
            left: .5rem;
            right: .5rem;
            top: calc(var(--header) + 4px);
        }
    }
}


/* --- menu ------------------------------------------------------------------- */

/* A details element dressed as a dropdown. Open state is the browser's; nothing here
   needs a script, and the items are plain links and one form. */
.menu {
    position: relative;

    > summary {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 44px;
        height: 44px;
        list-style: none;
        border-radius: var(--radius);
        cursor: pointer;

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

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

        svg {
            width: 22px;
            height: 22px;
            fill: none;
            stroke: currentColor;
            stroke-width: 2;
            stroke-linecap: round;
        }
    }

    .avatar {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 32px;
        height: 32px;
        border-radius: 50%;
        background: var(--colour, var(--track));
        color: #ffffff;
        font-size: .85rem;
        font-weight: 600;
    }

    .items {
        position: absolute;
        right: 0;
        top: calc(100% + 6px);
        z-index: 10;
        display: flex;
        flex-direction: column;
        gap: 2px;
        min-width: 220px;
        padding: .5rem;
        background: var(--card);
        border: 1px solid var(--edge);
        border-radius: var(--radius);
        box-shadow: 0 16px 40px rgb(0 0 0 / .55);

        a,
        button {
            display: flex;
            align-items: center;
            height: 40px;
            padding: 0 .9rem;
            font-size: .9rem;
            font-weight: 500;
            text-decoration: none;
            background: none;
            border: 0;
            border-radius: var(--radius);
            justify-content: flex-start;
            width: 100%;

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

        .who {
            padding: .5rem .9rem .35rem;
            font-size: .8rem;
            color: var(--muted);
            border-bottom: 1px solid var(--edge);
            margin-bottom: .25rem;
        }

        form {
            margin: 0;
        }

        /* The nav's copy for narrow screens, where the inline one is hidden. */
        .narrow {
            display: none;
        }

        @media (width < 52rem) {
            .narrow {
                display: flex;
            }

            .narrow:last-of-type {
                border-bottom: 1px solid var(--edge);
                border-radius: 0;
                margin-bottom: .25rem;
            }
        }
    }

    /* Slides down from the header at the room drawer's pace. The clip's sides and
       bottom stand outside the box so the shadow survives once it is open. Closing
       keeps the content rendered through the slide where the browser can. */
    @media (prefers-reduced-motion: no-preference) {
        &::details-content {
            transition: content-visibility .35s allow-discrete;
        }

        .items {
            clip-path: inset(0 -4rem -5rem);
            transition: clip-path .35s ease;

            @starting-style {
                clip-path: inset(0 -4rem 100%);
            }
        }

        &:not([open]) .items {
            clip-path: inset(0 -4rem 100%);
        }
    }
}


/* --- panels ----------------------------------------------------------------- */

/* A page's heading and what it says, above its panel. */
.intro {
    display: flex;
    align-items: flex-end;
    gap: 1rem;
    flex-wrap: wrap;

    > div {
        flex: 1;
        min-width: 14rem;
    }

    h1 {
        font-size: 1.6rem;
    }

    .lede {
        margin: 0;
    }
}

/* Everything under the heading, framed as the session page's panels are: tabs and a
   search along its head, then the page's contents. The library, People and the
   feed share it. */
.panel {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
    padding: 0 1.5rem 1.5rem;
    background: var(--card);
    border: 1px solid var(--edge);
    border-radius: var(--radius);

    @media (max-width: 640px) {
        padding: 0 1rem 1rem;
    }
}

/* The tabs as a strip along the panel's top, underlined where they stand, and the
   search at its far end above the same rule. */
.panel-head {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
    min-height: 52px;
    border-bottom: 1px solid var(--edge);

    .tabs {
        display: flex;
        align-self: stretch;
        gap: 0;
        flex-wrap: nowrap;

        a {
            display: inline-flex;
            align-items: center;
            gap: .4rem;
            height: 52px;
            padding: 0 1rem;
            font-size: .9rem;
            font-weight: 500;
            text-decoration: none;
            color: var(--muted);
            border: 0;
            border-radius: 0;
            background: none;
            box-shadow: inset 0 -2px 0 transparent;
            transition: color .15s ease, box-shadow .15s ease;

            &:hover {
                color: var(--text);
            }

            /* The one clicked is marked at once; the one it leaves gives the mark up. */
            &[aria-current],
            &.htmx-request {
                font-weight: 600;
                color: #ffffff;
                background: none;
                box-shadow: inset 0 -2px 0 var(--accent);
            }
        }

        &:has(a.htmx-request) a[aria-current]:not(.htmx-request) {
            font-weight: 500;
            color: var(--muted);
            box-shadow: inset 0 -2px 0 transparent;
        }

        .count {
            font-family: var(--mono);
            font-size: .7rem;
            color: var(--muted);
        }
    }

    /* On a phone the tabs keep the full width and scroll; the search drops under. */
    @media (max-width: 640px) {
        gap: 0;

        .tabs {
            width: calc(100% + 2rem);
            margin: 0 -1rem;
            padding: 0 1rem;
            overflow-x: auto;
            scrollbar-width: none;

            a {
                flex: none;
            }
        }
    }
}

.panel-head .search {
    display: flex;
    align-items: center;
    gap: .5rem;
    width: 18rem;
    max-width: 100%;
    height: 36px;
    margin-left: auto;
    padding: 0 .75rem;
    background: var(--inset);
    border: 1px solid var(--edge);
    border-radius: var(--radius);
    color: var(--muted);

    &:focus-within {
        outline: 2px solid var(--accent);
        outline-offset: 1px;
    }

    svg {
        width: 16px;
        height: 16px;
        fill: none;
        stroke: currentColor;
        stroke-width: 2;
        flex: none;
    }

    input {
        flex: 1;
        min-width: 0;
        height: 32px;
        font: inherit;
        color: var(--text);
        background: none;
        border: 0;
        outline: none;
    }

    @media (max-width: 640px) {
        width: 100%;
        margin: .75rem 0;
    }
}


/* --- account pages ---------------------------------------------------------- */

/* Moving between pages fades the old into the new rather than blanking between them,
   and whatever both name — the account head — holds still across it. */
@media (prefers-reduced-motion: no-preference) {
    @view-transition {
        navigation: auto;
    }
}

/* Who is signed in, and the tabs between their pages. Named for the transition
   between pages, so it holds still while the rest of the page fades. */
.account-head {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    margin-bottom: 1.5rem;
    view-transition-name: account-head;
}

.who {
    display: flex;
    align-items: center;
    gap: 1rem;

    h1 {
        margin: 0;
    }

    .lede {
        margin: 0;
    }
}

.account-tabs {
    display: flex;
    gap: 1.4rem;
    overflow-x: auto;
    scrollbar-width: none;
    border-bottom: 1px solid var(--edge);

    a {
        display: inline-flex;
        align-items: center;
        height: 40px;
        padding: 0 2px;
        font-size: .9rem;
        font-weight: 500;
        color: var(--muted);
        text-decoration: none;
        flex: none;

        &[aria-current] {
            font-weight: 600;
            color: #ffffff;
            box-shadow: inset 0 -2px 0 var(--accent);
        }
    }
}


/* --- profile colours -------------------------------------------------------- */

/* An account's colour, by the name its profile stores, so the palette can change
   here without touching a row. Anything coloured by it reads --colour. */
.colour-graphite { --colour: #3a3a3c; }
.colour-red      { --colour: #ff0035; }
.colour-blue     { --colour: #0a84ff; }
.colour-green    { --colour: #30d158; }
.colour-orange   { --colour: #ff9f0a; }
.colour-purple   { --colour: #bf5af2; }

.avatar.large {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    font-size: 1.4rem;
    font-weight: 700;
    color: #ffffff;
    background: var(--colour, var(--track));
}

.notice {
    padding: .6rem .8rem;
    background: var(--accent-soft);
    border-radius: var(--radius);
}


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

/* The way to the terms and the site's contact address, under every page but the
   session, which owns the whole window. */
footer.site {
    max-width: var(--frame);
    margin: 0 auto;
    padding: 0 var(--frame-padding) 2rem;
    font-size: .8rem;

    @media (width < 52rem) {
        padding-left: var(--gutter);
        padding-right: var(--gutter);
    }

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

body:has(main.session) footer.site {
    display: none;
}
