/* A couch app, used in a dark room, at night. Dark is the default, not a theme.

   Picture house (1 November 2026): an old cinema after the lights go down. Warm black rather
   than blue-black, ivory text, marquee gold, serif titles, a projector's glow at the top of
   the screen and a little film grain. Every pair below is measured against both grounds
   (DESIGN.md) - most are higher contrast than the palette they replace. */

:root {
    --bg: #0e0b09;
    --surface: #1a1511;
    --border: #2e2620;
    --text: #f3ebdd;
    /* 7.15:1 on --bg, 6.61:1 on --surface. */
    --muted: #a99a87;
    /* Marquee gold. 10.55:1 on --bg; --accent-ink on it is 9.95:1. */
    --accent: #f0b43c;
    --accent-ink: #1b1206;
    /* The accent under a pointer. Measured like everything else: --accent-ink on this is
       12.03:1, higher than on --accent itself, because it is a lighter gold. Hover is the
       one state where a second shade of the accent is not a second accent. */
    --accent-bright: #ffc85a;

    /* Leaving a room, deleting one, revoking an invite. Measured, not chosen by eye:
       7.95:1 on --bg and 7.34:1 on --surface, both well past AA's 4.5. Warm, because a cold
       red beside a warm gold reads as a different product's palette. */
    --danger: #ff7f6e;
    --danger-border: #6b2e26;
    --radius: 12px;
    --measure: 34rem;

    /* The marquee and the reading face. System stacks behind both, so first paint never
       waits on a font. */
    --serif: "Instrument Serif", Georgia, "Times New Roman", serif;
    --sans: "Albert Sans", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", sans-serif;

    /* The gold, as light rather than paint: glows and tints, never text. */
    --glow: rgb(240 180 60 / 0.16);
}

* {
    box-sizing: border-box;
}

html {
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    /* The projector: a warm cone of light from above the screen, falling off into the room.
       Fixed, so it stays where the projector is while the page scrolls under it. */
    background:
        radial-gradient(ellipse 70% 45% at 50% -8%, var(--glow), transparent 70%),
        radial-gradient(ellipse 120% 60% at 50% 110%, rgb(0 0 0 / 0.55), transparent 70%),
        var(--bg);
    background-attachment: fixed;
    color: var(--text);
    font-family: var(--sans);
    font-size: 1rem;
    line-height: 1.6;
    min-height: 100dvh;
}

/* Film grain. Behind the content rather than over it, so it textures the room and never
   lowers the contrast of anything measured above. An inline SVG, so it costs no request. */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    opacity: 0.07;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

.page,
#app {
    position: relative;
    z-index: 1;
}

a {
    color: var(--accent);
    text-decoration: none;
}

a:hover,
a:focus-visible {
    text-decoration: underline;
}

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

h1, h2, h3 {
    line-height: 1.2;
    letter-spacing: -0.01em;
    margin: 0 0 0.5rem;
}

/* The page title is the marquee: serif, roman, large. Serif display faces carry their weight
   in the drawing, so 400 - a bold Instrument Serif is a different and worse face. */
h1 {
    font-family: var(--serif);
    font-weight: 400;
    font-size: 2.4rem;
    line-height: 1.05;
    letter-spacing: -0.005em;
}

/* 1.3 rather than 1.15. A section heading a fifth larger than the body under it is not a
   heading, it is bold text - the scale read 1.75 / 1.15 / 1 and the middle step was doing no
   work. */
h2 {
    font-size: 1.3rem;
    margin-top: 2rem;
}

h3 {
    font-size: 1.05rem;
    margin-top: 1.5rem;
}

.page {
    display: flex;
    flex-direction: column;
    min-height: 100dvh;
}

.site-header {
    position: relative;
    padding: 1rem 1.25rem;
    padding-top: max(1rem, env(safe-area-inset-top));
}

/* The marquee's edge: a row of bulbs where a hairline was. Faint, and dimmer towards the
   sides, so it reads as light along the canopy rather than as a dotted border. */
.site-header::after {
    content: "";
    position: absolute;
    inset: auto 0 0;
    height: 4px;
    background: radial-gradient(circle, rgb(240 180 60 / 0.55) 1.2px, transparent 1.8px) 0 0 / 12px 4px repeat-x;
    mask-image: linear-gradient(90deg, transparent, #000 25%, #000 75%, transparent);
}

.site-header__inner,
.content,
.site-footer__inner {
    max-width: var(--measure);
    margin-inline: auto;
    width: 100%;
}

/* A discrete control, so it owes the same 44px every other tappable thing owes. It was 22px
   - the exact shape of #45, where a nav button reset to `font: inherit; padding: 0` came out
   at roughly 22px and passed review because it LOOKED aligned with the links beside it.
   inline-flex rather than padding so the header's own vertical rhythm is unchanged.

   The footer's "Attribution" link is deliberately NOT given this treatment: it sits inside a
   running sentence, and a 44px inline link inside prose breaks the line box it lives in. The
   rule is for discrete controls, which is what makes this an exception rather than a miss. */
.wordmark {
    display: inline-flex;
    align-items: baseline;
    gap: 0.04em;
    min-height: 44px;
    padding-top: 0.35rem;
    color: var(--text);
    font-family: var(--serif);
    font-weight: 400;
    font-size: 1.55rem;
    letter-spacing: 0;
}

/* Keep2U (rebranded 1 November 2026): "2U" in italic gold. Gold here is a name, not an
   action, and it is small - the screen's one accent stays the button. */
.wordmark em {
    color: var(--accent);
    font-style: italic;
}

.wordmark:hover {
    text-decoration: none;
}

.nav {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1.25rem;
    margin-top: 0.5rem;
    font-size: 0.9rem;
}

/* Two stacked rows is right on a phone, where the width is not there. On anything wider it
   was a mobile header shown on a desktop - a third of the fold spent on a wordmark and four
   links that fit beside it with room over. */
@media (min-width: 34rem) {
    .site-header__inner {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 1.5rem;
    }

    .nav {
        margin-top: 0;
    }
}

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

.nav a.active {
    color: var(--text);
}

/* Every header link gets a 44px target (#45): a product used one-handed on a sofa. */
.nav a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
}

/* The single most important thing a stranger can do, and it rendered as a muted 0.9rem link
   indistinguishable from About - .nav__signin was used by the header and defined by nothing
   at all, so it inherited `.nav a` and came out grey.

   `.nav .nav__signin` rather than `.nav__signin`, and this is the cascade trap DESIGN.md
   names, hit a second time: `.nav a` is a class plus a type, so a bare .nav__signin loses to
   it whatever order it is declared in, and the fix would have looked applied while doing
   nothing.

   Bordered rather than filled. The accent is already spent on the landing page's Start, and
   two accent-coloured things on one screen is the rule this system does not break. */
.nav .nav__signin {
    padding: 0 0.9rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--text);
    font-weight: 600;
}

/* Signed in, the header is the household and you (UX rethink, 3 October 2026). The room's
   name opens a small menu: this household, the other rooms, a new room (1 November 2026). */
.roommenu {
    position: relative;
}

.roommenu__toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    min-height: 44px;
    padding: 0;
    border: 0;
    background: none;
    color: var(--text);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}

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

/* Transparent and full-screen, under the panel: a tap anywhere else closes the menu. */
.roommenu__backdrop {
    position: fixed;
    inset: 0;
    z-index: 900;
    border: 0;
    background: transparent;
    cursor: default;
}

/* Left-aligned on a phone, where the nav wraps to its own row under the wordmark; right-aligned
   beside it once the header is one row. Never wider than the screen less its gutters. */
.roommenu__panel {
    position: absolute;
    top: calc(100% + 0.25rem);
    left: 0;
    z-index: 901;
    display: grid;
    gap: 0.25rem;
    width: min(20rem, calc(100vw - 2rem));
    padding: 0.5rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: 0 24px 48px -16px rgb(0 0 0 / 0.95);
}

@media (min-width: 34rem) {
    .roommenu__panel {
        right: 0;
        left: auto;
    }
}

.nav .roommenu__item,
.roommenu__item {
    display: grid;
    gap: 0.1rem;
    min-height: 44px;
    padding: 0.5rem 0.75rem;
    border: 0;
    border-radius: 8px;
    background: none;
    color: var(--text);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.nav .roommenu__item:hover,
.roommenu__item:hover,
.roommenu__item:focus-visible {
    background: var(--bg);
    text-decoration: none;
}

.roommenu__hint {
    color: var(--muted);
    font-size: 0.8rem;
}

/* After .roommenu__item, which it modifies: one specificity level, so order decides. */
.roommenu__item--new {
    display: flex;
    align-items: center;
    border-top: 1px solid var(--border);
    border-radius: 0 0 8px 8px;
}

.roommenu__new {
    display: flex;
    gap: 0.5rem;
    padding: 0.25rem;
}

.roommenu__new .input {
    flex: 1;
    min-width: 0;
}

.nav .nav__me {
    justify-content: center;
    width: 44px;
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--text);
    font-weight: 600;
}

.nav .nav__me.active {
    border-color: var(--accent);
}

.nav .nav__signin:hover,
.nav .nav__signin:focus-visible {
    border-color: var(--accent);
    color: var(--accent);
    text-decoration: none;
}

main {
    flex: 1;
    padding: 2rem 1.25rem 3rem;
}

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

/* Waiting for a screen to arrive. Deliberately not a skeleton: the API scales to zero and a
   cold start is measured in tens of seconds (#3), so a set of grey bars implying the content
   is nearly here would be a lie for exactly the wait that needs the most honesty. A sentence
   naming what is coming is what a skeleton is pretending to be anyway.

   role="status" on the element, so a screen reader is told rather than left on a page that
   silently has nothing in it. */
.loading {
    padding: 2.5rem 0;
    color: var(--muted);
    text-align: center;
}

/* The room being left, while the next one loads. Kept on screen rather than blanked, and
   inert so nothing on it can be tapped - dimmed so that it is visibly not the answer yet. */
.room[inert] {
    opacity: 0.6;
    transition: opacity 0.15s;
}

/* Surfaces catch a little of the projector's light along their top edge and sit in shadow
   below, so a card is an object in the room rather than a rectangle on it. */
.card {
    background: linear-gradient(180deg, rgb(255 236 210 / 0.035), transparent 45%), var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: 0 18px 40px -24px rgb(0 0 0 / 0.9), inset 0 1px 0 rgb(255 236 210 / 0.05);
    padding: 1.25rem;
    margin: 1.25rem 0;
}

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

.attribution__logo {
    display: block;
    width: 150px;
    height: auto;
    margin-bottom: 0.75rem;
}

.site-footer {
    border-top: 1px solid rgb(46 38 32 / 0.6);
    padding: 1rem 1.25rem;
    padding-bottom: max(1rem, env(safe-area-inset-bottom));
    color: var(--muted);
    font-size: 0.85rem;
}

/* --- Boot ------------------------------------------------------------------------------
   Shown before any page renders, so it cannot use anything Blazor provides. Deliberately
   quiet: it is a wait, not a greeting, and a loud splash on every cold launch of an
   installed app wears out fast. */

.boot {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 100dvh;
    padding: 1.25rem;
    text-align: center;
}

.boot__mark {
    margin: 0 0 0.35rem;
    font-family: var(--serif);
    font-size: 2rem;
    font-weight: 400;
    color: var(--text);
}

.boot__mark em {
    color: var(--accent);
}

.boot__line {
    margin: 0 0 2rem;
    font-size: 0.9rem;
    color: var(--muted);
}

/* Boot spinner and error bar, both from the Blazor template. */

/* margin-top was 20vh, which is the Blazor template's way of pushing a spinner down an
   otherwise empty page. Inside .boot - a centred flex column - it pushed the ring two thirds
   of the way down and left the wordmark stranded near the top with a hole between them. The
   column does the centring; this only needs the gap .boot__line has already opened.

   4rem rather than 8: the comment above says this is a wait rather than a greeting, and a
   128px ring is not a quiet one. */
.loading-progress {
    position: relative;
    display: block;
    width: 4rem;
    height: 4rem;
    margin: 0 auto 0.75rem;
}

.loading-progress circle {
    fill: none;
    stroke: var(--border);
    stroke-width: 0.3rem;
    transform-origin: 50% 50%;
    transform: rotate(-90deg);
}

.loading-progress circle:last-child {
    stroke: var(--accent);
    stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
    transition: stroke-dasharray 0.05s ease-in-out;
}

/* How far through the download this is. On a fast connection nobody reads it; on the mobile
   data this app is meant to survive, a cold load is several seconds of a page that otherwise
   gives no sign it is making progress. */
.loading-progress-text {
    text-align: center;
    color: var(--muted);
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
}

.loading-progress-text:after {
    content: var(--blazor-load-percentage-text, "Loading");
}

#blazor-error-ui {
    display: none;
    position: fixed;
    inset: auto 0 0 0;
    z-index: 1000;
    padding: 0.75rem 1.25rem;
    background: #2a1d1d;
    color: var(--text);
    /* Was a literal that predated --danger-border. One red in the product, named. */
    border-top: 1px solid var(--danger-border);
}

#blazor-error-ui .dismiss {
    position: absolute;
    top: 0.5rem;
    right: 1rem;
    cursor: pointer;
}

/* --- The ask ---------------------------------------------------------------------------
   Phone-first and one-handed: the metric is time from opening this to tapping a play link,
   so every control is thumb-sized and nothing needs a number pad. The grid is one column
   until there is real room for two - a two-column form on a phone is how a six-field form
   starts feeling like a tax return. */

.ask {
    margin-top: 1.5rem;
}

.field {
    display: block;
    margin-bottom: 1.25rem;
    border: 0;
    padding: 0;
}

.field--hero {
    margin-bottom: 1.75rem;
}

.field__label {
    display: block;
    font-size: 0.85rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--muted);
    margin-bottom: 0.4rem;
    padding: 0;
}

.field__hint {
    display: block;
    font-size: 0.85rem;
    color: var(--muted);
    margin-top: 0.35rem;
}

.input {
    width: 100%;
    /* 16px, because anything smaller makes iOS Safari zoom on focus and the zoom does not
       come back. A form that rearranges itself under the thumb is not one-handed. */
    font-size: 1rem;
    font-family: inherit;
    line-height: 1.5;
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 0.7rem 0.85rem;
    /* 44px of target, which is the smallest reliable thumb tap. */
    min-height: 2.75rem;
}

.input--hero {
    font-size: 1.1rem;
    resize: vertical;
}

/* Focus lights the field up the way a marquee bulb would: the border, and a soft glow around
   it. The outline above still draws, so this adds warmth without replacing the indicator. */
.input:focus-visible {
    border-color: var(--accent);
    box-shadow: 0 0 0 4px var(--glow);
}

.grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0 1rem;
}

/* --- The ask (UX rethink, 3 October 2026) -------------------------------------------------
   Who is here, the room's recent asks, the box, time, Decide - one screen on a phone. */
.ask__title {
    margin-bottom: 1rem;
}

.ask__recent {
    margin: 0.75rem 0;
}

.ask__box {
    position: relative;
}

/* Room for the microphone inside the box, so the text never runs under it. */
.ask__box .input--hero {
    padding-right: 3.25rem;
}

.ask__mic {
    position: absolute;
    top: 0.35rem;
    right: 0.35rem;
    min-width: 44px;
    min-height: 44px;
    border: 0;
    border-radius: 999px;
    background: none;
    font: inherit;
    font-size: 1.2rem;
    color: var(--text);
    cursor: pointer;
}

.ask__time {
    margin: 0.75rem 0 0.25rem;
}

/* "More options" on the ask form: the optional fields, folded so Decide stays on a phone's
   first screen. Colour is declared, not inherited, because inherited loses to a declared UA
   value on some elements and this is the trap DESIGN.md names. */
.more {
    margin: 0.5rem 0 1rem;
}

.more__toggle {
    display: flex;
    align-items: center;
    min-height: 44px;
    color: var(--muted);
    font-size: 0.95rem;
    cursor: pointer;
}

.more__toggle:hover,
.more__toggle:focus-visible {
    color: var(--text);
}

.more[open] > .more__toggle {
    color: var(--text);
}

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

.chip {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem 0.8rem;
    min-height: 2.75rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface);
    /* Stated, not inherited. A <span class="chip"> inherits --text and looks correct, but a
       <button class="chip"> does not: the UA stylesheet DECLARES `color: buttontext` on
       buttons, and a declared value beats an inherited one no matter how specific the
       inherited one's source is. Every unselected button chip was therefore rendering black
       on --surface at 1.18:1 - 35 of them across the ask form, the room and the services
       picker, which is the first screen a new account ever sees. Only .chip--on set a colour,
       so selection was the only state that was legible at all. */
    color: var(--text);
    cursor: pointer;
    user-select: none;
}

/* Hover before selection, so a chip reads as tappable before it reads as chosen. --muted is
   deliberate rather than the accent: the accent means "this one is on", and borrowing it for
   "your pointer is here" would make every hover look like a selection. */
.chip:hover {
    border-color: var(--muted);
}

/* The same selected state for a chip that is a <button aria-pressed> rather than a label
   wrapping a checkbox - the service picker and the attendance chips.

   It has to sit after .chip, not before it: both are one class of specificity, and .chip
   sets `border` as a shorthand, so an earlier .chip--on would have its border-color
   overwritten and the selection would be invisible. It was invisible - this rule was used
   by two pages and defined by none. */
/* Gold text on a faint gold wash: 8.09:1, measured on the tinted ground. */
.chip--on {
    border-color: var(--accent);
    background: #2f2515;
    color: var(--accent);
}

/* After .chip--on for the reason .chip--on is after .chip: same specificity, so the one that
   has to win goes last. A selected chip under the pointer must not fall back to the grey
   hover border and read as deselected. */
.chip--on:hover {
    border-color: var(--accent-bright);
    color: var(--accent-bright);
}

.chip[disabled] {
    opacity: 0.6;
    cursor: not-allowed;
}

/* .chip is already 44px and already a pointer. This variant exists for one real difference:
   a service name is longer than a genre tag and wants more horizontal room.

   After .chip rather than before it, for the same reason .chip--on is: .chip sets `padding`
   as a shorthand, so an earlier .chip--pick had its padding-left and padding-right silently
   overwritten. An earlier version also re-declared min-height and cursor, on a comment
   claiming .chip stayed dense at ~30px. It never did - .chip has been min-height: 2.75rem
   since Phase 1. */
.chip--pick {
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 0.9rem;
}

.chips--pick {
    gap: 0.6rem;
    margin: 1.5rem 0;
}

/* Provider marks. People recognise the Netflix N faster than they read the word, and a
   picker whose only job is "which of these do you pay for" is the most logo-shaped screen
   in the product (#53). Rounded because most provider logos ship as square tiles. */
.chip__logo {
    width: 1.5rem;
    height: 1.5rem;
    border-radius: 4px;
    object-fit: contain;
    flex: 0 0 auto;
}

/* A veto, shown rather than set. Struck through because it reads as a removal at a glance,
   which a colour alone does not - and colour is already spent on the accent. */
.chip--veto {
    text-decoration: line-through;
    opacity: 0.75;
}

/* --- The room --------------------------------------------------------------------------
   A list, not a dashboard. Everyone else is a hairline row; your own row is a .card with
   .card--me. A card earns its existence when the card IS the interaction, and yours is the
   only editable one - four cards for four people is a list wearing a costume, and it costs
   about a third of the vertical space on a phone. */

.card--me {
    border-color: var(--accent);
}

.member {
    padding: 1rem 0;
    border-bottom: 1px solid var(--border);
}

.member__head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
}

.member__name {
    font-weight: 600;
}

.member__who {
    font-size: 0.8rem;
    color: var(--muted);
}

/* When somebody else turned up. Shown on their row and not on yours - you know when you
   made your own room, and the question this answers is "who else is in here, and since
   when". Muted rather than marked: an arrival is worth seeing, not worth the one accent. */
.member__joined {
    display: block;
    margin-bottom: 0.5rem;
    font-size: 0.8rem;
    color: var(--muted);
}

.card .field__label {
    margin-top: 0.5rem;
}

/* Your own row carries three unrelated settings - what you pay for, how long you have, what
   you will not watch - and they ran together as one wall of labels and pills with no
   indication where one ended and the next began. A hairline is enough; a sub-card each would
   be four cards inside a card. */
.card--me .field__label {
    margin-top: 1.75rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--border);
}

/* --- Inviting --------------------------------------------------------------------------
   A link that admits somebody to a household's viewing history, so the warning is part of
   the control rather than fine print under it. */

.invite {
    margin-top: 2rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--border);
}

/* The household's own sections - services, then people - read as one block under the name,
   without the rules that separate the actions below them. */
.household {
    margin-top: 1.5rem;
}

/* Bordered in the accent rather than filled: it has to be read, and a filled block at this
   size reads as a banner people have learned to skip. */
.callout {
    border: 1px solid var(--accent);
    border-radius: var(--radius);
    padding: 0.75rem 1rem;
    margin: 0.75rem 0;
    font-size: 0.9rem;
}

.link {
    display: flex;
    gap: 0.5rem;
    align-items: stretch;
}

.link .input {
    font-size: 0.9rem;
    color: var(--muted);
}

.go {
    width: 100%;
    min-height: 3.25rem;
    font-size: 1.1rem;
    font-weight: 700;
    font-family: inherit;
    letter-spacing: 0.01em;
    color: var(--accent-ink);
    /* Lit from above, like everything else in the room, and casting a little of its own
       light below. The gradient runs between the two measured golds, so ink on any part of
       it is between 9.95:1 and 12.03:1. */
    background: linear-gradient(180deg, var(--accent-bright), var(--accent));
    border: 0;
    border-radius: var(--radius);
    box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.35), 0 10px 28px -12px rgb(240 180 60 / 0.6);
    cursor: pointer;
    margin-top: 0.5rem;
}

/* Every control in the app had exactly one state: sitting there. Nothing answered a pointer,
   which on a desktop reads as a page that has not finished loading rather than as one that is
   deliberately quiet.

   This is affordance, not animation. DESIGN.md rules out motion because nothing here is a
   first impression and an entrance on a utility is latency with a bow on it; a 120ms colour
   change under the cursor is the opposite - it is the control telling you it heard you. The
   one authored motion moment (#54) is still the verdict arriving, and still the only one. */
.go:hover,
.go:focus-visible {
    background: var(--accent-bright);
}

.go:active {
    /* Pressed, without moving the layout. transform would shift a 3.25rem target under a
       thumb that is already committed to the tap. */
    background: var(--accent);
    filter: brightness(0.94);
}

.go[disabled] {
    opacity: 0.6;
    /* not-allowed, not progress. The Decide button is disabled while a verdict is in flight
       AND when the room has nobody on the couch, and only the first of those is progress -
       a spinner cursor over a button waiting for you to answer a question is a lie. */
    cursor: not-allowed;
}

.go[disabled]:hover,
.go[disabled]:active {
    background: var(--accent);
    filter: none;
}

/* Set while the request is actually in flight. */
.go--busy[disabled] {
    cursor: progress;
}

/* --- .go modifiers, and all of them below .go on purpose -----------------------------------
   These three lived in the sections whose screens use them, which put them ABOVE .go in the
   file. One flat stylesheet, one level of specificity: `.go--quiet` and `.go` are both a
   single class, so the later one wins and it was .go.

   The result shipped. "Change services" and "Change the ask" rendered as solid yellow
   buttons - a second accent on screens that already had one, which is the single rule this
   palette does not break - and the inline Save beside the room name was full width at
   3.25rem rather than the compact control its own comment describes. Neither errored, neither
   warned, and both read as deliberate to anyone who had not read the stylesheet.

   Third instance of the same cascade trap, after .chip--on and .chip--pick. DESIGN.md names
   it; this is what it costs when a modifier is filed by topic instead of by order. */

/* A secondary action inside a card: present, but never competing with the one accent. */
.go--quiet {
    background: transparent;
    box-shadow: none;
    color: var(--text);
    border: 1px solid var(--border);
    font-weight: 600;
    font-size: 1rem;
    margin-bottom: 1.25rem;
}

/* A file picker that looks like the quiet button beside it (Phase 3 Stage C). The label is the
   visible control and the input stays in the page, keyboard-reachable, but out of sight - so
   the focus ring has to be drawn on the label, via focus-within, or a keyboard user tabs onto
   nothing visible. Below .go--quiet, because a modifier comes after what it modifies. */
.go--file {
    display: flex;
    align-items: center;
    justify-content: center;
}

.go--file:focus-within {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

.file__input {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.go--quiet:hover,
.go--quiet:focus-visible {
    background: transparent;
    border-color: var(--muted);
}

.go--quiet:active {
    background: transparent;
    filter: none;
    border-color: var(--text);
}

/* Beside a field rather than under it, so the button keeps its 44px without the row growing
   to two lines on a phone. */
.go--inline {
    width: auto;
    flex: 0 0 auto;
    min-height: 2.75rem;
    padding: 0 1rem;
    font-size: 0.95rem;
    margin-top: 0;
}

/* An <a> wearing a .go. Anchors bring their own underline and do not centre their text the
   way a button does, so both are restated - the same three lines .go--play already needs,
   and the reason it needs them. Combined with .go--quiet, never alone. */
.go--link {
    display: flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
}

.go--link:hover,
.go--link:focus-visible {
    text-decoration: none;
}

.note {
    font-size: 0.85rem;
    color: var(--muted);
    text-align: center;
    margin: 0.75rem 0 0;
}

.card--bad {
    border-color: var(--danger-border);
}

/* Destructive actions are quiet, not loud. A filled red button competes with Play for the
   eye, and leaving a room is never the outcome the product is hoping for - the same reason
   .reject is bordered rather than filled. Colour carries the warning; weight does not. */
.go--danger {
    color: var(--danger);
    background: transparent;
    box-shadow: none;
    border: 1px solid var(--danger-border);
    font-weight: 600;
}

.go--danger:hover,
.go--danger:focus-visible {
    /* The background is restated rather than inherited: .go:hover sets the accent, and a
       destructive control that turns yellow under the pointer is the loudest possible way to
       say "leave this room". Colour carries the warning; weight never does. */
    background: transparent;
    border-color: var(--danger);
}

.go--danger:active {
    background: transparent;
    filter: none;
}

@media (min-width: 34rem) {
    .grid {
        grid-template-columns: 1fr 1fr;
    }

    .field--wide {
        grid-column: 1 / -1;
    }
}

/* One rule for the whole vocabulary, short enough to feel instant rather than animated. Off
   entirely under prefers-reduced-motion: the states all still change, they just arrive
   without the ramp, so nothing is lost but the easing. */
@media (prefers-reduced-motion: no-preference) {
    .go,
    .chip,
    .reject,
    .restart,
    .nav a,
    .input {
        transition: background-color 120ms ease, border-color 120ms ease, color 120ms ease;
    }
}

/* --- The verdict -----------------------------------------------------------------------
   Three cards and a play button each. Nothing sorts, nothing filters, nothing paginates:
   a fourth result makes this a catalogue, and the catalogue is what it replaces. */

.verdict__heading {
    margin-bottom: 1rem;
}

.picks {
    list-style: none;
    margin: 0 0 1.25rem;
    padding: 0;
    display: grid;
    gap: 1rem;
}

/* The one authored motion moment in the product (#54). After six to thirteen seconds of
   waiting, three picks used to appear instantly and fully formed, with no emphasis at all
   on the payoff of the entire thing.

   Rules it obeys: once, here, nowhere else. It settles from an already-visible default
   rather than fading in from nothing, so a slow device or a stalled animation still shows
   the content - the answer is never hidden behind timing. */
@media (prefers-reduced-motion: no-preference) {
    .picks .pick {
        animation: pick-settle 260ms cubic-bezier(0.22, 1, 0.36, 1) backwards;
    }

    .picks .pick {
        animation-duration: 420ms;
    }

    .picks .pick:nth-child(2) {
        animation-delay: 110ms;
    }

    .picks .pick:nth-child(3) {
        animation-delay: 220ms;
    }
}

@keyframes pick-settle {
    from {
        opacity: 0.4;
        transform: translateY(10px);
    }
}

/* Each pick is a ticket: punched at both sides, lit along the top, with a tear line above
   Play. The punches are a mask, so they cut the border and the fill alike and show the room
   behind - which is what a hole in a ticket does. */
.pick {
    position: relative;
    display: flex;
    gap: 0.9rem;
    align-items: flex-start;
    background: linear-gradient(180deg, rgb(255 236 210 / 0.045), transparent 40%), var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    /* Inset only: the mask below clips anything drawn outside the border box. */
    box-shadow: inset 0 1px 0 rgb(255 236 210 / 0.06);
    padding: 1.1rem 1.25rem;
    --punch: radial-gradient(circle 9px at 0 62%, transparent 98%, #000 100%) left / 51% 100% no-repeat,
             radial-gradient(circle 9px at 100% 62%, transparent 98%, #000 100%) right / 51% 100% no-repeat;
    -webkit-mask: var(--punch);
    mask: var(--punch);
}

/* Poster first (UX rethink, 3 October 2026): people recognise a film by its poster faster
   than by its name. Fixed width so three cards line up; the body takes the rest. */
.pick__poster {
    flex: 0 0 auto;
    width: 76px;
    height: auto;
    aspect-ratio: 2 / 3;
    object-fit: cover;
    border-radius: 6px;
    background: var(--border);
    box-shadow: 0 12px 24px -10px rgb(0 0 0 / 0.9), 0 0 0 1px rgb(255 236 210 / 0.06);
}

.pick__body {
    flex: 1 1 auto;
    min-width: 0;
}

.pick__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 0.5rem;
}

/* The one way to say no. A 44px target that stays quiet beside the title, so it never
   competes with Play. Colour declared: a <button> does not inherit it (DESIGN.md). */
.pick__strike {
    flex: 0 0 auto;
    min-width: 44px;
    min-height: 44px;
    margin: -0.6rem -0.6rem 0 0;
    border: 0;
    background: none;
    font: inherit;
    font-size: 1.1rem;
    color: var(--muted);
    cursor: pointer;
}

.pick__strike:hover,
.pick__strike:focus-visible {
    color: var(--text);
}

/* The facts the reason no longer recites: runtime, year, genres. */
/* Set like the small print on a ticket: tracked capitals, numbers that line up. */
.pick__facts {
    margin: 0 0 0.6rem;
    color: var(--muted);
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    font-variant-numeric: tabular-nums;
}

/* The title identifies; the reason persuades. A title is what every catalogue shows and
   the reason is the only thing this product has that they do not - hybrid retrieval,
   Grounding, the three defences against invention all exist to produce that sentence, and
   it used to render smaller and lighter than the heading above it (#51).

   That fix overcorrected. Promoting the reason is right; it was done by pushing the title
   down to --muted at the reason's own size, which is the same demotion by another route -
   measured on the deployed page, the film's name sat at 6.47:1 while the sentence about it
   sat at 14.77:1, and .runners__title rendered the films that LOST at full --text and 600.
   The titles that lost were louder than the titles that won.

   So hierarchy comes from size and weight here, never from dimming the name of the thing
   somebody is choosing. The reason keeps the size and colour #51 gave it. */
/* The film's name on the marquee: the serif, larger than the reason, at full --text. Size
   carries the hierarchy, never dimming (above). */
.pick__title {
    margin: 0 0 0.3rem;
    font-family: var(--serif);
    font-size: 1.7rem;
    font-weight: 400;
    line-height: 1.05;
    color: var(--text);
    letter-spacing: 0;
}

/* The reason, then the tear line: a dashed rule between what the ticket says and the stub
   you hand over. */
.pick__reason {
    margin: 0 0 1rem;
    padding-bottom: 1rem;
    border-bottom: 1px dashed var(--border);
    color: var(--text);
    font-size: 1.1rem;
    line-height: 1.5;
}

.pick__nolink {
    margin: 0;
    color: var(--muted);
    font-size: 0.9rem;
}

.go--play {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    min-height: 3.25rem;
    text-align: center;
    text-decoration: none;
    padding: 0 0.75rem;
    margin: 0;
}

/* "Play on Netflix" with the service's own mark, so nobody wonders which app opens. */
.go__logo {
    width: 24px;
    height: 24px;
    border-radius: 5px;
}

.go--play:hover,
.go--play:focus-visible {
    text-decoration: none;
}

/* A first-class signal, so it is a real control rather than a link tucked underneath -
   but quieter than Play, because it is not the outcome we are hoping for. */
.reject {
    width: 100%;
    min-height: 3rem;
    font-size: 1rem;
    font-family: inherit;
    color: var(--text);
    background: transparent;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    cursor: pointer;
}

.reject:hover,
.reject:focus-visible {
    border-color: var(--muted);
}

.reject[disabled] {
    opacity: 0.6;
    cursor: not-allowed;
}

.reject[disabled]:hover {
    border-color: var(--border);
}

.runners {
    margin-top: 2rem;
}

.runners ul {
    list-style: none;
    margin: 0;
    padding: 0;
}

.runners li {
    display: flex;
    flex-wrap: wrap;
    gap: 0 0.5rem;
    padding: 0.6rem 0;
    border-bottom: 1px solid var(--border);
}

.runners__title {
    font-weight: 600;
}

.runners__killed {
    color: var(--muted);
    font-size: 0.9rem;
}

/* --- The shared verdict (Stage G) -----------------------------------------------------------
   Who asked, under the heading, because since Stage G the three on screen may be a housemate's.
   Left-aligned with the heading it qualifies rather than centred like a footnote. */
.verdict__asked {
    margin: -0.5rem 0 1.25rem;
    text-align: left;
}

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

/* Struck picks: the .runners row shape, because it is the same kind of fact - a title that is
   not on offer, and why. The line through the name says "gone" before anyone reads the words
   beside it, and the words say who, which is the part that stops it looking like a bug. */
.struck {
    list-style: none;
    margin: 0 0 1.25rem;
    padding: 0;
}

.struck li {
    display: flex;
    flex-wrap: wrap;
    gap: 0 0.5rem;
    padding: 0.5rem 0;
    border-bottom: 1px solid var(--border);
    font-size: 0.9rem;
}

.struck__title {
    color: var(--muted);
    text-decoration: line-through;
}

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

/* "Never offer it again" and its confirmation, inside the struck line. A link-weight button
   rather than a .go block: it is a follow-up to a no, not a decision of its own. Colour and
   font are declared because a <button> does not inherit them (DESIGN.md's third trap). */
.struck__ask {
    color: var(--text);
}

.struck__action {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 0.25rem;
    border: 0;
    background: none;
    font: inherit;
    color: var(--muted);
    text-decoration: underline;
    cursor: pointer;
}

.struck__action:hover,
.struck__action:focus-visible {
    color: var(--text);
}

.struck__action--danger {
    color: var(--danger);
    font-weight: 600;
}

/* --- "Expecting something else?" (Stage D1) ------------------------------------------------
   Below .runners because it answers the question .runners cannot: a title the ceiling, a veto
   or the provider filter removed was never retrieved, so it can never be listed as something
   that lost. Visually quieter than the picks - this is a question somebody goes looking for,
   not something competing with Play. */
.whynot {
    margin-top: 2.5rem;
}

.whynot h2 {
    margin-bottom: 0.75rem;
}

/* The answer's heading. h3 rather than h2 because it sits inside the section's own heading,
   and the document outline is what a screen reader navigates by. */
.whynot__title {
    margin: 0 0 0.5rem;
}

.whynot__reasons {
    margin: 0;
    padding-left: 1.1rem;
}

/* Each reason is a sentence, not a label - they are read, and often read aloud. */
.whynot__reasons li {
    margin-bottom: 0.4rem;
    line-height: 1.5;
}

.whynot__reasons li:last-child {
    margin-bottom: 0;
}

/* --- What happened last time -------------------------------------------------------------
   One question, about one title, and only when there is one to ask. It sits above the ask
   form - the screen this product is measured on - so it is bordered rather than filled and
   carries no accent: it must be visible without competing with Decide, which is the one
   thing on the page anybody came for. */
.asked {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    padding: 1rem;
    margin-bottom: 1.5rem;
}

.asked__question {
    margin: 0 0 0.75rem;
    color: var(--muted);
    font-size: 0.95rem;
    line-height: 1.5;
}

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

/* --- The one number the product is defined by --------------------------------------------
   The impression log wrote Shown, Chosen and Skipped from Phase 1 and nothing ever read one
   back, so "is this working?" could only be answered by counting features. Stated in titles
   rather than a percentage: a household knows what "7 of 51" means and has to work out what
   14% is a percentage of. */
.tally {
    margin: 0 0 1.5rem;
    padding-bottom: 1.25rem;
    border-bottom: 1px solid var(--border);
    color: var(--muted);
    font-size: 0.95rem;
    line-height: 1.6;
}

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

/* --- The decision log (Stages D2 and E3) -------------------------------------------------
   A history, so it reads as a list of evenings rather than a table of rows.

   Each entry leads with the TITLE. It shipped led by the date, which is the one thing nobody
   arguing about an evening is trying to establish - "why did we end up watching that" starts
   from the that. Then who wanted it and what they said, then what it beat. E3. */
.history {
    list-style: none;
    margin: 1.5rem 0 0;
    padding: 0;
}

.history__entry {
    padding: 1.1rem 0;
    border-top: 1px solid var(--border);
}

.history__entry:last-child {
    border-bottom: 1px solid var(--border);
}

/* The answer to the question this screen exists for, so it carries the weight a heading
   carries and nothing else on the entry competes with it. Sized off the page's own scale
   rather than an h2's default, which would be louder than the page title above it. */
.history__title-led {
    margin: 0;
    font-size: 1.25rem;
    line-height: 1.3;
    font-weight: 600;
}

/* An evening nothing got played on leads with what was asked, in the room's own words -
   quieter than a played title, because it is the question rather than the answer. */
.history__title-led--ask {
    color: var(--text);
    font-weight: 400;
}

.history__more {
    margin-top: 0.25rem;
}

.history__more > .more__toggle {
    font-size: 0.85rem;
}

/* Who wanted it and what they said, as one sentence rather than three stacked lines. The
   date rides along at the end: worth having, never worth a line of its own. */
.history__ask {
    margin: 0.35rem 0 0;
    color: var(--muted);
    font-size: 0.95rem;
    line-height: 1.5;
}

/* The two parts of that sentence somebody is actually scanning for. Full strength against
   the muted run they sit in, which is the whole reason the rest of the line is muted. */
.history__asker,
.history__words {
    color: var(--text);
}

/* No free text is a real ask - the form alone is a complete query - so it is named, and
   named as the absence it is rather than dressed up as a quotation. */
.history__words--none {
    color: var(--muted);
    font-style: italic;
}

.history__when::before {
    content: " · ";
}

/* The nearest miss, and why. One line and one runner-up: the argument being had is about the
   thing that nearly won, and a list of also-rans is the catalogue creeping back in. */
.history__nearly {
    display: flex;
    flex-wrap: wrap;
    gap: 0 0.5rem;
    align-items: baseline;
    margin: 0.5rem 0 0;
    font-size: 0.9rem;
    line-height: 1.5;
}

/* The frame the verdict screen gets from its "Close, and why not" heading. A log entry has
   no room for a heading per part, so the label carries it in three characters. */
.history__nearly-label {
    color: var(--muted);
}

.history__nearly .history__name {
    color: var(--text);
    font-weight: 600;
}

/* The same muted reason, at the same size, as .runners__killed on the verdict screen. Two
   screens showing one fact should not style it two ways. */
.history__killed {
    color: var(--muted);
}

.restart {
    display: block;
    margin: 1.5rem auto 0;
    padding: 0.5rem 1rem;
    min-height: 2.75rem;
    font-size: 0.9rem;
    font-family: inherit;
    color: var(--muted);
    background: transparent;
    border: 0;
    cursor: pointer;
    text-decoration: underline;
}

.restart:hover,
.restart:focus-visible {
    color: var(--text);
}

/* .restart is centred because it was written for "Start over" at the foot of the verdict,
   where centred is right. It then got reused for the inline edit affordances - rename this
   room, change your name, cancel - and `margin: auto` floated each of them into the middle
   of an otherwise left-aligned column. Nothing else on those screens is centred, so they
   read as dropped rather than placed. */
.restart--inline {
    margin: 0.25rem 0 0;
    padding-left: 0;
    padding-right: 0;
    text-align: left;
}

/* --- The landing page -------------------------------------------------------------------
   The only Persuade surface in the product. Everything above this comment is written for
   screens whose job is to get out of the way and whose metric is time-to-play; this one is
   measured by whether a stranger decides to bother, and here the design is the product.

   It shares the palette deliberately - a front door that looks nothing like the room behind
   it is a different kind of lie - and departs on exactly one axis: type. The app uses the
   font the phone already has, which is right for a utility read in the dark for thirty
   seconds and is the loudest "assembled rather than designed" signal there is on a page
   asking a household to trust it. DESIGN.md carried that deferral with an expiry on it.

   Instrument Serif, roman, display sizes only. High contrast against near-black, and
   uncommon enough that the page does not read as a template. Body text stays on the system
   stack, so the face is a few kilobytes used by six headings and nothing else. */

.landing {
    max-width: 38rem;
    margin-inline: auto;
}

.display {
    font-family: var(--serif);
    font-weight: 400;
    /* Fluid rather than a breakpoint: the first line has to hold its shape from a narrow
       phone to a laptop, and a step change mid-sentence is what a media query gives you. */
    font-size: clamp(2.3rem, 9vw, 3.4rem);
    line-height: 1.05;
    letter-spacing: -0.015em;
    margin: 0 0 1.25rem;
    color: var(--text);
}

.display--sm {
    font-size: clamp(1.6rem, 5.5vw, 2.1rem);
    line-height: 1.15;
    margin-bottom: 0.75rem;
}

/* Small, tracked, muted: it orients before the headline lands, and it is the one place a
   number belongs on this page. */
.kicker {
    font-size: 0.8rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--muted);
    margin: 0 0 1.25rem;
}

.lede-block {
    padding: 3rem 0 2.5rem;
    border-bottom: 1px solid var(--border);
}

.landing__sub {
    font-size: 1.1rem;
    line-height: 1.55;
    color: var(--muted);
    margin: 0 0 2rem;
    max-width: 30rem;
}

.landing__cta .go--play {
    max-width: 16rem;
}

.landing__cta .note {
    text-align: left;
    margin-top: 0.75rem;
}

.landing__section {
    padding: 2.5rem 0;
    border-bottom: 1px solid var(--border);
}

.landing__section p {
    color: var(--muted);
    max-width: 32rem;
}

.landing__section strong {
    color: var(--text);
    font-weight: 600;
}

.landing__aside {
    border-left: 2px solid var(--border);
    padding-left: 0.85rem;
    font-size: 0.9rem;
}

/* Real pick markup, so the sample can never drift from what the product renders. Inert:
   it is an illustration, not a control.

   margin-bottom zeroed: .picks carries 1.25rem for the verdict screen, where the list is
   followed by "None of these". Here it stacked on top of the section's own 2.5rem padding
   and opened a gap half again as large as every other break on the page - the kind of
   irregular rhythm that reads as carelessness without anyone being able to name it. */
.picks--sample {
    margin-top: 1.5rem;
    margin-bottom: 0;
}

/* An illustration of a Play button, not a Play button. It answers a pointer as though it
   were one unless it is told not to, which is worse than a flat picture: it invites a tap
   that does nothing. */
.go--sample,
.go--sample:hover,
.go--sample:focus-visible,
.go--sample:active {
    cursor: default;
    opacity: 0.85;
    background: linear-gradient(180deg, var(--accent-bright), var(--accent));
    filter: none;
}

.landing__close {
    padding: 3.5rem 0 4rem;
    text-align: center;
}

.landing__close .go--play {
    max-width: 16rem;
    margin-inline: auto;
}
