/*
 * Variables
 *
 * The colours, fonts & the reset come from the corporate design theme
 * (https://assets.confirm.ch/theme.css), whose tokens are mapped here.
 */

:root {
    --background: var(--theme-background);
    --surface: var(--theme-surface);
    --border: var(--theme-border);
    --text: var(--theme-text);

    /* Lighter than the theme's, as it's on top of the glowing background. */
    --text-muted: color-mix(in srgb, var(--theme-text-muted) 55%, var(--theme-text));
    --primary: var(--theme-primary);
}

/* Layout */

/* Its own stacking context, so the background layers are above its background. */
body {
    isolation: isolate;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    background: var(--background);
    color: var(--text);
}

/*
 * Background
 *
 * Soft glows in the hues of the theme, which slowly drift, behind a dotted
 * board (on which the post-its are pinned).
 */

body::before {
    position: fixed;
    z-index: -2;
    inset: -20vmax;
    background:
        radial-gradient(closest-side, color-mix(in srgb, var(--cd-orange-60) 34%, transparent), transparent) 20% 20% / 55vmax 55vmax no-repeat,
        radial-gradient(closest-side, color-mix(in srgb, var(--cd-fuchsia-60) 28%, transparent), transparent) 80% 25% / 50vmax 50vmax no-repeat,
        radial-gradient(closest-side, color-mix(in srgb, var(--cd-azure-60) 28%, transparent), transparent) 70% 80% / 60vmax 60vmax no-repeat,
        radial-gradient(closest-side, color-mix(in srgb, var(--cd-sea-green-60) 22%, transparent), transparent) 25% 75% / 50vmax 50vmax no-repeat;
    content: "";
    pointer-events: none;
    animation: drift 45s ease-in-out infinite alternate;
}

body::after {
    position: fixed;
    z-index: -1;
    inset: 0;
    background-image: radial-gradient(rgb(255 255 255 / 9%) 1px, transparent 1.5px);
    background-size: 1.5rem 1.5rem;
    content: "";
    pointer-events: none;
}

@keyframes drift {
    from {
        transform: translate3d(0, 0, 0) rotate(0deg);
    }

    to {
        transform: translate3d(4vmax, -3vmax, 0) rotate(10deg);
    }
}

@media (prefers-reduced-motion: reduce) {
    body::before {
        animation: none;
    }
}

/*
 * Header
 *
 * The brand, the actions (giving is the primary one), the role pages & the
 * user. It sticks to the top, with the rainbow of the theme's hues below.
 */

.header {
    position: sticky;
    z-index: 10;
    top: 0;
    display: flex;
    align-items: center;
    gap: 1rem 1.5rem;
    padding: 0.75rem 2rem;
    border-bottom: 3px solid;
    border-image: linear-gradient(
        90deg,
        var(--cd-orange-60), var(--cd-rose-60), var(--cd-fuchsia-60), var(--cd-violet-60),
        var(--cd-azure-60), var(--cd-sea-green-60), var(--cd-lime-60), var(--cd-yellow-60)
    ) 1;
    background: color-mix(in srgb, var(--background) 80%, transparent);
    backdrop-filter: blur(12px);
}

.header-logo {
    display: block;
    width: 2rem;
    height: 2rem;
}

.header-brand {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: var(--theme-font-size-lg);
    font-weight: 700;
    color: var(--text);
    text-decoration: none;
    white-space: nowrap;
}

/* Logged out, the login is on the right (where the user is when logged in). */
.header-login {
    margin-left: auto;
}

.header-nav {
    display: flex;
    flex: 1;
    align-items: center;
    gap: 1rem;
    min-width: 0;
}

.nav-group {
    display: flex;
    align-items: center;
    gap: 0.25rem;
}

/* The role pages are set apart from the actions by a divider. */
.nav-roles {
    padding-left: 1rem;
    border-left: 1px solid var(--border);
}

.nav-button {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.9rem;
    border-radius: var(--theme-radius-round);
    color: var(--text);
    font-size: var(--theme-font-size-sm);
    font-weight: 500;
    text-decoration: none;
    white-space: nowrap;
    transition: background var(--theme-transition), color var(--theme-transition);
}

.nav-button:hover {
    background: var(--surface);
}

.nav-button[aria-current="page"] {
    background: var(--surface);
    box-shadow: inset 0 0 0 1px var(--border);
}

.nav-button.primary {
    background: var(--primary);
    color: var(--theme-primary-contrast);
    font-weight: 600;
}

.nav-button.primary:hover,
.nav-button.primary[aria-current="page"] {
    background: var(--theme-primary-hover);
    box-shadow: none;
}

.nav-icon {
    line-height: 1;
}

.user {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-left: auto;
    white-space: nowrap;
}

/* Without role pages, the user is pushed to the right by the navigation. */
.header-nav + .user {
    margin-left: 0;
}

.balance {
    color: var(--text);
    text-decoration: none;
    transition: background var(--theme-transition), border-color var(--theme-transition);
    padding: 0.3rem 0.75rem;
    border: 1px solid var(--theme-warning);
    border-radius: var(--theme-radius-round);
    background: color-mix(in srgb, var(--theme-warning) 15%, transparent);
    font-size: var(--theme-font-size-sm);
}

.balance:hover,
.balance[aria-current="page"] {
    background: color-mix(in srgb, var(--theme-warning) 30%, transparent);
}

.balance strong {
    margin-left: 0.2rem;
    font-weight: 700;
}

.user-avatar {
    display: grid;
    width: 2rem;
    height: 2rem;
    place-items: center;
    border-radius: 50%;
    background: var(--kudo);
    color: var(--cd-cold-grey-10);
    font-size: 0.75rem;
    font-weight: 700;
}

.user-name {
    color: var(--text-muted);
    font-size: var(--theme-font-size-sm);
}

.icon-button {
    display: grid;
    width: 2rem;
    height: 2rem;
    place-items: center;
    border: 1px solid var(--border);
    border-radius: 50%;
    background: none;
    color: var(--text-muted);
    cursor: pointer;
    font: inherit;
    transition: color var(--theme-transition), border-color var(--theme-transition);
}

.icon-button:hover {
    border-color: var(--primary);
    color: var(--primary);
}

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

/*
 * Below wide screens, the brand & the user are in the first row, and the
 * navigation is in its own row below (i.e. nothing overlaps). On phones, the
 * navigation wraps (the actions first, then the role pages).
 */
@media (width < 80rem) {
    .header {
        flex-wrap: wrap;
    }

    .header-nav {
        order: 1;
        flex-basis: 100%;
        flex-wrap: wrap;
        gap: 0.5rem 1rem;
    }

    .header-nav + .user {
        margin-left: auto;
    }
}

@media (width < 90rem) {
    .user-name {
        display: none;
    }
}

@media (width < 48rem) {
    .header {
        padding: 0.75rem 1rem;
    }

    .header-nav {
        gap: 0.5rem;
    }

    .nav-roles {
        padding-left: 0;
        border-left: 0;
    }
}

.main {
    flex: 1;
    width: 100%;
    max-width: 48rem;
    margin: 0 auto;
    padding: 1rem;
}

/* The wide pages (e.g. the feed with four notes on a row), wider than the theme's content. */
.main-wide {
    max-width: 80rem;
}

/* The footer of Glancelot (logo left, text right). */
.footer {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 1rem 2rem;
    font-size: 0.8rem;
    font-weight: 300;
    opacity: 0.5;
}

.footer > div {
    margin-left: auto;
    text-align: right;
}

.footer-logo {
    display: block;
    height: 1.5rem;
}

@media (width < 40rem) {
    .footer {
        padding: 1rem;
    }
}

/* Elements */

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

.hints {
    margin: 0 0 0.75rem;
    padding-left: 1.25rem;
}

.badge {
    padding: 0.125rem 0.5rem;
    border: 1px solid var(--primary);
    border-radius: 1rem;
    color: var(--primary);
    font-size: 0.875rem;
}

.link-button {
    border: 0;
    background: none;
    color: var(--primary);
    cursor: pointer;
}

.toasts {
    max-width: 48rem;
    margin: 0 auto;
    padding: 0 1rem;
}

/* The messages are filled in the colour of their category, so they're noticed. */
.toast {
    margin-top: 1rem;
    padding: 0.85rem 1.1rem;
    border-radius: var(--theme-radius-lg);
    background: var(--theme-primary);
    color: var(--theme-primary-contrast);
    font-weight: 600;
    box-shadow: 0 0.75rem 1.5rem -0.75rem rgb(0 0 0 / 60%);
}

.toast.success {
    background: var(--theme-success);
    color: var(--theme-success-contrast);
}

.toast.error {
    background: var(--theme-error);
    color: var(--theme-error-contrast);
}

/* Feed */

.filters {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 1rem 0;
}

.filter {
    padding: 0.25rem 0.75rem;
    border: 1px solid var(--border);
    border-radius: 1rem;
    color: var(--text);
    text-decoration: none;
}

.filter.active {
    border-color: var(--primary);
    background: var(--primary);
    color: var(--theme-primary-contrast);
}

/*
 * Post-its
 *
 * Every kudo is a post-it in one of the hues of the theme (see COLOURS in
 * feed.py), slightly tilted, with a strip of tape & a folded corner.
 */

/*
 * Four notes on a row on wide screens (the main content is max. 80rem), wide
 * enough for the longest kudo (100 characters) on a square note.
 */
.feed {
    position: relative;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
    gap: 2rem 1.5rem;
    margin: 2rem 0;
    padding: 0;
    list-style: none;
}

.colour-orange {
    --kudo: var(--cd-orange-60);
    --kudo-dark: var(--cd-orange-40);
}

.colour-rose {
    --kudo: var(--cd-rose-60);
    --kudo-dark: var(--cd-rose-40);
}

.colour-fuchsia {
    --kudo: var(--cd-fuchsia-60);
    --kudo-dark: var(--cd-fuchsia-40);
}

.colour-purple {
    --kudo: var(--cd-purple-60);
    --kudo-dark: var(--cd-purple-40);
}

.colour-violet {
    --kudo: var(--cd-violet-60);
    --kudo-dark: var(--cd-violet-40);
}

.colour-azure {
    --kudo: var(--cd-azure-60);
    --kudo-dark: var(--cd-azure-40);
}

.colour-blue {
    --kudo: var(--cd-blue-60);
    --kudo-dark: var(--cd-blue-40);
}

.colour-sea-green {
    --kudo: var(--cd-sea-green-60);
    --kudo-dark: var(--cd-sea-green-40);
}

.colour-green {
    --kudo: var(--cd-green-60);
    --kudo-dark: var(--cd-green-40);
}

.colour-lime {
    --kudo: var(--cd-lime-60);
    --kudo-dark: var(--cd-lime-40);
}

.colour-olive {
    --kudo: var(--cd-olive-60);
    --kudo-dark: var(--cd-olive-40);
}

.colour-yellow {
    --kudo: var(--cd-yellow-60);
    --kudo-dark: var(--cd-yellow-40);
}

.colour-red {
    --kudo: var(--cd-red-60);
    --kudo-dark: var(--cd-red-40);
}

/* The handwriting of the notes (self-hosted, same as in noddl; SIL Open Font License). */
@font-face {
    font-display: swap;
    font-family: "Happy Monkey";
    font-style: normal;
    font-weight: 400;
    src: url("fonts/happy-monkey-latin.woff2") format("woff2");
}

/* The size of the folded corner, which is peeled by dragging it (see peel.js). */
@property --fold {
    syntax: "<length>";
    inherits: true;
    initial-value: 1.5rem;
}

.kudo {
    --fold: 1.5rem;
    --tilt: -1.5deg;
    --ink: var(--cd-cold-grey-10);

    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;

    /* All notes are squares of the same size (i.e. the width of the column). */
    aspect-ratio: 1;
    padding: 1.5rem 1.1rem 1rem;

    /* The folded corner is cut out of the note, and drawn by ::after. */
    background: linear-gradient(225deg, transparent var(--fold), var(--kudo) var(--fold));
    color: var(--ink);
    box-shadow: 0 1rem 1.5rem -0.75rem rgb(0 0 0 / 45%);
    transform: rotate(var(--tilt));
    transition: transform var(--theme-transition), box-shadow var(--theme-transition), --fold var(--theme-transition);
}

.kudo:nth-child(3n + 2) { --tilt: 1deg; }
.kudo:nth-child(3n + 3) { --tilt: -0.5deg; }
.kudo:nth-child(4n + 4) { --tilt: 2deg; }

.kudo:hover {
    z-index: 1;
    box-shadow: 0 1.5rem 2rem -0.75rem rgb(0 0 0 / 55%);
    transform: rotate(0deg) scale(1.04);
}

/* The strip of tape. */
.kudo::before {
    position: absolute;
    top: -0.75rem;
    left: 50%;
    width: 5.5rem;
    height: 1.5rem;
    background: rgb(255 255 255 / 35%);
    box-shadow: 0 1px 3px rgb(0 0 0 / 20%);
    content: "";
    transform: translateX(-50%) rotate(calc(var(--tilt) * -2));
}

/* The folded corner (its legs are the diagonal of the cut-out, i.e. 1.5rem × √2). */
.kudo::after {
    position: absolute;
    top: 0;
    right: 0;
    width: calc(var(--fold) * 1.4142);
    height: calc(var(--fold) * 1.4142);
    background: linear-gradient(225deg, transparent 50%, var(--kudo-dark) 50%);
    content: "";
}

/*
 * The crack behind a note (see peel.js), which is covered by the note until it
 * is peeled off. A dark gap in the wall, with the light of a parallel universe
 * leaking out of it.
 */
.feed-hole {
    position: absolute;
    display: grid;
    padding: 0;
    place-items: center;
    border: 0;
    background: none;
    cursor: pointer;
}

.feed-hole svg {
    width: 100%;
    height: 100%;
}

/* The light of the parallel universe, leaking out of the gap. */
.crack-light {
    animation: crack-glow 3s ease-in-out infinite alternate;
    filter: drop-shadow(0 0 0.5rem var(--cd-violet-60));
}

/* The gap, its light brightest in the middle (and blurred, so it leaks out). */
.crack-gap {
    fill: #04000d;
}

.crack-core {
    fill: var(--cd-azure-60);
}

.crack-hot {
    fill: #ffffff;
    opacity: 0.85;
}

/* The hairline cracks: dark grooves with a faintly lit edge. */
.crack-branch {
    fill: #000000;
    opacity: 0.8;
    paint-order: stroke;
    stroke: rgb(255 255 255 / 20%);
    stroke-linejoin: round;
    stroke-width: 0.25;
}

.feed-hole:focus-visible {
    outline: 3px solid var(--theme-focus);
    outline-offset: -3px;
}

/* Falling into it (above everything, see peel.js). */
.feed-hole.entering {
    z-index: 1000;
}

.glider {
    position: fixed;
    z-index: 1002;
    opacity: 0;
    inset: 0;
    transition: opacity 0.6s ease;
}

.glider.shown {
    opacity: 1;
}

.glider canvas {
    display: block;
    width: 100%;
    height: 100%;
    touch-action: none;
}

.glider-leave {
    position: absolute;
    top: 1rem;
    right: 1rem;
    padding: 0.4rem 0.9rem;
    border: 1px solid rgb(255 255 255 / 40%);
    border-radius: var(--theme-radius-round);
    background: rgb(4 0 13 / 60%);
    color: #ffffff;
    cursor: pointer;
}

.hole-portal {
    position: fixed;
    z-index: 1001;
    background: #04000d;
    inset: 0;
    pointer-events: none;
}

@keyframes crack-glow {
    to { filter: drop-shadow(0 0 1.25rem var(--cd-azure-60)); }
}

/* The handle to peel the note (on top of its folded corner, which it follows). */
.kudo-corner {
    position: absolute;
    top: 0;
    right: 0;
    z-index: 1;
    width: max(2rem, calc(var(--fold) * 1.4142));
    height: max(2rem, calc(var(--fold) * 1.4142));
    cursor: grab;
    touch-action: none;
}

/*
 * Clips everything (e.g. the text) beyond the fold, but not the shadow (4rem
 * around the note), only its own cut-out corner. Only while peeling, because
 * a clipped note is rendered a bit blurry (the corner is clear of the text otherwise).
 */
.kudo.peeling,
.kudo.peeled {
    clip-path: polygon(
        -4rem -4rem,
        calc(100% - var(--fold) * 1.4142 - 4rem) -4rem,
        calc(100% + 4rem) calc(var(--fold) * 1.4142 + 4rem),
        calc(100% + 4rem) calc(100% + 4rem),
        -4rem calc(100% + 4rem)
    );
}

.kudo.peeling {
    transition: none;
}

.kudo.peeling .kudo-corner {
    cursor: grabbing;
}

/* Peeled off, the note falls away and leaves a gap, until the page is reloaded. */
.kudo.peeled {
    opacity: 0;
    pointer-events: none;
    transform: translate(30%, 150%) rotate(35deg);
    transition: transform 1s ease-in, opacity 1s ease-in;
}

/* The type of the kudo (e.g. «🕹️ Arcade») at the top left, its points on the right. */
.kudo-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;

    /* Clear of the folded corner. */
    padding-right: 1.1rem;
}

.kudo-type {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0;
    font-size: var(--theme-font-size-sm);
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    opacity: 0.85;
}

.kudo-icon {
    font-size: 1.75rem;
    line-height: 1;
}

/* Small enough for the longest kudo (100 characters) on the smallest note. */
.kudo-content {
    display: flex;
    flex: 1;
    align-items: center;
    justify-content: center;
    margin: 0;
    text-align: center;
    font-family: "Happy Monkey", var(--theme-font-sans);
    font-size: 1.25rem;
    font-weight: 400;
    line-height: 1.3;
    overflow-wrap: anywhere;
    white-space: pre-line;
}

.kudo-meta {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding-top: 0.75rem;
    border-top: 1px dashed rgb(0 0 0 / 30%);
    font-size: var(--theme-font-size-sm);
}

.kudo-meta p {
    margin: 0;
}

.kudo-names {
    font-weight: 700;
}

.kudo-details {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.375rem;
}

.kudo .badge {
    border-color: rgb(0 0 0 / 40%);
    color: var(--ink);
    font-size: 0.75rem;
}

/* The points are a pill in the darker shade of the note (like its folded corner). */
.kudo .badge-amount {
    border-color: transparent;
    background: var(--kudo-dark);
    color: var(--cd-cold-grey-100);
    font-weight: 700;
}

@media (prefers-reduced-motion: reduce) {
    .kudo {
        transition: none;
    }

    .kudo:hover {
        transform: rotate(var(--tilt));
    }

    .kudo.peeled {
        transition: none;
    }

    .feed-hole svg {
        animation: none;
    }
}

.pager {
    display: flex;
    justify-content: space-between;
    margin: 1rem 0;
}

/*
 * Forms
 *
 * All fields share one look: a soft border, which lights up in the primary
 * colour on hover & focus (with a glow), and turns red when invalid.
 */

.form {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    max-width: 48rem;
    margin: 1.5rem 0;
}

/* Only the labels of fields (the people & checkboxes are labels too). */
.form > label,
.form .field > label,
.form legend {
    margin-top: 0.75rem;
    color: var(--text-muted);
    font-size: var(--theme-font-size-sm);
    font-weight: 600;
    letter-spacing: 0.02em;
}

.form input:is([type="text"], [type="email"], [type="number"], [type="date"], [type="search"]),
.form textarea {
    width: 100%;
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--border);
    border-radius: var(--theme-radius);
    background: color-mix(in srgb, var(--surface) 85%, transparent);
    color: var(--text);
    font: inherit;
    transition: border-color var(--theme-transition), box-shadow var(--theme-transition), background var(--theme-transition);
}

.form textarea {
    min-height: 6rem;
    line-height: 1.5;
    resize: vertical;
}

.form input::placeholder,
.form textarea::placeholder {
    color: var(--text-muted);
    opacity: 0.7;
}

.form input:hover,
.form textarea:hover {
    border-color: color-mix(in srgb, var(--primary) 50%, var(--border));
}

.form input:focus-visible,
.form textarea:focus-visible {
    border-color: var(--primary);
    outline: none;
    background: var(--surface);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 25%, transparent);
}

/* Only after the user interacted with the field (not on page load). */
.form input:user-invalid,
.form textarea:user-invalid {
    border-color: var(--theme-error);
}

.form .checkbox {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: 0.75rem;
    cursor: pointer;
}

.form input[type="checkbox"] {
    width: 1.15rem;
    height: 1.15rem;
    margin: 0;
    accent-color: var(--primary);
    cursor: pointer;
}

/* Buttons */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    padding: 0.65rem 1.4rem;
    border: 0;
    border-radius: var(--theme-radius-round);
    background: var(--primary);
    color: var(--theme-primary-contrast);
    cursor: pointer;
    font: inherit;
    font-weight: 600;
    text-decoration: none;
    transition: background var(--theme-transition), box-shadow var(--theme-transition), transform var(--theme-transition);
}

.btn:hover {
    background: var(--theme-primary-hover);
    box-shadow: 0 4px 14px color-mix(in srgb, var(--primary) 35%, transparent);
}

.btn:active {
    transform: translateY(1px);
}

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

.btn:disabled {
    background: var(--border);
    box-shadow: none;
    color: var(--text-muted);
    cursor: not-allowed;
}

.form .btn {
    align-self: flex-start;
    margin-top: 1.25rem;
}

/* Utilities */

[hidden] {
    display: none !important;
}

.count {
    display: inline-grid;
    min-width: 1.25rem;
    height: 1.25rem;
    padding: 0 0.375rem;
    place-items: center;
    border-radius: var(--theme-radius-round);
    background: var(--primary);
    color: var(--theme-primary-contrast);
    font-size: 0.75rem;
    font-weight: 700;
}

/* People (e.g. to pick the receiver of a kudo) */

.people {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    margin: 0.5rem 0 0;
    padding: 0;
    border: 0;
}

.people legend {
    margin-bottom: 0.25rem;
}

/* The search looks like the other fields (see forms), but is shorter & round. */
.form .people-search {
    max-width: 24rem;
    border-radius: var(--theme-radius-round);
}

.people-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
    gap: 0.75rem;
}

.person {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    padding: 1rem 0.5rem;
    border: 2px solid var(--border);
    border-radius: var(--theme-radius-lg);
    background: var(--surface);
    cursor: pointer;
    text-align: center;
    transition: border-color var(--theme-transition), transform var(--theme-transition);
}

.person:hover {
    border-color: var(--kudo);
    transform: translateY(-2px);
}

/* The radio / checkbox is only visually hidden, so it stays accessible. */
.person input:is([type="radio"], [type="checkbox"]) {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.person:has(input:checked) {
    border-color: var(--kudo);
    background: color-mix(in srgb, var(--kudo) 20%, var(--surface));
}

.person:has(input:focus-visible) {
    outline: 2px solid var(--theme-focus);
    outline-offset: 2px;
}

.person-avatar {
    display: grid;
    width: 3rem;
    height: 3rem;
    place-items: center;
    border-radius: 50%;
    background: var(--kudo);
    color: var(--cd-cold-grey-10);
    font-weight: 700;
}

.person-name {
    font-size: var(--theme-font-size-sm);
    font-weight: 600;
    overflow-wrap: anywhere;
}

/*
 * Claims
 *
 * The gifts to claim are wrapped presents (with a ribbon & a bow), which
 * wiggle on hover. Without gifts, there's a friendly empty state.
 */

.gifts {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: 2.5rem 1.5rem;
    margin: 2.5rem 0;
    padding: 0;
    list-style: none;
}

.gift {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 2rem 1.25rem 1.25rem;
    border-radius: var(--theme-radius-lg);

    /* The ribbon: a vertical & a horizontal band across the box. */
    background:
        linear-gradient(90deg, transparent calc(50% - 0.6rem), rgb(255 255 255 / 30%) calc(50% - 0.6rem), rgb(255 255 255 / 30%) calc(50% + 0.6rem), transparent calc(50% + 0.6rem)),
        linear-gradient(180deg, transparent 2.6rem, rgb(255 255 255 / 30%) 2.6rem, rgb(255 255 255 / 30%) 3.8rem, transparent 3.8rem),
        var(--kudo);
    color: var(--cd-cold-grey-10);
    box-shadow: 0 1rem 1.5rem -0.75rem rgb(0 0 0 / 45%);
    text-align: center;
}

.gift:hover {
    animation: wiggle 0.6s ease-in-out;
}

/* The buttons of all gifts are at the bottom (even with longer titles). */
.gift form {
    margin-top: auto;
}

.gift-bow {
    position: absolute;
    top: -1.4rem;
    left: 50%;
    font-size: 2.5rem;
    line-height: 1;
    transform: translateX(-50%);
}

.gift-amount {
    margin: 1.5rem 0 0;
    font-size: var(--theme-font-size-3xl);
    font-weight: 700;
    line-height: 1;
}

.gift-amount small {
    margin-left: 0.3rem;
    font-size: var(--theme-font-size);
    font-weight: 600;
}

.gift-title {
    margin: 0;
    font-size: var(--theme-font-size-lg);
    font-weight: 500;
    overflow-wrap: anywhere;
}

.gift-until {
    margin: 0;
    font-size: var(--theme-font-size-sm);
    font-weight: 600;
    opacity: 0.8;
}

.gift-button {
    width: 100%;
    margin-top: 0.75rem;
    padding: 0.6rem;
    border: 0;
    border-radius: var(--theme-radius-round);
    background: var(--cd-cold-grey-10);
    color: var(--kudo);
    cursor: pointer;
    font: inherit;
    font-weight: 700;
    transition: transform var(--theme-transition), box-shadow var(--theme-transition);
}

.gift-button:hover {
    box-shadow: 0 4px 14px rgb(0 0 0 / 35%);
    transform: scale(1.03);
}

.gift-button:focus-visible {
    outline: 2px solid var(--cd-cold-grey-10);
    outline-offset: 2px;
}

@keyframes wiggle {
    0%, 100% { transform: rotate(0deg); }
    20% { transform: rotate(-3deg); }
    40% { transform: rotate(3deg); }
    60% { transform: rotate(-2deg); }
    80% { transform: rotate(1deg); }
}

/* The empty state. */

.empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    max-width: 32rem;
    margin: 3rem auto;
    padding: 2.5rem 2rem;
    border: 1px dashed var(--border);
    border-radius: var(--theme-radius-lg);
    background: color-mix(in srgb, var(--surface) 70%, transparent);
    text-align: center;
}

.empty-icon {
    font-size: 4rem;
    line-height: 1;
    animation: float 3s ease-in-out infinite;
}

.empty h2 {
    margin: 1rem 0 0.25rem;
}

.empty p {
    margin: 0;
}

.empty-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    gap: 0.75rem;
    margin-top: 1.5rem;
}

@keyframes float {
    0%, 100% { transform: translateY(0) rotate(-4deg); }
    50% { transform: translateY(-0.6rem) rotate(4deg); }
}

@media (prefers-reduced-motion: reduce) {
    .gift:hover,
    .empty-icon {
        animation: none;
    }
}

/*
 * Tables
 *
 * Frosted glass cards: a translucent surface, which blurs the background
 * behind it, with rounded corners, a darker header & a highlight on hover.
 */

.table {
    width: 100%;
    margin: 1rem 0;
    overflow: hidden;
    border: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
    border-radius: var(--theme-radius-lg);
    border-collapse: separate;
    border-spacing: 0;
    background: color-mix(in srgb, var(--surface) 55%, transparent);
    backdrop-filter: blur(16px) saturate(140%);
    box-shadow: 0 1rem 2rem -1rem rgb(0 0 0 / 50%);
}

.table th,
.table td {
    padding: 0.75rem 1rem;
    border-bottom: 1px solid color-mix(in srgb, var(--border) 60%, transparent);
    text-align: left;
}

.table th {
    background: color-mix(in srgb, var(--background) 45%, transparent);
    color: var(--text-muted);
    font-size: var(--theme-font-size-sm);
    font-weight: 600;
    letter-spacing: 0.02em;
    white-space: nowrap;
}

.table tbody tr:where(:last-child) td {
    border-bottom: 0;
}

.table tbody tr {
    transition: background var(--theme-transition);
}

.table tbody tr:hover {
    background: color-mix(in srgb, var(--surface) 60%, transparent);
}

/*
 * On phones, the tables scroll instead of the page, the texts (e.g. titles)
 * keep a readable width, and the dates & times may stack.
 */
@media (width < 40rem) {
    .table {
        display: block;
        overflow-x: auto;
    }

    .claimed-gift,
    .gifts-title {
        min-width: 11rem;
    }

    .claimed td.claimed-date {
        white-space: normal;
    }

    .table th,
    .table td {
        padding: 0.6rem 0.75rem;
    }
}

/* The claimed gifts. */

.claimed-title {
    margin-top: 3rem;
}

/* The gift takes the remaining width, the points & dates stay on one line. */
.claimed-gift {
    width: 100%;
    overflow-wrap: anywhere;
}

/* The claimed points are green (like the received points in my points). */
.claimed .badge-amount {
    border-color: var(--theme-success);
    background: var(--theme-success);
    color: var(--theme-success-contrast);
    font-weight: 700;
}

.claimed td:not(.claimed-gift) {
    white-space: nowrap;
}

.claimed-date {
    color: var(--text-muted);
}

/* Back office */

/* The people are only picked when the gift isn't for everyone. */
.backoffice-form:has(input[name="everyone"]:checked) .people-picker {
    display: none;
}

/* The gifts of the back office (only the title wraps). */
.gifts-table td:not(.gifts-title) {
    white-space: nowrap;
}

.gifts-title {
    width: 100%;
    overflow-wrap: anywhere;
}

/* The states (filled pills, like the points). */
.state {
    padding: 0.125rem 0.6rem;
    border-radius: var(--theme-radius-round);
    font-size: 0.875rem;
    font-weight: 700;
}

.state-open {
    background: var(--theme-success);
    color: var(--theme-success-contrast);
}

.state-closed {
    background: var(--border);
    color: var(--text-muted);
}

/*
 * My points
 *
 * The totals (stats) & the history (a table).
 */

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

.stat {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 1rem 1.25rem;
    border: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
    border-radius: var(--theme-radius-lg);
    background: color-mix(in srgb, var(--surface) 55%, transparent);
    backdrop-filter: blur(16px);
}

.stat-label {
    color: var(--text-muted);
    font-size: var(--theme-font-size-sm);
    font-weight: 600;
}

.stat-value {
    font-size: var(--theme-font-size-2xl);
    font-weight: 700;
}

/* The balance is the highlight (in the colour of the balance in the header). */
.stat-total {
    border-color: var(--theme-warning);
    background: color-mix(in srgb, var(--theme-warning) 18%, transparent);
}

.points-table td:not(.points-why) {
    white-space: nowrap;
}

.points-why {
    width: 100%;
    overflow-wrap: anywhere;
}

.points-date {
    color: var(--text-muted);
}

/* The icon of the sender (e.g. 🙌 for peers) is a bit apart from the name. */
.points-from span {
    margin-right: 0.35rem;
}

.points-balance {
    font-weight: 700;
    text-align: right;
}

/* Received points are green, redeemed ones red. */
.points-table .badge-amount {
    border-color: var(--theme-success);
    background: var(--theme-success);
    color: var(--theme-success-contrast);
    font-weight: 700;
}

.points-table .badge-negative {
    border-color: var(--theme-error);
    background: var(--theme-error);
    color: var(--theme-error-contrast);
}

@media (width < 40rem) {
    .points-why {
        min-width: 11rem;
    }

    .points-table td.points-date {
        white-space: normal;
    }
}

/* Integrations */

.inline {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 0.5rem;
}

.field {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.field-emoji {
    width: 5rem;
}

.form .field-emoji input {
    text-align: center;
}

.field-name {
    flex: 1;
    min-width: 12rem;
}

.inline .btn {
    align-self: flex-end;
    margin-top: 0;
}

.table.integrations-table td {
    vertical-align: top;
}

/* Only the API key may wrap (the dates & names stay on one line). */
.integrations-table td:not(.integration-key) {
    white-space: nowrap;
}

.integration-emoji {
    margin-right: 0.25rem;
    font-size: 1.25rem;
}

.integration-actions {
    display: flex;
    gap: 1rem;
}

/* The API key column takes the remaining width. */
.integration-key {
    width: 100%;
    min-width: 12rem;
}

/*
 * Secrets (e.g. the API keys), which are masked in a field with an eye (to
 * show them) & a copy button on its right.
 */

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

.secret-value {
    width: 100%;
    min-width: 0;
    padding: 0.4rem 4.5rem 0.4rem 0.75rem;
    border: 1px solid var(--border);
    border-radius: var(--theme-radius);
    background: var(--background);
    color: var(--text);
    font-family: var(--theme-font-mono);
    font-size: var(--theme-font-size-sm);
    text-overflow: ellipsis;
}

.secret-value:focus-visible {
    border-color: var(--primary);
    outline: none;
}

.secret-button {
    position: absolute;
    right: 0.4rem;
    display: grid;
    width: 1.75rem;
    height: 1.75rem;
    place-items: center;
    border: 0;
    border-radius: var(--theme-radius-sm);
    background: none;
    color: var(--text-muted);
    cursor: pointer;
    transition: background var(--theme-transition), color var(--theme-transition);
}

/* The eye is left of the copy button. */
.secret-button[data-toggle-secret] {
    right: 2.25rem;
}

.secret-button:hover {
    background: var(--surface);
    color: var(--primary);
}

.secret-button:focus-visible {
    outline: 2px solid var(--theme-focus);
}

.secret-button svg {
    width: 1rem;
    height: 1rem;
    fill: none;
    stroke: currentcolor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 2;
}

/* The icons of the current state (shown or hidden, copied or not). */
.secret-button .icon-hide,
.secret-button .icon-copied,
.secret-button[aria-pressed="true"] .icon-show,
.secret-button.copied .icon-copy {
    display: none;
}

.secret-button[aria-pressed="true"] .icon-hide,
.secret-button.copied .icon-copied {
    display: block;
}

.secret-button.copied {
    color: var(--theme-success);
}

/* The explanation of the integrations. */

.facts {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    margin: 1rem 0;
    padding: 0;
    list-style: none;
}

.api-usage {
    margin: 1rem 0 1.5rem;
    padding: 0.75rem 1rem;
    border: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
    border-radius: var(--theme-radius-lg);
    background: color-mix(in srgb, var(--surface) 55%, transparent);
    backdrop-filter: blur(16px);
}

.api-usage summary {
    cursor: pointer;
    color: var(--text);
    font-weight: 600;
}

.api-usage pre {
    overflow-x: auto;
    padding: 0.75rem 1rem;
    border-radius: var(--theme-radius);
    background: var(--background);
    font-size: var(--theme-font-size-sm);
}

/*
 * Rewards (vouchers)
 *
 * The amounts are pills (unavailable ones dimmed), the open request a card,
 * and the requests of the back office cards with their decisions.
 */

.amounts {
    margin: 0;
    padding: 0;
    border: 0;
}

/* All amounts on one line (equal columns), two rows on phones. */
.amounts-grid {
    display: grid;
    grid-auto-columns: 1fr;
    grid-auto-flow: column;
    gap: 0.5rem;
    margin-top: 0.5rem;
}

@media (width < 40rem) {
    .amounts-grid {
        grid-auto-flow: row;
        grid-template-columns: repeat(4, 1fr);
    }
}

.amount {
    cursor: pointer;
}

.amount input[type="radio"] {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.amount span {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0.6rem 0.25rem;
    white-space: nowrap;
    border: 2px solid var(--border);
    border-radius: var(--theme-radius-lg);
    background: var(--surface);
    font-weight: 700;
    text-align: center;
    transition: border-color var(--theme-transition), background var(--theme-transition);
}

.amount strong {
    font-size: var(--theme-font-size);
}

.amount small {
    font-size: var(--theme-font-size-sm);
    font-weight: 500;
    opacity: 0.75;
}

.amount:hover span {
    border-color: var(--primary);
}

.amount:has(input:checked) span {
    border-color: var(--primary);
    background: var(--primary);
    color: var(--theme-primary-contrast);
}

.amount:has(input:focus-visible) span {
    outline: 2px solid var(--theme-focus);
    outline-offset: 2px;
}

.amount.unavailable {
    cursor: not-allowed;
    opacity: 0.35;
}

.pending,
.request {
    margin: 1.5rem 0;
    padding: 1.25rem;
    border: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
    border-radius: var(--theme-radius-lg);
    background: color-mix(in srgb, var(--surface) 55%, transparent);
    backdrop-filter: blur(16px);
}

.pending {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem;
    border-color: var(--theme-warning);
}

.pending-icon {
    font-size: 2rem;
}

.pending-text {
    flex: 1;
}

.pending-text h2,
.pending-text p {
    margin: 0;
}

.requests {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    margin: 1.5rem 0;
}

.request {
    margin: 0;
}

.request-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
}

.request-header h2,
.request-header p {
    margin: 0;
}

.request-amount {
    margin-left: auto;
    font-size: var(--theme-font-size-xl);
    font-weight: 700;
}

.request-description {
    margin: 0.75rem 0;
    font-size: var(--theme-font-size-lg);
}

.request-warning {
    color: var(--theme-error-text);
    font-weight: 600;
}

.request-form {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.request-form input[type="text"] {
    flex: 1 1 16rem;
    min-width: 0;
    padding: 0.6rem 0.8rem;
    border: 1px solid var(--border);
    border-radius: var(--theme-radius);
    background: var(--background);
    color: var(--text);
    font: inherit;
}

.request-form input:focus-visible {
    border-color: var(--primary);
    outline: none;
}

/* Destructive actions (e.g. delete or reject) are red. */
.btn-danger {
    background: var(--theme-error);
    color: var(--theme-error-contrast);
}

.btn-danger:hover {
    background: color-mix(in srgb, var(--theme-error) 85%, #000000);
    color: var(--theme-error-contrast);
}

/* Constructive actions (e.g. save or approve) are green. */
.btn-success {
    background: var(--theme-success);
    color: var(--theme-success-contrast);
}

.btn-success:hover {
    background: color-mix(in srgb, var(--theme-success) 85%, #000000);
    color: var(--theme-success-contrast);
}

.btn-secondary {
    background: var(--surface);
    box-shadow: inset 0 0 0 1px var(--border);
    color: var(--text);
}

.btn-secondary:hover {
    background: var(--surface);
    box-shadow: inset 0 0 0 1px var(--theme-error);
    color: var(--theme-error-text);
}

/* The states of the requests (like the states of the gifts). */
.state-approved {
    background: var(--theme-success);
    color: var(--theme-success-contrast);
}

.state-rejected {
    background: var(--theme-error);
    color: var(--theme-error-contrast);
}

.state-withdrawn {
    background: var(--border);
    color: var(--text-muted);
}

.rewards-table td:not(.rewards-reason) {
    white-space: nowrap;
}

.rewards-reason {
    width: 100%;
    overflow-wrap: anywhere;
}

.rewards-date {
    color: var(--text-muted);
}

/*
 * Moderation
 *
 * The back office clicks the edit button of a kudo in the feed (in its bottom
 * right corner), which opens a dialog to edit its points, reason & public flag, or delete it.
 */

/* The details of a kudo leave room for its edit button. */
.kudo[data-moderate] .kudo-meta {
    padding-right: 2.5rem;
}

.kudo-edit {
    position: absolute;
    right: 0.75rem;
    bottom: 0.75rem;
    display: grid;
    width: 2rem;
    height: 2rem;
    padding: 0;
    place-items: center;
    border: 0;
    border-radius: var(--theme-radius-round);
    background: rgb(0 0 0 / 12%);
    color: var(--ink);
    cursor: pointer;
    transition: background var(--theme-transition);
}

.kudo-edit:hover {
    background: rgb(0 0 0 / 25%);
}

.kudo-edit:focus-visible {
    outline: 3px solid var(--theme-focus);
    outline-offset: 2px;
}

.kudo-edit svg {
    width: 1rem;
    height: 1rem;
    fill: none;
    stroke: currentcolor;
    stroke-linecap: round;
    stroke-linejoin: round;
    stroke-width: 2;
}

.moderate-dialog {
    width: min(32rem, calc(100vw - 2rem));
    padding: 1.5rem;
    border: 1px solid var(--border);
    border-radius: var(--theme-radius-lg);
    background: var(--surface);
    color: var(--text);
    box-shadow: var(--theme-shadow);
}

.moderate-dialog::backdrop {
    background: rgb(0 0 0 / 60%);
    backdrop-filter: blur(4px);
}

.moderate-dialog h2 {
    margin-top: 0;
}

.moderate-dialog .form {
    margin: 0;
}

.moderate-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-top: 1.25rem;
    padding-top: 1rem;
    border-top: 1px solid var(--border);
}

.moderate-buttons {
    display: flex;
    gap: 0.5rem;
}

/* Cancel isn't destructive (unlike Delete), so no red on hover. */
.moderate-close:hover {
    box-shadow: inset 0 0 0 1px var(--primary);
    color: var(--text);
}

.toggle {
    padding: 0.2rem 0.7rem;
    border: 1px solid var(--border);
    border-radius: var(--theme-radius-round);
    background: none;
    color: var(--text-muted);
    cursor: pointer;
    font: inherit;
    font-size: 0.875rem;
    font-weight: 600;
}

.toggle.on {
    border-color: var(--theme-success);
    background: var(--theme-success);
    color: var(--theme-success-contrast);
}

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