/* The portal: sidebar, grid, and the two panels that open over them.
   Tokens, reset and .btn come from style.css. Nothing here belongs to the
   marketing pages, and nothing there is asked to carry a workspace. */

/* The two colours the portal is lit by — the brand's own and the one that goes
   with it, until somebody picks another pair under the gear. Two, because one
   light of one colour is a tint over the page; two that drift through each
   other mix where they overlap and the mix is never quite the same twice.
   Neighbours on the wheel, always, so what comes out of the overlap is a
   colour somebody could have chosen on purpose.

   Three numbers each, so every place below can decide its own alpha on them.
   How bright it all is lives in --glow-a, set only when somebody has turned
   the light off, so that one number takes every part of it out at once. */
:root {
    /* How far the floating sidebar stands from the edges of the window, and
       therefore how far the page starts from it. Said once: three places
       measure themselves against it. */
    --side-gap: 12px;

    /* The page is dark, and the parts the browser draws for itself — the
       scrollbars, the pickers, the dropdown a select opens — are told so once
       here rather than asked to guess panel by panel. */
    color-scheme: dark;

    --glow: 255, 107, 53;
    --glow-2: 236, 72, 153;

    /* How much of it there is, as the number on the bar under the swatches
       rather than as an alpha: fifty is what the light was when it had no
       number, forty is where it now starts, and every alpha below is a share
       of what that works out to. Nought is dark, and dark is nothing made. */
    --glow-lvl: 40;
    --glow-a: calc(var(--glow-lvl) * .0088);
}

/* No background of its own, deliberately: the dark comes from the root (see
   style.css) and the light below is painted behind this element. Give the body
   a colour and it covers the light with it. */
.app-body {
    min-height: 100vh;
    color: var(--text-primary);
    /* The page never moves sideways under a thumb, whatever is on it. Clipped
       at the root as well as here: a phone pans the root, not the body, and
       the body's own hidden (from style.css) would otherwise make it a box of
       its own that scrolls and unsticks the bar at the top. Clip is neither. */
    overflow-x: clip;
}

html { overflow-x: clip; }

/* The light the page sits in. Three of them wandering and a frame that does
   not: the colour somebody chose, the colour that goes with it, and a
   colourless one low down that lifts the middle of the page out of the dark.
   Each is a soft blob inside its own box, well clear of that box's edges, so
   it can be carried anywhere without an edge ever coming into view.

   They are separate layers because that is the whole point: three lights on
   one layer can only move together, which is a picture being dragged about.
   Moving apart, they cross, and where two of them lie over each other the
   colour is the pair mixed — which is a colour the page has for a minute and
   then does not.

   Fixed to the screen and not to the page. Laid on the page they would have to
   know how long the page is, and they would slide away the moment anybody
   scrolled. Fixed, they are simply the room: every screen of every view is lit
   the same.

   The colourless one is what makes the other two read as light. A colour over
   a background this dark only ever goes brown however much of it there is: the
   white does the lighting, the colours only say what the light is. Every alpha
   here, the dark in the corners included, is a share of one number, so the
   swatch that turns it off turns all of it out. */
/* Only once somebody has asked for them. The portal opens black — which is
   the room as it is, rather than the room as we would like it — and the light
   is a thing they turn on, not a thing they have to turn off. Nothing here is
   made at all until then: no layer, nothing carried about, nothing to pay for
   by anybody who never wanted it. */
html.is-lit::before,
html.is-lit::after,
html.is-lit .app-body::before,
html.is-lit .app-body::after {
    content: '';
    position: fixed;
    pointer-events: none;
}

/* Each of the three is the same shape: a curve of eight steps from full to
   nothing, reaching all the way to the edge of its own box, and every step on
   the light's own colour.

   Both halves of that matter. Written short — a stop or two and then
   `transparent` — a light on a background this dark ends in a faint grey ring
   and reads as a disc somebody blurred, because `transparent` is transparent
   BLACK and the last stretch of every fade was quietly going through it.
   Ending on the colour itself at nought alpha is the same fade with the grey
   taken out. And eight steps rather than two make the falloff a curve instead
   of a ramp, which is the difference between something lit and something
   drawn.

   The colour somebody chose. It starts over the top edge, which is where the
   light used to stay. */
html.is-lit::before {
    z-index: -4;
    left: -6vw; top: -34vh;
    width: 106vw; height: 90vh;
    background: radial-gradient(ellipse 50% 50% at 50% 50%,
        rgba(var(--glow), var(--glow-a)) 0%,
        rgba(var(--glow), calc(var(--glow-a) * 0.88)) 12%,
        rgba(var(--glow), calc(var(--glow-a) * 0.68)) 25%,
        rgba(var(--glow), calc(var(--glow-a) * 0.48)) 38%,
        rgba(var(--glow), calc(var(--glow-a) * 0.31)) 50%,
        rgba(var(--glow), calc(var(--glow-a) * 0.18)) 62%,
        rgba(var(--glow), calc(var(--glow-a) * 0.09)) 74%,
        rgba(var(--glow), calc(var(--glow-a) * 0.03)) 86%,
        rgba(var(--glow), 0) 100%);
    animation: wander-one 31s ease-in-out infinite;
}

/* The one that goes with it, coming the other way. */
html.is-lit::after {
    z-index: -3;
    right: -14vw; top: 0;
    width: 90vw; height: 80vh;
    background: radial-gradient(ellipse 50% 50% at 50% 50%,
        rgba(var(--glow-2), calc(var(--glow-a) * 0.82)) 0%,
        rgba(var(--glow-2), calc(var(--glow-a) * 0.7216)) 12%,
        rgba(var(--glow-2), calc(var(--glow-a) * 0.5576)) 25%,
        rgba(var(--glow-2), calc(var(--glow-a) * 0.3936)) 38%,
        rgba(var(--glow-2), calc(var(--glow-a) * 0.2542)) 50%,
        rgba(var(--glow-2), calc(var(--glow-a) * 0.1476)) 62%,
        rgba(var(--glow-2), calc(var(--glow-a) * 0.0738)) 74%,
        rgba(var(--glow-2), calc(var(--glow-a) * 0.0246)) 86%,
        rgba(var(--glow-2), 0) 100%);
    animation: wander-two 43s ease-in-out infinite;
}

/* The colourless one. */
html.is-lit .app-body::before {
    z-index: -2;
    left: 5vw; top: 38vh;
    width: 80vw; height: 72vh;
    background: radial-gradient(ellipse 50% 50% at 50% 50%,
        rgba(255, 255, 255, calc(var(--glow-a) * 0.2)) 0%,
        rgba(255, 255, 255, calc(var(--glow-a) * 0.176)) 12%,
        rgba(255, 255, 255, calc(var(--glow-a) * 0.136)) 25%,
        rgba(255, 255, 255, calc(var(--glow-a) * 0.096)) 38%,
        rgba(255, 255, 255, calc(var(--glow-a) * 0.062)) 50%,
        rgba(255, 255, 255, calc(var(--glow-a) * 0.036)) 62%,
        rgba(255, 255, 255, calc(var(--glow-a) * 0.018)) 74%,
        rgba(255, 255, 255, calc(var(--glow-a) * 0.006)) 86%,
        rgba(255, 255, 255, 0) 100%);
    animation: wander-three 37s ease-in-out infinite;
}

/* The corners, which stay where they are: they belong to the screen, not to
   anything moving across it. */
html.is-lit .app-body::after {
    z-index: -1;
    inset: 0;
    background: radial-gradient(ellipse 120% 115% at 50% 38%,
        transparent 30%, rgba(0, 0, 0, calc(var(--glow-a) * 1.25)) 100%);
}

/* The wandering. Transform and nothing else, which is the whole reason three
   moving lights can be afforded at all: it is the one thing a browser animates
   by moving what it has already drawn rather than drawing it again, so each
   blob is rasterised once and the card carries it from there. No filter, no
   blur, no canvas, no script, nothing repainted on any frame of any of it.

   Thirty-one seconds against forty-three against thirty-seven, none of which
   divide into each other, so the three never fall into step and the same
   arrangement does not come round again for something like five hours. Each
   one leaves where it started, goes down and across, and comes back a
   different way. */
@keyframes wander-one {
    0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
    25%      { transform: translate3d(12%, 38%, 0) scale(1.18); }
    50%      { transform: translate3d(-8%, 64%, 0) scale(1.06); }
    75%      { transform: translate3d(-20%, 26%, 0) scale(1.22); }
}

@keyframes wander-two {
    0%, 100% { transform: translate3d(0, 0, 0) scale(1.05); }
    30%      { transform: translate3d(-26%, 30%, 0) scale(1.2); }
    55%      { transform: translate3d(-10%, 62%, 0) scale(1); }
    80%      { transform: translate3d(16%, 24%, 0) scale(1.12); }
}

@keyframes wander-three {
    0%, 100% { transform: translate3d(0, 0, 0) scale(1.1); }
    28%      { transform: translate3d(20%, -26%, 0) scale(1); }
    60%      { transform: translate3d(-18%, -8%, 0) scale(1.24); }
    84%      { transform: translate3d(8%, 18%, 0) scale(1.08); }
}

.app-body.is-locked { overflow: hidden; }

/* Folding is one movement made of six: the sidebar narrows, the page widens
   into it, the rows lose their gap and their padding, the headings close up
   and the words fade. One curve and one length for all of them, or they arrive
   at different times and it reads as several things happening at once. */
:root { --fold-ms: .34s; --fold-ease: cubic-bezier(.4, 0, .2, 1); }

/* The sidebar is put back the way it was left before the page is drawn, and a
   transition would turn that into the sidebar folding itself in front of
   somebody who folded it days ago. Nothing moves until the first frame is up. */
.app-body.is-still .app-side,
.app-body.is-still .app-side *,
.app-body.is-still .app-main { transition: none !important; }

/* Several things on this page are shown and hidden through the hidden
   attribute, and a rule that gives one of them a display of its own would
   quietly outrank it. This keeps the attribute the last word. */
[hidden] { display: none !important; }

.btn { border: none; }

.visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

/* --- sidebar ------------------------------------------------------------ */
/* The sidebar does not reach any edge of the screen: it stands on the page
   with air all round it, a panel among the panels rather than a wall the page
   is built against. The light behind it goes on at the top and the bottom,
   which is the whole reason it is worth doing. */
.app-side {
    position: fixed;
    top: var(--side-gap); left: var(--side-gap); bottom: var(--side-gap);
    z-index: 40;
    width: 268px;
    display: flex;
    flex-direction: column;
    padding: 22px 18px 14px;
    background: rgba(16, 16, 21, .84);
    -webkit-backdrop-filter: blur(22px) saturate(140%);
    backdrop-filter: blur(22px) saturate(140%);
    border: 1px solid rgba(255, 255, 255, .08);
    border-radius: 22px;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .05), 0 24px 60px rgba(0, 0, 0, .45);
    transition: transform .35s cubic-bezier(0.25, 1, 0.5, 1),
                width var(--fold-ms) var(--fold-ease),
                padding var(--fold-ms) var(--fold-ease);
}

.app-head {
    display: flex;
    align-items: center;
    gap: 8px;
    /* No margin on the right: the button at that end is meant to finish on the
       same line as the rows of the nav below it, and eight pixels of margin
       held it short of them. */
    margin: 0 0 28px 8px;
    overflow: hidden;
    transition: margin var(--fold-ms) var(--fold-ease),
                gap var(--fold-ms) var(--fold-ease);
}

.app-brand {
    display: block;
    flex: 1;
    min-width: 0;
    overflow: hidden;
    transition: opacity .18s var(--fold-ease),
                flex-basis var(--fold-ms) var(--fold-ease),
                width var(--fold-ms) var(--fold-ease);
}
.app-brand-logo { width: 132px; height: auto; }

/* Folding the sidebar away. The icon is a panel with a rail down one side,
   which says the same thing whichever way it is about to go, so the button
   does not have to turn around and mean the opposite of what it just meant. */
.app-fold {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 32px; height: 32px;
    color: rgba(184, 186, 194, .55);
    background: none;
    border: 1px solid transparent;
    border-radius: 10px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.app-fold svg {
    width: 17px; height: 17px;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
}

.app-fold:hover {
    color: var(--text-primary);
    background: rgba(255, 255, 255, .06);
    border-color: rgba(255, 255, 255, .12);
}

/* --- folded --------------------------------------------------------------- */
/* A rail of icons. Everything that was words goes and everything that was a
   picture stays — and stays exactly where it was. The rail is as wide as it
   has to be for the column of icons the sidebar already had to be its middle,
   so folding is the panel's edge coming in and the words shrinking to nothing
   under a fade, and the icons are not part of the movement at all.

   Which is where the numbers come from: 18px of sidebar, a 1px row border and
   12px of row, a 32px icon, then 12 + 1 + 17 back out to the far edge is 94,
   and the middle of that is the 47 the icons were already sitting on. Change
   any one of them and the column steps sideways as it folds. Only above the
   fold: below it the sidebar is already a drawer that is either over the page
   or off it. */
@media (min-width: 901px) {
    .app-body.is-folded .app-side { width: 94px; padding-right: 17px; }
    .app-body.is-folded .app-main { margin-left: calc(94px + var(--side-gap) * 2); }

    /* The logo goes to nothing across as well as out, so the button beside it
       is the only thing left in the row and can simply take the middle, the
       same middle every square below it is on. */
    .app-body.is-folded .app-head { gap: 0; justify-content: center; }

    /* Faded and squeezed to nothing rather than taken away, so the room they
       were taking goes with the sidebar instead of an instant before it.
       Squeezed for real, though: fading a flex child that still holds flex: 1
       leaves the room exactly where it was, and the icon beside it stays shoved
       to one end of a box that is meant to have it in the middle. That is what
       made the rail look crooked — every icon sat left of centre, and the only
       box you could see was the selected one, which did not. */
    .app-body.is-folded .app-brand {
        flex: 0 0 0;
        width: 0;
        opacity: 0;
        pointer-events: none;
    }

    .app-body.is-folded .app-nav-text {
        flex: 0 0 0;
        width: 0;
        margin: 0;
        opacity: 0;
        pointer-events: none;
    }

    .app-body.is-folded .app-nav-label,
    .app-body.is-folded .app-grids-empty {
        max-height: 0;
        margin-bottom: 0;
        padding-top: 0; padding-bottom: 0;
        opacity: 0;
    }
    /* Folded, the rail is one column wide: the door stays, the word goes, and
       what is left is the same square as the rows above it. */
    .app-body.is-folded .app-me { gap: 6px; }

    .app-body.is-folded .app-me-out span { width: 0; opacity: 0; }

    .app-body.is-folded .app-me-lamp {
        margin-bottom: 0;
        padding-bottom: 0;
        border-bottom: 0;
    }

    .app-body.is-folded .app-fold,
    .app-body.is-folded .app-me-out {
        /* The way out grows to fill its row when there are words in it, which
           would stretch the square back out again. */
        flex: 0 0 auto;
        width: 46px; height: 46px;
        margin-left: auto; margin-right: auto;
        padding: 0;
        gap: 0;
        justify-content: center;
        border-radius: 14px;
    }

    .app-body.is-folded .app-fold svg,
    .app-body.is-folded .app-me-out svg { margin: 0; }

    /* Folded, every row becomes the same square, centred in the rail. A row
       that keeps a full-width box while only its icon is showing reads as a
       long tile with an icon stranded at one end of it, and the tiles no
       longer line up with each other — which is what the selected row looked
       like before this.
       One size for all of them, whatever the row was built from: a tile, a
       line drawing, or the plus at the foot of the projects. */
    .app-body.is-folded .app-nav-item,
    .app-body.is-folded .app-nav-new { position: relative; gap: 0; }

    .app-body.is-folded .app-nav-item,
    .app-body.is-folded .app-nav-new {
        width: 46px; height: 46px;
        margin-left: auto; margin-right: auto;
        padding: 0;
        justify-content: center;
        border-radius: 14px;
    }

    /* The icons carried side margins for the gap between them and their words.
       There are no words now, and a margin on a centred thing is a nudge off
       centre. */
    .app-body.is-folded .app-nav-item > svg,
    .app-body.is-folded .app-nav-new > svg { margin: 0; }

    .app-nav-item,
    .app-nav-new { transition: background .25s, color .25s, border-color .25s,
                               width var(--fold-ms) var(--fold-ease),
                               height var(--fold-ms) var(--fold-ease),
                               padding var(--fold-ms) var(--fold-ease),
                               border-radius var(--fold-ms) var(--fold-ease),
                               gap var(--fold-ms) var(--fold-ease); }

    /* The count of what is waiting has nowhere to be a number, and a number
       nobody can read is worse than a dot that says the same thing. It sits on
       the corner of the icon, the way a phone puts one on an app. */
    .app-body.is-folded .app-nav-badge,
    .app-body.is-folded .app-nav-count {
        position: absolute;
        top: 50%; right: 5px;
        transform: translateY(-16px);
        min-width: 9px; width: 9px; height: 9px;
        padding: 0;
        font-size: 0;
        border-radius: 50%;
    }

    /* An icon drawn as a line is smaller than one drawn as a tile, so the
       corner it has to hang off is not in the same place. Told apart by what
       the row was built from rather than by a class somebody has to remember
       to put there. */
    .app-body.is-folded .app-nav-item > svg ~ .app-nav-badge,
    .app-body.is-folded .app-nav-item > svg ~ .app-nav-count {
        right: 10px;
        transform: translateY(-13px);
    }

    .app-nav-badge,
    .app-nav-count { transition: width var(--fold-ms) var(--fold-ease),
                                 height var(--fold-ms) var(--fold-ease),
                                 min-width var(--fold-ms) var(--fold-ease),
                                 padding var(--fold-ms) var(--fold-ease),
                                 font-size .14s var(--fold-ease); }

}

.app-nav { flex: 1; overflow-y: auto; }

.app-nav-label {
    margin: 0 10px 10px;
    max-height: 30px;
    overflow: hidden;
    font-size: .68rem;
    letter-spacing: .14em;
    text-transform: uppercase;
    white-space: nowrap;
    color: rgba(184, 186, 194, .6);
    transition: max-height var(--fold-ms) var(--fold-ease),
                margin var(--fold-ms) var(--fold-ease),
                opacity .16s var(--fold-ease);
}

.app-grids { list-style: none; margin: 0 0 8px; padding: 0; }

/* The sections of the sidebar are different kinds of thing, and a faint line
   with a little air is all it takes to say so. The line belongs to whatever
   comes after it and only when something came before, so a section that is not
   shown leaves no line behind and two of them never stack up with nothing in
   between. Which is what a new account with no grid was looking at. */
.app-nav > .is-cut {
    margin-top: 18px;
    padding-top: 18px;
    border-top: 1px solid rgba(255, 255, 255, .07);
}


.app-grids-empty {
    max-height: 44px;
    padding: 10px 12px;
    overflow: hidden;
    font-size: .82rem;
    white-space: nowrap;
    color: rgba(184, 186, 194, .6);
    transition: max-height var(--fold-ms) var(--fold-ease),
                padding var(--fold-ms) var(--fold-ease),
                opacity .16s var(--fold-ease);
}

.app-nav-item {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    margin-bottom: 4px;
    padding: 10px 12px;
    overflow: hidden;
    font: inherit;
    text-align: left;
    color: var(--text-secondary);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 13px;
    cursor: pointer;
    transition: background .25s, color .25s, border-color .25s,
                gap var(--fold-ms) var(--fold-ease);
}

.app-nav-item:hover { background: rgba(255, 255, 255, .05); color: var(--text-primary); }

/* The one you are on: filled, with no line around it. A soft block of colour
   reads as the row being lit from underneath rather than as the row being
   outlined, which is nearer what the rest of the page is doing.
   Grey until somebody lights the portal, and from then on a dimmed version of
   the pair they chose — see html.is-lit, further down. */
.app-nav-item.is-on {
    color: var(--text-primary);
    background: rgba(255, 255, 255, .09);
    border-color: transparent;
}

.app-nav-item.is-on:hover { background: rgba(255, 255, 255, .13); }

.app-nav-dot {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 32px; height: 32px;
    font-size: .82rem;
    font-weight: var(--fw-semibold);
    border-radius: 11px;
}

.app-nav-item svg {
    flex: 0 0 auto;
    width: 19px; height: 19px;
    margin: 0 6px;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
}

.app-nav-text {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    transition: opacity .18s var(--fold-ease),
                width var(--fold-ms) var(--fold-ease),
                flex-basis var(--fold-ms) var(--fold-ease);
}
.app-nav-text strong {
    display: block;
    overflow: hidden;
    font-size: .9rem;
    font-weight: var(--fw-medium);
    text-overflow: ellipsis;
    white-space: nowrap;
}
.app-nav-text small {
    display: block;
    overflow: hidden;
    font-size: .72rem;
    text-overflow: ellipsis;
    color: rgba(184, 186, 194, .65);
}

/* The count of posts a client has written on and nobody has answered yet. */
.app-nav-badge {
    flex: 0 0 auto;
    min-width: 21px;
    padding: 2px 6px;
    font-size: .72rem;
    font-weight: var(--fw-semibold);
    text-align: center;
    color: var(--bg-dark);
    background: var(--accent-orange);
    border-radius: 999px;
}

/* Something is waiting. A tint and a small circle with a number in it, the way
   a phone puts one on an app, rather than anything that shouts. */
.app-nav-item.has-new { background: rgba(255, 107, 53, .07); }
.app-nav-item.has-new .app-nav-text strong { color: #ffc4a4; }
.app-nav-item.has-new:hover { background: rgba(255, 107, 53, .13); }

/* A row can be the one you are on and have something waiting on it at once.
   The tint of has-new would fill a row that is meant to be drawn, so being on
   it wins: the same weight of selector, further down. The peach title stays,
   because that is the part that says something is waiting. */
.app-nav-item.is-on.has-new { background: rgba(255, 255, 255, .09); }
.app-nav-item.is-on.has-new:hover { background: rgba(255, 255, 255, .13); }

.app-nav-count {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    min-width: 19px; height: 19px;
    padding: 0 5px;
    font-size: .67rem;
    font-weight: var(--fw-semibold);
    color: var(--bg-dark);
    background: var(--accent-orange);
    border-radius: 999px;
}

/* Starting a room is not opening one, so the line that does it is quieter than
   the rooms above it and says what it is rather than naming a thing. */
.app-nav-new { margin-top: 2px; color: var(--text-secondary); }
.app-nav-new strong { font-weight: var(--fw-regular); }
.app-nav-new svg { width: 17px; height: 17px; flex: 0 0 auto;
    fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }
.app-nav-new:hover { color: var(--text-primary); }

.app-me {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid rgba(255, 255, 255, .07);
}

/* The light, alone at the foot of the sidebar now that the way out has gone
   up to the bar beside the name it signs out of. Its panel comes up from
   here, and the row it sits in is the width of the sidebar so there is no
   small square to aim at. */
.app-me-lamp { position: relative; }

.app-me-out {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    width: 100%;
    padding: 10px 12px;
    font: inherit;
    font-size: .82rem;
    color: var(--text-secondary);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 13px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.app-me-out span {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    transition: opacity .16s var(--fold-ease),
                width var(--fold-ms) var(--fold-ease);
}

.app-me-out svg {
    flex: 0 0 auto;
    width: 19px; height: 19px;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
}

.app-me-out:hover { color: var(--text-primary); background: rgba(255, 255, 255, .06); }
.app-me-out:focus-visible { outline: 2px solid rgba(255, 255, 255, .3); outline-offset: 1px; }

/* Lit, the button says what it is about without being read. */
html.is-lit #tint-open,
html.is-lit #top-lamp { color: rgb(var(--glow)); background: rgba(var(--glow), .12); }

/* Which colour that light is, under the gear where the rest of what belongs to
   one person is. Six swatches rather than a picker: it is a colour you judge by
   looking at the page behind it, so it wants one press and a glance, not a
   second screen with a hex field on it. */
/* Out of the sidebar's menu and into a panel of its own on the bar. It kept
   its name because everything that already speaks to it calls it that, but it
   has no menu above it to be divided from any more. */
.me-tint {
    margin: 0;
    padding: 0;
    border-top: none;
}

.tint-panel {
    position: absolute; bottom: calc(100% + 12px); left: 0;
    z-index: 60;
    width: min(268px, calc(100vw - 32px));
    padding: 14px 16px 12px;
    background: rgba(20, 20, 26, .98);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 16px;
    box-shadow: 0 26px 60px rgba(0, 0, 0, .55);
}

/* Opened from the bar instead, it hangs down from the sun, centred under it as
   far as the screen allows (the script works out where), with a corner
   pointing up at it. */
.tint-panel.is-top { top: calc(100% + 12px); bottom: auto; right: auto; }

.tint-panel.is-top::before {
    content: '';
    position: absolute;
    top: -8px; left: var(--tint-point, calc(50% - 7px));
    width: 14px; height: 14px;
    background: rgba(20, 20, 26, .98);
    border-left: 1px solid rgba(255, 255, 255, .1);
    border-top: 1px solid rgba(255, 255, 255, .1);
    transform: rotate(45deg);
}

/* The bar's own switch, which only a phone needs. */
.lamp-wrap { position: relative; display: none; flex: 0 0 auto; }

.me-tint-what {
    margin: 0 0 9px;
    font-size: .66rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: rgba(184, 186, 194, .55);
}

.me-tint-dots { display: flex; flex-wrap: wrap; gap: 6px; }

/* How much of it there is. A native range, because a bar somebody drags is a
   range whatever it is made of — and this way it takes a keyboard, a tap and a
   screen reader without any of that being written here. What is written here
   is only what it looks like: a hairline, a small round handle, and the part
   already spent in the colour it is spending. */
.me-tint-bar {
    -webkit-appearance: none;
    appearance: none;
    display: block;
    width: 100%;
    height: 16px;
    margin: 13px 0 1px;
    padding: 0;
    background: none;
    cursor: pointer;
    /* A finger dragging sideways on a page that can move sideways moves the
       page, and the bar underneath never hears about it. This says the gesture
       belongs to the bar: without it the light could only be set with a mouse.
       Two fingers are still the browser's, so a pinch over the bar zooms. */
    touch-action: pinch-zoom;
}

.me-tint-bar::-webkit-slider-runnable-track {
    height: 3px;
    border-radius: 2px;
    background: linear-gradient(to right,
        rgb(var(--glow)) var(--fill, 40%), rgba(255, 255, 255, .13) var(--fill, 40%));
}

.me-tint-bar::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 11px; height: 11px;
    margin-top: -4px;
    background: #fff;
    border: none;
    border-radius: 50%;
    box-shadow: 0 1px 4px rgba(0, 0, 0, .5);
}

.me-tint-bar::-moz-range-track {
    height: 3px;
    border-radius: 2px;
    background: linear-gradient(to right,
        rgb(var(--glow)) var(--fill, 40%), rgba(255, 255, 255, .13) var(--fill, 40%));
}

.me-tint-bar::-moz-range-thumb {
    width: 11px; height: 11px;
    background: #fff;
    border: none;
    border-radius: 50%;
    box-shadow: 0 1px 4px rgba(0, 0, 0, .5);
}

/* Nothing to be the strength of, with no light chosen. */
.me-tint-bar:disabled { opacity: .35; cursor: default; }

.me-tint-bar:focus-visible {
    outline: 2px solid rgba(255, 255, 255, .5);
    outline-offset: 3px;
    border-radius: 3px;
}

.me-tint-dot {
    flex: 0 0 auto;
    width: 20px; height: 20px;
    padding: 0;
    background: linear-gradient(135deg,
        rgb(var(--dot, 255, 255, 255)) 32%, rgb(var(--dot-2, var(--dot, 255, 255, 255))));
    border: none;
    border-radius: 50%;
    outline: 2px solid transparent;
    outline-offset: 2px;
    cursor: pointer;
    transition: outline-color .2s, transform .2s var(--fold-ease);
}

.me-tint-dot:hover { transform: scale(1.14); }
.me-tint-dot[aria-checked="true"] { outline-color: rgba(255, 255, 255, .8); }

/* No light at all is a choice like any of the others, so it is a swatch like
   any of the others rather than a word off to one side. */
.me-tint-dot.is-off {
    background:
        linear-gradient(135deg, transparent calc(50% - 1px),
        rgba(255, 255, 255, .5) 50%, transparent calc(50% + 1px)),
        rgba(255, 255, 255, .07);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .16);
}

.me-name-field {
    width: 100%;
    padding: 10px 13px;
    font: inherit;
    font-size: .85rem;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 11px;
}

.me-name-field:focus { outline: none; border-color: var(--accent-orange); }

/* --- faces ---------------------------------------------------------------- */
/* A picture of somebody, or the initial that stands in for one. The colour
   behind an initial comes from the account's own id, so the same person is the
   same colour on every screen and a list of five reads as five people rather
   than five oranges. A picture takes the corners of whatever it sits in
   without clipping it, so the dot that says whether they are online still
   shows outside the edge. */
.app-avatar,
.chat-face,
.chat-head-face,
.lister-face,
.app-nav-dot {
    color: rgb(var(--face, 255, 255, 255));
    background: rgba(var(--face, 255, 255, 255), .16);
}

.has-photo img {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
    border-radius: inherit;
}

.app-avatar {
    position: relative;
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 34px; height: 34px;
    font-size: .85rem;
    font-weight: var(--fw-semibold);
    border-radius: 11px;
}

/* Who is signed in, on the bar rather than at the foot of the sidebar: it is
   a thing about you and not about the work, and the press opens the one page
   that is yours. Folds down to the face alone before the bar runs out of room. */
.top-me {
    position: relative;
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
    max-width: 230px;
    padding: 4px;
    font: inherit;
    color: inherit;
    text-align: left;
    background: none;
    border: 0;
    border-radius: 12px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.top-me:hover .top-me-text strong { color: #ffffff; }
.top-me:focus-visible { outline: 2px solid rgba(255, 255, 255, .3); outline-offset: 2px; }

.top-me .app-avatar { flex: 0 0 auto; width: 32px; height: 32px; border-radius: 10px; }

.top-me-text { min-width: 0; overflow: hidden; white-space: nowrap; }

.top-me-text strong {
    display: block;
    overflow: hidden;
    font-size: .82rem;
    font-weight: var(--fw-medium);
    text-overflow: ellipsis;
}

.top-me-text small {
    display: block;
    overflow: hidden;
    font-size: .68rem;
    color: rgba(184, 186, 194, .6);
    text-overflow: ellipsis;
}

.top-me .bell-dot { top: 2px; right: 2px; }

/* The way out, beside the name. A drawing and nothing else: no box, no rule
   beside it, nothing that lights up behind it — a door with an arrow going
   out of it is not a thing anybody has to read twice, and the only thing it
   does under the cursor is warm to the colour of the place. */
.top-out {
    position: relative;
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 34px; height: 34px;
    margin-left: 6px;
    color: rgba(184, 186, 194, .55);
    background: none;
    border: 0;
    cursor: pointer;
    transition: color .2s;
}

.top-out svg {
    width: 18px; height: 18px;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
}

.top-out:hover { color: var(--accent-orange); }
.top-out:focus-visible { outline: 2px solid rgba(255, 255, 255, .3); outline-offset: 1px; border-radius: 10px; }

@media (max-width: 720px) {
    .top-me { max-width: none; padding: 5px; }
    .top-me-text { display: none; }
}

/* --- Νέο --------------------------------------------------------------------- */
/* Beside the name of something new, until it has been tried: small, filled
   with the room's own two colours, so it changes when the light does. */
.new-tag {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 7px;
    font-size: .6rem;
    font-style: normal;
    font-weight: var(--fw-semibold);
    letter-spacing: .02em;
    line-height: 1.5;
    vertical-align: 2px;
    color: #fff;
    background: linear-gradient(135deg, rgb(var(--glow)), rgb(var(--glow-2)));
    border-radius: 999px;
    text-shadow: 0 1px 1px rgba(0, 0, 0, .25);
}

/* In a room kept dark it is black and silver: a black pill with a silver edge
   and silver letters, the one way silver on a light ground stays readable. */
html.is-dark .new-tag,
html.is-dark .app-tab-new {
    color: #e4e7ed;
    background: linear-gradient(135deg, rgb(62, 65, 74), rgb(16, 17, 21));
    box-shadow: inset 0 0 0 1px rgba(214, 218, 226, .5);
    text-shadow: none;
}

/* On a phone's tab there is no room beside the word, so it sits on the icon's
   shoulder, where the pip for a message would. */
.app-tab-new {
    position: absolute;
    top: 3px; left: 50%;
    margin-left: 4px;
    padding: 0 5px;
    font-size: .5rem;
    font-style: normal;
    font-weight: var(--fw-semibold);
    line-height: 1.6;
    color: #fff;
    background: linear-gradient(135deg, rgb(var(--glow)), rgb(var(--glow-2)));
    border-radius: 999px;
    text-shadow: 0 1px 1px rgba(0, 0, 0, .25);
    pointer-events: none;
}

/* --- a walk round a tool ---------------------------------------------------- */
/* The page is dimmed except for a lit frame round the part being talked about,
   and the card sits beside that part. Nothing here stops the page underneath
   from being used: only the card takes a press. */
.tour {
    position: fixed;
    inset: 0;
    z-index: 70;
    pointer-events: none;
}

.tour-ring {
    position: fixed;
    border-radius: 18px;
    box-shadow:
        0 0 0 2px rgba(var(--glow), .85),
        0 0 0 9999px rgba(6, 6, 9, .62);
    transition: left .25s var(--fold-ease), top .25s var(--fold-ease),
                width .25s var(--fold-ease), height .25s var(--fold-ease);
}

.tour-card {
    position: fixed;
    width: min(320px, calc(100vw - 32px));
    padding: 18px 18px 14px;
    pointer-events: auto;
    background: rgba(26, 26, 33, .99);
    border: 1px solid rgba(255, 255, 255, .16);
    border-radius: 17px;
    box-shadow:
        0 0 0 1px rgba(var(--glow), .4),
        0 26px 60px rgba(0, 0, 0, .68);
    transition: left .25s var(--fold-ease), top .25s var(--fold-ease);
    animation: hello-in .32s var(--fold-ease) both;
}

.tour-title {
    display: block;
    margin-bottom: 7px;
    font-size: 1.02rem;
    font-weight: var(--fw-semibold);
}

.tour-text {
    margin: 0 0 14px;
    font-size: .86rem;
    line-height: 1.55;
    color: rgba(226, 227, 232, .92);
}

.tour-never {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 14px;
    font-size: .78rem;
    color: var(--text-secondary);
    cursor: pointer;
}

.tour-never input { width: 15px; height: 15px; margin: 0; accent-color: var(--accent-orange); }

.tour-foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px; }
.tour-count { margin-right: auto; font-size: .74rem; font-variant-numeric: tabular-nums; color: var(--text-secondary); }
.tour-foot .btn { padding: 9px 16px; font-size: .82rem; }
.tour-foot .app-ghost { padding: 8px 12px; }

@media (prefers-reduced-motion: reduce) {
    .tour-ring, .tour-card { transition: none; animation: none; }
}

/* --- said once ------------------------------------------------------------ */
/* The light is now something somebody turns on, which is only an improvement
   if they can find the switch. This sits over the row that holds it, with a
   corner pointing down at it, and is never seen again once it has been read.
   On a phone the sidebar is a drawer and that row is not on screen, so there
   is nothing to point at: it sits along the bottom and says where to look
   instead. */
.hello {
    position: fixed;
    right: 18px; top: 74px;
    z-index: 60;
    width: 288px;
    padding: 18px 18px 15px;
    background: rgba(26, 26, 33, .99);
    -webkit-backdrop-filter: blur(20px);
    backdrop-filter: blur(20px);
    border: 1px solid rgba(255, 255, 255, .16);
    border-radius: 17px;
    /* A ring in the light's own colour, so the card and the thing it is about
       are plainly the same subject. */
    box-shadow:
        0 0 0 1px rgba(var(--glow), .45),
        0 0 34px rgba(var(--glow), .18),
        0 26px 60px rgba(0, 0, 0, .68);
    animation: hello-in .42s var(--fold-ease) both;
}

/* The corner that does the pointing: a square of the same panel turned
   forty-five degrees, so it is the card's own edge rather than a shape drawn
   to look like one. */
.hello::after {
    content: '';
    position: absolute;
    right: 26px; top: -8px;
    width: 14px; height: 14px;
    background: rgba(26, 26, 33, .99);
    border-left: 1px solid rgba(var(--glow), .45);
    border-top: 1px solid rgba(var(--glow), .45);
    transform: rotate(45deg);
}

/* On a computer the switch is not on the bar but at the foot of the sidebar,
   so the card stands just above it with its corner pointing down at the sun.
   Left where it was, top right, it pointed at the way out instead. */
@media (min-width: 901px) {
    .hello {
        top: auto; right: auto;
        left: calc(var(--side-gap) + 10px);
        bottom: calc(var(--side-gap) + 68px);
    }

    .hello::after {
        top: auto; right: auto;
        bottom: -8px; left: 49px;
        border: 0;
        border-right: 1px solid rgba(var(--glow), .45);
        border-bottom: 1px solid rgba(var(--glow), .45);
    }

    /* Folded, the sun is in the middle of a narrower rail. */
    .app-body.is-folded .hello::after { left: 31px; }
}

.hello strong {
    display: block;
    margin-bottom: 7px;
    font-size: 1.08rem;
    font-weight: var(--fw-semibold);
}

.hello p {
    margin: 0 0 15px;
    font-size: .88rem;
    line-height: 1.55;
    color: rgba(226, 227, 232, .92);
}

.hello button { width: 100%; padding: 10px 14px; border-radius: 10px; }

/* While it is up, the row it is pointing at breathes, so the eye goes there
   rather than reading the sentence and then hunting. It is one ring fading in
   and out — opacity, which costs nothing to animate — and it goes with the
   card. */
.is-greeting .app-me::after {
    content: '';
    position: absolute;
    left: 2px; right: 2px; top: 10px; bottom: -4px;
    border: 1px solid rgba(var(--glow), .85);
    border-radius: 14px;
    pointer-events: none;
    animation: nudge 2.1s ease-in-out infinite;
}

@keyframes nudge {
    0%, 100% { opacity: 0; }
    45%      { opacity: .9; }
}

@keyframes hello-in {
    from { opacity: 0; transform: translateY(10px); }
    to   { opacity: 1; transform: none; }
}

.app-ghost {
    padding: 8px 14px;
    font: inherit;
    font-size: .78rem;
    color: var(--text-secondary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .09);
    border-radius: 10px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.app-ghost:hover { color: var(--text-primary); border-color: var(--border-hover); }
.app-danger:hover { color: #ff8b6b; border-color: rgba(255, 107, 53, .45); }

/* --- main --------------------------------------------------------------- */
.app-main {
    margin-left: calc(268px + var(--side-gap) * 2);
    transition: margin-left var(--fold-ms) var(--fold-ease);
    /* dvh, so that the messages view ends where the phone's screen does rather
       than under its address bar, taking the box you type in with it. */
    min-height: 100vh;
    min-height: 100dvh;
    display: flex;
    flex-direction: column;
    /* Nothing on a page is ever wider than the page, and if something one day
       is, it is cut at the edge rather than letting a phone slide the whole
       screen sideways under a thumb. Clip rather than hidden, so this is not a
       scrolling box and the bar at the top stays stuck where it is. */
    overflow-x: clip;
}

.app-top {
    position: sticky; top: 0; z-index: 20;
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 18px 32px;
    background: none;
    border: 0;
}

/* Who is signed in and the way out, on one line: the name opens a page, the
   door ends the visit, and the rule between them is drawn by the door. */
.top-who { display: flex; align-items: center; flex: 0 0 auto; }

/* Read, not seen. Clipped to a pixel rather than display: none, because a
   heading that is not rendered at all is a heading a screen reader skips. */
.app-top-title {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* Seen after all on a tool's page, beside the arrow back, as big as the name
   over any other page, which a tool's page then does not have. On a phone it
   stands where the logo would. Not clipped, so the i beside it can open. */
body.is-titled .app-top-title {
    position: static;
    width: auto; height: auto;
    margin: 0;
    overflow: visible;
    clip-path: none;
    flex: 0 1 auto;
    min-width: 0;
    font-size: 1.32rem;
    font-weight: var(--fw-semibold);
    letter-spacing: -.01em;
}

body.is-titled .app-top-brand { display: none; }

/* What the i holds is in the plain weight of the page, not the title's. Up
   here there is no room above the i, so it hangs below it; on a phone it is
   the card along the foot of the screen, as every other one is. */
.app-top-title .why-i { vertical-align: middle; margin-left: 8px; }

.app-top-title .why-more { font-weight: var(--fw-regular); letter-spacing: 0; }

@media (min-width: 621px) {
    .app-top-title .why-more { top: calc(100% + 9px); bottom: auto; }
}

/* The logo in its place, and only where there is no sidebar holding one. */
.app-top-brand { display: none; flex: 1; min-width: 0; }
.app-top-brand img { width: 108px; height: auto; display: block; }

/* The title used to be what held the two ends of the bar apart. With it out of
   the way this does it instead, and says what it has to say in the space. */
.app-top-status { flex: 1; min-width: 0; font-size: .8rem; color: var(--text-secondary); }

/* --- the bell ------------------------------------------------------------ */
/* What everybody else did on the grids you are on. Your own doing is not in
   here: a bell that lights up at your own comment is noise. */
.bell-wrap { position: relative; flex: 0 0 auto; }

.bell {
    position: relative;
    display: grid;
    place-items: center;
    width: 38px; height: 38px;
    color: var(--text-secondary);
    background: none;
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 12px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.bell:hover,
.bell[aria-expanded="true"] {
    color: var(--text-primary);
    background: rgba(255, 255, 255, .06);
    border-color: var(--border-hover);
}

.bell svg {
    width: 18px; height: 18px;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
}

/* The light. It is the whole point of the icon, so it takes the accent colour
   and sits proud of the corner rather than inside it. */
.bell-dot {
    position: absolute; top: -5px; right: -5px;
    min-width: 18px;
    padding: 1px 5px;
    font-size: .68rem;
    font-weight: var(--fw-semibold);
    line-height: 1.45;
    text-align: center;
    color: var(--bg-dark);
    background: var(--accent-orange);
    border: 2px solid #0e0e13;
    border-radius: 999px;
    animation: bell-wake .45s cubic-bezier(0.25, 1, 0.5, 1);
}

@keyframes bell-wake {
    from { transform: scale(.3); opacity: 0; }
    to   { transform: scale(1); opacity: 1; }
}

.bell-panel {
    position: absolute; top: calc(100% + 10px); right: 0;
    z-index: 60;
    width: min(360px, calc(100vw - 32px));
    max-height: min(460px, 70vh);
    overflow-y: auto;
    background: rgba(20, 20, 26, .98);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 16px;
    box-shadow: 0 26px 60px rgba(0, 0, 0, .55);
}

.bell-head {
    position: sticky; top: 0;
    margin: 0;
    padding: 14px 16px 10px;
    font-size: .72rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: rgba(184, 186, 194, .65);
    background: rgba(20, 20, 26, .98);
}

.bell-list { list-style: none; margin: 0; padding: 0 8px 8px; display: grid; gap: 2px; }

.bell-item {
    display: grid;
    grid-template-columns: 38px minmax(0, 1fr);
    align-items: center;
    gap: 11px;
    width: 100%;
    padding: 10px;
    font: inherit;
    text-align: left;
    color: var(--text-primary);
    background: none;
    border: none;
    border-radius: 11px;
    cursor: pointer;
}

.bell-item:hover { background: rgba(255, 255, 255, .06); }

/* Unread ones carry the colour of the light that announced them. */
.bell-item.is-new { background: rgba(255, 107, 53, .07); }
.bell-item.is-new:hover { background: rgba(255, 107, 53, .12); }

/* The picture the event is about. A like says which one of a carousel it was,
   which is the whole reason this is a thumbnail and not a plain sentence. */
.bell-thumb {
    width: 38px; height: 38px;
    overflow: hidden;
    background: rgba(255, 255, 255, .05);
    border-radius: 9px;
}

.bell-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

.bell-thumb svg {
    width: 100%; height: 100%;
    padding: 10px;
    color: rgba(184, 186, 194, .55);
    fill: none; stroke: currentColor; stroke-width: 1.8;
    stroke-linecap: round; stroke-linejoin: round;
}

.bell-said { min-width: 0; }

.bell-what {
    display: block;
    font-size: .83rem;
    line-height: 1.45;
    color: var(--text-secondary);
}

.bell-what strong { font-weight: var(--fw-medium); color: var(--text-primary); }

.bell-when {
    display: block;
    margin-top: 3px;
    overflow: hidden;
    font-size: .71rem;
    color: rgba(184, 186, 194, .6);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.bell-none { padding: 4px 10px 14px; font-size: .82rem; color: rgba(184, 186, 194, .6); }

.app-view { padding: 28px 32px 80px; }

/* The name of a screen, above the screen. It used to be the head of the first
   panel, which made every page open with a box whose first job was to repeat
   the bar. Out here it reads as the title of everything below it rather than
   of the one card it happened to be inside. */
.view-head { margin: 0 4px 18px; }

.view-head h2 {
    font-size: 1.32rem;
    font-weight: var(--fw-semibold);
    letter-spacing: -.01em;
}

.view-head p {
    margin-top: 6px;
    font-size: .84rem;
    line-height: 1.6;
    color: var(--text-secondary);
}

/* The one thing a page is chiefly for. White with square corners it belonged
   to the marketing site rather than to this one; here it takes the light the
   person chose, with the corners everything else in the portal has.
   Unlit it is the same quiet fill as a selected row, so it still reads as the
   button that matters without borrowing a colour nobody asked for. */
.btn-glow {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 12px 20px;
    font: inherit;
    font-size: .9rem;
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    background: rgba(255, 255, 255, .09);
    border: 1px solid rgba(255, 255, 255, .13);
    border-radius: 14px;
    cursor: pointer;
    transition: background .2s, border-color .2s;
}

.btn-glow:hover { background: rgba(255, 255, 255, .14); }

html.is-lit .btn-glow {
    background: linear-gradient(135deg,
        rgba(var(--glow), min(calc(var(--glow-a) * 1.15), .52)) 0%,
        rgba(var(--glow-2), min(calc(var(--glow-a) * .72), .34)) 100%);
    border-color: rgba(var(--glow), min(calc(var(--glow-a) * .9), .42));
}

html.is-lit .btn-glow:hover {
    background: linear-gradient(135deg,
        rgba(var(--glow), min(calc(var(--glow-a) * 1.4), .62)) 0%,
        rgba(var(--glow-2), min(calc(var(--glow-a) * .9), .42)) 100%);
}

/* The action sits between the heading and the list, out in the open where the
   heading is, rather than inside the box it is about to add a row to. */
.view-do { margin-bottom: 18px; }
.view-do .do-plus {
    width: 17px; height: 17px;
    fill: none; stroke: currentColor; stroke-width: 2;
    stroke-linecap: round;
}

/* --- your own page -------------------------------------------------------
   A page about a person, laid out like one: a cover of the portal's own two
   colours, the face large across the foot of it, the name beside the face and
   the two things you can do right under the name. The house's card follows as
   tiles, which read at a glance where a column of rows had to be read down. */
.me-page { max-width: 1080px; margin: 0 auto; }

.me-hero { position: relative; margin-bottom: 44px; }

/* A long, low band in the two colours of whoever the page belongs to: the
   last light they lit their room with, set on the element as --cover-1 and
   --cover-2. Nelyce's own pair until they have lit it. Fixed strengths rather
   than the room's brightness, so it is the same cover with the lamp off and
   the same cover whoever is looking at it. */
.me-cover {
    --cover-1: 255, 107, 53;
    --cover-2: 236, 72, 153;
    position: relative;
    height: 190px;
    border-radius: 26px;
    border: 1px solid rgba(255, 255, 255, .08);
    /* Everything in it is the two colours of its owner's light: the glows,
       and the night under them, which is those same two colours sunk almost
       to black rather than a purple of its own. A green room has a green
       night, and a dark one a graphite night with silver over it. */
    background:
        radial-gradient(60% 120% at 22% 0%, rgba(var(--cover-2), .55), transparent 70%),
        radial-gradient(55% 140% at 78% 100%, rgba(var(--cover-1), .5), transparent 70%),
        radial-gradient(50% 100% at 100% 0%, rgba(var(--cover-2), .45), transparent 70%),
        radial-gradient(40% 90% at 50% 55%, rgba(var(--cover-1), .14), transparent 75%),
        linear-gradient(120deg,
            color-mix(in srgb, rgb(var(--cover-1)) 14%, #0c0b11) 0%,
            color-mix(in srgb, rgb(var(--cover-2)) 20%, #0c0b11) 50%,
            color-mix(in srgb, rgb(var(--cover-1)) 10%, #0a090e) 100%);
}

.me-body {
    display: flex;
    align-items: center;
    gap: 28px;
    margin-top: -76px;
    padding: 0 40px;
}

/* The one way into changing anything about your own profile, in the corner
   of the cover: glass over the colour, so it belongs to the cover rather than
   sitting on it. */
.me-edit {
    position: absolute;
    top: 16px; right: 16px;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 8px 14px 8px 12px;
    font: inherit;
    font-size: .78rem;
    font-weight: var(--fw-medium);
    color: #fff;
    background: rgba(10, 8, 16, .34);
    border: 1px solid rgba(255, 255, 255, .18);
    border-radius: 999px;
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
    cursor: pointer;
    transition: var(--transition-smooth);
}

.me-edit .fold-pen { width: 14px; height: 14px; }
.me-edit:hover { background: rgba(10, 8, 16, .52); border-color: rgba(255, 255, 255, .32); }
.me-edit:focus-visible { outline: 2px solid rgba(255, 255, 255, .5); outline-offset: 2px; }

/* The face, over the foot of the cover. */
.me-face-wrap { position: relative; flex: 0 0 auto; }

.me-face {
    position: relative;
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 216px; height: 216px;
    overflow: hidden;
    font-size: 3.6rem;
    font-weight: var(--fw-semibold);
    color: rgb(var(--face, 255, 255, 255));
    background: #15121d;
    border: 1px solid rgba(255, 255, 255, .14);
    border-radius: 48px;
    box-shadow: 0 24px 48px rgba(0, 0, 0, .45);
}

/* The tint sits on the dark rather than instead of it, so initials never
   show the cover through the face. */
.me-face:not(.has-photo) {
    background: linear-gradient(rgba(var(--face, 255, 255, 255), .16),
                                rgba(var(--face, 255, 255, 255), .16)), #15121d;
}

.me-face img { width: 100%; height: 100%; object-fit: cover; display: block; }


/* --- framing a new face ---------------------------------------------------- */
.modal-crop { width: min(420px, 94vw); padding: 30px 28px 24px; }

/* The frame is the face's own shape, so what is framed is what is shown. */
.crop-stage {
    position: relative;
    width: 100%;
    aspect-ratio: 1;
    overflow: hidden;
    background: #0d0b12;
    border-radius: 22%;
    cursor: grab;
    touch-action: none;
    user-select: none;
}

.crop-stage.is-grabbing { cursor: grabbing; }

.crop-img {
    position: absolute;
    left: 50%; top: 50%;
    max-width: none;
    pointer-events: none;
    user-select: none;
    -webkit-user-drag: none;
}

/* A hairline round the frame, drawn over the picture so it is always seen. */
.crop-stage::after {
    content: '';
    position: absolute; inset: 0;
    border: 1px solid rgba(255, 255, 255, .16);
    border-radius: inherit;
    pointer-events: none;
}

.crop-zoom { margin: 20px 0 2px; touch-action: none; }

/* The face is well up on the cover and the words stand in the middle of the
   part of it that is below. Padding the top by exactly what is on the cover
   is what does it: centred with that on, the words are centred on the rest,
   and they can never rise onto the cover however tall they run. */
.me-intro { min-width: 0; padding-top: 76px; }

.me-name {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 12px;
    font-size: 1.55rem;
    font-weight: var(--fw-semibold);
    line-height: 1.15;
    letter-spacing: -.02em;
}

.me-badge {
    padding: 4px 9px;
    font-size: .68rem;
    font-weight: var(--fw-medium);
    letter-spacing: 0;
    color: #fff;
    background: linear-gradient(120deg, rgb(var(--glow)), rgb(var(--glow-2)));
    border-radius: 8px;
}


/* What can be done from somebody else's profile, under their name. */
.me-acts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }

.me-job { margin-top: 6px; font-size: .95rem; color: var(--text-primary); }
.me-handle { margin-top: 4px; font-size: .84rem; color: var(--text-secondary); }

/* --- the tabs under a face ---------------------------------------------------
   A client's or a partner's own page: the same panels it always had, one per
   tab, under the same head the admin's page has. A line of words with a bar
   under the one that is open, sitting on a hairline across the page. */
.me-tabs {
    display: flex;
    gap: 6px;
    margin: 0 0 26px;
    /* Sideways only, for the page that has more tabs than a phone is wide.
       Saying so for x alone makes y scroll as well, so y is shut outright,
       and the hairline is drawn inside the bar rather than as a border, so
       the bar under the open tab sits on it without reaching past the edge. */
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    box-shadow: inset 0 -1px 0 rgba(255, 255, 255, .08);
}

.me-tabs::-webkit-scrollbar { display: none; }

.me-tab {
    position: relative;
    flex: 0 0 auto;
    padding: 12px 14px 14px;
    font: inherit;
    font-size: .88rem;
    font-weight: var(--fw-medium);
    white-space: nowrap;
    color: var(--text-secondary);
    background: none;
    border: 0;
    cursor: pointer;
    transition: color .2s ease;
}

.me-tab::after {
    content: '';
    position: absolute;
    left: 14px; right: 14px; bottom: 0;
    height: 2px;
    border-radius: 2px;
    background: linear-gradient(90deg, rgb(var(--glow)), rgb(var(--glow-2)));
    opacity: 0;
    transform: scaleX(.4);
    transition: opacity .2s ease, transform .25s ease;
}

.me-tab:hover { color: var(--text-primary); }
.me-tab.is-on { color: var(--text-primary); }
.me-tab.is-on::after { opacity: 1; transform: none; }
.me-tab:focus-visible { outline: 2px solid rgba(255, 255, 255, .35); outline-offset: -2px; border-radius: 8px; }

/* Inside a tab the panels take the page's width, and a section cannot be
   folded: the tab is the fold, so the arrow goes and the head is a heading. */
.is-tabbed .panel { max-width: none; }
.is-tabbed .panel-fold > .panel-head { cursor: default; }
.is-tabbed .panel-fold > .panel-head .fold-arrow { display: none; }

/* The card: one box, a heading with its button across the top and a rule
   under them, then the details in three columns of a label over its answer.
   Nothing drawn round any single detail: the columns are the table. */
.me-card {
    padding: 22px 28px 28px;
    background: rgba(255, 255, 255, .03);
    border: 1px solid rgba(255, 255, 255, .08);
    border-radius: 22px;
}

.me-card-top {
    padding-bottom: 18px;
    border-bottom: 1px solid rgba(255, 255, 255, .08);
}

.me-card-edit {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    flex: 0 0 auto;
    padding: 7px 12px 7px 14px;
    font: inherit;
    font-size: .78rem;
    font-weight: var(--fw-medium);
    color: #fff;
    background: var(--accent-orange);
    border: 0;
    border-radius: 9px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.me-card-edit .fold-pen { width: 13px; height: 13px; }
.me-card-edit:hover { filter: brightness(1.08); transform: translateY(-1px); }
.me-card-edit:focus-visible { outline: 2px solid rgba(255, 255, 255, .5); outline-offset: 2px; }

/* The admin's second step, under Ασφάλεια. The QR code keeps its own white
   ground in both lights, since a scanner wants dark on white and nothing
   else. */
.two-body { padding-top: 18px; display: flex; flex-direction: column; align-items: flex-start; gap: 12px; }
.two-note { margin-top: 14px; font-size: .82rem; line-height: 1.6; color: var(--text-secondary); }
.two-note.is-warn { color: #ffb38a; }
.two-say { font-size: .86rem; line-height: 1.6; color: var(--text-secondary); }

.two-setup {
    display: flex;
    gap: 28px;
    align-items: flex-start;
    padding-top: 20px;
}
.two-qr {
    flex: 0 0 auto;
    width: 184px;
    height: 184px;
    padding: 6px;
    background: #fff;
    border-radius: 14px;
}
.two-qr svg { display: block; width: 100%; height: 100%; }

.two-steps {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
    padding-left: 20px;
    display: flex;
    flex-direction: column;
    gap: 16px;
    font-size: .86rem;
    line-height: 1.6;
    color: var(--text-secondary);
}
.two-steps li::marker { color: var(--accent-orange); font-weight: var(--fw-semibold); }

.two-key {
    display: block;
    margin-top: 8px;
    padding: 9px 12px;
    font: inherit;
    font-size: .9rem;
    font-variant-numeric: tabular-nums;
    letter-spacing: .06em;
    color: var(--text-primary);
    text-align: left;
    overflow-wrap: anywhere;
    background: rgba(255, 255, 255, .05);
    border: 1px dashed rgba(255, 255, 255, .16);
    border-radius: 10px;
    cursor: copy;
}
.two-key:hover { background: rgba(255, 255, 255, .08); }
.two-open { display: inline-block; margin-top: 8px; color: var(--accent-orange); font-weight: var(--fw-medium); }

.two-form { display: flex; gap: 8px; margin-top: 8px; max-width: 320px; }
.two-form .two-code { flex: 1 1 auto; min-width: 0; letter-spacing: .12em; font-variant-numeric: tabular-nums; }

.two-quiet {
    margin-top: 16px;
    padding: 0;
    font: inherit;
    font-size: .8rem;
    color: rgba(184, 186, 194, .7);
    background: none;
    border: 0;
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
}
.two-body .two-quiet { margin-top: 0; }
.two-quiet:hover { color: var(--text-primary); }

@media (max-width: 640px) {
    .two-setup { flex-direction: column; align-items: center; gap: 20px; }
    .two-steps { align-self: stretch; }
    .two-form { max-width: none; }
}

.me-card-top h2 { font-size: 1.15rem; font-weight: var(--fw-semibold); }
.me-card-top p { margin-top: 6px; font-size: .84rem; line-height: 1.6; color: var(--text-secondary); }

.me-facts {
    list-style: none;
    margin: 24px 0 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 24px 32px;
}

.me-facts li { display: flex; flex-direction: column; gap: 6px; min-width: 0; }

/* Every line of our card is a press that copies it. Still a label over its
   answer and nothing drawn round it until the pointer is on it; then a soft
   ground and the copy mark, so it says what it does as it is about to. On a
   screen with no pointer the mark is always there, faintly. */
.me-fact {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 6px;
    width: calc(100% + 20px);
    margin: -8px -10px;
    padding: 8px 34px 8px 10px;
    font: inherit;
    color: inherit;
    text-align: left;
    background: none;
    border: 0;
    border-radius: 10px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.me-fact .pay-icon {
    position: absolute;
    top: 50%; right: 10px;
    transform: translateY(-50%);
    opacity: 0;
    transition: opacity .2s ease;
}

.me-fact-val small { display: block; margin-top: 3px; font-size: .74rem; color: rgba(184, 186, 194, .55); }
.me-fact:hover { background: rgba(255, 255, 255, .05); }
.me-fact:hover .pay-icon,
.me-fact:focus-visible .pay-icon { opacity: 1; }
.me-fact:focus-visible { outline: 2px solid rgba(255, 255, 255, .35); outline-offset: 0; }
.me-fact .me-fact-val { font-variant-numeric: tabular-nums; }

@media (hover: none) {
    .me-fact .pay-icon { opacity: .45; }
}

.me-fact-key { font-size: .74rem; color: rgba(184, 186, 194, .6); }
.me-fact-val { font-size: .95rem; color: var(--text-primary); overflow-wrap: anywhere; }

/* On a phone it all comes to the middle: the cover shorter, the face smaller
   and still over its foot, everything else under it in one column. */
@media (max-width: 900px) {
    .me-hero { margin-bottom: 32px; }
    .me-cover { height: 140px; border-radius: 20px; }
    .me-body {
        flex-direction: column;
        align-items: center;
        gap: 16px;
        margin-top: -64px;
        padding: 0;
        text-align: center;
    }
    .me-face { width: 124px; height: 124px; font-size: 2.4rem; border-radius: 32px; }
    .me-intro { padding-top: 0; }
    .me-name { justify-content: center; font-size: 1.6rem; }
    .me-acts { justify-content: center; }
    .me-card { padding: 18px 18px 22px; border-radius: 18px; }
    /* One column: an email or an address is as wide as the card, and two
       columns of them wrap into four lines each. */
    .me-facts { grid-template-columns: minmax(0, 1fr); gap: 14px; margin-top: 16px; }
    .me-card-top { padding-bottom: 14px; }
    .me-card-top h2 { font-size: 1.02rem; }
    .me-card-top p { font-size: .8rem; }
    .me-fact-val { font-size: .9rem; }
    .me-edit { top: 12px; right: 12px; padding: 7px 12px 7px 10px; }
    .me-tabs { margin: 0 0 20px; }
    .me-tab { padding: 11px 10px 13px; font-size: .84rem; }
    .me-tab::after { left: 10px; right: 10px; }
}

.app-blank { max-width: 520px; margin: 60px auto; text-align: center; }

/* The messages screen holds still at every size: the name at the top and the
   box at the bottom stay where they are, and the conversation between them is
   the only thing that moves. A phone had this already; there is no reason a
   desktop should be the one that scrolls its whole window. */
body.is-messages { overflow: hidden; }

body.is-messages .app-main {
    height: 100vh;
    height: 100dvh;
    min-height: 0;
}

/* --- messages ------------------------------------------------------------- */
/* Two conversations that must never be mistaken for each other: the project
   thread, which everybody on the grid reads, and a private one, which two
   people read. They are apart in the list, apart on the server, and each one
   says in plain words who can see it before a word is typed. */
.app-chat {
    display: grid;
    grid-template-columns: 300px minmax(0, 1fr);
    flex: 1;
    min-height: 0;
    padding: 0;
}

.chat-side {
    display: flex;
    flex-direction: column;
    min-height: 0;
    overflow-y: auto;
    padding: 20px 14px 24px;
    border-right: 1px solid rgba(255, 255, 255, .07);
}

/* Two kinds of conversation, told apart before either is opened. A dot on a tab
   says the other kind has something waiting, so neither is the one being
   missed. */
.chat-kinds {
    display: flex;
    gap: 4px;
    margin-bottom: 12px;
    padding: 4px;
    background: rgba(255, 255, 255, .04);
    border: 1px solid rgba(255, 255, 255, .07);
    border-radius: 13px;
}

.chat-kinds button {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    flex: 1;
    min-width: 0;
    padding: 8px 6px;
    font: inherit;
    font-size: .78rem;
    color: var(--text-secondary);
    background: none;
    border: none;
    border-radius: 10px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.chat-kinds button:hover { color: var(--text-primary); }

.chat-kinds button.is-on {
    color: var(--text-primary);
    background: rgba(255, 255, 255, .08);
}

.chat-kinds svg {
    flex: 0 0 auto;
    width: 15px; height: 15px;
    fill: none; stroke: currentColor; stroke-width: 1.8;
    stroke-linecap: round; stroke-linejoin: round;
}

.chat-kinds button[data-kind="people"].is-on { color: #9fd4b8; }

.chat-pip {
    position: absolute; top: 5px; right: 8px;
    width: 7px; height: 7px;
    background: var(--accent-orange);
    border-radius: 50%;
}

.chat-picks { display: grid; gap: 3px; }

.chat-pick {
    display: grid;
    grid-template-columns: 38px minmax(0, 1fr) auto;
    align-items: center;
    gap: 11px;
    padding: 9px 10px;
    font: inherit;
    text-align: left;
    color: var(--text-primary);
    background: none;
    border: 1px solid transparent;
    border-radius: 12px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.chat-pick:hover { background: rgba(255, 255, 255, .05); }

.chat-pick.is-on {
    background: rgba(255, 255, 255, .09);
    border-color: transparent;
}

/* --- the selection takes the colour of the light ---------------------------
   Nothing here until somebody has lit the portal, so a first visit opens grey
   and stays grey: the colour of a selection is not a decision anybody should
   have to arrive at having already made.
   Once they have chosen, the outline is that colour and carries a little of it
   outwards, the way the light at the top of the page does. The second colour
   of the pair sits on the title, so the row is drawn in both of them rather
   than only the first. */
html.is-lit .app-nav-item.is-on,
html.is-lit .chat-pick.is-on,
html.is-lit .app-nav-item.is-on.has-new {
    background: linear-gradient(135deg,
        rgba(var(--glow), min(calc(var(--glow-a) * .95), .46)) 0%,
        rgba(var(--glow-2), min(calc(var(--glow-a) * .58), .28)) 100%);
    border-color: transparent;
}

html.is-lit .app-nav-item.is-on:hover,
html.is-lit .chat-pick.is-on:hover,
html.is-lit .app-nav-item.is-on.has-new:hover {
    background: linear-gradient(135deg,
        rgba(var(--glow), min(calc(var(--glow-a) * 1.15), .56)) 0%,
        rgba(var(--glow-2), min(calc(var(--glow-a) * .74), .35)) 100%);
}

/* --- keeping the words off the colour ----------------------------------
   The fill rises with the light, so anything written on it has to rise too or
   the two meet in the middle. The title is white outright on a row that is
   filled, and the line under it climbs from the grey it has in the dark to
   nearly white at the top of the bar, which is where the fill is strongest.
   Both stop short of the fill's own ceiling, so the gap never closes. */
html.is-lit .app-nav-item.is-on .app-nav-text strong,
html.is-lit .chat-pick.is-on .chat-pick-text strong { color: #fff; }

html.is-lit .app-nav-item.is-on .app-nav-text small,
html.is-lit .chat-pick.is-on .chat-pick-text small {
    color: rgba(255, 255, 255, min(calc(.58 + var(--glow-a) * .52), .94));
}

/* Except when something is waiting on the row you are on. The peach is the
   only thing in the column that means "answer me", and it reads on any of
   these fills, so it keeps the title. */
html.is-lit .app-nav-item.is-on.has-new .app-nav-text strong,
html.is-lit .chat-pick.is-on.has-new .chat-pick-text strong { color: #ffc4a4; }

/* --- the lines that divide things --------------------------------------
   A rule across the sidebar is the one thing on the page long enough to show
   a gradient running along it, so the light falls on these the way it falls
   on the top of the page: strongest where it starts, gone by the far end.
   Every alpha is a small share of --glow-a, which is the bar under the
   swatches, so turning the light down takes these down with it and they never
   become a thing you notice. They are meant to be felt, not read. */
html.is-lit .app-nav > .is-cut,
html.is-lit .app-me {
    border-image: linear-gradient(90deg,
        rgba(var(--glow), calc(var(--glow-a) * .45)) 0%,
        rgba(var(--glow-2), calc(var(--glow-a) * .28)) 62%,
        rgba(255, 255, 255, .04) 100%) 1;
}

html.is-lit .app-top {
    border-image: linear-gradient(90deg,
        rgba(var(--glow), calc(var(--glow-a) * .40)) 0%,
        rgba(var(--glow-2), calc(var(--glow-a) * .24)) 55%,
        rgba(255, 255, 255, .035) 100%) 1;
}

/* --- and a little of it on the icons -----------------------------------
   Mixed into the colour the icon already has rather than replacing it, so a
   row that is not selected stays as quiet as it was and only leans towards the
   light. The row you are on leans further, because it is the one thing in the
   column that is allowed to.
   color-mix carries its own fallback: where it is not understood the whole
   declaration is dropped and the icon keeps its currentColor stroke. */
html.is-lit .app-nav-item svg,
html.is-lit .app-nav-new svg,
html.is-lit .chat-pick svg {
    stroke: color-mix(in srgb, rgb(var(--glow)) 34%, currentColor);
}

html.is-lit .app-nav-item.is-on svg,
html.is-lit .chat-pick.is-on svg {
    stroke: color-mix(in srgb, rgb(var(--glow)) 62%, currentColor);
}


.chat-pick.has-new .chat-pick-text strong { color: #ffc4a4; }
.chat-pick.has-new .chat-pick-text small { color: rgba(255, 196, 164, .75); }

/* The project thread wears the accent colour and a group of people; a private
   one wears initials and a padlock. You can tell them apart from across the
   room, which is the point. */
.chat-face {
    display: grid;
    place-items: center;
    width: 38px; height: 38px;
    font-size: .85rem;
    font-weight: var(--fw-medium);
    border-radius: 50%;
}

.chat-face.is-team {
    color: var(--bg-dark);
    background: linear-gradient(135deg, var(--accent-amber), var(--accent-orange));
    border-radius: 13px;
}

/* --- setting a face ------------------------------------------------------- */
.face-edit { display: flex; align-items: center; gap: 18px; margin: 6px 0 4px; }

.face-big {
    position: relative;
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 84px; height: 84px;
    font-size: 1.9rem;
    font-weight: var(--fw-semibold);
    color: rgb(var(--face, 255, 255, 255));
    background: rgba(var(--face, 255, 255, 255), .16);
    border-radius: 50%;
}

.face-acts { display: flex; flex-direction: column; align-items: flex-start; gap: 9px; }
.face-acts .btn { padding: 10px 18px; font-size: .84rem; }

.modal-me { width: min(480px, 94vw); padding: 30px 28px 24px; }

/* Long enough on a phone to need to scroll inside itself; said on both classes
   so it outranks the modal box's own overflow, which comes later. */
.modal-box.modal-me { max-height: 90vh; max-height: 90dvh; overflow-y: auto; }

/* The admin's panel carries the house's card too, laid out in two columns. */
.modal-me.is-wide { width: min(600px, 94vw); }

/* One rhythm for the whole panel: a name over each box, six pixels between
   them, the same gap between one box and the next, and a clear space before
   each new part. The names are said in plain letters, not small capitals:
   there are a lot of them, and a page of shouting labels is what tired the
   eye. */
.modal-me .face-edit { margin: 4px 0 26px; }
.modal-me .edit-label,
.me-field > span {
    display: block;
    margin-bottom: 7px;
    font-size: .8rem;
    letter-spacing: 0;
    text-transform: none;
    color: var(--text-secondary);
}
.modal-me .pw-head { margin-top: 18px; }
.modal-me #me-name-head { margin-top: 0; }
.modal-me .me-name-field,
.me-field input { padding: 11px 14px; font-size: .88rem; }

.me-house {
    margin-top: 32px;
    padding-top: 28px;
    border-top: 1px solid rgba(255, 255, 255, .08);
}

.me-house-head { margin: 0 0 20px; font-size: 1.05rem; font-weight: var(--fw-semibold); }

.me-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 18px 14px;
}

.me-field { display: block; min-width: 0; }
.me-field.is-wide { grid-column: 1 / -1; }

.me-field input {
    width: 100%;
    font: inherit;
    font-size: .88rem;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 11px;
    color-scheme: dark;
}

.me-field input:focus { outline: none; border-color: var(--accent-orange); }
.me-field input[data-bf="iban"] { font-variant-numeric: tabular-nums; letter-spacing: .02em; }

.me-house .pw-note { margin-top: 10px; }

/* Each part of the card starts well clear of the one above it, with its name
   and what it is for, and then its boxes. */
.me-part {
    margin: 34px 0 0;
    font-size: .95rem;
    font-weight: var(--fw-semibold);
    color: var(--text-primary);
}

.me-part + .pw-note { margin: 6px 0 18px; }
.me-part + .me-grid { margin-top: 16px; }

.me-bank + .me-bank { margin-top: 24px; }
.me-bank-name { margin: 0 0 12px; font-size: .8rem; font-weight: var(--fw-medium); color: var(--text-primary); opacity: .8; }
.me-bank-add { margin-top: 18px; }

@media (max-width: 560px) {
    .me-grid { grid-template-columns: minmax(0, 1fr); gap: 16px; }
    .modal-me .me-name-field,
    .me-field input { font-size: 16px; }
}

.chat-face { position: relative; }

.chat-face svg {
    width: 19px; height: 19px;
    fill: none; stroke: currentColor; stroke-width: 1.8;
    stroke-linecap: round; stroke-linejoin: round;
}

/* At their screen, or not. A dot in the corner of the face, where every
   messenger has put one for twenty years: green for here, grey for away. */
.chat-live {
    position: absolute; right: -1px; bottom: -1px;
    width: 11px; height: 11px;
    background: #4ade80;
    border: 2px solid #15151b;
    border-radius: 50%;
}

/* Away is a fact, not a warning, so it sits back rather than forward. */
.chat-live.is-away { background: #5c5e6a; }

.chat-head-face { position: relative; }
.chat-head-face .chat-live { border-color: #0e0e13; }

/* Said in words as well, under the name, for whoever does not read dots. */
.chat-head-text small.is-live { color: #7fc7a4; }

.chat-pick-text { min-width: 0; }

.chat-pick-text strong,
.chat-pick-text small {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.chat-pick-text strong { font-size: .88rem; font-weight: var(--fw-medium); }
.chat-pick-text small { margin-top: 2px; font-size: .74rem; color: rgba(184, 186, 194, .65); }

/* The conversation with whoever runs the place, at the head of the private
   list. Dark and quiet, one step up from the rows under it: a surface, a
   hairline, and their face in a thin ring of the first colour of their cover.
   The colours arrive on the element as --cover-1 and --cover-2, as they do on
   the cover of their profile, and the ring is all of them that shows. */
.chat-boss {
    --cover-1: 255, 107, 53;
    --cover-2: 236, 72, 153;
    display: grid;
    grid-template-columns: 40px minmax(0, 1fr) auto;
    align-items: center;
    gap: 12px;
    margin: 0;
    padding: 11px 12px 11px 11px;
    font: inherit;
    text-align: left;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .035);
    border: 1px solid rgba(255, 255, 255, .07);
    border-radius: 14px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.chat-boss .chat-face {
    width: 40px; height: 40px;
    box-shadow: 0 0 0 2px #121018, 0 0 0 3.5px rgba(var(--cover-1), .85);
}

.chat-boss .chat-pick-text strong { font-size: .88rem; font-weight: var(--fw-medium); }
.chat-boss .chat-pick-text small { color: rgba(184, 186, 194, .65); }

.chat-boss:hover { background: rgba(255, 255, 255, .06); border-color: rgba(255, 255, 255, .12); }
.chat-boss.is-on { background: rgba(255, 255, 255, .08); border-color: rgba(255, 255, 255, .14); }
.chat-boss.has-new .chat-pick-text strong { color: #ffc4a4; }
.chat-boss:focus-visible { outline: 2px solid rgba(255, 255, 255, .4); outline-offset: 2px; }

/* The admin's message to everybody, where the others have her card: a row
   like the rest, set on the same quiet surface so it reads as an action. */
.chat-loud {
    background: rgba(255, 255, 255, .035);
    border-color: rgba(255, 255, 255, .07);
}

.chat-loud:hover { background: rgba(255, 255, 255, .06); border-color: rgba(255, 255, 255, .12); }

/* A soft rule between the card and the conversations under it, fading out
   at both ends rather than stopping. */
.chat-boss-rule {
    display: block;
    height: 1px;
    margin: 12px 6px 10px;
    background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .09) 18%,
                                rgba(255, 255, 255, .09) 82%, transparent);
}

.chat-count {
    display: grid;
    place-items: center;
    min-width: 19px; height: 19px;
    padding: 0 5px;
    font-size: .67rem;
    font-weight: var(--fw-semibold);
    color: var(--bg-dark);
    background: var(--accent-orange);
    border-radius: 999px;
}

.chat-none { padding: 4px 10px; font-size: .8rem; line-height: 1.5; color: rgba(184, 186, 194, .6); }

/* --- the conversation itself ---------------------------------------------- */
.chat-room { display: flex; flex-direction: column; min-width: 0; min-height: 0; }

.chat-head {
    display: flex;
    align-items: center;
    gap: 11px;
    flex: 0 0 auto;
    padding: 14px 24px;
    border-bottom: 1px solid rgba(255, 255, 255, .07);
}

.chat-back {
    display: none;
    place-items: center;
    width: 34px; height: 34px;
    color: var(--text-secondary);
    background: none;
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 10px;
    cursor: pointer;
}

.chat-back svg {
    width: 17px; height: 17px;
    fill: none; stroke: currentColor; stroke-width: 2;
    stroke-linecap: round; stroke-linejoin: round;
}

.chat-head-face {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 36px; height: 36px;
    font-size: .85rem;
    font-weight: var(--fw-medium);
    border-radius: 50%;
}

.chat-head-face.is-team {
    color: var(--bg-dark);
    background: linear-gradient(135deg, var(--accent-amber), var(--accent-orange));
    border-radius: 12px;
}

.chat-head-face svg {
    width: 18px; height: 18px;
    fill: none; stroke: currentColor; stroke-width: 1.8;
    stroke-linecap: round; stroke-linejoin: round;
}

.chat-head-text { min-width: 0; flex: 1 1 auto; }

/* The way from a private conversation to the person in it, at the far end
   of the bar that names them. */
.chat-head-profile {
    flex: 0 0 auto;
    padding: 7px 13px;
    font: inherit;
    font-size: .76rem;
    white-space: nowrap;
    color: var(--text-secondary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .09);
    border-radius: 10px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.chat-head-profile:hover { color: var(--text-primary); border-color: var(--border-hover); }
.chat-head-profile:focus-visible { outline: 2px solid rgba(255, 255, 255, .35); outline-offset: 2px; }
.chat-head-text strong { display: block; font-size: .95rem; font-weight: var(--fw-semibold); }
.chat-head-text small { display: block; font-size: .74rem; color: rgba(184, 186, 194, .65); }

/* The sentence that says who is reading. It is the first thing in the room and
   it never goes away, because forgetting which of the two you are in is the
   one mistake this whole screen exists to prevent. */
.chat-banner {
    display: flex;
    align-items: flex-start;
    gap: 9px;
    flex: 0 0 auto;
    margin: 0;
    padding: 11px 24px;
    font-size: .79rem;
    line-height: 1.45;
}

.chat-banner svg {
    flex: 0 0 auto;
    width: 16px; height: 16px;
    fill: none; stroke: currentColor; stroke-width: 1.8;
    stroke-linecap: round; stroke-linejoin: round;
}

.chat-banner.is-team {
    color: #ffc9a3;
    background: rgba(255, 107, 53, .1);
    border-bottom: 1px solid rgba(255, 107, 53, .2);
}

.chat-banner.is-private {
    color: #a9d8c2;
    background: rgba(76, 175, 130, .1);
    border-bottom: 1px solid rgba(76, 175, 130, .22);
}

/* --- the three faces of the project thread --------------------------------- */
/* Talk, the links everybody needs to hand, and the ideas nobody has decided
   on. A private conversation has none of these: it is a conversation. */
.room-tabs {
    display: flex;
    gap: 4px;
    flex: 0 0 auto;
    padding: 10px 24px 0;
}

.room-tabs button {
    padding: 8px 14px 10px;
    font: inherit;
    font-size: .82rem;
    color: var(--text-secondary);
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    cursor: pointer;
    transition: color .25s, border-color .25s;
}

.room-tabs button:hover { color: var(--text-primary); }

.room-tabs button.is-on {
    color: var(--text-primary);
    border-bottom-color: var(--accent-orange);
}

.room-pane { display: flex; flex-direction: column; flex: 1; min-width: 0; min-height: 0; }
.room-scroll { flex: 1; min-height: 0; overflow-y: auto; padding: 16px 24px 22px; }

/* On a phone the form for adding one is a button until somebody wants it. The
   screen is small and a list is what they came to read; three empty boxes at
   the top of it are three boxes in the way. */
.room-open { display: none; }
.room-cancel { display: none; }

/* --- useful links ---------------------------------------------------------- */
.link-new {
    display: flex;
    flex-wrap: wrap;
    gap: 9px;
    flex: 0 0 auto;
    padding: 16px 24px 0;
}

.link-new input {
    flex: 1 1 200px;
    min-width: 0;
    padding: 10px 13px;
    font: inherit;
    font-size: .85rem;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 12px;
}

.link-new input:focus { outline: none; border-color: var(--accent-orange); }
.link-new .btn { flex: 0 0 auto; padding: 10px 20px; font-size: .84rem; }

.link-new .link-group { flex: 0 1 190px; }

.link-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }

/* A heading over the links that were put under it, and nothing at all over the
   ones that were not. It exists for as long as a link is still under it and no
   longer, which is why there is nowhere to make one and nowhere to delete one. */
.link-head {
    margin: 0;
    padding: 14px 4px 2px;
    font-size: .7rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: rgba(184, 186, 194, .6);
}

.link-list li.link-head:first-child { padding-top: 2px; }

.link-list li[data-link] {
    position: relative;
    display: grid;
    grid-template-columns: 26px 34px minmax(0, 1fr) auto;
    align-items: center;
    gap: 10px;
    padding: 11px 12px 11px 6px;
    background: rgba(255, 255, 255, .03);
    border: 1px solid rgba(255, 255, 255, .07);
    border-radius: 13px;
}

/* Faint until the row is pointed at, because it is a cue rather than a
   control: the whole row is what you actually pick up. */
/* The handle, and the only part of the row that a drag begins on. The rest of
   it is a link, and a link is for following. */
.link-grip {
    display: grid;
    place-items: center;
    align-self: stretch;
    width: 26px;
    color: rgba(184, 186, 194, .3);
    border-radius: 8px;
    cursor: grab;
    user-select: none;
    -webkit-user-select: none;
    transition: color .2s, background .2s;
}

.link-grip:hover {
    color: rgba(184, 186, 194, .9);
    background: rgba(255, 255, 255, .06);
}

.link-list li[data-link]:hover .link-grip { color: rgba(184, 186, 194, .65); }

.link-grip svg {
    width: 15px; height: 15px;
    fill: none; stroke: currentColor; stroke-width: 1.8;
    stroke-linecap: round;
}

/* --- carrying a link ------------------------------------------------------- */
/* The same gesture as a square on the grid: a mouse only has to move, a finger
   has to hold still first so that an ordinary swipe still scrolls. A list has
   an order rather than places, though, so a row is put between two others
   instead of trading with one. */
.link-list li.is-lifting { opacity: .3; }

.link-list li.is-before::before,
.link-list li.is-after::after {
    content: "";
    position: absolute;
    left: 10px; right: 10px;
    height: 2px;
    background: var(--accent-orange);
    border-radius: 2px;
}

.link-list li.is-before::before { top: -5px; }
.link-list li.is-after::after { bottom: -5px; }

body.is-dragging-link,
body.is-dragging-link .link-grip { cursor: grabbing; }

.link-ghost {
    position: fixed;
    top: 0; left: 0;
    z-index: 120;
    display: grid;
    grid-template-columns: 26px 34px minmax(0, 1fr);
    align-items: center;
    gap: 12px;
    padding: 11px 12px;
    pointer-events: none;
    background: rgba(28, 28, 35, .97);
    border: 1px solid rgba(255, 107, 53, .4);
    border-radius: 13px;
    box-shadow: 0 22px 50px rgba(0, 0, 0, .55);
    opacity: .94;
    will-change: transform;
}

.link-ghost .link-text a { pointer-events: none; }

.link-list li[data-link]:hover { border-color: rgba(255, 255, 255, .14); }

.link-face {
    display: grid;
    place-items: center;
    width: 34px; height: 34px;
    color: var(--accent-orange);
    background: rgba(255, 107, 53, .1);
    border-radius: 10px;
}

.link-face svg {
    width: 16px; height: 16px;
    fill: none; stroke: currentColor; stroke-width: 1.8;
    stroke-linecap: round; stroke-linejoin: round;
}

.link-text { min-width: 0; }

.link-text a,
.link-text small {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.link-text a {
    -webkit-user-drag: none;
    font-size: .88rem;
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    text-decoration: none;
}

.link-text a:hover { color: var(--accent-orange); }
.link-text small { margin-top: 2px; font-size: .73rem; color: rgba(184, 186, 194, .62); }

.link-acts { display: flex; gap: 6px; }

.link-drop {
    padding: 6px 10px;
    font: inherit;
    font-size: .73rem;
    color: var(--text-secondary);
    background: none;
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 9px;
    cursor: pointer;
}

.link-drop:hover { color: #ff8b6b; border-color: rgba(255, 107, 53, .45); }
.link-edit-btn:hover { color: var(--text-primary); border-color: var(--border-hover); }

/* The row, while it is being renamed or moved under another heading. */
.link-edit { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; grid-column: 2 / -1; }

.link-edit input {
    flex: 1 1 150px;
    min-width: 0;
    padding: 8px 11px;
    font: inherit;
    font-size: .82rem;
    color: var(--text-primary);
    background: rgba(0, 0, 0, .25);
    border: 1px solid rgba(255, 107, 53, .4);
    border-radius: 9px;
}

.link-edit input:focus { outline: none; border-color: var(--accent-orange); }
.link-edit button { flex: 0 0 auto; }

/* --- brainstorming --------------------------------------------------------- */
/* Paper, lit from behind. Five highlighter tones, kept faint enough that a
   wall of them still reads as a wall of writing rather than of colour. */
:root {
    --note-amber: 255, 196, 61;
    --note-rose:  255, 122, 162;
    --note-mint:  84, 214, 155;
    --note-sky:   96, 176, 255;
    --note-lilac: 178, 146, 255;
}

.idea-new {
    display: grid;
    gap: 9px;
    flex: 0 0 auto;
    padding: 16px 24px 0;
}

/* --- writing a note ---------------------------------------------------------
   A box that takes formatting rather than a box that takes text. What comes
   out of it is markup, and what happens to that markup before anybody else's
   browser sees it is api/_rich.js, which is where the safety lives: nothing in
   this stylesheet is a defence. */
.ink {
    width: 100%;
    min-height: 74px;
    max-height: 260px;
    overflow-y: auto;
    padding: 11px 14px;
    font: inherit;
    font-size: .87rem;
    line-height: 1.6;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 0 0 13px 13px;
    border-top: none;
}

.ink:focus { outline: none; border-color: var(--accent-orange); }

/* A contenteditable has no placeholder of its own, so it borrows one. */
.ink:empty::before {
    content: attr(data-empty);
    color: rgba(184, 186, 194, .5);
    pointer-events: none;
}

.ink ul, .ink ol { margin: 6px 0; padding-left: 22px; }
.ink li { margin: 2px 0; }

.ink-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 3px;
    padding: 6px 7px;
    background: rgba(255, 255, 255, .04);
    border: 1px solid rgba(255, 255, 255, .1);
    border-bottom: none;
    border-radius: 13px 13px 0 0;
}

.ink-bar button {
    display: grid;
    place-items: center;
    min-width: 28px;
    height: 28px;
    padding: 0 7px;
    font: inherit;
    font-size: .82rem;
    color: var(--text-secondary);
    background: none;
    border: none;
    border-radius: 7px;
    cursor: pointer;
}

.ink-bar button:hover { color: var(--text-primary); background: rgba(255, 255, 255, .08); }

.ink-bar select {
    height: 28px;
    max-width: 120px;
    padding: 0 6px;
    font: inherit;
    font-size: .74rem;
    color: var(--text-secondary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 7px;
    cursor: pointer;
}

/* A colour has to be seen to be chosen. A menu cannot show one on a Mac,
   where the list is drawn by the system and ignores what the page asks for, so
   the colours are the buttons. */
.ink-dab {
    width: 19px; height: 19px;
    padding: 0;
    background: var(--dab);
    border: 1px solid rgba(255, 255, 255, .25);
    border-radius: 50%;
    cursor: pointer;
    transition: transform .15s;
}

.ink-dab:hover { transform: scale(1.18); border-color: rgba(255, 255, 255, .7); }

.ink-bar .ink-dab { min-width: 0; height: 19px; }

.ink-sep {
    width: 1px;
    height: 17px;
    margin: 0 4px;
    background: rgba(255, 255, 255, .12);
}

/* What a finished note looks like on the wall. The tags it may contain are the
   ones the server let through and no others. */
.idea-said.is-rich { white-space: normal; }
.idea-said.is-rich ul, .idea-said.is-rich ol { margin: 6px 0; padding-left: 20px; }
.idea-said.is-rich li { margin: 2px 0; }
.idea-said.is-rich p, .idea-said.is-rich div { margin: 0; }

.idea-bits { display: flex; flex-wrap: wrap; align-items: center; gap: 9px; }

.idea-bits input {
    flex: 1 1 180px;
    min-width: 0;
    padding: 9px 13px;
    font: inherit;
    font-size: .82rem;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 11px;
}

.idea-new textarea:focus,
.idea-bits input:focus { outline: none; border-color: var(--accent-orange); }
.idea-bits .btn { flex: 0 0 auto; padding: 9px 18px; font-size: .82rem; }

.idea-colours { display: flex; gap: 6px; flex: 0 0 auto; }

.idea-swatch {
    width: 24px; height: 24px;
    padding: 0;
    background: rgb(var(--tone));
    border: 2px solid transparent;
    border-radius: 8px;
    cursor: pointer;
    opacity: .55;
    transition: opacity .2s, transform .2s;
}

.idea-swatch:hover { opacity: .85; }

.idea-swatch[aria-checked="true"] {
    opacity: 1;
    border-color: rgba(255, 255, 255, .85);
    transform: scale(1.08);
}

/* Tags are whatever was typed on the note. The row of them is built from what
   is on the wall, so it is empty until somebody invents the first one. */
.idea-filter {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    flex: 0 0 auto;
    padding: 14px 24px 0;
}

.idea-chip {
    padding: 5px 11px;
    font: inherit;
    font-size: .74rem;
    color: var(--text-secondary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 999px;
    cursor: pointer;
}

.idea-chip:hover { color: var(--text-primary); }

.idea-chip.is-on {
    color: var(--bg-dark);
    background: var(--accent-orange);
    border-color: transparent;
}

.idea-wall { columns: 260px; column-gap: 12px; }

.idea {
    break-inside: avoid;
    margin: 0 0 12px;
    padding: 14px 15px 11px;
    background: rgba(var(--tone), .1);
    border: 1px solid rgba(var(--tone), .26);
    border-left: 3px solid rgb(var(--tone));
    border-radius: 4px 13px 13px 4px;
}

.idea-said {
    margin: 0;
    font-size: .87rem;
    line-height: 1.6;
    color: var(--text-primary);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.idea-tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 10px; }

.idea-tag {
    padding: 3px 9px;
    font-size: .7rem;
    color: rgb(var(--tone));
    background: rgba(var(--tone), .14);
    border-radius: 999px;
}

.idea-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin: 11px 0 0;
    padding-top: 9px;
    font-size: .7rem;
    color: rgba(184, 186, 194, .6);
    border-top: 1px solid rgba(var(--tone), .18);
}

.idea-foot button {
    padding: 0;
    font: inherit;
    font-size: .7rem;
    color: rgba(184, 186, 194, .6);
    background: none;
    border: none;
    cursor: pointer;
}

.idea-foot button:hover { color: var(--text-primary); }
.idea-foot .is-danger:hover { color: #ff8b6b; }
.idea-foot .idea-spacer { margin-left: auto; }

.idea-edit .ink-bar {
    padding: 4px 5px;
    background: rgba(0, 0, 0, .2);
    border-color: rgba(var(--tone), .5);
    border-radius: 9px 9px 0 0;
}

.idea-edit .ink {
    min-height: 66px;
    margin-bottom: 8px;
    padding: 9px 11px;
    font-size: .86rem;
    background: rgba(0, 0, 0, .25);
    border-color: rgba(var(--tone), .5);
    border-radius: 0 0 9px 9px;
}

.idea-edit .ink:focus { border-color: rgb(var(--tone)); }

.idea-edit input {
    width: 100%;
    margin-bottom: 8px;
    padding: 8px 11px;
    font: inherit;
    font-size: .78rem;
    color: var(--text-primary);
    background: rgba(0, 0, 0, .25);
    border: 1px solid rgba(var(--tone), .5);
    border-radius: 9px;
}

.idea-edit input:focus { outline: none; border-color: rgb(var(--tone)); }

/* Said in the middle of whatever is empty, and across the whole of it: a grid
   would otherwise put it in one column and a wall of notes in one of its. */
.room-none {
    grid-column: 1 / -1;
    column-span: all;
    margin: 30px auto;
    max-width: 340px;
    font-size: .84rem;
    line-height: 1.6;
    text-align: center;
    color: rgba(184, 186, 194, .6);
}

.chat-log {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    list-style: none;
    margin: 0;
    padding: 20px 24px;
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.chat-day {
    align-self: center;
    margin: 14px 0 10px;
    padding: 4px 12px;
    font-size: .7rem;
    color: rgba(184, 186, 194, .7);
    background: rgba(255, 255, 255, .05);
    border-radius: 999px;
}

/* The message, and the three dots in the dark beside it. Mine sit to the left
   of what I said and everybody else's to the right of what they said, which is
   the empty side in each case. */
.chat-msg {
    display: flex;
    align-items: center;
    gap: 2px;
    max-width: min(560px, 86%);
    align-self: flex-start;
    flex-direction: row-reverse;
}

.chat-msg.is-mine { align-self: flex-end; flex-direction: row; }
.chat-said { min-width: 0; }

/* The bubble hugs its words. Without this it was as wide as the line of small
   print under it, so "Test" came out the width of "12:00 μμ · Απάντηση ·
   Διαγραφή" and looked like a mistake. */
.chat-bubble { width: fit-content; max-width: 100%; }
.chat-msg.is-mine .chat-bubble { margin-left: auto; }

.chat-name {
    margin: 10px 0 4px 13px;
    font-size: .72rem;
    font-weight: var(--fw-medium);
    color: rgba(184, 186, 194, .75);
}

.chat-bubble {
    position: relative;
    padding: 10px 14px;
    font-size: .87rem;
    line-height: 1.55;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .06);
    border: 1px solid rgba(255, 255, 255, .07);
    border-radius: 16px 16px 16px 5px;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.chat-msg.is-mine .chat-bubble {
    color: var(--bg-dark);
    background: linear-gradient(135deg, var(--accent-amber), var(--accent-orange));
    border-color: transparent;
    border-radius: 16px 16px 5px 16px;
}

/* Lit by the same lamp as the page. Your own words are the one thing on this
   screen that is only ever yours, so they are where the light somebody chose
   should show: the bubble takes that pair rather than the house's amber, and
   goes back to the amber when the light is out.
   The words on it stay dark. Every colour on the wheel is a bright one — the
   deepest of them, the violet, still reads darker-on-lighter the same way the
   amber did — so there is no swatch here that turns this the wrong way round. */
html.is-lit .chat-msg.is-mine .chat-bubble {
    background: linear-gradient(135deg, rgb(var(--glow)), rgb(var(--glow-2)));
}

/* Above the words, inside the bubble, and quieter than them: what is being
   answered is context rather than the message. */
.chat-quote {
    display: block;
    margin: -2px 0 7px;
    padding: 5px 9px;
    font-size: .78rem;
    line-height: 1.4;
    text-align: left;
    color: inherit;
    background: rgba(255, 255, 255, .1);
    border: none;
    border-left: 2px solid currentColor;
    border-radius: 4px 8px 8px 4px;
    cursor: pointer;
    opacity: .75;
}

.chat-msg.is-mine .chat-quote { background: rgba(0, 0, 0, .14); }
.chat-quote:hover { opacity: 1; }

.chat-quote strong {
    display: block;
    font-size: .72rem;
    font-weight: var(--fw-medium);
}

.chat-quote span {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The one being answered, when its answer has been tapped. */
.chat-msg.is-found .chat-bubble { animation: chat-found 1.6s ease-out; }

@keyframes chat-found {
    0%, 55% { box-shadow: 0 0 0 3px rgba(255, 107, 53, .55); }
    100%    { box-shadow: 0 0 0 3px rgba(255, 107, 53, 0); }
}

/* What the next message will be answering, over the box it is typed in. */
.chat-answering {
    display: flex;
    align-items: center;
    gap: 9px;
    flex: 0 0 auto;
    margin: 0;
    padding: 9px 24px;
    font-size: .78rem;
    color: var(--text-secondary);
    background: rgba(255, 255, 255, .04);
    border-top: 1px solid rgba(255, 255, 255, .07);
}

.chat-answering-who { flex: 0 0 auto; color: var(--accent-orange); }

.chat-answering-said {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.chat-answering button {
    flex: 0 0 auto;
    padding: 0 4px;
    font: inherit;
    font-size: 1.1rem;
    line-height: 1;
    color: var(--text-secondary);
    background: none;
    border: none;
    cursor: pointer;
}

.chat-answering button:hover { color: var(--text-primary); }

/* --- what a message can be asked ------------------------------------------- */
/* Behind a long press on a touch screen and behind three dots on hover
   everywhere else, which is where every messenger keeps them. Two spelled-out
   buttons under every single message was a wall of words that had nothing to do
   with the conversation. */
.chat-more {
    display: grid;
    gap: 3px;
    flex: 0 0 auto;
    padding: 8px 6px;
    background: none;
    border: none;
    cursor: pointer;
    opacity: .3;
    transition: opacity .18s;
}

.chat-more span {
    width: 3px; height: 3px;
    background: var(--text-secondary);
    border-radius: 50%;
}

.chat-more:hover { opacity: .95; }
.chat-msg:hover .chat-more { opacity: .6; }
.chat-msg:hover .chat-more:hover { opacity: 1; }

.msg-menu {
    position: fixed;
    z-index: 130;
    min-width: 168px;
    padding: 6px;
    background: rgba(26, 26, 33, .98);
    border: 1px solid rgba(255, 255, 255, .13);
    border-radius: 13px;
    box-shadow: 0 22px 48px rgba(0, 0, 0, .6);
}

.msg-menu button {
    display: block;
    width: 100%;
    padding: 10px 12px;
    font: inherit;
    font-size: .85rem;
    text-align: left;
    color: var(--text-primary);
    background: none;
    border: none;
    border-radius: 9px;
    cursor: pointer;
}

.msg-menu button:hover { background: rgba(255, 255, 255, .08); }
.msg-menu .is-danger { color: #ff8b6b; }

/* The reactions along the top of the menu: seven emoji in a row, the one you
   already gave marked. */
.msg-menu .msg-reacts {
    display: flex;
    gap: 2px;
    padding: 2px 2px 6px;
    margin-bottom: 4px;
    border-bottom: 1px solid rgba(255, 255, 255, .08);
}

.msg-menu .msg-reacts button {
    display: grid;
    place-items: center;
    width: 34px; height: 34px;
    padding: 0;
    font-size: 1.15rem;
    line-height: 1;
    text-align: center;
    border-radius: 50%;
}

.msg-menu .msg-reacts button.is-mine { background: rgba(255, 255, 255, .14); }

/* Under a message, what it has been given: each emoji once, with a count when
   more than one person gave it. Yours has a ring. */
.chat-reacts {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    margin: 4px 6px 0;
}

.chat-msg.is-mine .chat-reacts { justify-content: flex-end; }

.chat-react {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    height: 24px;
    padding: 0 8px;
    font: inherit;
    font-size: .8rem;
    line-height: 1;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .07);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 999px;
    cursor: pointer;
}

.chat-react small { font-size: .7rem; color: rgba(184, 186, 194, .85); }
.chat-react:hover { background: rgba(255, 255, 255, .11); }
.chat-react.is-mine { border-color: rgba(255, 255, 255, .38); background: rgba(255, 255, 255, .12); }

/* The one the menu is about, so it is never in doubt which message is being
   answered or thrown away. */
.chat-msg.is-asked .chat-bubble { box-shadow: 0 0 0 2px rgba(255, 255, 255, .3); }

.chat-foot {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 4px 13px 0;
    font-size: .68rem;
    color: rgba(184, 186, 194, .6);
}

.chat-msg.is-mine .chat-foot { justify-content: flex-end; }

.chat-foot button {
    padding: 0;
    font: inherit;
    font-size: .68rem;
    color: rgba(184, 186, 194, .6);
    background: none;
    border: none;
    cursor: pointer;
}

.chat-foot button:hover { color: #ff8b6b; }

/* Three dots while a conversation is on its way. Without them the room shows
   either the last conversation's messages or the line that says nobody has
   written yet, and both of those are a lie for as long as they are up. */
.chat-wait {
    display: flex;
    justify-content: center;
    gap: 6px;
    margin: auto;
}

.chat-wait span {
    width: 7px; height: 7px;
    background: rgba(255, 255, 255, .28);
    border-radius: 50%;
    animation: chat-wait 1.1s ease-in-out infinite;
}

.chat-wait span:nth-child(2) { animation-delay: .16s; }
.chat-wait span:nth-child(3) { animation-delay: .32s; }

@keyframes chat-wait {
    0%, 70%, 100% { opacity: .25; transform: translateY(0); }
    35%           { opacity: .9;  transform: translateY(-4px); }
}

.chat-empty {
    margin: auto;
    max-width: 330px;
    font-size: .84rem;
    line-height: 1.6;
    text-align: center;
    color: rgba(184, 186, 194, .6);
}

/* One rounded field with the send inside its end, which is the shape every
   messenger has settled on. */
.chat-form {
    display: flex;
    align-items: flex-end;
    gap: 8px;
    flex: 0 0 auto;
    padding: 12px 24px 16px;
    border-top: 1px solid rgba(255, 255, 255, .07);
}

.chat-form textarea {
    flex: 1;
    min-width: 0;
    max-height: 140px;
    padding: 11px 16px;
    font: inherit;
    /* Never under sixteen pixels. An iPhone zooms the whole page into any
       field smaller than that the moment it is tapped, and the page it zooms
       into is one nobody asked to be in. */
    font-size: 16px;
    line-height: 1.45;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .06);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 21px;
    resize: none;
}

.chat-form textarea:focus {
    outline: none;
    background: rgba(255, 255, 255, .08);
    border-color: rgba(255, 255, 255, .22);
}

.chat-send {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 42px; height: 42px;
    color: var(--bg-dark);
    background: linear-gradient(135deg, var(--accent-amber), var(--accent-orange));
    border: none;
    border-radius: 50%;
    cursor: pointer;
    transition: transform .18s, opacity .18s;
}

.chat-send:hover { transform: scale(1.07); }
.chat-send:active { transform: scale(.94); }
.chat-send:disabled { opacity: .45; cursor: default; transform: none; }

.chat-send svg {
    width: 19px; height: 19px;
    margin-left: -1px;
    fill: none; stroke: currentColor; stroke-width: 1.9;
    stroke-linecap: round; stroke-linejoin: round;
}
.app-blank h2 { font-size: 1.25rem; font-weight: var(--fw-semibold); margin-bottom: 10px; }
.app-blank p { color: var(--text-secondary); font-size: .92rem; }

/* --- the profile card --------------------------------------------------- */
.profile-card {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 22px;
    align-items: center;
    max-width: 780px;
    margin: 0 auto 26px;
    padding: 24px 26px;
    background: rgba(255, 255, 255, .035);
    border: 1px solid rgba(255, 255, 255, .08);
    border-radius: 22px;
}

.profile-avatar {
    position: relative;
    display: grid;
    place-items: center;
    width: 84px; height: 84px;
    padding: 0;
    overflow: hidden;
    font: inherit;
    font-size: 1.8rem;
    font-weight: var(--fw-semibold);
    color: var(--bg-dark);
    background: linear-gradient(135deg, var(--accent-amber), var(--accent-orange));
    border: none;
    border-radius: 50%;
    cursor: default;
}

.profile-avatar.is-editable { cursor: pointer; }

.profile-photo {
    position: absolute;
    inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
}

/* The word only appears on the way to being pressed, so the profile reads as
   a profile and not as a form. */
.profile-avatar-hint {
    position: absolute;
    inset: auto 0 0;
    padding: 5px 0 7px;
    font-size: .62rem;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: #fff;
    background: rgba(10, 10, 14, .72);
    opacity: 0;
    transition: opacity .25s;
}

.profile-avatar.is-editable:hover .profile-avatar-hint,
.profile-avatar.is-editable:focus-visible .profile-avatar-hint { opacity: 1; }

/* ---- highlights --------------------------------------------------------- */
/* A row of circles under the profile, scrolling sideways when there are more
   of them than there is room for. */
.highlights {
    display: flex;
    gap: 20px;
    max-width: 780px;
    margin: 0 auto 22px;
    padding: 2px 2px 6px;
    overflow-x: auto;
    scrollbar-width: thin;
}

.hl {
    flex: 0 0 auto;
    display: grid;
    justify-items: center;
    gap: 7px;
    width: 76px;
    padding: 0;
    font: inherit;
    color: var(--text-secondary);
    background: none;
    border: none;
    cursor: pointer;
}

.hl-ring {
    display: grid;
    place-items: center;
    width: 66px; height: 66px;
    overflow: hidden;
    background: rgba(255, 255, 255, .04);
    border: 1px solid rgba(255, 255, 255, .16);
    border-radius: 50%;
    transition: border-color .25s;
}

.hl:hover .hl-ring { border-color: var(--accent-orange); }
.hl-ring img { width: 100%; height: 100%; object-fit: cover; }
.hl-new .hl-ring { border-style: dashed; }
.hl-plus { font-size: 1.5rem; font-weight: var(--fw-light); line-height: 1; }

.hl-label {
    max-width: 76px;
    overflow: hidden;
    font-size: .72rem;
    text-align: center;
    text-overflow: ellipsis;
    white-space: nowrap;
    word-break: normal;
}

/* A grid item will not shrink below its content unless it is told it may, and
   a handle or a pasted address has nowhere to break. Between them that is what
   pushes a narrow screen sideways, so both are answered here rather than by
   hiding the overflow further up. */
.profile-text { min-width: 0; }

.profile-name,
.profile-handle,
.note-text,
.note-who strong,
.post-caption,
.hl-label,
.app-nav-text strong {
    overflow-wrap: anywhere;
    word-break: break-word;
}

.profile-name { font-size: 1.3rem; font-weight: var(--fw-semibold); }
.profile-handle { margin-top: 2px; font-size: .88rem; color: var(--accent-orange); }

.profile-stats {
    display: flex;
    flex-wrap: wrap;
    gap: 18px;
    margin-top: 12px;
    font-size: .82rem;
    color: var(--text-secondary);
}

.profile-stats strong { color: var(--text-primary); font-weight: var(--fw-semibold); }
.stat-flag { color: var(--accent-orange); }
.stat-flag strong { color: var(--accent-orange); }

.profile-hint {
    grid-column: 1 / -1;
    padding-top: 16px;
    border-top: 1px solid rgba(255, 255, 255, .07);
    font-size: .8rem;
    line-height: 1.6;
    color: rgba(184, 186, 194, .8);
}

/* --- the grid ----------------------------------------------------------- */
/* Three across and square, the way the profile it stands in for is laid out.
   The gap is the hairline Instagram leaves between pictures, so a grid that is
   working reads as a grid at a glance and not as a gallery of cards. */
.ig-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 4px;
    max-width: 780px;
    margin: 0 auto;
}

.cell {
    position: relative;
    /* Instagram's grid is no longer square: a tile is four across by five
       down, and a mockup that is square would flatter every picture that is
       about to be cropped. */
    aspect-ratio: 4 / 5;
    overflow: hidden;
    background: rgba(255, 255, 255, .03);
    border: none;
    border-radius: 4px;
}

.cell img {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
    cursor: zoom-in;
    transition: transform .5s cubic-bezier(0.25, 1, 0.5, 1), filter .3s;
}

.cell.is-filled:hover img { transform: scale(1.04); filter: brightness(.82); }

.cell-empty {
    display: grid;
    place-items: center;
    color: rgba(245, 245, 247, .35);
    border: 1px solid rgba(255, 255, 255, .07);
    cursor: pointer;
    transition: var(--transition-smooth);
}

/* A square that is spoken for but still empty. It stays quiet until the
   pointer is over it, so a grid being planned still reads as a grid. */
.cell-empty { border-style: dashed; }

.cell-fill {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    font: inherit;
    color: inherit;
    background: none;
    border: none;
    cursor: pointer;
}

/* Takes this square off the grid. Only empty squares carry it: a square with a
   picture in it is emptied by deleting the picture, which is a different thing
   and says so. */
.cell-x {
    position: absolute;
    z-index: 1;
    top: 5px; right: 6px;
    display: grid;
    place-items: center;
    width: 22px; height: 22px;
    padding: 0;
    font: inherit;
    font-size: 1rem;
    line-height: 1;
    color: var(--text-secondary);
    background: rgba(10, 10, 14, .7);
    border: 1px solid rgba(255, 255, 255, .12);
    border-radius: 7px;
    cursor: pointer;
    opacity: 0;
    transition: opacity .25s, color .25s;
}

.cell-empty:hover .cell-x,
.cell-x:focus-visible { opacity: 1; }
.cell-x:hover { color: #ff8b6b; border-color: rgba(255, 107, 53, .45); }
.cell-empty .cell-plus { opacity: .22; transition: opacity .25s; }
.cell-empty:hover .cell-plus { opacity: 1; }

.cell-empty:hover {
    color: var(--accent-orange);
    background: rgba(255, 107, 53, .07);
    border-color: rgba(255, 107, 53, .4);
}

.cell-empty.is-quiet { cursor: default; border-style: solid; }
.cell-empty.is-quiet:hover { background: rgba(255, 255, 255, .03); border-color: rgba(255, 255, 255, .07); }


.cell-plus { font-size: 1.8rem; font-weight: var(--fw-light); line-height: 1; }

/* Marks in the corners: what kind of post it is, and whether somebody is
   waiting on an answer about it. */
.cell-mark {
    position: absolute; top: 8px; right: 8px;
    pointer-events: none;
    filter: drop-shadow(0 1px 3px rgba(0, 0, 0, .6));
}

.cell-mark svg {
    width: 17px; height: 17px;
    fill: none; stroke: #fff; stroke-width: 1.9;
    stroke-linecap: round; stroke-linejoin: round;
}

.cell-note {
    position: absolute; top: 8px; left: 8px;
    min-width: 20px;
    padding: 1px 6px;
    font-size: .7rem;
    font-weight: var(--fw-semibold);
    text-align: center;
    color: var(--bg-dark);
    background: var(--accent-orange);
    border-radius: 999px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, .4);
}

.cell-dots {
    position: absolute; right: 6px; bottom: 6px;
    display: grid;
    place-items: center;
    width: 30px; height: 30px;
    padding: 0;
    background: rgba(10, 10, 14, .72);
    border: 1px solid rgba(255, 255, 255, .12);
    border-radius: 9px;
    cursor: pointer;
    opacity: 0;
    transition: opacity .25s, background .25s;
}

.cell:hover .cell-dots, .cell-dots:focus-visible { opacity: 1; }
.cell-dots:hover { background: rgba(10, 10, 14, .92); }
.cell-dots svg { width: 16px; height: 16px; fill: currentColor; color: var(--text-primary); }

/* A post being carried, and the slot under it.
   pan-y leaves an ordinary swipe to the page: the tile only takes the gesture
   over once a finger has held still long enough to mean it. pinch-zoom leaves
   two fingers to the browser, so a zoomed page can be pinched back out from
   anywhere on the grid and not only from the bar at the bottom. */
.cell {
    touch-action: pan-y pinch-zoom;
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
}

.cell.is-dragging { opacity: .3; }

/* --- landing ---------------------------------------------------------------
   An iPhone gives a web page no way to knock on the hand, so the eye is told
   instead. Whatever was carried settles into its new place with a short press
   and a smaller rebound, which is the shape of a thing being put down. It runs
   once, on the one that moved, and is over before anybody would call it an
   animation. */
@keyframes settle {
    0%   { transform: scale(1.055); }
    55%  { transform: scale(.975); }
    100% { transform: scale(1); }
}

@keyframes settle-row {
    0%   { background: rgba(255, 107, 53, .2);  border-color: rgba(255, 107, 53, .45); }
    100% { background: rgba(255, 255, 255, .03); border-color: rgba(255, 255, 255, .07); }
}

.cell.is-landed,
.strip-tile.is-landed { animation: settle .34s cubic-bezier(0.34, 1.35, 0.64, 1); }
.link-list li.is-landed { animation: settle-row .5s ease-out; }

/* Told, not shaken at. */
@media (prefers-reduced-motion: reduce) {
    .cell.is-landed,
    .strip-tile.is-landed,
    .link-list li.is-landed { animation: none; }

    /* Folding still happens; it just happens at once. */
    :root { --fold-ms: 0s; }

    .hello { animation: none; }
    .is-greeting .app-me::after { animation: none; opacity: .75; }

    /* And the light stops where it stands, rather than wandering. */
    html.is-lit::before,
    html.is-lit::after,
    html.is-lit .app-body::before { animation: none; }
}

.cell.is-over {
    outline: 2px solid var(--accent-orange);
    outline-offset: -2px;
    transform: scale(.96);
}

/* The tile that follows the pointer. It is a copy: the real one stays faded in
   place, so the grid never reflows while something is in the air. */
.drag-ghost {
    position: fixed;
    top: 0; left: 0;
    z-index: 120;
    overflow: hidden;
    pointer-events: none;
    border-radius: 6px;
    box-shadow: 0 22px 50px rgba(0, 0, 0, .55);
    opacity: .92;
    will-change: transform;
}

.drag-ghost img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* An empty square being carried looks like the empty square it is. */
.drag-ghost.is-empty {
    display: grid;
    place-items: center;
    font-size: 1.6rem;
    font-weight: var(--fw-light);
    color: var(--accent-orange);
    background: rgba(20, 20, 26, .92);
    border: 1px dashed rgba(255, 107, 53, .5);
}

/* Pictures are opened by clicking and moved by dragging, and only one of those
   is worth a cursor. Dragging wins where it is allowed, because clicking is
   what a picture looks like it does anyway. */
.ig-grid.is-editable .cell.is-filled img,
.ig-grid.is-editable .cell-empty { cursor: grab; }

body.is-dragging-cell { cursor: grabbing; }
body.is-dragging-cell .cell img { cursor: grabbing; }

/* A post on its way to another slot, and the slots it may land in. */
.cell.is-moving { outline: 2px solid var(--accent-orange); outline-offset: -2px; opacity: .55; }
.cell.is-target { outline: 1px dashed rgba(255, 107, 53, .55); outline-offset: -2px; cursor: copy; }

/* --- how long the grid is -------------------------------------------------- */
/* Deliberately not a square. Adding a place for a picture and adding a picture
   are different acts, and the grid above is only ever the second one. */
.grid-plan {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    max-width: 780px;
    margin: 16px auto 0;
}

.plan-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 9px 16px;
    font: inherit;
    font-size: .82rem;
    color: var(--text-secondary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 999px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.plan-btn span { font-size: 1.05rem; line-height: 1; }
.plan-btn:hover { color: var(--text-primary); border-color: var(--border-hover); }
.plan-btn[disabled] { opacity: .35; cursor: not-allowed; }
.plan-btn[disabled]:hover { color: var(--text-secondary); border-color: rgba(255, 255, 255, .1); }

.plan-count { margin-left: auto; font-size: .76rem; color: rgba(184, 186, 194, .7); }

/* --- the three-dot menu -------------------------------------------------- */
.cell-menu {
    position: absolute;
    z-index: 60;
    min-width: 208px;
    padding: 6px;
    background: rgba(24, 24, 30, .98);
    -webkit-backdrop-filter: blur(18px);
    backdrop-filter: blur(18px);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 14px;
    box-shadow: 0 18px 44px rgba(0, 0, 0, .5);
}

.cell-menu button {
    display: block;
    width: 100%;
    padding: 9px 12px;
    font: inherit;
    font-size: .84rem;
    text-align: left;
    color: var(--text-primary);
    background: none;
    border: none;
    border-radius: 9px;
    cursor: pointer;
}

.cell-menu button:hover { background: rgba(255, 255, 255, .07); }
.cell-menu button[disabled] { opacity: .4; cursor: not-allowed; }
.cell-menu button[disabled]:hover { background: none; }
.cell-menu .is-danger { color: #ff8b6b; }

/* --- the bar along the bottom of a phone ---------------------------------
   The same rail the desktop folds down to, laid flat where a thumb is. A
   drawer is a thing you have to remember to open; this is the sections of the
   portal, always there, in the order the sidebar has them.
   Hidden outright above a phone: there is a sidebar there and two navigations
   saying the same thing is one too many. */
.app-tabs { display: none; }

.app-tab {
    position: relative;
    display: grid;
    justify-items: center;
    align-content: center;
    gap: 4px;
    padding: 7px 2px 6px;
    font: inherit;
    font-size: .63rem;
    letter-spacing: .01em;
    color: rgba(184, 186, 194, .7);
    background: none;
    border: 1px solid transparent;
    border-radius: 14px;
    cursor: pointer;
    transition: background .2s, color .2s;
}

.app-tab svg {
    width: 21px; height: 21px;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
}

.app-tab span:not(.app-tab-pip) {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.app-tab.is-on { color: var(--text-primary); background: rgba(255, 255, 255, .09); }

/* The same dimmed pour as the selected row in the sidebar, so the two
   navigations are lit by one light. */
html.is-lit .app-tab.is-on {
    background: linear-gradient(135deg,
        rgba(var(--glow), min(calc(var(--glow-a) * .95), .46)) 0%,
        rgba(var(--glow-2), min(calc(var(--glow-a) * .58), .28)) 100%);
}

html.is-lit .app-tab svg {
    stroke: color-mix(in srgb, rgb(var(--glow)) 34%, currentColor);
}
html.is-lit .app-tab.is-on svg {
    stroke: color-mix(in srgb, rgb(var(--glow)) 62%, currentColor);
}

/* Something waiting, in the one place a number would not fit. */
.app-tab-pip {
    position: absolute;
    top: 6px; left: 50%;
    margin-left: 7px;
    width: 7px; height: 7px;
    background: var(--accent-orange);
    border-radius: 50%;
}

@media (max-width: 900px) {
    .app-tabs {
        position: fixed;
        left: 0; right: 0; bottom: 0;
        z-index: 45;
        display: grid;
        grid-auto-flow: column;
        grid-auto-columns: 1fr;
        gap: 2px;
        padding: 6px 8px;
        padding-bottom: calc(6px + env(safe-area-inset-bottom));
        background: rgba(10, 10, 14, .9);
        -webkit-backdrop-filter: blur(16px);
        backdrop-filter: blur(16px);
        border-top: 1px solid rgba(255, 255, 255, .07);
    }

    html.is-lit .app-tabs {
        border-image: linear-gradient(90deg,
            rgba(var(--glow), calc(var(--glow-a) * .40)) 0%,
            rgba(var(--glow-2), calc(var(--glow-a) * .24)) 55%,
            rgba(255, 255, 255, .035) 100%) 1;
    }

    /* Room for it under everything that scrolls, and above everything that was
       already sitting on the bottom edge.
       Not the conversation, though: it is an app-view like the others, but it
       owns the bottom edge itself — the box you type in lives there, with the
       keyboard under it — and the bar is not on screen while it is open. Room
       left for a bar that is not there is a band of nothing between the box
       and the keys. */
    .app-view:not(.app-chat) { padding-bottom: calc(78px + env(safe-area-inset-bottom)); }
    .toast { bottom: calc(78px + env(safe-area-inset-bottom)); }
    .why-more { bottom: calc(78px + env(safe-area-inset-bottom)); }

    /* A conversation takes the whole screen and puts its own box on the bottom
       edge, with the keyboard under it. The bar would be over that box. It
       goes, the same way the title bar above already does. */
    body.is-inroom .app-tabs { display: none; }

    /* A box around a list of boxes. On a wide screen the panel is what holds
       a page together and keeps it off the edges; on a phone there are no
       edges to speak of and every row already has its own card, so the outer
       one was a border drawn a few pixels outside another border.
       The panel stays in the markup — it is what the page is built from — and
       here it simply stops being drawn. */
    #view-myprojects > .panel,
    #view-mygrids > .panel {
        padding: 0;
        background: none;
        border: none;
    }

    #view-myprojects .lister,
    #view-mygrids .lister { gap: 8px; }

    #view-myprojects .lister li,
    #view-mygrids .lister li { padding: 10px 10px 10px 12px; }

    /* Nothing to fold: there is no sidebar here at all, which is also why the
       bar is the only place left that can say whose portal this is. */
    .app-fold { display: none; }
    .app-top-brand { display: block; }
    .lamp-wrap { display: block; }

    /* The switch is on the bar here too, so the card can go on pointing at
       it. Only its width gives, so it never runs off the side. */
    .hello { left: 16px; right: 16px; top: 66px; width: auto; }
    /* The card stays where it is; only its corner moves, to sit under the
       sun rather than at the far end over the way out. The script measures
       where the sun is and puts it there. */
    .hello::after { right: auto; left: var(--hello-point, calc(100% - 40px)); }
    /* Not slid off the side any more: gone. Every section it held has a tab
       along the bottom and a page of its own to land on, so there is no drawer
       to open and nothing to remember to close. */
    .app-side { display: none; }
    .app-main { margin-left: 0; }
    /* The conversation is left out of this for the same reason: it ends at the
       bottom of the screen, on purpose, and every pixel of padding under it is
       a pixel between the box you type in and the keys. */
    .app-view:not(.app-chat) { padding: 20px 16px 72px; }
    .app-chat { padding: 0; }
    /* On a phone the bar is a bar again. The sidebar is not on screen, so
       this is the only place the logo can say whose portal this is, and the
       bell has to stay legible over whatever is scrolling underneath it. */
    .app-top {
        padding: 14px 16px;
        background: rgba(10, 10, 14, .82);
        -webkit-backdrop-filter: blur(16px);
        backdrop-filter: blur(16px);
        border-bottom: 1px solid rgba(255, 255, 255, .06);
    }

    /* The rail along the bottom already carries who you are and the page that
       is yours, so the bar does not say it a second time. The bell and the
       way out stay: neither of them has a tab. */
    .top-me { display: none; }
    .profile-card { grid-template-columns: auto 1fr; gap: 16px; padding: 18px; }
    .profile-avatar { width: 64px; height: 64px; font-size: 1.4rem; }
    .highlights { gap: 14px; }
    .hl { width: 64px; }
    .hl-ring { width: 58px; height: 58px; }
    .hl-label { max-width: 64px; }
    .cell-dots, .cell-x { opacity: 1; }

    /* A phone shows one thing at a time: the list of conversations, or the one
       that has been opened, with a way back to the list. */
    .app-chat { grid-template-columns: minmax(0, 1fr); }
    .app-chat .chat-side { border-right: none; }
    .app-chat.is-open .chat-side { display: none; }
    .app-chat:not(.is-open) .chat-room { display: none; }

    .chat-back { display: grid; }
    .chat-head, .chat-banner, .chat-log, .chat-form,
    .room-tabs, .room-scroll, .link-new, .idea-new, .idea-filter {
        padding-left: 16px;
        padding-right: 16px;
    }

    .chat-msg { max-width: 88%; }

    /* The conversation takes the screen the keyboard has left, measured by the
       browser and handed over in --vvh.
       
       The page itself is pinned first. Telling a phone not to scroll is not
       enough: as long as there is a page under the keyboard it can move, it
       moves it, and the box being typed in goes behind the keys. Pinned, there
       is nothing left to move, and the only thing that scrolls is the messages.

       --vvtop is what a phone offsets the view by when it decides to lift it
       anyway. Taking it back here is the difference between a conversation that
       sits still and one that creeps off the top. */
    /* The page is exactly as tall as what is showing, keyboard or no keyboard,
       and --vvh is what that turned out to be rather than what anything claimed
       it would be. 100dvh is only the first guess, used for the blink before
       the first measurement lands.

       Units were tried here and every one of them was wrong on some phone in
       some state: dvh does not shrink for a keyboard, svh and lvh are the two
       ends of a browser's toolbars rather than where they are now, and a
       keyboard's accessory bar is in none of them. */
    body.is-chatting {
        position: fixed;
        top: var(--vvtop, 0px);
        left: 0;
        width: 100%;
        height: var(--vvh, 100dvh);
        min-height: 0;
        overflow: hidden;
    }

    body.is-chatting .app-main { height: 100%; min-height: 0; }
    body.is-chatting .app-chat { min-height: 0; }
    body.is-chatting .chat-log { overscroll-behavior: contain; }

    /* Nothing between the box and the bottom of what is showing. */
    body.is-chatting .chat-form { margin-bottom: 0; padding-bottom: 10px; }

    /* Nothing moves when somebody taps the box.

       Folding bands away at that moment was the jump: the box was already
       sitting at the bottom of the flex column, exactly where it belongs, and
       then a third of the screen above it disappeared and everything leapt.
       Whatever is going to be out of the way is out of the way before anybody
       touches anything.

       So the app's own bar goes when a conversation opens, which is a whole
       screen changing anyway and hides nothing. The conversation has a name of
       its own three lines below it, and one of the two was saying it twice. */
    body.is-inroom .app-top { display: none; }

    /* And the sentence about how long messages are kept is a desktop's to
       carry. On a phone it is a third line on a band that has to earn every
       one of them. */
    .chat-kept { display: none; }

    /* No safe-area padding. The height above already stops short of the home
       indicator, and adding the same inset a second time is the black band
       that was sitting between the box and the bottom of the screen. */
    .chat-form { padding: 10px 12px; }
    .chat-send { width: 40px; height: 40px; }

    /* Same reason as the message box: sixteen pixels or the page jumps. */
    .ink, #note-text, .link-new input, .idea-bits input, .me-name-field { font-size: 16px; }

    .room-open {
        display: inline-flex;
        align-items: center;
        gap: 8px;
        align-self: flex-start;
        flex: 0 0 auto;
        margin: 16px 16px 0;
        padding: 10px 16px;
        font: inherit;
        font-size: .84rem;
        color: var(--text-secondary);
        background: rgba(255, 255, 255, .05);
        border: 1px solid rgba(255, 255, 255, .12);
        border-radius: 999px;
        cursor: pointer;
    }

    .room-open span { font-size: 1.05rem; line-height: 1; color: var(--accent-orange); }
    .room-cancel { display: inline-flex; }

    .link-new, .idea-new { display: none; }
    .is-adding .link-new { display: flex; }
    .is-adding .idea-new { display: grid; }
    .is-adding .room-open { display: none; }
    .room-tabs { overflow-x: auto; }
    .room-tabs button { white-space: nowrap; }
    .idea-wall { columns: 1; }
}


/* --- panels that open over the grid -------------------------------------- */
.modal { position: fixed; inset: 0; z-index: 80; display: grid; place-items: center; padding: 20px; }

.modal-scrim {
    position: absolute; inset: 0;
    background: rgba(6, 6, 9, .82);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
}

.modal-box {
    position: relative;
    z-index: 1;
    background: rgba(20, 20, 26, .97);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 22px;
    box-shadow: 0 34px 90px rgba(0, 0, 0, .6);
    overflow: hidden;
}

.modal-x {
    position: absolute; top: 10px; right: 12px;
    z-index: 3;
    width: 34px; height: 34px;
    font-size: 1.5rem;
    line-height: 1;
    color: var(--text-primary);
    background: rgba(10, 10, 14, .6);
    border: 1px solid rgba(255, 255, 255, .12);
    border-radius: 11px;
    cursor: pointer;
}

.modal-x:hover { background: rgba(10, 10, 14, .9); }

/* --- the post, full size -------------------------------------------------- */
/* The picture is shown in Instagram's portrait frame, four across by five
   down, which is the shape the tiles in the grid already have. The panel is
   built around that frame rather than the other way round: the height is
   whatever the screen can spare, and the width of the stage follows from it,
   so the shape holds at every window size. */
.modal-post {
    --stage-h: min(760px, 86vh);
    display: grid;
    grid-template-columns: calc(var(--stage-h) * 4 / 5) minmax(0, 360px);
    width: min(calc(var(--stage-h) * 4 / 5 + 360px), 94vw);
    height: var(--stage-h);
}

/* Nothing in here is in normal flow: the picture, the arrows and the dots are
   all laid on the stage by its own edges. */
.post-stage {
    position: relative;
    background: #000;
    overflow: hidden;
    /* Up and down is the page's, side to side is the carousel's, and a pinch
       is the browser's. Saying so here is what lets a finger do all three
       without any of them fighting. */
    touch-action: pan-y pinch-zoom;
}

/* The element is the stage, edge to edge, and the picture is centred inside
   the element by object-fit. A box that is only as wide as its picture has to
   be placed somewhere, and whatever was placing it left it against the left
   edge with all the slack on the right. This has no placement to get wrong. */
/* Every picture of the carousel side by side on one strip, and the strip is
   what moves. A finger drags the whole row, so the next one is already coming
   in as the last one leaves rather than appearing once it has gone. */
.post-track {
    position: absolute;
    inset: 0;
    display: flex;
    will-change: transform;
    transition: transform .3s cubic-bezier(0.22, 1, 0.36, 1);
}

.post-track img {
    flex: 0 0 100%;
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
    -webkit-user-drag: none;
    user-select: none;
}

/* Which one of how many, said quietly. The dots below say the same thing to
   anybody reading the shape rather than the number. */
.post-count {
    position: absolute; top: 14px; right: 14px;
    z-index: 2;
    padding: 4px 10px;
    font-size: .72rem;
    font-weight: var(--fw-medium);
    color: #fff;
    background: rgba(10, 10, 14, .55);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
    border-radius: 999px;
}

/* Nothing to click with, nothing to click on. Where a finger is the only
   pointer there is, the arrows are two things in the way of the picture. */
@media (hover: none) and (pointer: coarse) {
    .post-arrow { display: none; }
}

.post-arrow {
    position: absolute; top: 50%;
    transform: translateY(-50%);
    display: grid;
    place-items: center;
    width: 38px; height: 38px;
    color: var(--text-primary);
    background: rgba(10, 10, 14, .6);
    border: 1px solid rgba(255, 255, 255, .14);
    border-radius: 50%;
    cursor: pointer;
    transition: background .25s;
}

.post-arrow:hover { background: rgba(10, 10, 14, .92); }
.post-prev { left: 12px; }
.post-next { right: 12px; }

.post-arrow svg {
    width: 18px; height: 18px;
    fill: none; stroke: currentColor; stroke-width: 2;
    stroke-linecap: round; stroke-linejoin: round;
}

.post-dots {
    position: absolute; bottom: 14px; left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 6px;
}

.post-dots span {
    width: 6px; height: 6px;
    background: rgba(255, 255, 255, .4);
    border-radius: 50%;
    transition: background .25s;
}

.post-dots span.is-on { background: #fff; }

.post-side {
    display: flex;
    flex-direction: column;
    /* A grid item is as wide as its widest word unless it is told otherwise,
       and one long word here would widen the whole panel and slide the picture
       out of the middle. */
    min-width: 0;
    min-height: 0;
    padding: 26px 24px 20px;
    border-left: 1px solid rgba(255, 255, 255, .08);
}

/* --- the heart ------------------------------------------------------------ */
/* One picture at a time: in a carousel each picture keeps its own, so the
   count changes as you page through. */
.post-like {
    display: flex;
    align-items: center;
    gap: 11px;
    margin-bottom: 16px;
}

.like-btn {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 7px 13px 7px 11px;
    font: inherit;
    font-size: .82rem;
    color: var(--text-secondary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 999px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

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

.like-btn svg {
    width: 17px; height: 17px;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
    transition: transform .3s cubic-bezier(0.25, 1, 0.5, 1);
}

/* Yours is filled in, the way a heart says so everywhere else. */
.like-btn[aria-pressed="true"] {
    color: #ff6b6b;
    background: rgba(255, 107, 107, .12);
    border-color: rgba(255, 107, 107, .35);
}

.like-btn[aria-pressed="true"] svg { fill: currentColor; transform: scale(1.08); }

.like-who {
    margin: 0;
    overflow: hidden;
    font-size: .76rem;
    line-height: 1.45;
    color: rgba(184, 186, 194, .7);
    text-overflow: ellipsis;
    white-space: nowrap;
}

#post-export { display: inline-flex; align-items: center; gap: 7px; }

#post-export svg {
    width: 15px; height: 15px;
    fill: none; stroke: currentColor; stroke-width: 1.8;
    stroke-linecap: round; stroke-linejoin: round;
}

.post-caption {
    margin-bottom: 18px;
    padding-bottom: 18px;
    font-size: .88rem;
    line-height: 1.65;
    color: var(--text-secondary);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    border-bottom: 1px solid rgba(255, 255, 255, .07);
}

/* Beside the picture the column is as tall as the picture and no taller, and a
   caption written at the length people actually write them ran off the bottom
   of it: no scrollbar, no comments under it, and no way to reach either.
   It scrolls on its own now, inside a share of the column, so that however
   long it is there is always a panel of comments under it and a box to write
   one in. A short caption is untouched — this is a ceiling, not a height.
   Only here: on a phone the whole modal is one scroll, and a second one
   nested inside it would fight the finger. */
@media (min-width: 861px) {
    .post-caption {
        flex: 0 1 auto;
        min-height: 0;
        max-height: 46%;
        overflow-y: auto;
        overscroll-behavior: contain;
    }
}

.post-notes { flex: 1; display: flex; flex-direction: column; min-height: 0; }

.post-notes-title {
    margin-bottom: 12px;
    font-size: .72rem;
    letter-spacing: .13em;
    text-transform: uppercase;
    color: rgba(184, 186, 194, .65);
}

.note-list { flex: 1; overflow-y: auto; list-style: none; margin: 0 0 14px; padding: 0; }

.note {
    position: relative;
    margin-bottom: 10px;
    padding: 12px 14px;
    background: rgba(255, 255, 255, .04);
    border: 1px solid rgba(255, 255, 255, .07);
    border-radius: 13px;
}

/* A client's note is a request for a change, so it is the one that is meant to
   catch the eye until somebody marks it as handled. */
.note.is-client { border-color: rgba(255, 107, 53, .3); background: rgba(255, 107, 53, .07); }
.note.is-done { opacity: .5; }
.note.is-done .note-text { text-decoration: line-through; }

.note-who { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; margin-bottom: 5px; }
.note-who strong { font-size: .82rem; font-weight: var(--fw-medium); }
.note-who small { font-size: .68rem; color: rgba(184, 186, 194, .7); }
.note-text {
    font-size: .85rem;
    line-height: 1.55;
    color: var(--text-secondary);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* An answer sits under the note it answers, indented once and no further. */
.note.is-reply {
    margin-left: 22px;
    background: rgba(255, 255, 255, .025);
}

.note.is-reply::before {
    content: "";
    position: absolute;
    left: -13px; top: 18px;
    width: 9px; height: 1px;
    background: rgba(255, 255, 255, .18);
}

.note-tools { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }

.note-tools button {
    padding: 5px 10px;
    font: inherit;
    font-size: .71rem;
    color: var(--text-secondary);
    background: rgba(255, 255, 255, .06);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 8px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.note-tools button:hover { color: var(--text-primary); border-color: var(--border-hover); }

.note-edit textarea {
    width: 100%;
    padding: 9px 11px;
    font: inherit;
    font-size: .85rem;
    line-height: 1.55;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 107, 53, .4);
    border-radius: 10px;
    resize: vertical;
}

.note-edit textarea:focus { outline: none; border-color: var(--accent-orange); }

/* What the next comment is answering, said above the box it is typed in. */
.note-replying {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 7px 10px 7px 12px;
    font-size: .74rem;
    color: var(--text-secondary);
    background: rgba(255, 107, 53, .1);
    border: 1px solid rgba(255, 107, 53, .26);
    border-radius: 10px;
}

.note-replying span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.note-replying button {
    padding: 0 4px;
    font: inherit;
    font-size: 1rem;
    line-height: 1;
    color: var(--text-secondary);
    background: none;
    border: none;
    cursor: pointer;
}

.note-replying button:hover { color: var(--text-primary); }
.note-none { font-size: .82rem; color: rgba(184, 186, 194, .6); }

.note-form { display: grid; gap: 8px; }

.note-form textarea,
.edit-caption {
    padding: 11px 13px;
    font: inherit;
    font-size: .85rem;
    line-height: 1.55;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .04);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 12px;
    resize: vertical;
}

.note-form textarea:focus,
.edit-caption:focus { outline: none; border-color: var(--accent-orange); }

.note-send { padding: 10px 18px; font-size: .82rem; justify-self: end; }

.post-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid rgba(255, 255, 255, .07);
}

/* --- the editor ---------------------------------------------------------- */
.modal-edit { width: min(580px, 94vw); max-height: 90vh; overflow-y: auto; padding: 30px 28px 24px; }

.modal-title { font-size: 1.2rem; font-weight: var(--fw-semibold); }
.modal-sub { margin: 8px 0 20px; font-size: .82rem; line-height: 1.6; color: var(--text-secondary); }

.edit-strip {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
    gap: 10px;
    margin-bottom: 22px;
}

.strip-tile {
    position: relative;
    aspect-ratio: 4 / 5;
    overflow: hidden;
    background: rgba(255, 255, 255, .04);
    border: 1px solid rgba(255, 255, 255, .09);
    border-radius: 13px;
}

.strip-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* --- carrying a picture along the carousel -------------------------------- */
/* Which picture is first is which one the grid shows, so the order of these is
   a decision rather than the order they happened to be chosen in. Nothing here
   reaches the server until the post is saved: this is a list in a panel that
   has not been submitted yet. */
.strip-tile[data-index] {
    user-select: none;
    -webkit-user-select: none;
    cursor: grab;
}

.strip-tile[data-index] img { -webkit-user-drag: none; pointer-events: none; }
.strip-tile.is-lifting { opacity: .3; }

/* A bar down the side the picture is going to land on. The strip wraps onto as
   many rows as it needs, so the mark is on the tile rather than in the gap. */
.strip-tile.is-before::before,
.strip-tile.is-after::after {
    content: "";
    position: absolute;
    top: 4px; bottom: 4px;
    z-index: 2;
    width: 3px;
    background: var(--accent-orange);
    border-radius: 3px;
}

.strip-tile.is-before::before { left: -6px; }
.strip-tile.is-after::after { right: -6px; }

body.is-dragging-strip,
body.is-dragging-strip .strip-tile[data-index] { cursor: grabbing; }

.strip-ghost {
    position: fixed;
    top: 0; left: 0;
    z-index: 130;
    overflow: hidden;
    pointer-events: none;
    border: 1px solid rgba(255, 107, 53, .5);
    border-radius: 13px;
    box-shadow: 0 22px 50px rgba(0, 0, 0, .55);
    opacity: .94;
    will-change: transform;
}

.strip-ghost img { width: 100%; height: 100%; object-fit: cover; display: block; }

.strip-flag {
    position: absolute; left: 6px; bottom: 6px;
    padding: 2px 7px;
    font-size: .64rem;
    font-weight: var(--fw-medium);
    color: var(--bg-dark);
    background: var(--accent-amber);
    border-radius: 999px;
}

.strip-first {
    position: absolute; left: 6px; right: 6px; bottom: 6px;
    padding: 4px 6px;
    font: inherit;
    font-size: .64rem;
    color: var(--text-primary);
    background: rgba(10, 10, 14, .78);
    border: 1px solid rgba(255, 255, 255, .14);
    border-radius: 8px;
    cursor: pointer;
    opacity: 0;
    /* Invisible and still in the way: without this it swallows a tap on a
       touch screen, where nothing ever hovers, and a drag that begins on the
       bottom of a tile never starts. */
    pointer-events: none;
    transition: opacity .25s;
}

.strip-tile:hover .strip-first { opacity: 1; pointer-events: auto; }

.strip-x {
    position: absolute; top: 5px; right: 5px;
    width: 24px; height: 24px;
    font-size: 1.05rem;
    line-height: 1;
    color: var(--text-primary);
    background: rgba(10, 10, 14, .75);
    border: 1px solid rgba(255, 255, 255, .14);
    border-radius: 8px;
    cursor: pointer;
}

.strip-x:hover { color: #ff8b6b; }

.strip-add {
    display: grid;
    place-items: center;
    gap: 2px;
    color: var(--text-secondary);
    border-style: dashed;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.strip-add span { font-size: 1.5rem; line-height: 1; }
.strip-add small { font-size: .68rem; }
.strip-add:hover { color: var(--accent-orange); border-color: rgba(255, 107, 53, .45); background: rgba(255, 107, 53, .06); }

.strip-tile.is-waiting { display: grid; place-items: center; }

.strip-spin {
    width: 20px; height: 20px;
    border: 2px solid rgba(255, 255, 255, .18);
    border-top-color: var(--accent-orange);
    border-radius: 50%;
    animation: strip-spin .8s linear infinite;
}

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

.edit-label {
    display: block;
    margin-bottom: 7px;
    font-size: .72rem;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: rgba(184, 186, 194, .7);
}

.edit-caption { width: 100%; }

.panel-error,
.edit-error {
    margin: 0 0 16px;
    padding: 10px 13px;
    font-size: .82rem;
    line-height: 1.5;
    color: #ffb4a0;
    background: rgba(255, 107, 53, .1);
    border: 1px solid rgba(255, 107, 53, .28);
    border-radius: 11px;
}

.edit-error { margin: 14px 0 0; }

.modal-foot { display: flex; justify-content: flex-end; gap: 10px; margin-top: 22px; }
.modal-foot .btn { padding: 11px 22px; font-size: .85rem; }

/* --- accounts ------------------------------------------------------------ */
/* The way back to the list a page was opened from. It sits above the card
   rather than on it, because it is about the page and not about the person. */
/* The way back, on the bar. An arrow and nothing else: what it does is the
   arrow, and where it goes is the page it goes back to, which is one press
   away from being seen anyway. */
.app-top-back {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 36px; height: 36px;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .06);
    border: 1px solid rgba(255, 255, 255, .14);
    border-radius: 50%;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.app-top-back svg {
    width: 19px; height: 19px;
    margin-left: -1px;            /* a chevron sits right of its own centre */
    fill: none; stroke: currentColor; stroke-width: 2.2;
    stroke-linecap: round; stroke-linejoin: round;
}

.app-top-back:hover {
    background: rgba(255, 255, 255, .12);
    border-color: var(--border-hover);
    transform: translateX(-2px);
}

.panel {
    --pad-x: 26px;
    --pad-b: 26px;
    --card-r: 22px;
    max-width: 880px;
    margin: 0 auto 28px;
    padding: 26px;
    background: rgba(255, 255, 255, .03);
    border: 1px solid rgba(255, 255, 255, .08);
    border-radius: 22px;
}

/* The admin's grids and the list of tools are lines that are each a box
   already, so a box around the lot is a border drawn round borders. The panel
   stays in the markup, where a form finds its error line, and is simply not
   drawn, on every screen. */
#view-accounts > .panel,
#view-tools > .panel {
    padding: 0;
    background: none;
    border: none;
    border-radius: 0;
}

.panel-head h2 { font-size: 1.15rem; font-weight: var(--fw-semibold); }
.panel-head p,
.panel-why { margin: 8px 0 20px; font-size: .84rem; line-height: 1.6; color: var(--text-secondary); }

/* The i beside a line, and what it holds. The button sits on the baseline of
   the sentence it belongs to, small enough to ignore and big enough to hit:
   the tap target is padded out past the circle you can see. */
.why-tip { position: relative; display: inline-block; vertical-align: baseline; }

.why-i {
    width: 15px; height: 15px; margin-left: 5px; padding: 0;
    border: 1px solid rgba(255, 255, 255, .22); border-radius: 50%;
    background: none; color: var(--text-secondary);
    font: 600 10px/13px var(--font-sans);
    cursor: pointer; transition: color .15s, border-color .15s;
}
.why-i::after { content: ''; position: absolute; inset: -9px; }
.why-i:hover, .why-tip.is-open .why-i {
    color: var(--text-primary); border-color: rgba(255, 255, 255, .55);
}
.why-i:focus-visible { outline: 2px solid var(--accent-orange); outline-offset: 2px; }

.why-more {
    position: absolute; left: 50%; bottom: calc(100% + 9px); z-index: 40;
    width: max-content; max-width: min(320px, 74vw);
    transform: translateX(-50%) translateY(3px);
    padding: 11px 13px; border-radius: 11px;
    background: rgba(24, 24, 30, .98);
    border: 1px solid rgba(255, 255, 255, .12);
    box-shadow: 0 20px 44px rgba(0, 0, 0, .55);
    font-size: .82rem; line-height: 1.55; color: var(--text-secondary);
    text-align: left; white-space: normal;
    opacity: 0; visibility: hidden;
    transition: opacity .16s, transform .16s, visibility .16s;
}

.why-tip.is-open .why-more {
    opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0);
}

/* A mouse gets it for free. A finger has no hover, which is why the click
   above is the real way in and this only ever adds to it. */
@media (hover: hover) and (pointer: fine) {
    .why-tip:hover .why-more {
        opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0);
    }
}

/* On a phone it stops trying to hang off the i at all. Centring a bubble on a
   letter works where there is room either side of it and nowhere else: written
   this way it sat half off the left of the screen, unreadable, and wide enough
   to give the page a sideways scroll it never had.
   It becomes a card along the foot of the screen instead — always the same
   place, always the full width it can have, and nothing to calculate. There is
   no hover on a phone, so the tap that opens it is the only way in and the
   bottom of the screen is where a thumb already is. */
@media (max-width: 620px) {
    .why-more {
        position: fixed;
        left: 16px; right: 16px; bottom: 16px;
        top: auto;
        z-index: 200;
        width: auto; max-width: none;
        padding: 14px 16px;
        transform: translateY(8px);
    }

    .why-tip.is-open .why-more { transform: translateY(0); }
}

/* A section that opens. Three of them one under the other would be a page
   nobody can see the end of, so each one is a heading until it is asked for.
   The whole heading is the handle, which is what a thumb needs. */
.panel-fold > .panel-head {
    display: flex;
    align-items: center;
    gap: 12px;
    cursor: pointer;
    list-style: none;
    user-select: none;
}

.panel-fold > .panel-head::-webkit-details-marker { display: none; }
.panel-fold > .panel-head:hover .fold-arrow { color: var(--text-primary); }

.panel-fold > .panel-head:focus-visible {
    outline: 2px solid rgba(255, 255, 255, .35);
    outline-offset: 6px;
    border-radius: 10px;
}

.fold-arrow {
    flex: 0 0 auto;
    width: 18px; height: 18px;
    margin-left: auto;
    /* The chevron is drawn well inside its own box, so a box flush with the
       panel's padding leaves the mark itself sitting further in than the title
       on the other side. Pulled out by what the drawing leaves empty. */
    margin-right: -4px;
    color: var(--text-secondary);
    fill: none; stroke: currentColor; stroke-width: 1.8;
    stroke-linecap: round; stroke-linejoin: round;
    transition: var(--transition-smooth);
}

.panel-fold[open] > .panel-head .fold-arrow { transform: rotate(180deg); }

/* Shut, a section is one line, and the room underneath it belonged to what is
   no longer there. What is left is the panel's own padding, the same above the
   line as below it: an uneven one is visible the moment a panel is closed. */
.panel-fold:not([open]) { padding-bottom: 26px; }

.new-row {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-bottom: 22px;
    padding-bottom: 22px;
    border-bottom: 1px solid rgba(255, 255, 255, .07);
}

.new-row input,
.new-row select,
.row-fields input,
.row-fields select,
.pw-field input {
    flex: 1 1 150px;
    min-width: 0;
    padding: 10px 13px;
    font: inherit;
    font-size: .85rem;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 11px;
    /* The browser draws its own parts inside these boxes: a calendar, a
       dropdown arrow, the picker that opens under them. Told which way round
       the page is, it draws them light instead of black on near black. */
    color-scheme: dark;
}

/* A date field lays its own value out centred on iOS and pays no attention to
   the padding around it, which leaves it sitting differently from every other
   box on the line. */
.new-row input[type="date"],
.row-fields input[type="date"] {
    -webkit-appearance: none;
    appearance: none;
}

input[type="date"]::-webkit-date-and-time-value { text-align: left; }

/* The little calendar, as quiet as the text beside it until it is aimed at. */
input[type="date"]::-webkit-calendar-picker-indicator {
    opacity: .5;
    cursor: pointer;
    transition: var(--transition-smooth);
}

input[type="date"]::-webkit-calendar-picker-indicator:hover,
input[type="date"]:focus::-webkit-calendar-picker-indicator { opacity: 1; }

.new-row input:focus,
.new-row select:focus,
.row-fields input:focus,
.row-fields select:focus,
.pw-field input:focus { outline: none; border-color: var(--accent-orange); }

.new-row .btn { flex: 0 0 auto; padding: 10px 22px; font-size: .85rem; }

/* An account is one line: who it is, and a gear. A studio with forty clients
   should be able to read down the list, which a column of open forms makes
   impossible. */
.lister { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }

.lister li {
    display: grid;
    grid-template-columns: 34px minmax(0, 1fr) auto;
    align-items: center;
    gap: 12px;
    padding: 8px 8px 8px 12px;
    background: rgba(255, 255, 255, .03);
    border: 1px solid rgba(255, 255, 255, .07);
    border-radius: 13px;
}

.lister li:hover { border-color: rgba(255, 255, 255, .14); }

/* A row that opens something is pressable all the way across, not only at the
   arrow. The pointer says so on a desktop, and on a phone the row dips under
   the finger, so the press is answered by the thing that was actually hit. */
.lister li[data-grid], .lister li[data-project], .lister li[data-row] { cursor: pointer; }

.lister li[data-grid]:active, .lister li[data-project]:active {
    background: rgba(255, 255, 255, .07);
    border-color: rgba(255, 255, 255, .14);
}

.lister-face {
    position: relative;
    display: grid;
    place-items: center;
    width: 34px; height: 34px;
    font-size: .85rem;
    font-weight: var(--fw-medium);
    border-radius: 50%;
}

.lister-who { display: grid; gap: 2px; min-width: 0; }

/* A line has three columns and always will, however many buttons end up on
   the end of it. They travel together in the third. */
.lister-acts { display: flex; align-items: center; gap: 2px; }

.lister-name,
.lister-sub {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.lister-name { font-size: .9rem; font-weight: var(--fw-medium); }
.lister-sub { font-size: .74rem; color: rgba(184, 186, 194, .7); }

.lister-gear {
    display: grid;
    place-items: center;
    width: 34px; height: 34px;
    color: var(--text-secondary);
    background: none;
    border: 1px solid transparent;
    border-radius: 10px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.lister-gear:hover {
    color: var(--text-primary);
    background: rgba(255, 255, 255, .06);
    border-color: rgba(255, 255, 255, .12);
}

.lister-gear svg {
    width: 17px; height: 17px;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
}

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

/* A date that is no longer somebody's to type, because something else decides
   it. Still readable, plainly not editable. */
.row-fields input:disabled {
    opacity: .5;
    cursor: not-allowed;
}

.row-fields label {
    flex: 1 1 160px;
    display: grid;
    gap: 5px;
    font-size: .68rem;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: rgba(184, 186, 194, .65);
}

.row-members {
    margin-top: 14px;
    padding: 12px 14px;
    border: 1px solid rgba(255, 255, 255, .08);
    border-radius: 12px;
}

.row-members legend {
    padding: 0 6px;
    font-size: .68rem;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: rgba(184, 186, 194, .65);
}

.member { display: inline-flex; align-items: center; gap: 8px; margin: 5px 14px 5px 0; font-size: .85rem; cursor: pointer; }
.member input { width: 15px; height: 15px; accent-color: var(--accent-orange); }
.member small { color: rgba(184, 186, 194, .65); font-size: .72rem; }

/* A choice in the send panel stands on its own line. The ways to pay are
   sentences about where money goes, and flowing three of them into a row
   turns a decision into a form to be skimmed past. */
.tell-line { display: flex; margin-right: 0; }

/* Sending somebody the guide to the portal is a thing done once, so it asks
   for none of the attention the gear beside it has earned. The two share the
   one cell the header's grid keeps for them, rather than the header growing a
   column that stands empty on every page but the admin's. */
.client-do { display: flex; align-items: center; gap: 8px; }
.client-intro { font-size: .74rem; padding: 5px 10px; opacity: .55; }
.client-intro:hover, .client-intro:focus-visible { opacity: 1; }
.row-none { font-size: .8rem; color: rgba(184, 186, 194, .6); }

/* --- a password, and the eye that reads it back --------------------------- */
.pw-field { position: relative; display: flex; align-items: center; min-width: 0; }
.new-row .pw-field { flex: 1 1 190px; }
.pw-field input { width: 100%; padding-right: 42px; }
.pw-head { margin-top: 18px; }

.pw-eye {
    position: absolute;
    right: 5px;
    display: grid;
    place-items: center;
    width: 30px; height: 30px;
    color: var(--text-secondary);
    background: none;
    border: none;
    border-radius: 8px;
    cursor: pointer;
}

.pw-eye:hover { color: var(--text-primary); background: rgba(255, 255, 255, .07); }

.pw-eye svg {
    width: 17px; height: 17px;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
}

.pw-eye .pw-shut { display: none; }
.pw-eye[aria-pressed="true"] .pw-open { display: none; }
.pw-eye[aria-pressed="true"] .pw-shut { display: block; }
.pw-eye[aria-pressed="true"] { color: var(--accent-orange); }

.pw-note {
    margin: 9px 0 0;
    font-size: .74rem;
    line-height: 1.55;
    color: rgba(184, 186, 194, .65);
}

/* --- the panel behind a gear ---------------------------------------------- */
.modal-acct { width: min(560px, 94vw); max-height: 90vh; overflow-y: auto; padding: 30px 28px 24px; }
.modal-acct .row-fields { margin-top: 4px; }
.modal-acct .modal-foot .app-danger { margin-right: auto; }

/* --- the picker ----------------------------------------------------------- */
/* Floats over whatever asked for it, including the editor, so it sits above
   every panel on the page. */
.picker {
    position: absolute;
    z-index: 95;
    width: 268px;
    padding: 12px;
    background: rgba(24, 24, 30, .98);
    -webkit-backdrop-filter: blur(18px);
    backdrop-filter: blur(18px);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 15px;
    box-shadow: 0 18px 44px rgba(0, 0, 0, .5);
}

.picker-file {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    padding: 10px 12px;
    font: inherit;
    font-size: .84rem;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .09);
    border-radius: 11px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.picker-file:hover { border-color: var(--border-hover); background: rgba(255, 255, 255, .08); }

.picker-file svg {
    width: 17px; height: 17px;
    fill: none; stroke: currentColor; stroke-width: 1.8;
    stroke-linecap: round; stroke-linejoin: round;
}

.picker-or {
    margin: 10px 0;
    font-size: .7rem;
    text-align: center;
    color: rgba(184, 186, 194, .6);
}

.picker-link { display: flex; gap: 7px; }

.picker-url {
    flex: 1;
    min-width: 0;
    padding: 9px 11px;
    font: inherit;
    font-size: .8rem;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 10px;
}

.picker-url:focus { outline: none; border-color: var(--accent-orange); }
.picker-link .app-ghost { flex: 0 0 auto; padding: 9px 13px; }

/* --- the highlight panel --------------------------------------------------- */
.modal-hl { width: min(440px, 94vw); padding: 30px 28px 24px; }

.hl-edit { display: flex; align-items: center; gap: 20px; margin-bottom: 4px; }

.hl-cover {
    position: relative;
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 92px; height: 92px;
    padding: 0;
    overflow: hidden;
    font: inherit;
    color: var(--text-secondary);
    background: rgba(255, 255, 255, .04);
    border: 1px dashed rgba(255, 255, 255, .18);
    border-radius: 50%;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.hl-cover:hover { color: var(--accent-orange); border-color: rgba(255, 107, 53, .5); }
.hl-cover img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.hl-cover span { font-size: 1.6rem; font-weight: var(--fw-light); line-height: 1; }
.hl-cover.is-waiting { opacity: .5; pointer-events: none; }

.hl-name-label { flex: 1; min-width: 0; }

.hl-name {
    width: 100%;
    margin-top: 7px;
    padding: 11px 13px;
    font: inherit;
    font-size: .9rem;
    letter-spacing: normal;
    text-transform: none;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 11px;
}

.hl-name:focus { outline: none; border-color: var(--accent-orange); }
.modal-hl .modal-foot { justify-content: space-between; }
.modal-hl .modal-foot .app-ghost + .app-ghost { margin-left: auto; }

/* --- toast ---------------------------------------------------------------- */
.toast {
    position: fixed; left: 50%; bottom: 26px;
    z-index: 100;
    transform: translateX(-50%);
    padding: 12px 20px;
    font-size: .85rem;
    color: var(--bg-dark);
    background: var(--text-primary);
    border-radius: 12px;
    box-shadow: 0 16px 40px rgba(0, 0, 0, .45);
}

.toast-bad { color: #fff; background: #c2410c; }

@media (max-width: 860px) {
    .modal { padding: 0; }

    /* Full screen, and measured in the height the phone actually leaves after
       its address bar, which is what dvh means and vh does not.

       One column, and the whole of it scrolls: the picture goes up off the top
       with everything else, the way a post does on a phone. Nothing is pinned,
       so nothing has to be given a height of its own and nothing can end up
       squeezed to nothing. The scrolling is the browser's own, which is what
       makes it follow a finger and carry on after it lets go. */
    .modal-post {
        display: flex;
        flex-direction: column;
        width: 100%;
        height: 100vh;
        height: 100dvh;
        overflow-y: auto;
        /* The page behind must not start moving when this reaches its end. */
        overscroll-behavior: contain;
        -webkit-overflow-scrolling: touch;
        border-radius: 0;
        border: none;
    }

    /* Scrolled away with the rest, the close button would be gone after an
       inch, so it stays where it was put. */
    .modal-post .modal-x { position: fixed; top: 10px; right: 12px; }

    #post-modal.is-bare .modal-post { justify-content: center; }

    /* The same four by five frame as on a desktop, at its full size: there is
       nothing left to leave room for, since what follows it is below it rather
       than beside it. */
    .post-stage {
        flex: none;
        aspect-ratio: 4 / 5;
        height: auto;
        max-height: none;
    }

    /* Clear of the close button, which is pinned to the same corner. */
    .post-count { right: 58px; }
    /* Everything here is its own height and the panel above does the
       scrolling. A caption of its natural height over a list with a scrollbar
       of its own was the old arrangement, and a long caption squeezed that
       list down to nothing; a list of nothing scrolls nowhere. */
    .post-side {
        flex: none;
        overflow: visible;
        border-left: none;
        border-top: 1px solid rgba(255, 255, 255, .08);
        padding: 18px 16px 24px;
    }

    .post-notes { flex: none; }
    .note-list { flex: none; overflow: visible; }

    .modal-edit,
    .modal-acct {
        width: 100%;
        max-height: 100vh;
        height: 100vh;
        height: 100dvh;
        border-radius: 0;
        border: none;
        padding: 26px 18px 20px;
    }

    .bell-panel {
        position: fixed;
        top: 64px; right: 12px; left: 12px;
        width: auto;
        max-height: calc(100dvh - 88px);
    }

    .modal-hl { width: 100%; padding: 26px 18px 20px; }
    .toast {
        left: 16px; right: 16px;
        bottom: calc(78px + env(safe-area-inset-bottom));
        transform: none; text-align: center;
    }
}

/* --- a phone -------------------------------------------------------------- */
@media (max-width: 560px) {
    .app-view:not(.app-chat) { padding: 16px 12px calc(78px + env(safe-area-inset-bottom)); }

    /* Instagram's own gap, which is most of what makes a grid read as a grid. */
    .ig-grid { gap: 2px; }
    .cell, .cell-empty { border-radius: 2px; }

    .profile-card { padding: 16px; border-radius: 18px; gap: 14px; }
    .profile-name { font-size: 1.12rem; }
    .profile-stats { gap: 12px; font-size: .78rem; }
    .profile-hint { padding-top: 12px; font-size: .75rem; }

    .panel { --pad-x: 16px; --pad-b: 18px; --card-r: 18px; padding: 18px 16px; border-radius: 18px; }
    .panel-fold:not([open]) { padding-bottom: 18px; }
    .file { grid-template-columns: minmax(0, 1fr); }
    .file-do { justify-content: flex-start; flex-wrap: wrap; }
    .file-do .btn { flex: 1 1 auto; }
    .panel-fold > .panel-head { gap: 10px; }
    .new-row input, .new-row select { flex-basis: 100%; }
    .new-row .btn { width: 100%; }

    /* 16px exactly, or Safari zooms the page the moment a field is tapped and
       never brings it back. A date field is the worst of them: the zoom lands
       while the picker is opening. */
    .new-row input,
    .new-row select,
    .row-fields input,
    .row-fields select,
    .pw-field input { font-size: 16px; }

    /* And with the value laid out from the left, it fills the line it was
       given rather than floating in the middle of it. */
    .new-row input[type="date"] { min-height: 42px; }

    /* A menu that opens next to a thumb is a menu half of which is under the
       thumb. On a phone both of them come up from the bottom of the screen
       instead, wide enough to be hit and clear of whatever they belong to.
       The positions are written by script, so they have to be overruled. */
    .cell-menu,
    .picker {
        position: fixed !important;
        left: 12px !important;
        right: 12px;
        top: auto !important;
        bottom: max(14px, env(safe-area-inset-bottom));
        width: auto;
        min-width: 0;
        padding: 8px;
    }

    .cell-menu button { padding: 13px 14px; font-size: .9rem; }
    .picker-file { padding: 13px 14px; font-size: .9rem; }
    .picker-url { padding: 12px 13px; font-size: .88rem; }

    .hl-edit { gap: 14px; }
    .hl-cover { width: 76px; height: 76px; }

    .note.is-reply { margin-left: 14px; }
    .note.is-reply::before { left: -9px; width: 6px; }
}

/* --- a client's page ------------------------------------------------------ */
/* Their work, their invoices, and what is still running. Two people read this
   screen: the one who issues the invoices and the one who pays them. What
   separates them is only which controls are drawn, so everything here has to
   read as well without a gear beside it as with one. */
.client-card { display: grid; gap: 18px; }

.client-id {
    display: grid;
    grid-template-columns: 56px minmax(0, 1fr) auto;
    align-items: center;
    gap: 14px;
}

.client-face {
    display: grid;
    place-items: center;
    width: 56px; height: 56px;
    font-size: 1.3rem;
    font-weight: var(--fw-semibold);
    border-radius: 50%;
    color: rgb(var(--face, 255, 255, 255));
    background: rgba(var(--face, 255, 255, 255), .16);
}

.client-who { min-width: 0; }
.client-who h2 { font-size: 1.25rem; font-weight: var(--fw-semibold); }

.client-who p {
    margin-top: 4px;
    font-size: .82rem;
    color: var(--text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
}

/* What each of the two cards comes to, in figures: the two directions money
   runs with a partner, what a client owes and is paid up to. No box and no
   rule, because a number that size with a word under it is already a thing on
   the page, and a frame round it only says the same thing twice. A zero stays
   where it is and goes quiet, because a zero is the answer. */
.sums {
    list-style: none;
    margin: 22px 0 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 14px 44px;
}

.sums li { display: grid; gap: 3px; }

/* The room above is for the name row. A card that opens on the sums, as a
   partner's does under their own face, has nothing up there to keep off. */
.client-card > .sums:first-child { margin-top: 0; }

.sums strong {
    font-size: 1.5rem;
    font-weight: var(--fw-semibold);
    line-height: 1.15;
    color: var(--text-primary);
}

.sums span { font-size: .76rem; color: rgba(184, 186, 194, .62); }
.sums .is-due strong { color: var(--accent-orange); }
.sums .is-zero strong { color: rgba(255, 255, 255, .4); }

/* Your own note about them, on the same page they read. It is marked rather
   than merely placed, because one day something will be written here that
   would be awkward to find out was shared. */
.client-note {
    display: flex;
    gap: 10px;
    padding: 12px 14px;
    font-size: .84rem;
    line-height: 1.6;
    white-space: pre-wrap;
    color: var(--text-secondary);
    background: rgba(255, 255, 255, .03);
    border: 1px dashed rgba(255, 255, 255, .14);
    border-radius: 13px;
}

.client-note span {
    flex: 0 0 auto;
    align-self: flex-start;
    padding: 2px 8px;
    font-size: .64rem;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--bg-dark);
    background: rgba(184, 186, 194, .8);
    border-radius: 999px;
}

/* A heading with something to press beside it, rather than above or below it
   where it would read as belonging to the first row instead of the panel. */
.panel-top { display: flex; align-items: flex-start; gap: 12px; }
.panel-top .panel-head { flex: 1; min-width: 0; }
.panel-top .lister-gear { flex: 0 0 auto; }

.bill {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 10px;
}

.bill li {
    display: grid;
    gap: 3px;
    padding: 11px 13px;
    background: rgba(255, 255, 255, .03);
    border: 1px solid rgba(255, 255, 255, .07);
    border-radius: 12px;
}

.bill span {
    font-size: .66rem;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: rgba(184, 186, 194, .6);
}

/* A VAT number has no spaces to break at, and an address is long. */
.bill strong { font-size: .88rem; font-weight: var(--fw-medium); overflow-wrap: anywhere; }

.bill-none { padding: 6px 2px 2px; font-size: .84rem; color: rgba(184, 186, 194, .6); }

/* The one press that opens our own card. Quiet, and in the heading row it
   takes the empty middle: the title keeps the left, the arrow keeps the far
   right, and this sits between them without either of them moving. */
.ours-line { margin: 14px 0 0; }
.ours-all { margin: 22px 0 0; }
.ours-line .app-ghost,
.ours-all .app-ghost { font-size: .8rem; }

/* --- our own card, read ------------------------------------------------------
   Six details and two accounts, and nothing drawn round any of them. One
   column of words and one of answers is already a table; giving each answer a
   box as well only repeats in ink what the alignment has said. */
.facts,
.ours-pay { list-style: none; margin: 0; padding: 0; display: grid; }

/* The air between two rows is what tells them apart, so there is enough of it
   to do the job on its own. */
.facts { gap: 15px; margin-top: 18px; }
.ours-pay { gap: 6px; }

.facts li,
.facts.is-editing label,
.ours-pay li button {
    display: grid;
    grid-template-columns: 124px minmax(0, 1fr);
    gap: 18px;
    align-items: baseline;
}

.fact-key { font-size: .76rem; color: rgba(184, 186, 194, .6); }

.fact-val {
    font-size: .92rem;
    color: var(--text-primary);
    overflow-wrap: anywhere;
}

.fact-val small {
    display: block;
    margin-top: 3px;
    font-size: .72rem;
    color: rgba(184, 186, 194, .5);
}

/* The whole row is the press, so the target is a row and not a number, and it
   shows itself only under the cursor rather than standing there as a button. */
.ours-pay li button {
    grid-template-columns: 124px minmax(0, 1fr) 16px;
    width: 100%;
    padding: 11px 12px;
    margin: 0 -12px;
    font: inherit;
    color: inherit;
    text-align: left;
    background: none;
    border: 0;
    border-radius: 11px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

/* An IBAN is read in a column against the one above it. */
.ours-pay .fact-val { font-variant-numeric: tabular-nums; }

.ours-pay li button:hover,
.ours-pay li button:focus-visible { background: rgba(255, 255, 255, .04); outline: none; }
.ours-pay li button:hover .pay-icon,
.ours-pay li button:focus-visible .pay-icon { opacity: 1; }
.ours-pay .pay-icon { align-self: center; }

.ours-head {
    margin: 34px 0 14px;
    font-size: .76rem;
    letter-spacing: .07em;
    text-transform: uppercase;
    color: rgba(184, 186, 194, .6);
}

.ours-why { margin: 18px 0 0; font-size: .76rem; color: rgba(184, 186, 194, .5); }

/* --- the details, folded into the card they belong to ------------------------
   Not a section stacked under the card but the foot of the card itself: it
   reaches both edges, takes the card's own bottom corners, and sits a shade
   darker than what it hangs from, which is all the separation a strip like
   this needs. Shut it is one line; open it grows into the two columns it
   always was, and the card grows with it. */
.card-fold {
    margin: 4px calc(var(--pad-x) * -1) calc(var(--pad-b) * -1);
    background: rgba(0, 0, 0, .22);
    border-radius: 0 0 calc(var(--card-r) - 1px) calc(var(--card-r) - 1px);
    overflow: hidden;
}

.card-fold-head {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0;
    padding: 15px var(--pad-x);
    font-size: .88rem;
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    list-style: none;
    cursor: pointer;
    user-select: none;
    transition: var(--transition-smooth);
}

.card-fold-head::-webkit-details-marker { display: none; }
.card-fold-head:hover { background: rgba(255, 255, 255, .035); }
.card-fold-head:hover .fold-arrow { color: var(--text-primary); }
/* Safari counts a click on a summary as a reason to draw the keyboard's ring,
   and a frame left round the strip after opening it with the mouse reads as a
   mistake. The same shade as hovering says where the keyboard is instead. */
.card-fold-head:focus-visible { outline: none; background: rgba(255, 255, 255, .05); }

/* What the line says while it is shut: enough to answer whether it is worth
   opening, quiet enough not to read as a second heading. */
.card-fold-head small {
    min-width: 0;
    font-weight: var(--fw-regular);
    font-size: .78rem;
    color: rgba(184, 186, 194, .45);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.card-fold[open] > .card-fold-head .fold-arrow { transform: rotate(180deg); }

/* A heading that is not a handle, while the details underneath are being
   typed: nothing here opens or shuts. */
.card-fold-head.as-still { cursor: default; }
.card-fold.is-editing { padding-bottom: 4px; }

.card-fold-body { display: grid; grid-template-rows: 1fr; min-height: 0; }
.card-fold .facts { margin-top: 0; }

/* Room enough under the handle for the first row not to hang off it, and no
   more underneath than the last row needs: there is nothing down there any
   more to make space for. */
.card-fold-body > div { min-height: 0; overflow: hidden; padding: 9px var(--pad-x) 19px; }
.card-fold form.facts { padding: 9px var(--pad-x) 20px; }

/* The pencil, beside the handle rather than at the foot of what it changes.
   It shows itself once the details are open, which is the only time there is
   anything to change. */
.fold-do {
    display: none;
    flex: 0 0 auto;
    margin-left: auto;
    padding: 6px;
    color: var(--text-secondary);
    background: none;
    border: 0;
    border-radius: 9px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.card-fold[open] > .card-fold-head .fold-do,
.fold-do.as-shown { display: inline-flex; }
.fold-do:hover { color: var(--text-primary); background: rgba(255, 255, 255, .06); }
.fold-do:focus-visible { outline: 2px solid rgba(255, 255, 255, .3); outline-offset: 1px; }

.fold-pen {
    width: 16px; height: 16px;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
}

/* With the pencil taking the empty middle, the arrow follows it home. Only
   while the pencil is showing: a hidden one takes no room, and the arrow then
   keeps to the far edge as it does on every other strip. */
.card-fold[open] > .card-fold-head .fold-do + .fold-arrow,
.fold-do.as-shown + .fold-arrow { margin-left: 8px; }

/* The opening itself. A row that goes from nothing to its own height carries
   the eye down with it; where the browser cannot do that yet, it simply
   appears, which is what it did before. */
@supports (interpolate-size: allow-keywords) {
    /* The one thing that lets a height of auto be animated to at all. */
    :root { interpolate-size: allow-keywords; }

    .card-fold::details-content {
        block-size: 0;
        overflow: hidden;
        transition: block-size var(--fold-ms) var(--fold-ease),
                    content-visibility var(--fold-ms) allow-discrete;
        transition-behavior: allow-discrete;
    }

    .card-fold[open]::details-content { block-size: auto; }

    @media (prefers-reduced-motion: reduce) {
        .card-fold::details-content { transition: none; }
    }
}

/* Typed where it was read: the same two columns, and an answer that is now a
   line you can write on. No box round it, because the box was never what said
   this could be changed — the press above it said that. */
.facts.is-editing { gap: 12px; }
.facts.is-editing label { align-items: center; cursor: text; }

.fact-field {
    width: 100%;
    padding: 6px 2px;
    font: inherit;
    font-size: .92rem;
    color: var(--text-primary);
    background: none;
    border: 0;
    border-bottom: 1px solid rgba(255, 255, 255, .13);
    border-radius: 0;
    transition: var(--transition-smooth);
}

.fact-field:hover { border-bottom-color: rgba(255, 255, 255, .24); }
.fact-field:focus { outline: none; border-bottom-color: var(--accent-orange); }

.fact-do { display: flex; justify-content: flex-end; gap: 10px; margin-top: 24px; }
.fact-do.as-one { margin: 20px 0 0; }
.fact-do .btn { padding: 10px 22px; font-size: .85rem; }

/* The press that turns the reading into writing, quiet beside the heading. */
.fact-edit { flex: 0 0 auto; padding: 7px 14px; font-size: .78rem; }

.facts .panel-error { margin: 6px 0 0; }

@media (max-width: 560px) {
    .facts li,
    .facts.is-editing label,
    .ours-pay li button { grid-template-columns: minmax(0, 1fr); gap: 3px; }
    .ours-pay li button { grid-template-columns: minmax(0, 1fr) 16px; }
    .ours-pay .fact-key { grid-column: 1 / -1; }
    .facts { gap: 17px; }
    .ours-pay { gap: 10px; }
    .fact-field { font-size: 16px; }
    .fact-do .btn, .fact-do .app-ghost { flex: 1 1 auto; }
}

/* The house colours, on the one press that opens the house's own card. The
   pair and the direction are the site's, the same two ends the logo is painted
   out of; white sits on the red end and still reads on the orange one. */
.ours-open {
    flex: 0 0 auto;
    /* Its own padding, handed back to the row: the press keeps the size a
       thumb needs, and the heading keeps the height it has in every other
       panel, so a closed one beside a closed one is the same box twice. */
    margin: -8px 0 -8px auto;
    padding: 8px 17px;
    font-size: .78rem;
    font-weight: var(--fw-medium);
    white-space: nowrap;
    color: #ffffff;
    background: linear-gradient(100deg, #d6341a, var(--accent-orange));
    border: 0;
    border-radius: 999px;
}

.ours-open:hover,
.ours-open:focus-visible {
    color: #ffffff;
    box-shadow: 0 6px 18px rgba(255, 107, 53, .3);
}

/* On the client's card it stands on a line of its own, where an automatic
   margin means nothing and a negative one would only shorten the line. */
.ours-line .ours-open { margin: 0; }

/* The arrow keeps an automatic margin of its own, and two of those split the
   empty space between them, which left the press stranded in the middle. Here
   the press takes all of it and the arrow follows right behind. */
.ours-open + .fold-arrow { margin-left: 0; }

/* The title gives way first, since it is the one thing here that can be read
   in half and still be understood. */
.panel-fold > .panel-head > h2 { min-width: 0; overflow-wrap: anywhere; }

@media (max-width: 560px) {
    .panel-fold > .panel-head { flex-wrap: wrap; }
    .ours-open { order: 3; margin: 2px 0 0; width: 100%; }
    .fold-arrow { order: 2; }
}

/* --- where the money goes ---------------------------------------------------
   The same details as the card above, in the shape somebody uses rather than
   the shape somebody reads. Each row is a button end to end, because the thing
   being aimed at on a phone is a line of twenty-seven characters and the way
   to hit it is to make the target the row. */
.pay {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 8px;
}

.pay li { display: grid; gap: 5px; }

.pay-what {
    font-size: .66rem;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: rgba(184, 186, 194, .6);
}

.pay-copy {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    width: 100%;
    padding: 11px 13px;
    font: inherit;
    text-align: left;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .03);
    border: 1px solid rgba(255, 255, 255, .07);
    border-radius: 12px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.pay-copy:hover { border-color: var(--border-hover); background: rgba(255, 255, 255, .05); }
.pay-copy:active { background: rgba(255, 255, 255, .08); }

/* Digits that have to be read back against a bank's own screen: same width
   each, so a mistyped one shows up as a shape that does not line up. */
.pay-val {
    font-size: .95rem;
    font-weight: var(--fw-medium);
    font-variant-numeric: tabular-nums;
    letter-spacing: .02em;
    overflow-wrap: anywhere;
}

.pay-icon {
    flex: 0 0 auto;
    width: 16px; height: 16px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.7;
    stroke-linecap: round;
    stroke-linejoin: round;
    opacity: .55;
}

.pay-copy:hover .pay-icon { opacity: 1; }

/* Whose account it is, under the account. Three accounts at one firm can
   stand in three different names, and the bank now checks the name against
   the IBAN before it lets a transfer through. */
.pay-who { padding: 0 2px; font-size: .74rem; color: rgba(184, 186, 194, .55); }

.pay-note { margin: 12px 0 0; font-size: .78rem; color: rgba(184, 186, 194, .6); }

/* The same button, shrunk to sit among a row's other buttons. */
.pay-mini {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-variant-numeric: tabular-nums;
    letter-spacing: .02em;
}

.pay-mini .pay-icon { width: 13px; height: 13px; }

/* The one box in the row of boxes that is not about the charge but about
   whether anybody is told of it. It takes the whole width so it reads as a
   sentence under the row rather than as another field in it. */
.new-tell {
    display: flex;
    align-items: center;
    gap: 9px;
    flex: 1 0 100%;
    margin-top: -2px;
    font-size: .8rem;
    color: var(--text-secondary);
    cursor: pointer;
}

.new-tell input { flex: 0 0 auto; width: 16px; height: 16px; accent-color: var(--accent-orange); }

li.none-yet {
    display: block;
    grid-column: 1 / -1;
    padding: 18px 4px;
    font-size: .84rem;
    text-align: center;
    color: rgba(184, 186, 194, .6);
    background: none;
    border: 0;
}

/* --- subscriptions -------------------------------------------------------- */
.subs, .ledger, .files { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }

/* Whether this client's accounts can open a grid, said where the reason for it
   is. Not a warning: both answers are ordinary, and only one of them is a
   thing to do something about. */
.sees-grids {
    margin: 0 0 18px;
    padding: 10px 14px;
    font-size: .78rem;
    line-height: 1.55;
    color: var(--text-secondary);
    background: rgba(255, 255, 255, .03);
    border: 1px solid rgba(255, 255, 255, .08);
    border-left: 2px solid rgba(255, 107, 53, .45);
    border-radius: 10px;
}

.sees-grids.is-on { border-left-color: rgba(76, 175, 118, .5); }
.not-social { color: rgba(184, 186, 194, .75); }

/* A tick box standing in a row of boxes to type in. */
.row-mark { flex: 0 0 auto; align-self: center; white-space: nowrap; }

/* --- what one person asks another ---------------------------------------- */
/* A request is a card rather than a row: it carries a sentence, sometimes a
   picture, and a thing to press, and none of that fits on a line. */
.asks { list-style: none; margin: 16px 0 0; padding: 0; display: grid; gap: 10px; }

.ask {
    display: grid;
    gap: 10px;
    padding: 14px 16px;
    background: rgba(255, 255, 255, .03);
    border: 1px solid rgba(255, 255, 255, .07);
    border-radius: 14px;
}

/* The three faces of a request, which are faces of whoever is looking at it.
   Lit: something happened and it was not yours. Quiet: you are up to date.
   Closed: whoever asked has said it is finished, and it steps back. */
.ask.is-lit {
    border-color: rgba(255, 107, 53, .4);
    background: rgba(255, 107, 53, .05);
}

.ask.is-lit .ask-title { color: var(--text-primary); }

.ask.is-lit::before {
    content: '';
    position: absolute;
    left: 0; top: 14px; bottom: 14px;
    width: 3px;
    border-radius: 0 3px 3px 0;
    background: var(--accent-orange);
}

.ask.is-closed { opacity: .55; }
.ask.is-closed .ask-title { text-decoration: line-through; text-decoration-thickness: 1px; }
.ask { position: relative; cursor: pointer; }
.ask.is-open { border-color: rgba(255, 255, 255, .16); }
.ask-do, .ask-thread { cursor: auto; }

.ask-head { display: grid; gap: 3px; padding-right: 26px; }

/* A card that opens says so. The arrow leans in when the pointer is anywhere
   on the card, because the whole card is the button. */
.ask-go {
    position: absolute;
    top: 16px; right: 14px;
    width: 16px; height: 16px;
    color: rgba(184, 186, 194, .45);
    fill: none; stroke: currentColor; stroke-width: 1.9;
    stroke-linecap: round; stroke-linejoin: round;
    transition: var(--transition-smooth);
}

.ask:hover .ask-go { color: var(--text-primary); transform: translateX(2px); }
.ask.is-lit .ask-go { color: var(--accent-orange); }
.ask-title { font-size: .95rem; font-weight: var(--fw-medium); }
.ask-who { font-size: .74rem; color: rgba(184, 186, 194, .7); }

.ask-said {
    margin: 0;
    font-size: .84rem;
    line-height: 1.6;
    color: var(--text-secondary);
    white-space: pre-wrap;
}

.ask-shots { display: flex; flex-wrap: wrap; gap: 8px; }

/* A thumbnail is a way into the picture, so it is a button and says so under
   the pointer, rather than a picture that happens to react. */
.ask-shot-open {
    padding: 0;
    background: none;
    border: 0;
    border-radius: 10px;
    cursor: zoom-in;
    line-height: 0;
    transition: var(--transition-smooth);
}

.ask-shot-open img { border-radius: 10px; }
.ask-shot-open:hover { opacity: .82; transform: translateY(-1px); }
.ask-shot-open:focus-visible { outline: 2px solid var(--accent-orange); outline-offset: 2px; }

/* Pictures with nothing to say about them: no caption, no heart, no comments,
   so the column that holds those is not there either and the stage has the box
   to itself. */
#post-modal.is-bare .post-side { display: none; }

/* With the column beside it gone, the right edge of the stage is the right
   edge of the box, and the counter and the close button were both put in that
   corner by different rules that had never met there. The counter steps aside
   by the width of the button and the gap it sits in. */
#post-modal.is-bare .post-count { right: 58px; }

/* Above the fold the box is two columns and one of them has just gone, so the
   box narrows to what is left rather than standing half empty. Kept to the
   wide layout on purpose: below it the box is a column that scrolls, and
   giving it a width here would take away the full screen it has there. */
@media (min-width: 861px) {
    #post-modal.is-bare .modal-post {
        grid-template-columns: calc(var(--stage-h) * 4 / 5);
        width: min(calc(var(--stage-h) * 4 / 5), 94vw);
    }
}

.ask-shots img {
    width: 84px; height: 84px;
    object-fit: cover;
    border-radius: 10px;
    border: 1px solid rgba(255, 255, 255, .08);
}

/* While one is being written, each picture carries the way to take it off. */
.ask-shot { position: relative; display: inline-flex; }
.ask-shot .file-drop { position: absolute; top: -6px; right: -6px; width: 24px; height: 24px;
    background: var(--bg-dark); border-color: rgba(255, 255, 255, .18); }

.ask-do { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* Received. The same mark on both sides of it: a button while it is something
   to do, and a plain word once it is something that happened. */
.tick {
    width: 14px; height: 14px;
    flex: 0 0 auto;
    fill: none; stroke: currentColor; stroke-width: 2.4;
    stroke-linecap: round; stroke-linejoin: round;
}

.ask-note { display: inline-flex; align-items: center; gap: 7px; }

.ask-noted {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: .78rem;
    color: #9fe0b5;
}

.ask-seen {
    display: flex;
    align-items: center;
    gap: 7px;
    margin: 10px 0 0;
    font-size: .76rem;
    color: #9fe0b5;
}
.ask-when { font-size: .74rem; color: rgba(184, 186, 194, .55); margin-right: auto; }
.ask-do .btn { padding: 8px 16px; font-size: .78rem; text-decoration: none; }

/* What came back, under the thing it came back to. */
.ask-thread {
    list-style: none;
    margin: 2px 0 0;
    padding: 12px 0 0;
    display: grid;
    gap: 12px;
    border-top: 1px solid rgba(255, 255, 255, .07);
}

.ask-thread.is-empty {
    margin: 2px 0 0;
    font-size: .78rem;
    color: rgba(184, 186, 194, .55);
}

.say strong { font-size: .8rem; font-weight: var(--fw-medium); }
.say small { margin-left: 8px; font-size: .7rem; color: rgba(184, 186, 194, .5); }

.say p {
    margin: 4px 0 0;
    font-size: .84rem;
    line-height: 1.6;
    color: var(--text-secondary);
    white-space: pre-wrap;
}

/* Taking note is not answering, and does not look like it. */
.say.is-note p { font-style: italic; color: rgba(184, 186, 194, .55); }

/* The history inside the panel scrolls on its own, so a long argument never
   pushes the box for answering it off the bottom of the screen. */
#say-thread { max-height: 44vh; overflow-y: auto; margin-bottom: 4px; }
#say-thread .ask-said { margin-bottom: 12px; }
.say-link { margin: 12px 0 0; }

#say-text { min-height: 96px; resize: vertical; }

/* The three shelves of a room, read the way an inbox is read. Always all
   three, because a shelf that disappears when it empties is one you stop
   trusting to be the whole picture. */
.asks-tabs {
    margin: 20px 0 0;
    padding: 0;
    border-bottom: 1px solid rgba(255, 255, 255, .07);
}

.asks-tabs button { display: inline-flex; align-items: center; gap: 7px; }

/* The first two shelves are what somebody opened the room to find: what the
   team has been asked, and what they have been asked. The third is their own
   sent folder, which is a different question — so it sits at the far end of
   the row and is quieter, chosen or not, and its underline is grey rather than
   the colour that means something wants you.
   Where the row is too narrow to have a far end, it simply follows the other
   two and the row scrolls, which is what it already did. */
.asks-tabs .is-apart { margin-left: auto; color: rgba(184, 186, 194, .45); }
.asks-tabs .is-apart:hover { color: rgba(184, 186, 194, .85); }

.asks-tabs .is-apart.is-on {
    color: var(--text-secondary);
    border-bottom-color: rgba(255, 255, 255, .26);
}

.asks-pip {
    display: grid;
    place-items: center;
    min-width: 18px; height: 18px;
    padding: 0 5px;
    font-size: .66rem;
    font-weight: var(--fw-semibold);
    color: var(--bg-dark);
    background: var(--accent-orange);
    border-radius: 999px;
}

.asks-none {
    margin: 26px 0 8px;
    font-size: .84rem;
    text-align: center;
    color: rgba(184, 186, 194, .55);
}

/* The box that filters a long list of clients, on the page that holds them. */
#client-hunt { margin-bottom: 14px; }

#ask-new { margin-top: 2px; }
#ask-add { margin-top: 10px; }

/* --- what a client has been handed --------------------------------------- */
/* A name, how long the link is theirs, and the one thing there is to do about
   it. A row whose link has run out is not an error and does not shout; it is
   simply a row with nothing to open. */
.file {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 10px 14px;
    padding: 11px 14px;
    background: rgba(255, 255, 255, .03);
    border: 1px solid rgba(255, 255, 255, .07);
    border-radius: 13px;
}

.file-said { min-width: 0; display: grid; gap: 3px; }

.file-said strong {
    font-size: .88rem;
    font-weight: var(--fw-medium);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.file-said small { font-size: .72rem; color: rgba(184, 186, 194, .6); }
.file.is-old .file-said small { color: rgba(255, 196, 164, .75); }

.file-do { display: flex; align-items: center; gap: 8px; }
.file-do .btn { padding: 8px 14px; font-size: .78rem; }

/* Somebody is waiting for an answer, and the row says so until it is given. */
.file.is-asked { border-color: rgba(255, 107, 53, .32); }

.file-asked {
    padding: 4px 10px;
    font-size: .7rem;
    white-space: nowrap;
    color: #ffc4a4;
    background: rgba(255, 107, 53, .12);
    border: 1px solid rgba(255, 107, 53, .3);
    border-radius: 999px;
}

.file-drop {
    width: 30px; height: 30px;
    display: grid; place-items: center;
    font-size: 1.1rem; line-height: 1;
    color: var(--text-secondary);
    background: none;
    border: 1px solid transparent;
    border-radius: 9px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.file-drop:hover { color: #ffc4a4; background: rgba(255, 107, 53, .1); border-color: rgba(255, 107, 53, .28); }

/* The address itself is the admin's business, so it is behind a word rather
   than in the row: a column of long links is not a list anybody can read. */
.file-edit { grid-column: 1 / -1; }

.file-edit > summary {
    display: inline-block;
    font-size: .74rem;
    color: var(--text-secondary);
    cursor: pointer;
    list-style: none;
}

.file-edit > summary::-webkit-details-marker { display: none; }
.file-edit > summary:hover { color: var(--text-primary); }
.file-edit .new-row { margin: 10px 0 2px; }


.sub {
    padding: 15px 16px;
    background: rgba(255, 255, 255, .03);
    border: 1px solid rgba(255, 255, 255, .07);
    border-radius: 15px;
}

.sub.is-stopped { opacity: .55; }

.sub-top {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto;
    align-items: center;
    gap: 12px;
}

.sub-name { font-size: .95rem; font-weight: var(--fw-medium); }
.sub-price { font-size: .84rem; color: var(--text-secondary); white-space: nowrap; }

.sub-when { margin-top: 5px; font-size: .78rem; color: rgba(184, 186, 194, .7); }
.sub-when .is-due,
/* A partner waiting to be paid, on the line their name is on. */
.lister-sub .is-due { color: #ffc4a4; }

/* Twelve squares, one per month of this year. Full is paid, outlined is
   invoiced and waiting, faint is nothing at all. A quarter fills three of them
   and a year fills the row, which is the whole reason it is months and not
   one line per payment. */
.months {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: 3px;
    margin-top: 12px;
}

.months-year {
    grid-column: 1 / -1;
    margin-bottom: 2px;
    font-size: .64rem;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: rgba(184, 186, 194, .45);
}

/* The colour under a square is painted on in a gradient, one band per turn of
   the subscription, so the shorthand is never used here: it would wipe the
   bands out along with everything else. */
.month {
    padding: 8px 0;
    font-size: .6rem;
    text-align: center;
    color: rgba(184, 186, 194, .32);
    background-color: rgba(255, 255, 255, .03);
    border-radius: 7px;
}

/* Something of it is covered, but not all of it paid for. */
.month.is-part { color: #ffc4a4; }

/* Something of it is charged and not settled. An inset shadow rather than a
   border: a border is a millimetre of the box that the fill underneath has to
   be drawn through, and at a rounded corner that leaves a hair of a line at
   each end of the square. This sits on top of the fill and takes no width. */
.month.is-due { box-shadow: inset 0 0 0 1px rgba(255, 107, 53, .55); }

/* The whole of it, paid. */
.month.is-full {
    color: var(--bg-dark);
    font-weight: var(--fw-semibold);
}

.month.is-now { outline: 1px solid rgba(255, 255, 255, .28); outline-offset: -1px; }



/* --- the ledger ----------------------------------------------------------- */
.led {
    display: grid;
    grid-template-columns: 86px minmax(0, 1fr) auto auto auto;
    align-items: center;
    gap: 14px;
    padding: 11px 14px;
    background: rgba(255, 255, 255, .03);
    border: 1px solid rgba(255, 255, 255, .07);
    border-radius: 13px;
}

.led:hover { border-color: rgba(255, 255, 255, .14); }
.ledger.is-live .led { cursor: pointer; }
.ledger.is-live .led a { cursor: pointer; }

.led-when { font-size: .78rem; color: rgba(184, 186, 194, .7); white-space: nowrap; }
.led-what { min-width: 0; display: grid; gap: 2px; }

.led-what strong {
    font-size: .88rem;
    font-weight: var(--fw-medium);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.led-what small { font-size: .72rem; color: rgba(184, 186, 194, .6); }
.led-sum { font-size: .9rem; font-weight: var(--fw-medium); white-space: nowrap; }

.led-state {
    padding: 4px 10px;
    font-size: .7rem;
    white-space: nowrap;
    border-radius: 999px;
    border: 1px solid transparent;
}

.led-state.is-paid { color: #9fe0b5; background: rgba(76, 175, 118, .12); }
.led-state.is-due { color: #ffc4a4; background: rgba(255, 107, 53, .12); border-color: rgba(255, 107, 53, .3); }

/* Not a button, because there is nothing to press. It sits where the invoice
   link will sit, so the column answers the same question on every line. */
.led-nobill {
    font-size: .72rem;
    white-space: nowrap;
    color: rgba(184, 186, 194, .55);
    border: 1px dashed rgba(255, 255, 255, .13);
    border-radius: 9px;
    padding: 6px 11px;
}

/* Wraps, because a row that is owed money carries one button more than a
   row that is not: the code to copy sits beside the invoice and the rest. */
.led-acts { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.led-acts a { text-decoration: none; white-space: nowrap; }

.led-acts .is-pay {
    color: var(--bg-dark);
    background: var(--accent-orange);
    border-color: transparent;
}

.led-acts .is-pay:hover { color: var(--bg-dark); filter: brightness(1.08); }

.sub-stop { margin-top: 16px; }

@media (max-width: 860px) {
    .sums { gap: 12px 30px; }

    /* On a narrow screen a table stops being a table. What it was saying, in
       the order somebody would say it out loud: what, how much, when, and
       whether it has been settled. */
    .led {
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas: "what sum" "when state";
        gap: 8px 12px;
    }

    .led-what { grid-area: what; }
    .led-sum { grid-area: sum; }
    .led-when { grid-area: when; }
    .led-state { grid-area: state; justify-self: end; }
    /* Onto a row of its own, and only when there is something to put there. */
    .led-acts { grid-column: 1 / -1; }
}

@media (max-width: 560px) {
    .client-id { grid-template-columns: 46px minmax(0, 1fr) auto; gap: 12px; }
    .client-face { width: 46px; height: 46px; font-size: 1.1rem; }
    .client-who h2 { font-size: 1.08rem; }
    .month { font-size: .54rem; padding: 7px 0; border-radius: 5px; }
    .sub-top {
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas: "name gear" "price price";
        gap: 4px 12px;
    }

    .sub-name { grid-area: name; }
    .sub-price { grid-area: price; font-size: .8rem; }
    .sub-top .lister-gear { grid-area: gear; }
}

/* --- the scrollbars ----------------------------------------------------------
   A workspace with six scrollable panels in it should not have six white bars
   down the side of it. Slim, unpainted until something is under the cursor,
   and drawn inside its own track so that the thumb is a lozenge in the margin
   rather than a bar stuck to the edge.

   The width stands whether or not the Mac is set to show scrollbars always;
   when it is set to show them only while scrolling, this is what fades in. */
* {
    scrollbar-width: thin;
    scrollbar-color: rgba(255, 255, 255, .16) transparent;
}

::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-corner { background: transparent; }

::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, .14);
    /* The border is transparent and the paint stops at it, which is what puts
       air on both sides of the thumb without a second element to hold it. */
    border: 3px solid transparent;
    background-clip: padding-box;
    border-radius: 999px;
}

::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, .26);
    background-clip: padding-box;
}

::-webkit-scrollbar-thumb:active {
    background: rgba(255, 255, 255, .34);
    background-clip: padding-box;
}

/* --- the smart tools -------------------------------------------------------
   The admin's page of things that make something, and the first of them. A
   tool is a row like any other row, with a square of the room's light where a
   face would be, since there is nobody to put there. */
.lister li[data-tool] { cursor: pointer; }

.lister li[data-tool]:active {
    background: rgba(255, 255, 255, .07);
    border-color: rgba(255, 255, 255, .14);
}

.tool-face {
    border-radius: 10px;
    background: rgba(255, 255, 255, .06);
    border: 1px solid rgba(255, 255, 255, .1);
}

.tool-face svg {
    width: 17px; height: 17px;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
}

html.is-lit .tool-face {
    background: linear-gradient(135deg,
        rgba(var(--glow), min(calc(var(--glow-a) * 1.1), .5)) 0%,
        rgba(var(--glow-2), min(calc(var(--glow-a) * .7), .32)) 100%);
    border-color: transparent;
}

/* The website we made a company, at the top of its page: a picture of it as
   it is now, which opens it, and beside it its name and the two things they
   come back for. Side by side on a computer, one over the other on a phone. */
.site-card {
    display: grid;
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    gap: 24px;
    align-items: center;
    margin-bottom: 16px;
}

.site-shot {
    position: relative;
    display: block;
    aspect-ratio: 16 / 10;
    overflow: hidden;
    border-radius: 16px;
    background:
        radial-gradient(70% 90% at 20% 0%, rgba(var(--glow), .22), transparent 70%),
        radial-gradient(60% 90% at 90% 100%, rgba(var(--glow-2), .18), transparent 70%),
        rgba(255, 255, 255, .04);
    border: 1px solid rgba(255, 255, 255, .1);
}

.site-shot-none {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    color: rgba(255, 255, 255, .35);
}

.site-shot-none svg { width: 40px; height: 40px; fill: none; stroke: currentColor; stroke-width: 1.3; }

.site-shot img {
    position: absolute;
    inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    object-position: top;
    opacity: 0;
    transition: opacity .5s, transform .6s var(--fold-ease);
}

.site-shot img.is-in { opacity: 1; }
.site-shot:hover img { transform: scale(1.02); }

.site-shot-go {
    position: absolute;
    right: 12px; bottom: 12px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 12px;
    font-size: .78rem;
    font-weight: var(--fw-medium);
    color: #fff;
    background: rgba(10, 8, 16, .55);
    border: 1px solid rgba(255, 255, 255, .2);
    border-radius: 999px;
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
}

.site-shot-go svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 2;
    stroke-linecap: round; stroke-linejoin: round; }

.site-side { min-width: 0; }
.site-side h2 { margin: 0 0 4px; font-size: 1.2rem; font-weight: var(--fw-semibold); }

.site-host {
    display: block;
    overflow: hidden;
    margin-bottom: 20px;
    font-size: .88rem;
    color: var(--text-secondary);
    text-overflow: ellipsis;
    white-space: nowrap;
    text-decoration: none;
}

.site-host:hover { color: var(--text-primary); }

.site-links { display: grid; gap: 10px; }

.site-link {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 14px 16px;
    font: inherit;
    font-size: .92rem;
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    text-align: left;
    text-decoration: none;
    background: rgba(255, 255, 255, .04);
    border: 1px solid rgba(255, 255, 255, .09);
    border-radius: 14px;
    cursor: pointer;
    transition: background .2s, border-color .2s, transform .2s;
}

.site-link:hover { background: rgba(255, 255, 255, .07); border-color: rgba(255, 255, 255, .18); }
.site-link:active { transform: scale(.98); }

.site-link svg {
    flex: 0 0 auto;
    width: 20px; height: 20px;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
    color: var(--text-secondary);
}

/* The one they will press most, filled with the room's light. */
.site-link.is-main {
    color: #fff;
    background: linear-gradient(135deg, rgba(var(--glow), .55), rgba(var(--glow-2), .4));
    border-color: rgba(var(--glow), .5);
}

.site-link.is-main svg { color: #fff; }
.site-link.is-main:hover { background: linear-gradient(135deg, rgba(var(--glow), .7), rgba(var(--glow-2), .52)); }

@media (max-width: 720px) {
    .site-card { grid-template-columns: minmax(0, 1fr); gap: 18px; }
}

/* --- the way into a website's back office -------------------------------- */
.modal-login { width: min(460px, 94vw); padding: 30px 28px 24px; }

.login-facts { grid-template-columns: minmax(0, 1fr); gap: 22px; margin-top: 8px; }

.login-pass { position: relative; }
.login-pass .me-fact { padding-right: 70px; }
.login-pass .me-fact-val { font-variant-numeric: tabular-nums; letter-spacing: .04em; }
.login-facts .me-fact-val { word-break: break-all; }

@media (max-width: 560px) {
    .modal-login { padding: 26px 18px 20px; }
    .login-pass .me-fact { padding-right: 64px; }
    .login-eye { right: 24px; }
}

.login-eye {
    position: absolute;
    right: 28px; top: 50%;
    display: grid;
    place-items: center;
    width: 32px; height: 32px;
    margin-top: -16px;
    color: var(--text-secondary);
    background: none;
    border: 0;
    border-radius: 8px;
    cursor: pointer;
}

.login-eye:hover { color: var(--text-primary); background: rgba(255, 255, 255, .07); }

.login-eye svg {
    width: 18px; height: 18px;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
}

.login-eye .pw-shut { display: none; }
.login-eye[aria-pressed="true"] .pw-open { display: none; }
.login-eye[aria-pressed="true"] .pw-shut { display: block; }

.login-go { display: block; margin-top: 26px; padding: 12px 18px; text-align: center; font-size: .88rem; }

/* --- how the website is doing ---------------------------------------------- */
.modal-stats { width: min(620px, 94vw); max-height: 90vh; max-height: 90dvh; overflow-y: auto; padding: 30px 28px 24px; }

.stats-none { margin: 8px 0 4px; font-size: .9rem; line-height: 1.6; color: var(--text-secondary); }

/* The headline: one number, big, and what it means in words under it. */
.stats-hero { display: grid; gap: 4px; margin: 4px 0 18px; }
.stats-hero strong { font-size: 2.6rem; font-weight: var(--fw-semibold); line-height: 1.05; letter-spacing: -.02em;
    font-variant-numeric: tabular-nums; }
.stats-hero > span:not(.stats-trend) { font-size: .95rem; color: var(--text-secondary); }

.stats-trend {
    justify-self: start;
    margin-top: 8px;
    padding: 4px 10px;
    font-size: .78rem;
    color: var(--text-secondary);
    background: rgba(255, 255, 255, .06);
    border-radius: 999px;
}

.stats-trend.is-up { color: var(--text-primary); background: rgba(var(--glow), .2); }

.stats-line { position: relative; height: 120px; touch-action: pan-y; cursor: crosshair; }
.stats-line svg { display: block; width: 100%; height: 100%; overflow: visible; }
.stats-area { fill: rgba(var(--glow), .16); }
.stats-stroke { fill: none; stroke: rgb(var(--glow)); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }

.stats-cross {
    position: absolute;
    top: 0; bottom: 0;
    width: 1px;
    background: rgba(255, 255, 255, .25);
    pointer-events: none;
}

.stats-dot {
    position: absolute;
    width: 10px; height: 10px;
    margin: -5px 0 0 -5px;
    background: rgb(var(--glow));
    border: 2px solid var(--bg-dark);
    border-radius: 50%;
    pointer-events: none;
}

.stats-tip {
    position: absolute;
    top: -34px;
    transform: translateX(-50%);
    padding: 5px 10px;
    font-size: .76rem;
    white-space: nowrap;
    color: var(--text-primary);
    background: rgba(26, 26, 33, .98);
    border: 1px solid rgba(255, 255, 255, .14);
    border-radius: 9px;
    pointer-events: none;
}

.stats-tip b { font-weight: var(--fw-semibold); }

.stats-ends { display: flex; justify-content: space-between; margin-top: 6px; font-size: .72rem;
    color: rgba(184, 186, 194, .6); }

.stats-facts {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 10px;
    margin: 22px 0 6px;
    padding: 0;
    list-style: none;
}

.stats-facts li {
    display: grid;
    gap: 3px;
    padding: 12px 14px;
    background: rgba(255, 255, 255, .04);
    border: 1px solid rgba(255, 255, 255, .07);
    border-radius: 13px;
}

.stats-facts strong { font-size: 1.15rem; font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; }
.stats-facts span { font-size: .74rem; color: var(--text-secondary); }

.stats-head { margin: 26px 0 12px; font-size: .95rem; font-weight: var(--fw-semibold); }

.stats-bars { display: grid; gap: 12px; margin: 0; padding: 0; list-style: none; }

.stats-bars li {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 6px 12px;
    font-size: .84rem;
}

.stats-bar-name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.stats-bar-n { font-variant-numeric: tabular-nums; color: var(--text-secondary); }

.stats-bar {
    grid-column: 1 / -1;
    height: 6px;
    background: rgba(255, 255, 255, .06);
    border-radius: 999px;
    overflow: hidden;
}

.stats-bar i { display: block; height: 100%; background: rgb(var(--glow)); border-radius: 999px; }

.stats-foot { margin: 24px 0 0; font-size: .76rem; color: rgba(184, 186, 194, .65); }
.stats-foot a { color: var(--text-secondary); }

@media (max-width: 560px) {
    .modal-stats { padding: 26px 18px 20px; }
    .stats-hero strong { font-size: 2.2rem; }
    .stats-facts { grid-template-columns: minmax(0, 1fr); }
}

/* The tools as tiles: a big picture of what each does, lit by the room, and
   its name under it. Two to a row on a phone, as many as fit elsewhere. */
.tool-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 12px;
    max-width: 880px;
    margin: 0 auto;
}

.tool-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: space-between;
    gap: 18px;
    min-height: 140px;
    padding: 18px;
    font: inherit;
    text-align: left;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .035);
    border: 1px solid rgba(255, 255, 255, .08);
    border-radius: 20px;
    cursor: pointer;
    transition: background .2s, border-color .2s, transform .2s;
}

.tool-tile:hover { background: rgba(255, 255, 255, .06); border-color: rgba(255, 255, 255, .16); }
.tool-tile:active { transform: scale(.98); }
.tool-tile:focus-visible { outline: 2px solid rgba(255, 255, 255, .3); outline-offset: 2px; }

.tool-tile strong { font-size: .98rem; font-weight: var(--fw-semibold); }

.tool-tile-icon {
    display: grid;
    place-items: center;
    width: 54px; height: 54px;
    border-radius: 16px;
    background: rgba(255, 255, 255, .06);
    border: 1px solid rgba(255, 255, 255, .1);
}

.tool-tile-icon svg {
    width: 26px; height: 26px;
    fill: none; stroke: currentColor; stroke-width: 1.6;
    stroke-linecap: round; stroke-linejoin: round;
}

html.is-lit .tool-tile:not(.is-soon) .tool-tile-icon {
    background: linear-gradient(135deg,
        rgba(var(--glow), min(calc(var(--glow-a) * 1.1), .5)) 0%,
        rgba(var(--glow-2), min(calc(var(--glow-a) * .7), .32)) 100%);
    border-color: transparent;
}

/* The admin's grids, as tiles: the grid's face, its name, and under it the
   handle and who works on it. The gear sits in the corner. */
.grid-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 12px;
    margin: 18px 0 0;
    padding: 0;
    list-style: none;
}

.grid-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    min-width: 0;
    padding: 18px;
    background: rgba(255, 255, 255, .035);
    border: 1px solid rgba(255, 255, 255, .08);
    border-radius: 20px;
    cursor: pointer;
    transition: background .2s, border-color .2s;
}

.grid-tile:hover { background: rgba(255, 255, 255, .06); border-color: rgba(255, 255, 255, .14); }

.grid-tile-face {
    display: grid;
    place-items: center;
    width: 56px; height: 56px;
    margin-bottom: 16px;
    font-size: 1.1rem;
    font-weight: var(--fw-medium);
    color: rgb(var(--face, 255, 255, 255));
    background: rgba(var(--face, 255, 255, 255), .16);
    border-radius: 50%;
    overflow: hidden;
}

.grid-tile-name {
    max-width: 100%;
    font-size: .95rem;
    font-weight: var(--fw-semibold);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.grid-tile-sub {
    max-width: 100%;
    margin-top: 3px;
    font-size: .76rem;
    color: rgba(184, 186, 194, .65);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.grid-tile-name + .grid-tile-sub { margin-top: 5px; }

.grid-tile-gear { position: absolute; top: 12px; right: 12px; }

@media (max-width: 520px) {
    .grid-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
    .grid-tile { padding: 14px; }
    .grid-tile-face { width: 46px; height: 46px; margin-bottom: 12px; }
}

/* Νέο sits in the tile's corner rather than beside its name. */
.tool-tile-new { position: absolute; top: 14px; right: 14px; margin: 0; }

/* The promise of more: not a tool, so not a button, and drawn as a space that
   is waiting to be filled. A dashed edge, a faint light passing across it now
   and then, and a spark that breathes. Nothing that asks to be pressed. */
.tool-tile.is-soon {
    overflow: hidden;
    color: var(--text-secondary);
    background: none;
    border: 1px dashed rgba(255, 255, 255, .14);
    cursor: default;
}

.tool-tile.is-soon:hover { background: none; border-color: rgba(255, 255, 255, .14); }
.tool-tile.is-soon:active { transform: none; }
.tool-tile.is-soon strong { font-weight: var(--fw-medium); }

.tool-tile.is-soon .tool-tile-icon {
    background: none;
    border: 1px dashed rgba(255, 255, 255, .14);
}

.tool-tile.is-soon .tool-tile-icon svg {
    stroke: rgb(var(--glow));
    animation: soon-spark 2.8s ease-in-out infinite;
}

.tool-tile.is-soon::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(105deg,
        transparent 35%, rgba(var(--glow), .09) 50%, transparent 65%);
    transform: translateX(-100%);
    animation: soon-sweep 5.5s ease-in-out infinite;
    pointer-events: none;
}

@keyframes soon-spark {
    0%, 100% { opacity: .55; transform: scale(.92); }
    50%      { opacity: 1;   transform: scale(1.06); }
}

@keyframes soon-sweep {
    0%, 55%  { transform: translateX(-100%); }
    85%, 100% { transform: translateX(100%); }
}

@media (max-width: 560px) {
    .tool-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
    .tool-tile { min-height: 128px; padding: 16px; border-radius: 18px; }
    .tool-tile-icon { width: 50px; height: 50px; border-radius: 15px; }
}

@media (prefers-reduced-motion: reduce) {
    .tool-tile.is-soon .tool-tile-icon svg,
    .tool-tile.is-soon::after { animation: none; }
}

/* Flash Reel: the pictures and the settings down the left, and on the right
   what comes out, playing, held on screen while the left scrolls. */
.reel {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 380px);
    gap: 20px;
    align-items: start;
    max-width: 1180px;
    margin: 0 auto;
}

.reel .panel { max-width: none; margin: 0 0 20px; }

.reel-look { position: sticky; top: 92px; }

/* While a video is being made, the things that would change it rest. */
.reel.is-making .reel-work { opacity: .5; pointer-events: none; }

.reel-count {
    margin-left: 6px;
    font-size: .8rem;
    font-weight: var(--fw-medium);
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
}

.reel-pics {
    list-style: none;
    margin: 0; padding: 0;
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: 8px;
}

.reel-pic,
.reel-add {
    position: relative;
    aspect-ratio: 1;
    border-radius: 12px;
}

/* Held by a finger, a picture must not scroll the page or offer to save
   itself: it is being moved. */
.reel-pic {
    overflow: hidden;
    background: #000;
    cursor: grab;
    touch-action: none;
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
    transition: transform .15s, box-shadow .15s;
}

.reel-pic img {
    display: block;
    width: 100%; height: 100%;
    object-fit: cover;
    pointer-events: none;
}

.reel-pic.is-held {
    z-index: 2;
    cursor: grabbing;
    transform: scale(1.06);
    box-shadow: 0 10px 28px rgba(0, 0, 0, .55), 0 0 0 2px rgba(255, 255, 255, .7);
}

html.is-lit .reel-pic.is-held {
    box-shadow: 0 10px 28px rgba(0, 0, 0, .55), 0 0 0 2px rgb(var(--glow));
}

.reel-pic-no {
    position: absolute;
    left: 6px; top: 6px;
    display: grid;
    place-items: center;
    min-width: 20px; height: 20px;
    padding: 0 5px;
    font-size: .68rem;
    font-weight: var(--fw-semibold);
    color: #fff;
    background: rgba(0, 0, 0, .6);
    border-radius: 10px;
}

.reel-pic-x {
    position: absolute;
    right: 5px; top: 5px;
    display: grid;
    place-items: center;
    width: 24px; height: 24px;
    padding: 0;
    color: #fff;
    background: rgba(0, 0, 0, .6);
    border: 0;
    border-radius: 50%;
    cursor: pointer;
    transition: background .2s;
}

.reel-pic-x::after { content: ''; position: absolute; inset: -6px; }

.reel-pic-x svg {
    width: 13px; height: 13px;
    fill: none; stroke: currentColor; stroke-width: 2.2;
    stroke-linecap: round;
}

.reel-pic-x:hover { background: rgba(255, 107, 53, .85); }

.reel-add label {
    display: grid;
    place-content: center;
    justify-items: center;
    gap: 5px;
    width: 100%; height: 100%;
    font-size: .72rem;
    color: var(--text-secondary);
    border: 1px dashed rgba(255, 255, 255, .2);
    border-radius: 12px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.reel-add label:hover {
    color: var(--text-primary);
    border-color: var(--border-hover);
    background: rgba(255, 255, 255, .04);
}

.reel-add svg {
    width: 20px; height: 20px;
    fill: none; stroke: currentColor; stroke-width: 1.8;
    stroke-linecap: round;
}

.tool-label {
    margin: 18px 0 8px;
    font-size: .74rem;
    letter-spacing: .02em;
    color: var(--text-secondary);
}

.tool-chips { display: flex; flex-wrap: wrap; gap: 6px; }

.tool-chip {
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
    padding: 8px 13px;
    font: inherit;
    font-size: .82rem;
    color: var(--text-secondary);
    background: rgba(255, 255, 255, .04);
    border: 1px solid rgba(255, 255, 255, .09);
    border-radius: 10px;
    cursor: pointer;
    transition: background .2s, border-color .2s, color .2s;
}

.tool-chip strong { font-weight: var(--fw-semibold); }
.tool-chip small { font-size: .7rem; opacity: .75; }
.tool-chip:hover { color: var(--text-primary); border-color: rgba(255, 255, 255, .18); }

.tool-chip.is-on {
    color: var(--text-primary);
    background: rgba(255, 255, 255, .11);
    border-color: rgba(255, 255, 255, .24);
}

html.is-lit .tool-chip.is-on {
    background: rgba(var(--glow), min(calc(var(--glow-a) * .9), .4));
    border-color: rgba(var(--glow), min(calc(var(--glow-a) * 1.2), .6));
}

/* The shape of the video, as wide as the column allows and never taller than
   most of the screen. --w and --h are the video's own size, set on it. */
.reel-stage {
    position: relative;
    aspect-ratio: var(--w) / var(--h);
    width: min(100%, calc(62vh * var(--w) / var(--h)));
    margin: 0 auto;
    background: #000;
    border-radius: 14px;
    overflow: hidden;
}

.reel-stage img,
.reel-stage video {
    position: absolute;
    inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
}

.reel-stage img { visibility: hidden; }
.reel-stage img.is-on { visibility: visible; }
.reel-stage[data-fit="contain"] img,
.reel-stage video { object-fit: contain; }

/* A picture that does not fill the frame sits on the ground chosen for it,
   and a mounted one inside a border of it, as a share of each side (see
   REEL_INSET). The finished video carries its own ground. */
.reel-stage[data-fit="contain"],
.reel-stage[data-fit="inset"] { background: var(--ground, #000); }
.reel-stage[data-fit="inset"] img {
    top: var(--pad-y);
    left: var(--pad-x);
    width: calc(100% - 2 * var(--pad-x));
    height: calc(100% - 2 * var(--pad-y));
    object-fit: contain;
}

/* The grounds: a chip each, with its colour in a dot. The last is a colour
   picker dressed as one of them. */
.reel-ground { align-items: center; position: relative; }
.reel-ground i {
    width: 14px; height: 14px;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, .25);
    flex: 0 0 auto;
}
.reel-ground input[type="color"] {
    position: absolute;
    inset: 0;
    width: 100%; height: 100%;
    opacity: 0;
    cursor: pointer;
    border: 0;
    padding: 0;
}

.reel-empty {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    padding: 20px;
    font-size: .82rem;
    text-align: center;
    color: rgba(184, 186, 194, .6);
}

.reel-sum {
    margin: 12px 0 0;
    font-size: .76rem;
    text-align: center;
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
}

.reel-look .panel-error { margin: 14px 0 0; }

.reel-bar {
    height: 4px;
    margin: 14px 0 0;
    background: rgba(255, 255, 255, .08);
    border-radius: 4px;
    overflow: hidden;
}

.reel-bar span {
    display: block;
    width: 0; height: 100%;
    background: linear-gradient(90deg, rgb(var(--glow)), rgb(var(--glow-2)));
    transition: width .2s;
}

.tool-do { display: flex; gap: 8px; margin-top: 16px; }
.tool-do .btn-glow { flex: 1; text-decoration: none; }
.tool-do .btn-glow:disabled { opacity: .45; cursor: default; }
.tool-do .app-ghost { padding: 12px 16px; font-size: .86rem; }

/* One column on anything narrower than a laptop: the pictures, the settings,
   then what they make and the button that makes it, in the order they are
   done. */
@media (max-width: 900px) {
    .reel { grid-template-columns: minmax(0, 1fr); gap: 0; }
    .reel-look { position: static; }
    .reel-pics { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (max-width: 560px) {
    .reel .panel { padding: 18px; }
    .tool-chip { padding: 8px 11px; }
}

/* Easy Mockup: the reel's layout, with the screenshot where the pictures
   were and the device where the video was. A device on no ground shows the
   usual chequer behind it, so that nothing is mistaken for white. */
.mock-drop {
    display: grid;
    place-content: center;
    justify-items: center;
    gap: 6px;
    min-height: 160px;
    padding: 12px;
    font-size: .78rem;
    color: var(--text-secondary);
    border: 1px dashed rgba(255, 255, 255, .2);
    border-radius: 12px;
    cursor: pointer;
    transition: var(--transition-smooth);
}
.mock-drop:hover { color: var(--text-primary); border-color: var(--border-hover); background: rgba(255, 255, 255, .04); }
.mock-drop svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; }
.mock-drop.has-shot { border-style: solid; border-color: rgba(255, 255, 255, .1); }
.mock-drop img { display: block; max-width: 100%; max-height: 260px; border-radius: 8px; }

/* The other way round from the reel: the device is the thing being looked
   at, so it takes the wide column, and the few settings the narrow one. */
.reel.mock {
    grid-template-columns: minmax(0, 330px) minmax(0, 1fr);
    max-width: 1360px;
}

@media (max-width: 900px) {
    .reel.mock { grid-template-columns: minmax(0, 1fr); }
}

.mock-stage {
    position: relative;
    aspect-ratio: var(--w) / var(--h);
    width: min(100%, calc(74vh * var(--w) / var(--h)));
    margin: 0 auto;
    background: var(--ground, transparent);
    border-radius: 14px;
    overflow: hidden;
}
.mock-stage.is-clear,
.reel-ground i.is-clear {
    background: repeating-conic-gradient(rgba(255, 255, 255, .1) 0 25%, rgba(255, 255, 255, .02) 0 50%) 0 0 / 16px 16px;
}
.reel-ground i.is-clear { background-size: 7px 7px; }
.mock-stage canvas { display: block; width: 100%; height: 100%; }

/* A tool's loading screen: the whole screen, dimmed and blurred, with the
   tool's icon, its name and a bar that fills with what has arrived. Before
   anything is known the bar sweeps instead. */
.tool-loader {
    position: fixed;
    inset: 0;
    z-index: 300;
    display: grid;
    place-items: center;
    padding: 24px;
    background: rgba(10, 10, 12, .78);
    -webkit-backdrop-filter: blur(16px);
    backdrop-filter: blur(16px);
    opacity: 0;
    transition: opacity .3s ease;
}
.tool-loader.is-on { opacity: 1; }

.tool-loader-card {
    display: grid;
    justify-items: center;
    gap: 14px;
    width: min(260px, 100%);
    transform: translateY(8px) scale(.97);
    transition: transform .45s cubic-bezier(.2, .8, .2, 1);
}
.tool-loader.is-on .tool-loader-card { transform: none; }

.tool-loader-icon {
    display: grid;
    place-items: center;
    width: 76px; height: 76px;
    color: var(--text-primary);
    background: linear-gradient(145deg, rgba(255, 255, 255, .09), rgba(255, 255, 255, .02));
    border: 1px solid rgba(255, 255, 255, .12);
    border-radius: 22px;
    box-shadow: 0 18px 40px rgba(0, 0, 0, .35);
}
.tool-loader-icon svg {
    width: 34px; height: 34px;
    fill: none; stroke: currentColor; stroke-width: 1.6;
    stroke-linecap: round; stroke-linejoin: round;
}

.tool-loader strong { font-size: 1rem; font-weight: var(--fw-semibold); color: var(--text-primary); }

.tool-loader-bar {
    position: relative;
    width: 100%;
    height: 5px;
    background: rgba(255, 255, 255, .08);
    border-radius: 5px;
    overflow: hidden;
}
.tool-loader-bar span {
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, var(--accent-orange), #ffb36b 55%, #ff7aa8);
    border-radius: inherit;
    transform-origin: left center;
    transform: scaleX(0);
    transition: transform .25s ease-out;
}
.tool-loader:not(.is-known) .tool-loader-bar span {
    width: 40%;
    right: auto;
    transform: translateX(-100%);
    animation: tool-loader-sweep 1.1s ease-in-out infinite;
}
@keyframes tool-loader-sweep {
    to { transform: translateX(260%); }
}

/* Everything is under it but the sidebar, which is lifted over it while it
   is up. Beside the sidebar the card is centred on the page rather than on
   the whole screen, so it sits where the tool is about to be. */
body.is-tool-loading .app-side { z-index: 310; }

@media (min-width: 901px) {
    .tool-loader { padding-left: calc(268px + var(--side-gap) * 2 + 24px); }
    .app-body.is-folded .tool-loader { padding-left: calc(94px + var(--side-gap) * 2 + 24px); }
}

.tool-loader small {
    font-size: .74rem;
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
    .tool-loader, .tool-loader-card, .tool-loader-bar span { transition: none; }
    .tool-loader:not(.is-known) .tool-loader-bar span { animation: none; transform: none; width: 100%; opacity: .35; }
}

/* Easy Crop: the picture with its frame on the left, what it is cut to and
   saved as on the right. */
.cut {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 380px);
    gap: 20px;
    align-items: start;
    max-width: 1180px;
    margin: 0 auto;
}

.cut .panel { max-width: none; margin: 0 0 20px; }

.cut-room { width: 100%; }

/* A checkerboard under the picture, so what is see-through in a PNG shows as
   see-through rather than as the colour of the page. */
.cut-stage {
    position: relative;
    margin: 0 auto;
    overflow: hidden;
    border-radius: 6px;
    background: repeating-conic-gradient(#1d1d22 0 25%, #28282e 0 50%) 0 0 / 16px 16px;
    touch-action: none;
    -webkit-user-select: none;
    user-select: none;
}

.cut-stage img {
    display: block;
    width: 100%; height: 100%;
    pointer-events: none;
    -webkit-user-drag: none;
}

/* Everything outside the frame is dimmed by the frame's own shadow, cut off
   at the edge of the picture. */
.cut-box {
    position: absolute;
    box-shadow: 0 0 0 9999px rgba(0, 0, 0, .58);
    outline: 1px solid rgba(255, 255, 255, .9);
    cursor: move;
    touch-action: none;
}

.cut-thirds {
    position: absolute;
    inset: 0;
    pointer-events: none;
    opacity: .35;
    transition: opacity .2s;
    background:
        linear-gradient(to right, transparent calc(100% / 3 - .5px), #fff calc(100% / 3 - .5px),
            #fff calc(100% / 3 + .5px), transparent calc(100% / 3 + .5px),
            transparent calc(200% / 3 - .5px), #fff calc(200% / 3 - .5px),
            #fff calc(200% / 3 + .5px), transparent calc(200% / 3 + .5px)),
        linear-gradient(to bottom, transparent calc(100% / 3 - .5px), #fff calc(100% / 3 - .5px),
            #fff calc(100% / 3 + .5px), transparent calc(100% / 3 + .5px),
            transparent calc(200% / 3 - .5px), #fff calc(200% / 3 - .5px),
            #fff calc(200% / 3 + .5px), transparent calc(200% / 3 + .5px));
}

.cut-box.is-held .cut-thirds { opacity: .7; }

/* Corners a thumb can find: drawn small, pressed large. */
.cut-corner {
    position: absolute;
    width: 22px; height: 22px;
    border: 3px solid #fff;
}

.cut-corner::after { content: ''; position: absolute; inset: -14px; }

.cut-corner[data-corner="nw"] { left: -3px; top: -3px; border-right: 0; border-bottom: 0; cursor: nwse-resize; }
.cut-corner[data-corner="ne"] { right: -3px; top: -3px; border-left: 0; border-bottom: 0; cursor: nesw-resize; }
.cut-corner[data-corner="sw"] { left: -3px; bottom: -3px; border-right: 0; border-top: 0; cursor: nesw-resize; }
.cut-corner[data-corner="se"] { right: -3px; bottom: -3px; border-left: 0; border-top: 0; cursor: nwse-resize; }

/* The sides, for a free frame only: the whole length of each can be taken
   hold of, and a short bar in its middle says so. Before the corners in the
   page, so where the two meet the corner is the one that is held. */
.cut-edge { position: absolute; display: none; }
.cut-box.is-free .cut-edge { display: block; }

.cut-edge[data-corner="n"] { left: 18px; right: 18px; top: -8px; height: 16px; cursor: ns-resize; }
.cut-edge[data-corner="s"] { left: 18px; right: 18px; bottom: -8px; height: 16px; cursor: ns-resize; }
.cut-edge[data-corner="w"] { top: 18px; bottom: 18px; left: -8px; width: 16px; cursor: ew-resize; }
.cut-edge[data-corner="e"] { top: 18px; bottom: 18px; right: -8px; width: 16px; cursor: ew-resize; }

.cut-edge::before {
    content: '';
    position: absolute;
    left: 50%; top: 50%;
    background: #fff;
    border-radius: 3px;
    transform: translate(-50%, -50%);
}

.cut-edge[data-corner="n"]::before,
.cut-edge[data-corner="s"]::before { width: 26px; height: 5px; }
.cut-edge[data-corner="w"]::before,
.cut-edge[data-corner="e"]::before { width: 5px; height: 26px; }

.cut-swap:disabled { opacity: .35; cursor: default; }
.cut-swap:disabled:hover { color: var(--text-secondary); border-color: rgba(255, 255, 255, .1); }

.cut-sum {
    margin: 14px 0 0;
    font-size: .8rem;
    line-height: 1.55;
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
}

.cut-sum.is-bad { color: #ffc4a4; }

.cut-under {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-top: 12px;
    font-size: .76rem;
    color: var(--text-secondary);
}

.cut-under-do { display: flex; gap: 6px; }
.cut-under label.app-ghost { cursor: pointer; }

.cut-drop {
    display: grid;
    place-content: center;
    justify-items: center;
    gap: 6px;
    min-height: 340px;
    padding: 24px;
    text-align: center;
    color: var(--text-secondary);
    border: 1px dashed rgba(255, 255, 255, .2);
    border-radius: 16px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.cut-drop:hover {
    color: var(--text-primary);
    border-color: var(--border-hover);
    background: rgba(255, 255, 255, .03);
}

.cut-drop svg {
    width: 30px; height: 30px;
    margin-bottom: 6px;
    fill: none; stroke: currentColor; stroke-width: 1.6;
    stroke-linecap: round; stroke-linejoin: round;
}

.cut-drop strong { font-size: .95rem; font-weight: var(--fw-medium); color: var(--text-primary); }
.cut-drop small { font-size: .78rem; }

/* The sizes, as one line that opens into the list of them: only the one in use
   is shown until somebody wants another. */
.cut-pick { position: relative; }

.cut-pick-now {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 10px 12px;
    font: inherit;
    font-size: .88rem;
    color: var(--text-primary);
    text-align: left;
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 11px;
    cursor: pointer;
    transition: border-color .2s, background .2s;
}

.cut-pick-now:hover { border-color: rgba(255, 255, 255, .2); }
.cut-pick-now:focus-visible { outline: none; border-color: var(--accent-orange); }

.cut-pick-arrow {
    flex: 0 0 auto;
    width: 16px; height: 16px;
    fill: none; stroke: var(--text-secondary); stroke-width: 2;
    stroke-linecap: round; stroke-linejoin: round;
    transition: transform .2s;
}

.cut-pick-now[aria-expanded="true"] .cut-pick-arrow { transform: rotate(180deg); }

.cut-pick-line,
.cut-pick-one {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
}

.cut-pick-line { flex: 1 1 auto; }

.cut-pick-line strong,
.cut-pick-one strong {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-weight: var(--fw-semibold);
}

.cut-pick-line small,
.cut-pick-one small {
    margin-left: auto;
    flex: 0 0 auto;
    font-size: .74rem;
    font-variant-numeric: tabular-nums;
    color: var(--text-secondary);
}

.cut-pick-list {
    position: absolute;
    left: 0; right: 0; top: calc(100% + 6px);
    z-index: 20;
    max-height: min(340px, 60vh);
    overflow-y: auto;
    padding: 6px;
    background: rgba(26, 26, 33, .99);
    border: 1px solid rgba(255, 255, 255, .12);
    border-radius: 13px;
    box-shadow: 0 20px 50px rgba(0, 0, 0, .6);
}

.cut-pick-one {
    width: 100%;
    padding: 9px 10px;
    font: inherit;
    font-size: .86rem;
    color: var(--text-secondary);
    text-align: left;
    background: transparent;
    border: 0;
    border-radius: 9px;
    cursor: pointer;
}

.cut-pick-one:hover { color: var(--text-primary); background: rgba(255, 255, 255, .06); }

.cut-pick-one.is-on { color: var(--text-primary); background: rgba(255, 255, 255, .1); }
html.is-lit .cut-pick-one.is-on { background: rgba(var(--glow), min(calc(var(--glow-a) * .9), .4)); }

/* Two tabs over the sizes, when there is a website to keep sizes for:
   everybody's, and that website's own. The same pill as the one over the
   conversations, so it reads as a choice of what to look at. */
.cut-tabs {
    display: flex;
    gap: 4px;
    margin-top: 16px;
    padding: 4px;
    background: rgba(255, 255, 255, .04);
    border: 1px solid rgba(255, 255, 255, .07);
    border-radius: 13px;
}

.cut-tabs button {
    flex: 1;
    min-width: 0;
    padding: 8px 6px;
    font: inherit;
    font-size: .8rem;
    color: var(--text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    background: none;
    border: none;
    border-radius: 10px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.cut-tabs button:hover { color: var(--text-primary); }
.cut-tabs button.is-on { color: var(--text-primary); background: rgba(255, 255, 255, .09); }

/* Whatever the tab holds starts the same distance under it as the list did
   under the heading when there are no tabs. */
.cut-tabs + .cut-sizes { margin-top: 14px; }
.cut-sizes > .cut-pick:first-child { margin-top: 16px; }
.cut-tabs + .cut-sizes > .cut-pick:first-child { margin-top: 0; }

/* The admin chooses whose website she is looking at. */
.cut-site-for { display: block; margin-bottom: 14px; }

.cut-site-for span {
    display: block;
    margin-bottom: 6px;
    font-size: .74rem;
    color: var(--text-secondary);
}

.cut-site-for select {
    width: 100%;
    padding: 10px 12px;
    font: inherit;
    font-size: .88rem;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 11px;
    color-scheme: dark;
}

.cut-site-for select:focus { outline: none; border-color: var(--accent-orange); }

@media (max-width: 560px) {
    .cut-site-for select { font-size: 16px; }
}

/* A website's size in the list, with the way to let it go beside it. */
.cut-site-row { display: flex; align-items: center; gap: 2px; }
.cut-site-row .cut-pick-one { flex: 1 1 auto; min-width: 0; }

.cut-pick-ask { font-weight: var(--fw-regular); color: var(--text-secondary); }

/* The way to let a size go: faint until its line is under the pointer, and
   always there on a touch screen, where nothing ever is. */
.cut-pick-x {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 32px; height: 32px;
    font: inherit;
    font-size: 1.15rem;
    line-height: 1;
    color: var(--text-secondary);
    background: none;
    border: 0;
    border-radius: 9px;
    opacity: 0;
    cursor: pointer;
    transition: opacity .15s, background .15s, color .15s;
}

.cut-site-row:hover .cut-pick-x,
.cut-pick-x:focus-visible { opacity: .7; }
.cut-pick-x:hover { opacity: 1; color: var(--text-primary); background: rgba(255, 255, 255, .07); }

@media (hover: none) {
    .cut-pick-x { opacity: .55; }
}

.cut-site-none {
    margin: 0;
    font-size: .84rem;
    line-height: 1.6;
    color: var(--text-secondary);
}

/* Adding one: a plain line that says so, and in its place once pressed, the
   name and the two sides. */
.cut-site-new {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    margin-top: 12px;
    padding: 10px 12px;
    font: inherit;
    font-size: .84rem;
    color: var(--text-secondary);
    background: none;
    border: 1px dashed rgba(255, 255, 255, .16);
    border-radius: 11px;
    cursor: pointer;
    transition: color .2s, border-color .2s;
}

.cut-site-new:hover { color: var(--text-primary); border-color: rgba(255, 255, 255, .3); }

.cut-site-new svg {
    width: 16px; height: 16px;
    fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round;
}

.cut-site-form {
    display: grid;
    gap: 14px;
    margin-top: 12px;
    padding-top: 16px;
    border-top: 1px solid rgba(255, 255, 255, .07);
}

.cut-site-sides {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 10px;
}

.cut-site-form .cut-field input[name="name"] { padding-right: 13px; }
.cut-site-do { display: flex; justify-content: flex-end; gap: 8px; }
.cut-site-do .btn { padding: 10px 18px; font-size: .84rem; }

/* The shape of the size: its longer side always eighteen pixels, the other
   what it is against it, in a square box so every name starts in line. */
.cut-shape {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 22px; height: 22px;
}

.cut-shape i {
    display: block;
    border: 1.5px solid currentColor;
    border-radius: 3px;
    opacity: .85;
}

/* The same little frame on a chip, as Flash Reel's shapes wear it: a size
   smaller, and centred on the line rather than sitting on its baseline. */
.tool-chip .cut-shape { align-self: center; width: 16px; height: 16px; margin-right: 1px; }

.cut-shape.is-free i { width: 17px; height: 17px; border-style: dashed; }

.cut-size {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    align-items: end;
    gap: 8px;
    margin-top: 18px;
}

.cut-field { position: relative; display: block; min-width: 0; }

.cut-field span {
    display: block;
    margin-bottom: 6px;
    font-size: .74rem;
    color: var(--text-secondary);
}

/* A sixteen-pixel figure, so a phone does not zoom in on the box the moment
   it is touched. */
.cut-field input {
    width: 100%;
    padding: 11px 34px 11px 13px;
    font: inherit;
    font-size: 1rem;
    font-variant-numeric: tabular-nums;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 11px;
    -moz-appearance: textfield;
    appearance: textfield;
}

.cut-field input::-webkit-inner-spin-button,
.cut-field input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }

.cut-field input:focus { outline: none; border-color: var(--accent-orange); }

.cut-field em {
    position: absolute;
    right: 12px; bottom: 14px;
    font-size: .74rem;
    font-style: normal;
    color: rgba(184, 186, 194, .6);
    pointer-events: none;
}

.cut-swap {
    display: grid;
    place-items: center;
    width: 40px; height: 46px;
    color: var(--text-secondary);
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 11px;
    cursor: pointer;
    transition: var(--transition-smooth);
}

.cut-swap:hover { color: var(--text-primary); border-color: var(--border-hover); }

.cut-swap svg {
    width: 18px; height: 18px;
    fill: none; stroke: currentColor; stroke-width: 1.8;
    stroke-linecap: round; stroke-linejoin: round;
}

.cut-quality .tool-label span {
    margin-left: 6px;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}

.cut .me-tint-bar { margin: 4px 0 2px; }

.cut-set .tool-do { margin-top: 22px; }

.cut-web {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    margin-top: 10px;
    padding: 12px 16px;
    font: inherit;
    text-align: left;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .04);
    border: 1px solid rgba(255, 255, 255, .12);
    border-radius: 14px;
    cursor: pointer;
    transition: background .2s, border-color .2s;
}

.cut-web:hover:not(:disabled) { background: rgba(255, 255, 255, .07); border-color: var(--border-hover); }
.cut-web:disabled { opacity: .45; cursor: default; }

.cut-web svg {
    flex: 0 0 auto;
    width: 20px; height: 20px;
    fill: none; stroke: currentColor; stroke-width: 1.7;
    stroke-linecap: round; stroke-linejoin: round;
}

html.is-lit .cut-web svg { color: rgb(var(--glow)); }

.cut-web strong { display: block; font-size: .9rem; font-weight: var(--fw-medium); }
.cut-web small { display: block; margin-top: 2px; font-size: .74rem; color: var(--text-secondary); }

.cut-out {
    margin-top: 16px;
    padding: 14px;
    background: rgba(255, 255, 255, .03);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 14px;
}

.cut-out-name { font-size: .84rem; overflow-wrap: anywhere; }

.cut-out-meta {
    margin-top: 4px;
    font-size: .76rem;
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
}

.cut-set .cut-out .tool-do { margin-top: 12px; }

@media (max-width: 900px) {
    .cut { grid-template-columns: minmax(0, 1fr); gap: 0; }
}

@media (max-width: 560px) {
    .cut .panel { padding: 18px; }
    .cut-drop { min-height: 240px; }
}

/* --- a company's card in a conversation ------------------------------------
   The + beside the box, the admin's alone and only in private: quieter than
   the send button, since it is the less usual thing to do. */
.chat-add {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 42px; height: 42px;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .06);
    border: 1px solid rgba(255, 255, 255, .1);
    border-radius: 50%;
    cursor: pointer;
    transition: background .18s;
}

.chat-add[hidden] { display: none; }
.chat-add:hover { background: rgba(255, 255, 255, .1); }

.chat-add svg {
    width: 18px; height: 18px;
    fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round;
}

/* The card inside a message: one plain row, the company's face, its name and
   an arrow, on the same quiet surface as any other message whoever sent it. */
.chat-bubble.is-card,
.chat-msg.is-mine .chat-bubble.is-card,
html.is-lit .chat-msg.is-mine .chat-bubble.is-card {
    padding: 0;
    color: var(--text-primary);
    background: none;
    border: none;
}

.chat-card {
    display: grid;
    grid-template-columns: 40px minmax(0, 1fr) auto;
    align-items: center;
    gap: 11px;
    width: 250px;
    max-width: 100%;
    padding: 9px 10px 9px 9px;
    font: inherit;
    text-align: left;
    white-space: normal;
    color: var(--text-primary);
    background: rgba(255, 255, 255, .06);
    border: 1px solid rgba(255, 255, 255, .09);
    border-radius: 14px;
    cursor: pointer;
}

.chat-msg.is-mine .chat-card { margin-left: auto; }

.chat-card:hover { background: rgba(255, 255, 255, .09); }

.chat-card:focus-visible { outline: 2px solid rgba(255, 255, 255, .4); outline-offset: 2px; }

.chat-card-face {
    display: grid;
    place-items: center;
    width: 40px; height: 40px;
    font-size: .85rem;
    font-weight: var(--fw-medium);
    color: rgb(var(--face, 255, 255, 255));
    background: rgba(var(--face, 255, 255, 255), .16);
    border-radius: 11px;
    overflow: hidden;
}

.chat-card-text { display: flex; flex-direction: column; min-width: 0; }

.chat-card-text strong {
    font-size: .88rem;
    font-weight: var(--fw-medium);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.chat-card-text small { margin-top: 1px; font-size: .74rem; color: rgba(184, 186, 194, .7); }

.chat-card > svg {
    width: 16px; height: 16px;
    fill: none; stroke: rgba(255, 255, 255, .5); stroke-width: 2;
    stroke-linecap: round; stroke-linejoin: round;
}

.cardpick-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-height: 50vh;
    margin-top: 12px;
    overflow-y: auto;
}
