/* ============================================================================
   Weekly Compass & Planner — the whole stylesheet.

   One rule governs everything here: design the environment so the right choice
   is easy. In practice that means

     1. The week is read top to bottom in the order you should think in:
        Big Rock, then the four priorities, then roles, then the grid.
     2. Nothing shouts. State (done, today, overdue, full) is carried by colour
        and a quiet rule, never by a banner you have to dismiss.
     3. The four priority lines of the paper planner stay four visible lines, so
        an empty one reads as a place waiting for something rather than as
        missing furniture.
     4. A day that is too full looks too full before you read a word of it.
     5. Every control has a no-JavaScript path, so a form must look like a form.

   No web fonts: one file, one request, and it renders offline on a phone.
   ========================================================================= */

/* --- tokens -------------------------------------------------------------- */

/* The palette is the one the other app is built on: a dark slate ground, one
   blue that means "this is where you are", violet for the button that makes
   something new, and green, amber and red for the three states a plan can be in.
   Every colour on every page is one of these tokens, so the whole application is
   recoloured from this block and nothing else. */
:root {
    color-scheme: dark;

    --ink: #f8fafc;
    --ink-soft: #cbd5e1;
    --muted: #94a3b8;
    --line: #334155;
    --line-strong: #475569;

    --paper: #0f172a;
    --surface: #1e293b;
    --surface-sunk: #0f172a;

    --accent: #3b82f6;
    --accent-strong: #2563eb;
    --accent-ink: #93c5fd;
    --accent-soft: rgb(59 130 246 / 16%);
    --violet: #8b5cf6;

    /* The warm half of the palette. It marks the things that are happening
       today, and it is used for fills and rules only - coral this light is not
       text, which is what --hot-ink is for. */
    --hot: #ef8354;
    --hot-ink: #f7b28c;
    --hot-soft: rgb(239 131 84 / 16%);

    --sun: #f2c14e;
    --sun-soft: rgb(242 193 78 / 16%);

    --warn: #f59e0b;
    --warn-soft: rgb(245 158 11 / 14%);
    --danger: #ef4444;
    --danger-soft: rgb(239 68 68 / 15%);
    --info: #38bdf8;
    --info-soft: rgb(56 189 248 / 14%);

    /* The three colours the palette uses as *ink* rather than as a fill - a
       refusal, a caution, a name for the thing you are looking at - and the one
       colour that goes on top of a filled button. They are tokens because a dark
       page needs a light warning and a light page needs a dark one, and a colour
       chosen for one of them is unreadable on the other. */
    --warn-ink: #fcd34d;
    --danger-ink: #fca5a5;
    --on-accent: #fff;

    /* The glass the bar at the foot of the screen is made of, and the wash a slot
       takes when a finger is on it. Both are their own tokens because glass is the
       one surface that cannot be mixed from the palette: what is behind it decides
       whether it has to be light or dark, and that is a question about the theme
       rather than about the page. */
    --glass: rgb(30 41 59 / 74%);
    --glass-solid: rgb(30 41 59 / 92%);
    --glass-line: rgb(255 255 255 / 9%);
    --glass-glow: rgb(255 255 255 / 7%);
    --glass-shadow: rgb(2 6 23 / 55%);
    --wash: rgb(255 255 255 / 6%);
    --scrim: rgb(2 6 23 / 62%);

    /* The bar's own ink. It is a token of its own because the bar is the one
       control that floats on glass rather than standing on the page: its icons and
       its words have to be lighter than the page's quiet ink on a dark screen and
       darker than it on a light one, and the page's --muted is an answer to neither
       question. --tabbar-ink-quiet is a step softer for the motto under the bar,
       which is read rather than pressed. */
    --tabbar-ink: #cbd5e1;
    --tabbar-ink-quiet: #a3b1c4;

    /* How strong the two lights in the page's own ground are. The same two
       gradients are used on a light page, where a 22% tint is a stain rather than
       a light. */
    --glow: 22%;

    /* The tick switch's own three colours. They are state rather than palette - a
       switch that is off is red and one that is on is green, and that is the whole
       of what it says - but they are still written here, because a colour written
       where it is used is a colour the light page cannot answer for. */
    --switch-off: #dc2626;
    --switch-on: #16a34a;
    --switch-thumb: radial-gradient(circle at 34% 28%, #fff 0%, #f1f5f9 55%, #cbd5e1 100%);

    /* The five kinds of entry, coloured once. A kind is a hue you learn, and it
       is the same hue on the grid, on the front door and in the dial. They are
       the bright end of each hue because a dark page reads them as text. */
    --kind-task: #94a3b8;
    --kind-appointment: #a78bfa;
    --kind-commitment: #34d399;
    --kind-block: #38bdf8;
    --kind-focus: #fbbf24;

    /* A hue per day of the week, so the days of "this week" are told apart before
       a word of them is read. They are tints, not fills: the colour is a wash
       behind the lines and a rule down the side, at a strength the text never has
       to fight - which is why the soft half is its own token rather than an
       opacity, so the wash can be tuned once. */
    --day-1: #60a5fa; --day-1-soft: rgb(96 165 250 / 13%);
    --day-2: #22d3ee; --day-2-soft: rgb(34 211 238 / 13%);
    --day-3: #34d399; --day-3-soft: rgb(52 211 153 / 13%);
    --day-4: #fbbf24; --day-4-soft: rgb(251 191 36 / 13%);
    --day-5: #fb923c; --day-5-soft: rgb(251 146 60 / 13%);
    --day-6: #a78bfa; --day-6-soft: rgb(167 139 250 / 14%);
    --day-7: #f472b6; --day-7-soft: rgb(244 114 182 / 13%);

    --radius: 12px;
    --radius-sm: 8px;
    --shadow: 0 1px 2px rgb(2 6 23 / 45%);
    --shadow-lift: 0 10px 25px -5px rgb(0 0 0 / 50%);

    --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;

    --gap: 1.15rem;
    --pad: 1.35rem;
    --page-max: 74rem;

    /* How tall one row of the bar at the foot of the screen is. It is a token
       rather than a number written where it is used because two other things
       have to agree with it: the room a page keeps at its own bottom, and where
       the notice about the connection sits when it is up. */
    --tabbar-row: 3.25rem;
}

/* --- the light page ------------------------------------------------------ */

/* The other half of the palette, in the same names.

   The app was drawn dark and it stays honest to that: this is not a second design,
   it is the same design in the other light. Every colour is a token already, so the
   whole application is recoloured from one block — the only things any other rule
   had to be told about are the colours a dark page takes for granted: the ink a
   refusal is written in, the glass the bar is made of, and the strength of the two
   lights in the page's ground.

   It is chosen rather than guessed: `data-theme` is written on the document by the
   reader's own choice, which is remembered, and by nothing else — the stylesheet
   cannot know what the reader wants, and the script's job is only to say it before
   the first paint so the page never changes colour under a finger. @see app.js */
[data-theme="light"] {
    color-scheme: light;

    --ink: #0f172a;
    --ink-soft: #334155;
    --muted: #64748b;
    --line: #e2e8f0;
    --line-strong: #cbd5e1;

    --paper: #f5f7fb;
    --surface: #fff;
    --surface-sunk: #eef2f7;

    --accent: #2563eb;
    --accent-strong: #1d4ed8;
    --accent-ink: #1d4ed8;
    --accent-soft: rgb(37 99 235 / 11%);
    --violet: #7c3aed;

    --hot: #f97316;
    --hot-ink: #c2410c;
    --hot-soft: rgb(249 115 22 / 12%);

    --sun: #b45309;
    --sun-soft: rgb(180 83 9 / 12%);

    --warn: #b45309;
    --warn-soft: rgb(245 158 11 / 16%);
    --danger: #dc2626;
    --danger-soft: rgb(220 38 38 / 9%);
    --info: #0369a1;
    --info-soft: rgb(3 105 161 / 9%);

    --warn-ink: #92400e;
    --danger-ink: #991b1b;
    --on-accent: #fff;

    /* Glass on a light page has to be light, or the bar reads as a hole in the
       screen: white at four fifths, a hairline of the ink rather than of a light,
       and a shadow that lifts it off the page instead of deepening it. */
    --glass: rgb(255 255 255 / 82%);
    --glass-solid: rgb(255 255 255 / 96%);
    --glass-line: rgb(15 23 42 / 10%);
    --glass-glow: rgb(255 255 255 / 70%);
    --glass-shadow: rgb(15 23 42 / 18%);
    --wash: rgb(15 23 42 / 6%);
    --scrim: rgb(15 23 42 / 42%);

    /* The bar's own ink, dark on a light page for the same reason it is light on a
       dark one. @see the dark page's copy of these two tokens */
    --tabbar-ink: #334155;
    --tabbar-ink-quiet: #4e5d75;

    --glow: 10%;

    --shadow: 0 1px 2px rgb(15 23 42 / 8%);
    --shadow-lift: 0 10px 25px -5px rgb(15 23 42 / 18%);

    /* The five kinds and the seven days, dark enough to be read as ink on a white
       card - which is the whole of the difference: on the dark page they are the
       bright end of each hue, and here they are the deep end. */
    --kind-task: #475569;
    --kind-appointment: #6d28d9;
    --kind-commitment: #047857;
    --kind-block: #0369a1;
    --kind-focus: #b45309;

    --day-1: #1d4ed8; --day-1-soft: #eef2ff;
    --day-2: #0e7490; --day-2-soft: #ecfeff;
    --day-3: #047857; --day-3-soft: #ecfdf5;
    --day-4: #b45309; --day-4-soft: #fffbeb;
    --day-5: #c2410c; --day-5-soft: #fff7ed;
    --day-6: #6d28d9; --day-6-soft: #f5f3ff;
    --day-7: #be185d; --day-7-soft: #fdf2f8;
}

/* --- base --------------------------------------------------------------- */

*,
*::before,
*::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

/* How big the words are, as the reader asked for them.

   One step on the document's own size, and nothing else: every size in this file is
   a `rem`, so the words, the gaps between them, the capsules and the bar all grow
   together and no rule has to know the question was ever asked. The step is the
   pill's own - the size the page is drawn at, or 112.5% of it (16px to 18px), which
   is a size a page can take without being rearranged around. A reader who has never
   chosen gets the first. @see app/Views/partials/prefs.php, App\Support\Prefs::text() */
html[data-text="large"] { font-size: 112.5%; }

body {
    margin: 0;
    background:
        radial-gradient(70rem 46rem at 12% -6rem, color-mix(in srgb, var(--accent) var(--glow), transparent) 0%, transparent 62%),
        radial-gradient(60rem 40rem at 100% 2rem, color-mix(in srgb, var(--violet) var(--glow), transparent) 0%, transparent 58%),
        var(--paper);
    background-repeat: no-repeat;
    color: var(--ink);
    /* In the document's own size rather than in a number, so that the page's own
       text follows the size the reader asked the whole page for - which is the same
       thing every other size here is measured against. */
    font: 400 1rem/1.55 var(--sans);
    text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 {
    margin: 0 0 .35em;
    line-height: 1.22;
    font-weight: 650;
    letter-spacing: -.011em;
    color: var(--ink);
}

h1 { font-size: 1.6rem; }
h2 { font-size: 1.18rem; }
h3 { font-size: 1rem; }

p { margin: 0 0 .7em; }
p:last-child { margin-bottom: 0; }

form { margin: 0; }

a { color: var(--accent-ink); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--accent); }

blockquote { margin: 0; }

code { font: 500 .95em/1.4 var(--mono); }

img, svg { max-width: 100%; }

:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 3px;
}

/* --- utilities ---------------------------------------------------------- */

.sr {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.muted { color: var(--muted); font-weight: 400; }
.em { font-style: italic; }

/* A background save in progress: quieter than a spinner, hard to miss. */
.is-saving { opacity: .55; transition: opacity .15s ease-out; }

.skip {
    position: absolute;
    left: -9999px;
    z-index: 60;
    padding: .5rem .9rem;
    background: var(--surface);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
}

.skip:focus {
    left: .6rem;
    top: .6rem;
}

.linkish {
    display: inline;
    padding: 0;
    border: 0;
    background: none;
    color: var(--accent-ink);
    font: inherit;
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
    cursor: pointer;
}

.linkish:hover { color: var(--accent); }

.linkish--quiet {
    color: var(--muted);
    text-decoration-color: var(--line-strong);
}

.linkish--danger { color: var(--danger-ink); }
.linkish--danger:hover { color: var(--danger); }

.linkish--open { text-decoration: none; }

/* --- the screen that waits ---------------------------------------------- */

/* A cold page is a page with nothing behind it yet: the shell is still being
   read in and the stylesheet is still on its way, so the first thing the site
   says to a tab that has never been here is that it is coming.

   One arrival is all it is for. A page after the first one is the same site
   already read in - the same styles, the same scripts, the same shell - so
   covering it would put a screen over a press that was only changing the page.
   Which arrival this is is decided by the script in the head, before the first
   paint, and it is said with one name on the root: `is-ready`, which every
   arrival after the first one carries from the start. A page that carries it
   never draws the screen at all, and the one arrival that does not carry it has
   the screen drawn by the server with the rest of the page.

   Nothing is required of it and nothing is ever caught by it: it is a screen
   and not a gate, and it takes no press of its own. Without scripts it is never
   drawn at all. */

.loader {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 80;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .75rem;
    background: color-mix(in srgb, var(--paper) 88%, transparent);
    backdrop-filter: blur(3px);
    transition: opacity .18s ease-out, visibility .18s ease-out;
}

html.has-js .loader { display: flex; }

/* The screen is drawn by the server with the page, and what takes it down is the
   page saying it is here. If nothing ever says it - a script that failed to load,
   a document that never finishes arriving - the stylesheet takes the screen away
   on its own, because the thing that draws a screen is the one thing that can
   always promise it goes. */
html.has-js:not(.is-ready) .loader {
    animation: loader-away .5s ease-out 12s forwards;
}

html.has-js.is-ready .loader {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
}

/* A press that has to wait for the server says so at once.

   The language is the one preference a browser cannot answer on its own - it is
   every word on the page, and only the server writes those - so that press is a
   real journey. The screen it puts up is the same one the first arrival is waited
   for behind, because it is the same wait, and saying it at the moment of the press
   is the difference between a slow page and a page that looks broken. @see
   setUpPrefs() in assets/js/app.js */
html.has-js.is-waiting .loader {
    display: flex;
    opacity: 1;
    visibility: visible;
}

.loader__ring {
    width: 1.6rem;
    height: 1.6rem;
    border: 2px solid var(--line-strong);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: loader-turn 1.1s linear infinite;
}

.loader__text {
    margin: 0;
    color: var(--muted);
    font-size: .78rem;
    font-weight: 650;
    letter-spacing: .1em;
    text-transform: uppercase;
}

@keyframes loader-turn {
    to { transform: rotate(360deg); }
}

/* The last word on a screen that was never taken down, and what an animation is
   wanted for at all: it is a delay with a promise on it, not a movement, so it
   runs whether or not the reader has asked for less motion. */
@keyframes loader-away {
    to {
        opacity: 0;
        visibility: hidden;
        pointer-events: none;
    }
}

/* --- the offline bar ---------------------------------------------------- */

/* The one thing the site says when the connection is gone. It is printed with
   the page and stays down until the script says the connection has gone, because
   without scripts every page is a real request: a page that arrived is a page
   that was reached, and there is nothing to warn anybody about.

   It stands in front of the page rather than inside it, so a page being read is
   never pushed around by a notice, and it sits below the wheel in the stack: the
   wheel rests at the bottom of the window too, and the bar is something to be
   seen beside it rather than a thing that covers it. */

.offline { display: none; }

html.is-offline .offline {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 35;
    display: flex;
    align-items: center;
    gap: .55rem;
    margin: 0;
    padding: .55rem 1rem;
    padding-bottom: max(.55rem, env(safe-area-inset-bottom));
    border-top: 1px solid var(--line);
    background: color-mix(in srgb, var(--surface) 96%, transparent);
    backdrop-filter: blur(6px);
    color: var(--ink-soft);
    font-size: .78rem;
    line-height: 1.4;
}

.offline__dot {
    flex: 0 0 auto;
    width: .5rem;
    height: .5rem;
    border-radius: 50%;
    background: var(--hot);
}

/* The notice and the bar at the foot of the screen are both always there, so the
   notice sits above the bar rather than under it: where they meet is decided in
   the bar's own block, at the foot of this file. */

/* --- shell -------------------------------------------------------------- */

.topbar {
    position: sticky;
    top: 0;
    z-index: 30;
    background: color-mix(in srgb, var(--surface) 92%, transparent);
    backdrop-filter: blur(6px);
    border-bottom: 1px solid var(--line);
}

.topbar__row {
    display: flex;
    align-items: center;
    gap: .75rem;
    max-width: var(--page-max);
    margin: 0 auto;
    padding: .55rem 1rem;
}

.brand {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    margin-right: auto;
    color: var(--ink);
    text-decoration: none;
    font-weight: 650;
}

.brand__mark { font-size: 1.15rem; color: var(--accent); line-height: 1; }
.brand__text { letter-spacing: -.01em; }
.brand:hover .brand__text { color: var(--accent-ink); }

/* --- the reader's own three answers -------------------------------------- */

/*
    The questions that belong to the reader rather than to the plan: which language
    the app is written in, whether the page is light or dark, and how big the words
    are.

    They are racks rather than switches, because the first two are not two-state
    things that mean the same to everybody: a switch called "dark" leaves you to work
    out whether it is on or off, and a switch called "language" cannot say *which*.
    Two named options, with the one you are on carried by a raised capsule, is the
    same shape the bar already uses for the slot you are standing in - so the app is
    answerable on one question everywhere: what is chosen. The third rack is that
    shape too rather than the slider it could have been: its two answers are named by
    the letter they are drawn in, so which one is on is read at a glance rather than
    worked out from which side a thumb is resting on.

    Every option is a real link, so all three questions can be answered with no script
    at all: the address answers, remembers, and sends you back to the page you were
    reading. The script only makes the answer instant - and for the theme and the size
    there is nothing to wait for, because the palette is one block of tokens and every
    size in the page is measured against the document's own, so the page can turn
    over, or grow, where it stands. Which is also why the shelf is drawn by the shell
    rather than by a page: it is about the reader, not the plan.
    @see App\Support\Prefs
*/
.prefs {
    display: flex;
    flex: 0 0 auto;
    align-items: center;
    gap: .35rem;
}

.prefs__rack {
    display: flex;
    align-items: center;
    gap: .1rem;
    padding: .16rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--surface-sunk);
}

.prefs__opt {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .3rem;
    min-width: 2.5rem;
    height: 2.4rem;
    padding: 0 .7rem;
    border: 0;
    border-radius: 999px;
    background: none;
    color: var(--muted);
    text-decoration: none;
    font-size: .84rem;
    font-weight: 650;
    letter-spacing: .02em;
    cursor: pointer;
    /* The drawn cap is 2.4rem, which is how big the words need; the reach is the
       size a thumb is measured in. */
    box-shadow: 0 0 0 .3rem transparent;
    transition: background-color .18s ease, color .18s ease, box-shadow .18s ease;
}

.prefs__opt:hover { color: var(--ink); background: var(--wash); }

/* The one you are on: lifted off the track, in the accent, with a hairline
   rather than a fill - a filled capsule up here would shout louder than the page
   it is sitting on. */
.prefs__opt.is-on {
    background: var(--surface);
    color: var(--accent-ink);
    box-shadow: var(--shadow), inset 0 0 0 1px color-mix(in srgb, var(--accent) 40%, transparent);
}

.prefs__icon { width: 1.05rem; height: 1.05rem; }

/* The third rack says its two answers with the letter itself, so the difference
   between them is the one thing it has to show: one step, drawn at one step. */
.prefs__letter { font-size: .84rem; font-weight: 700; line-height: 1; }
.prefs__letter--big { font-size: 1.1rem; }

@media (prefers-reduced-motion: reduce) {
    .prefs__opt { transition: none; }
}

/* On a phone the space's name is in the page itself, so the header keeps the mark
   and gives the room to the three racks - and nothing in it is allowed to wrap,
   because a header that grows a second line is a header that pushes the page down
   every time you look at it. */

@media (max-width: 36rem) {
    .topbar__row { gap: .5rem; padding: .5rem .7rem; }
    .brand__text { display: none; }
    .prefs { gap: .2rem; }
    .prefs__rack { padding: .13rem; }
    .prefs__opt { min-width: 1.95rem; height: 2.3rem; padding: 0 .4rem; }
}

/* Under this, three racks and the mark are more than a screen holds once the reader
   has asked for the larger words, and a capsule pushed off the right edge is a
   control that is not there at all. So every one of them gives back a little of its
   width - only at that size, and only on a screen that narrow. */
@media (max-width: 20rem) {
    html[data-text="large"] .topbar__row { gap: .4rem; padding: .5rem; }
    html[data-text="large"] .prefs { gap: .1rem; }
    html[data-text="large"] .prefs__rack { gap: .04rem; padding: .08rem; }
    html[data-text="large"] .prefs__opt { min-width: 1.6rem; padding: 0 .25rem; }
}

/* --- the places, and the week -------------------------------------------- */

/* Two of the app's pages are places rather than parts of the week - the day
   page, which is the one you live on, and the notes, which are kept across weeks
   rather than inside one. The bar at the foot of the screen is the way between
   them and between everything else - the week, the notes, the settings, the
   history, the insights, the review - so the header above carries no menu at all:
   it is the space you are in, its name, and the three questions that are about
   the reader rather than about the plan. One way round, one place it lives. */

.page {
    max-width: var(--page-max);
    margin: 0 auto;
    padding: var(--pad) 1rem 3rem;
}

.page-narrow { max-width: 46rem; margin: 0 auto; }

.page--bare {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-height: 82vh;
}

.footer {
    max-width: var(--page-max);
    margin: 0 auto;
    padding: 1.5rem 1rem 2.5rem;
    border-top: 1px solid var(--line);
    color: var(--muted);
    font-size: .88rem;
    text-align: center;
}

.footer em { letter-spacing: .01em; }

/* --- hero (the top of every page) --------------------------------------- */

.hero {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end;
    gap: .5rem 1.25rem;
    padding: 0 0 1rem;
    margin-bottom: var(--gap);
    border-bottom: 1px solid var(--line-strong);
}

.hero--plain { align-items: center; }

.hero__middle { min-width: 0; }
.hero__sub { margin: .15rem 0 0; color: var(--muted); font-size: .93rem; }

.hero__links {
    display: flex;
    flex-wrap: wrap;
    gap: .9rem;
    margin: 0;
    font-size: .9rem;
}

.hero__links a { text-decoration: none; }
.hero__links a:hover { text-decoration: underline; }

/* --- blocks ------------------------------------------------------------- */

.block {
    margin: 0 0 var(--gap);
    padding: var(--pad);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

.block--quiet { background: transparent; border-color: transparent; box-shadow: none; padding-inline: 0; }

.block__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
}

.block__note {
    margin: .1rem 0 .9rem;
    color: var(--muted);
    font-size: .89rem;
}

.block__empty {
    padding: .8rem 0;
    color: var(--muted);
    font-size: .93rem;
}

.subhead {
    margin: 0 0 .5rem;
    font-size: .82rem;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--ink-soft);
}

.block > .subhead + * { margin-top: 0; }

/* --- flashes and notices ------------------------------------------------ */

.flashes { margin: 0 0 1rem; }

.flashes:empty { display: none; }

.flash {
    margin: 0 0 .5rem;
    padding: .6rem .85rem;
    border: 1px solid var(--line-strong);
    border-left: 3px solid var(--muted);
    border-radius: var(--radius-sm);
    background: var(--surface);
    font-size: .93rem;
    transition: opacity .24s ease, transform .24s ease;
}

/* A message that has said its piece goes rather than sits there. @see dismiss() in
   assets/js/app.js - the card is taken out of the page once it has faded, so the
   list of what has happened is not a list that grows all afternoon. */
.flash.is-going { opacity: 0; transform: translateY(-.2rem); }

@media (prefers-reduced-motion: reduce) {
    .flash { transition: none; }
}

.flash--success { border-left-color: var(--accent); background: var(--accent-soft); }
.flash--warn { border-left-color: var(--warn); background: var(--warn-soft); }
.flash--info { border-left-color: var(--info); background: var(--info-soft); }
.flash--error { border-left-color: var(--danger); background: var(--danger-soft); }

.notices { display: grid; gap: .5rem; margin: 0 0 var(--gap); }

.notice {
    display: flex;
    align-items: baseline;
    gap: .75rem;
    padding: .6rem .85rem;
    border: 1px solid var(--line);
    border-left: 3px solid var(--muted);
    border-radius: var(--radius-sm);
    background: var(--surface);
    font-size: .92rem;
}

.notice--guide { border-left-color: var(--info); }
.notice--nudge { border-left-color: var(--warn); }
.notice--flag { border-left-color: var(--danger); }

.notice__action { margin-left: auto; white-space: nowrap; }

.banner {
    display: flex;
    align-items: baseline;
    gap: .75rem;
    margin: 0 0 var(--gap);
    padding: .7rem .95rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: var(--surface);
    font-size: .93rem;
}

.banner--carry { border-left: 3px solid var(--warn); background: var(--warn-soft); }
.banner__list { margin: .25rem 0 0; padding-left: 1.1rem; }
.banner form { margin: 0; }

/* --- buttons ------------------------------------------------------------ */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
    padding: .45rem .95rem;
    border: 1px solid var(--accent);
    border-radius: var(--radius-sm);
    background: var(--accent);
    color: var(--on-accent);
    font: 600 .93rem/1.3 var(--sans);
    text-decoration: none;
    cursor: pointer;
}

.btn:hover { background: var(--accent-strong); border-color: var(--accent-strong); color: var(--on-accent); }

.btn--ghost {
    background: transparent;
    border-color: var(--line-strong);
    color: var(--ink-soft);
}

.btn--ghost:hover { background: var(--surface-sunk); border-color: var(--muted); color: var(--ink); }

.btn--small { padding: .28rem .6rem; font-size: .86rem; }
.btn--wide { width: 100%; }

.btn[disabled] { opacity: .55; cursor: default; }

/* --- dots, tags, checks, bars ------------------------------------------ */

.dot {
    display: inline-block;
    width: .62rem;
    height: .62rem;
    border-radius: 50%;
    background: var(--role, var(--muted));
    vertical-align: baseline;
}

.dot.is-on { box-shadow: 0 0 0 2px color-mix(in srgb, var(--role, var(--muted)) 30%, transparent); }

.tag {
    display: inline-block;
    padding: .1rem .5rem;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    background: var(--surface-sunk);
    color: var(--ink-soft);
    font-size: .76rem;
    font-weight: 650;
    letter-spacing: .02em;
    white-space: nowrap;
}

.tag--closed { background: var(--surface); color: var(--muted); }
.tag--urgent { border-color: color-mix(in srgb, var(--danger) 45%, var(--line)); background: var(--danger-soft); color: var(--danger-ink); }
.tag--high { border-color: color-mix(in srgb, var(--warn) 45%, var(--line)); background: var(--warn-soft); color: var(--warn-ink); }
.tag--medium { border-color: color-mix(in srgb, var(--accent) 45%, var(--line)); background: var(--accent-soft); color: var(--accent-ink); }
.tag--low { border-color: var(--line-strong); background: var(--surface-sunk); color: var(--muted); }

/* A kind is a hue, so the tag on a row is the same colour as the edge of the
   row - the grid and the front door agree without either of them being told. */
.tag--task { border-color: color-mix(in srgb, var(--kind-task) 40%, var(--line)); background: color-mix(in srgb, var(--kind-task) 16%, var(--surface)); color: var(--kind-task); }
.tag--appointment { border-color: color-mix(in srgb, var(--kind-appointment) 40%, var(--line)); background: color-mix(in srgb, var(--kind-appointment) 16%, var(--surface)); color: var(--kind-appointment); }
.tag--commitment { border-color: color-mix(in srgb, var(--kind-commitment) 40%, var(--line)); background: color-mix(in srgb, var(--kind-commitment) 16%, var(--surface)); color: var(--kind-commitment); }
.tag--block { border-color: color-mix(in srgb, var(--kind-block) 40%, var(--line)); background: color-mix(in srgb, var(--kind-block) 16%, var(--surface)); color: var(--kind-block); }
.tag--focus { border-color: color-mix(in srgb, var(--kind-focus) 45%, var(--line)); background: color-mix(in srgb, var(--kind-focus) 18%, var(--surface)); color: var(--kind-focus); }

.tag--repeat { border-color: color-mix(in srgb, var(--hot) 45%, var(--line)); background: var(--hot-soft); color: var(--hot-ink); }
.tag--star { border-color: color-mix(in srgb, var(--sun) 50%, var(--line)); background: var(--sun-soft); color: var(--sun); }

.check {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.05rem;
    height: 1.05rem;
    padding: 0;
    border: 1px solid var(--line-strong);
    border-radius: 4px;
    background: var(--surface);
    color: transparent;
    font-size: .75rem;
    line-height: 1;
}

.check.is-on,
.check.is-done,
.check[aria-pressed="true"] {
    border-color: var(--accent);
    background: var(--accent);
    color: var(--on-accent);
}

.bar {
    --fill: 0%;
    display: inline-block;
    width: 100%;
    min-width: 4rem;
    height: .45rem;
    border-radius: 999px;
    background: var(--line);
    overflow: hidden;
    vertical-align: middle;
}

.bar__fill {
    display: block;
    width: var(--fill);
    height: 100%;
    border-radius: inherit;
    background: var(--accent);
}

/* --- the load meter (how full the worst day is) ------------------------- */

.load {
    --load: var(--muted);
    padding: .85rem 1rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
}

.load--empty { --load: var(--muted); }
.load--healthy { --load: var(--accent); }
.load--busy { --load: var(--warn); }
.load--over { --load: var(--danger); }

.load__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
}

.load__title { font-weight: 650; }
.load__done { color: var(--muted); font-size: .85rem; }

.load__bar {
    display: block;
    height: .5rem;
    margin: .55rem 0 .7rem;
    border-radius: 999px;
    background: var(--line);
    overflow: hidden;
}

/* .load__bar carries --fill and has no child element, so the fill is drawn. */
.load__bar::after {
    content: "";
    display: block;
    width: var(--fill, 0%);
    height: 100%;
    border-radius: inherit;
    background: var(--load);
}

.load__note { margin: 0; color: var(--muted); font-size: .88rem; }

.load__days {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: .4rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.load__day { text-align: center; }
.load__label { display: block; color: var(--muted); font-size: .78rem; }

.load__day .load__bar { height: 2.6rem; margin: .25rem 0 0; }
.load__day .load__bar::after { width: 100%; height: var(--fill, 0%); }

.load__day.is-today .load__label { color: var(--hot-ink); font-weight: 700; }
.load__day.is-today .load__bar { box-shadow: 0 0 0 1px var(--hot); }

/* The hour's own account of itself: one line per page that asked for something,
   biggest first. The numbers come first and are set in the same figure width
   wherever they appear, because a column of costs is read by comparing them; the
   page's name is the code it is, in the mono face, so a route reads as a route
   rather than as a sentence. The line wraps rather than pushing the page wide,
   for the reason every other list here does: a list that cannot wrap is a page
   that slides sideways on a phone. */
.hourly {
    display: grid;
    gap: .2rem;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: .9rem;
}

.hourly__line {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .15rem .5rem;
    padding: .25rem 0;
    border-top: 1px solid var(--line);
}

.hourly__route { overflow-wrap: anywhere; }

.hourly__cost {
    min-width: 2.4em;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    text-align: right;
}

.hourly__said { color: var(--muted); font-size: .84rem; }

/* --- the four priority slots ------------------------------------------- */

.slots {
    display: grid;
    gap: .6rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.slots--extra { margin-top: 1rem; }

.slot {
    display: grid;
    grid-template-columns: 1.6rem minmax(0, 1fr) auto;
    align-items: start;
    gap: .6rem;
    padding: .6rem .7rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface);
}

.slot__no {
    padding-top: .15rem;
    color: var(--line-strong);
    font: 700 1.05rem/1 var(--sans);
    text-align: center;
}

.slot__body { min-width: 0; }
.slot__main { display: grid; gap: .3rem; }
.slot__title { font-weight: 600; }
.slot__meta { display: flex; flex-wrap: wrap; gap: .4rem .7rem; align-items: center; color: var(--muted); font-size: .85rem; }
.slot__hint { margin: .2rem 0 0; color: var(--muted); font-size: .82rem; }
.slot__side { display: flex; align-items: center; gap: .4rem; }
.slot__side form { margin: 0; }
.slot__mark { color: var(--accent); font-weight: 700; }

.slot.is-blank { border-style: dashed; background: transparent; }
.slot.is-blank .slot__no { color: var(--line-strong); }
.slot.is-unplaced { border-left: 3px solid var(--warn); }
.slot.is-done { background: var(--surface-sunk); }
.slot.is-done .slot__title { color: var(--muted); }

/* --- the week's head, at the top of the front side ---------------------- */

/* The week's own head: the range, the step either side of it, and the ＋. It is
   small on purpose - the page under it is the week, and a heading that took a
   third of a screen would be a heading you scroll past every time you look at
   what the week holds. */
.week-head {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem .8rem;
    align-items: center;
    justify-content: space-between;
    padding: .7rem .9rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: var(--shadow);
}

.week-head--quiet { box-shadow: none; background: var(--surface-sunk); }

.week-head__nav {
    display: flex;
    gap: .5rem;
    align-items: center;
    min-width: 0;
}

.week-head__middle { min-width: 0; }
.week-head__middle h1 { margin: 0; font-size: 1.15rem; line-height: 1.2; }

.week-head__sub {
    margin: .12rem 0 0;
    color: var(--muted);
    font-size: .8rem;
}

.week-head__step {
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--surface-sunk);
    color: var(--ink-soft);
    font-size: 1.1rem;
    text-decoration: none;
}

.week-head__step:hover { border-color: var(--accent); color: var(--accent-ink); }

.week-head__tools { display: flex; gap: .4rem; align-items: center; }

/* --- the ＋, and the sections it opens ---------------------------------- */

/* The press itself: round, and beside the week it writes into. It is fixed to
   the corner of the head rather than the corner of the screen - the bar already
   holds the one fixed control this app needs - so it is found where the week is
   read. 44px to hit, as everything a thumb reaches for is. */
.composer__plus {
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    border: 1px solid transparent;
    border-radius: 999px;
    background: var(--accent);
    color: var(--on-accent);
    font-size: 1.3rem;
    line-height: 1;
    box-shadow: var(--shadow-lift);
    text-decoration: none;
}

.composer__plus:hover { filter: brightness(1.08); }

/* The sections, folded, one press below the ＋.

   With a script this whole stack is out of the way: the pop-up is handed it when
   it opens and hands it back when it closes, so there is one copy of every field
   and it belongs to the page. With no script it is what the week page is: six
   folded sections, and the ＋ on the bar travels to them. @see setUpComposer() */
.composer {
    display: grid;
    gap: .5rem;
    padding: var(--pad);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: var(--shadow);
}

/* The pop-up is the way in once there is a script, so the stack itself stands
   down. What is hidden is the page's own copy of the sections; the same elements
   are moved into the pop-up when it opens, and there they are wanted. */
html.has-js .composer { display: none; }

.composer__head h2 { margin: 0; font-size: 1rem; }
.composer__note { margin: .2rem 0 0; color: var(--muted); font-size: .84rem; }

.composer__panels { display: grid; gap: .5rem; }

.composer__panel {
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface);
}

.composer__open {
    padding: .55rem .7rem;
    color: var(--ink);
    font-weight: 650;
    cursor: pointer;
}

.composer__panel > .sheet__form { padding: .2rem .7rem .7rem; }

/* In the pop-up the tab is the fold, so the summary has nothing left to do. */
.sheet--composer .composer__open { display: none; }
.sheet--composer .composer__panels { gap: 0; }
.sheet--composer .composer__panel { border: 0; border-radius: 0; }

.composer__sheet { display: grid; gap: 0; }

.composer__tabs {
    display: flex;
    gap: .3rem;
    overflow-x: auto;
    padding: 0 .8rem .6rem;
    border-bottom: 1px solid var(--line);
}

.composer__tab {
    flex: none;
    padding: .38rem .8rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--surface-sunk);
    color: var(--ink-soft);
    font: inherit;
    font-size: .84rem;
    font-weight: 650;
    cursor: pointer;
}

.composer__tab[aria-selected="true"],
.composer__tab.is-here {
    border-color: transparent;
    background: var(--accent);
    color: var(--on-accent);
}

.composer__slot { min-height: 0; }

.composer__subhead {
    margin: .2rem 0 .4rem;
    color: var(--muted);
    font-size: .78rem;
    letter-spacing: .08em;
    text-transform: uppercase;
}

.composer__rules { padding: .2rem .7rem .3rem; }

/* --- what comes back ---------------------------------------------------- */

.rules { display: grid; gap: .4rem; margin: 0; padding: 0; list-style: none; }

.rule {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem .6rem;
    align-items: baseline;
    justify-content: space-between;
    padding: .45rem .6rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface-sunk);
}

.rule.is-paused { opacity: .68; }
.rule__said { margin: 0; font-size: .88rem; }
.rule__when { color: var(--muted); }
.rule__tools { display: flex; gap: .6rem; align-items: baseline; }

/* A rule is changed where it is written down rather than in a dialog of its own: it
   is a handful of fields, and the sentence above them already says which rule they
   belong to. The word that opens it is drawn like the two tools beside it, so the
   row reads as three ways to change one rule; the form itself takes the whole row
   when it is open, because a field beside a sentence is half the width it needs.
   @see app/Views/week/show.php */
.rule__edit { flex: none; }

.rule__edit-open {
    color: var(--accent-ink);
    font-size: .89rem;
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
    cursor: pointer;
    list-style: none;
}

.rule__edit-open::-webkit-details-marker { display: none; }
.rule__edit-open:hover { color: var(--accent); }

.rule:has(.rule__edit[open]) .rule__tools { flex: 1 1 100%; flex-wrap: wrap; }
.rule__edit[open] { flex: 1 1 100%; }

.rule__form {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: .5rem .7rem;
    margin-top: .5rem;
    padding: .6rem;
    border: 1px dashed var(--line-strong);
    border-radius: var(--radius-sm);
}

.block__foot {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem .7rem;
    align-items: baseline;
    margin: .6rem 0 0;
    color: var(--muted);
    font-size: .84rem;
}

/* --- roles -------------------------------------------------------------- */

.roles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
    gap: .6rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.role {
    --role: var(--muted);
    padding: .65rem .8rem;
    border: 1px solid var(--line);
    border-left: 3px solid var(--role);
    border-radius: var(--radius-sm);
    background: var(--surface);
}

.role__form { margin: 0; }
.role__name { display: flex; align-items: center; gap: .45rem; font-weight: 650; }
.role__goal { margin: .2rem 0 .5rem; color: var(--muted); font-size: .86rem; }

.role__focus {
    display: grid;
    gap: .35rem;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: .88rem;
}

.role__focus label { display: flex; align-items: baseline; gap: .45rem; }

.role__done {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    margin-top: .5rem;
    padding-top: .45rem;
    border-top: 1px solid var(--line);
    font-size: .85rem;
    color: var(--muted);
}

.role.is-achieved { background: var(--accent-soft); border-left-color: var(--accent); }
.role.is-achieved .role__name { color: var(--accent-ink); }

/* --- the time grid ----------------------------------------------------- */

#grid { padding-bottom: 1rem; }

.grid {
    --hour: 58px;
    --span: 14;

    /* `position: relative` is load-bearing: this box scrolls horizontally, and a
       scroll container only clips the absolutely positioned descendants it is the
       containing block for. Without it the screen-reader labels inside each
       `summary` anchor above the grid and drag the whole page sideways. */
    position: relative;
    display: grid;
    grid-auto-flow: column;
    grid-template-columns: 2.9rem repeat(7, minmax(6.5rem, 1fr));
    overflow-x: auto;
    overscroll-behavior-x: contain;
}

.grid__ruler {
    position: relative;
    height: calc(var(--hour) * var(--span));
    border-right: 1px solid var(--line);
}

.grid__tick {
    position: absolute;
    right: .35rem;
    transform: translateY(-50%);
    color: var(--muted);
    font-size: .72rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.day {
    min-width: 0;
    border-right: 1px solid var(--line);
}

.day:last-child { border-right: 0; }

.day__head {
    display: flex;
    align-items: baseline;
    gap: .35rem;
    padding: .3rem .5rem .4rem;
    border-bottom: 1px solid var(--line);
}

.day__name { font-weight: 650; font-size: .88rem; }
.day__date { color: var(--muted); font-size: .78rem; }
.day__load { margin-left: auto; color: var(--muted); font-size: .74rem; font-variant-numeric: tabular-nums; }
.day__empty { padding: .5rem; color: var(--muted); font-size: .82rem; }

.lane { display: block; }

.lane__label {
    padding: .2rem .5rem 0;
    color: var(--muted);
    font-size: .72rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.lane__track {
    position: relative;
    height: calc(var(--hour) * var(--span));
    background-image: repeating-linear-gradient(
        to bottom,
        var(--line) 0,
        var(--line) 1px,
        transparent 1px,
        transparent var(--hour)
    );
    background-position: 0 0;
}

/* An entry is placed by inline top/height percentages of the day window. */
.entry {
    position: absolute;
    left: .18rem;
    right: .18rem;
    display: flex;
    flex-direction: column;
    gap: .1rem;
    padding: .2rem .35rem;
    overflow: hidden;
    border: 1px solid var(--line-strong);
    border-left: 3px solid var(--entry, var(--muted));
    border-radius: 5px;
    background: var(--surface);
    box-shadow: var(--shadow);
    font-size: .82rem;
    line-height: 1.25;
}

.entry--task, .plan__item--task, .band--task { --entry: var(--kind-task); }
.entry--appointment, .plan__item--appointment, .band--appointment { --entry: var(--kind-appointment); }
.entry--commitment, .plan__item--commitment, .band--commitment { --entry: var(--kind-commitment); }
.entry--block, .plan__item--block, .band--block { --entry: var(--kind-block); }
.entry--focus, .plan__item--focus, .band--focus { --entry: var(--kind-focus); }

.entry__head {
    display: flex;
    align-items: baseline;
    gap: .3rem;
    min-width: 0;
}

.entry__tick { flex: none; }
.entry__title { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.entry__time { margin-left: auto; color: var(--muted); font-size: .74rem; font-variant-numeric: tabular-nums; white-space: nowrap; }
.entry.has-mark { box-shadow: 0 0 0 1px var(--warn) inset; }

.entry.is-done {
    background: var(--surface-sunk);
    border-left-color: var(--line-strong);
}

.entry.is-done .entry__title {
    color: var(--muted);
    text-decoration: line-through;
    text-decoration-thickness: 1px;
}

.entry__more {
    display: none;
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    z-index: 12;
    gap: .35rem;
    margin-top: .15rem;
    padding: .55rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    box-shadow: var(--shadow-lift);
}

.entry__more input[type="text"],
.entry__more input[type="time"] { width: 100%; }

.entry:focus-within .entry__more,
.entry__more:focus-within { display: grid; }

/* The hidden helper form exists only to associate the tick button with
   entry.toggle; it must not take part in layout. */
.entry__tick-form { display: contents; }

.entry__kill {
    position: absolute;
    right: .12rem;
    z-index: 4;
    margin: 0;
    line-height: 1;
}

.entry__kill .linkish { text-decoration: none; font-size: .95rem; opacity: .45; }
.entry:hover .entry__kill .linkish { opacity: 1; }
.entry__kill:hover .linkish { opacity: 1; }

.day__add {
    border-top: 1px dashed var(--line-strong);
}

.day__add > summary {
    padding: .35rem .5rem;
    color: var(--muted);
    font-size: .82rem;
    cursor: pointer;
}

.day__add > summary:hover { color: var(--accent-ink); }
.day__add[open] > summary { color: var(--ink-soft); }

.day__form {
    display: grid;
    gap: .5rem;
    padding: .6rem;
    background: var(--surface);
    border-top: 1px solid var(--line);
}

.day__form .btn { justify-self: start; }

/* The blocks that belong to a part of the day rather than to an hour: before
   work, after work, all day. They are not on the clock - there is no hour to
   put them at without inventing one - so they are listed under the lanes and
   above the day's own add button, in the order the day happens. */
.offclock {
    display: grid;
    gap: .4rem;
    padding: .5rem;
    border-top: 1px dashed var(--line-strong);
    background: var(--surface-sunk);
}

.offclock__lane {
    margin: 0;
    color: var(--muted);
    font-size: .72rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.band {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: .1rem;
    padding: .3rem .4rem;
    border: 1px solid var(--line);
    border-left: 3px solid var(--entry, var(--line-strong));
    border-radius: 5px;
    background: var(--surface);
    font-size: .82rem;
    line-height: 1.25;
}

.band--repeat {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .35rem;
    border-style: dashed;
    background: transparent;
}

.band.is-done { background: var(--surface-sunk); }

.band.is-done .band__title {
    color: var(--muted);
    text-decoration: line-through;
    text-decoration-thickness: 1px;
}

.band__form { display: flex; flex-direction: column; gap: .1rem; }

.band__head {
    display: flex;
    align-items: baseline;
    gap: .3rem;
    min-width: 0;
}

.band__title {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.band__when {
    flex: none;
    color: var(--muted);
    font-size: .74rem;
    white-space: nowrap;
}

/* The same editor the lanes open, hung under the card instead of under a block
   on the track. */
.band:focus-within .entry__more,
.band .entry__more:focus-within { display: grid; }

/* --- sharpen the saw ---------------------------------------------------- */

.sharpen { margin: 0 0 .8rem; }
.sharpen__name { font-weight: 600; }

/* Seven day columns and a tally do not fit a phone, and squeezing them would
   shrink the tick targets this block exists to make easy. Scroll the table
   instead, on its own, so the heading and the add form stay put.

   `position: relative` is load-bearing for the same reason as on .grid: a scroll
   container only clips the absolutely positioned descendants it is the
   containing block for, and each button carries an .sr label. */
.sharpen__scroll {
    position: relative;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: thin;
}

.sharpen__table {
    width: 100%;
    min-width: 22rem;
    border-collapse: collapse;
    font-size: .88rem;
}

.sharpen__table th,
.sharpen__table td {
    padding: .3rem .25rem;
    border-bottom: 1px solid var(--line);
    text-align: left;
}

.sharpen__table thead th {
    color: var(--muted);
    font-size: .74rem;
    font-weight: 650;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.sharpen__what { width: 30%; font-weight: 600; }
.sharpen__day { width: 2.6rem; text-align: center; }
.sharpen__table td { text-align: center; }
.sharpen__table span.dot { background: var(--line-strong); }
.sharpen__table span.dot.is-on { background: var(--accent); }

/* The tick target fills the cell so a thumb can find it without aiming; the
   visible mark stays small and centred inside it. Twenty-eight of these get
   tapped in a week - a 10px target would mean re-opening the page to fix
   mistakes, which is exactly the wrong choice to make easy. */
.sharpen__table button.dot {
    display: grid;
    place-items: center;
    width: 100%;
    min-height: 2.2rem;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
}

.sharpen__table button.dot > [aria-hidden='true'] {
    width: .62rem;
    height: .62rem;
    border-radius: 50%;
    background: var(--line-strong);
    transition: background .12s ease, box-shadow .12s ease;
}

.sharpen__table button.dot:hover > [aria-hidden='true'],
.sharpen__table button.dot:focus-visible > [aria-hidden='true'] { background: var(--muted); }

.sharpen__table button.dot.is-on > [aria-hidden='true'] {
    background: var(--accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 30%, transparent);
}
.sharpen__tally { width: 3.4rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.sharpen__tally.is-reached { color: var(--accent-ink); font-weight: 700; }
.sharpen__of { color: var(--muted); font-size: .8rem; }
.sharpen__empty { padding: .6rem 0; color: var(--muted); font-size: .9rem; }

/* --- commitments -------------------------------------------------------- */

.commitments {
    display: grid;
    gap: .45rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.commitment {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .45rem .6rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface);
}

.commitment__toggle { margin: 0; }
.commitment__form { display: flex; align-items: center; gap: .6rem; margin: 0; }
.commitment__title { font-weight: 600; }
.commitment__title input { width: 100%; }
.commitment__toggle { flex: none; }
.commitment__rec { display: inline-flex; align-items: center; gap: .35rem; color: var(--muted); font-size: .85rem; }
.commitment__kill { flex: none; margin-left: auto; }
.commitment.is-done .commitment__title { color: var(--muted); text-decoration: line-through; }

/* --- reflection: the nights -------------------------------------------- */

.nights {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: .6rem;
}

.night {
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface);
}

.night > summary {
    display: flex;
    align-items: baseline;
    gap: .5rem;
    padding: .5rem .7rem;
    cursor: pointer;
    font-size: .9rem;
}

.night__day { font-weight: 650; }
.night__date { color: var(--muted); font-size: .8rem; }
.night__mood { margin-left: auto; color: var(--muted); font-size: .8rem; }
.night[open] > summary { border-bottom: 1px solid var(--line); }

.night__form { display: grid; gap: .5rem; padding: .7rem; }
.night__foot { display: flex; align-items: center; justify-content: space-between; gap: .6rem; }
.night__read { color: var(--muted); font-size: .86rem; }
.night__read textarea { width: 100%; min-height: 3.4rem; }

/* --- tables (insights) -------------------------------------------------- */

.table {
    width: 100%;
    border-collapse: collapse;
    font-size: .9rem;
}

.table th,
.table td {
    padding: .45rem .5rem;
    border-bottom: 1px solid var(--line);
    text-align: left;
    vertical-align: middle;
}

.table thead th {
    color: var(--muted);
    font-size: .74rem;
    font-weight: 650;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.table tbody th { font-weight: 600; }
.table td { font-variant-numeric: tabular-nums; }
.table__bar { display: flex; align-items: center; gap: .5rem; min-width: 8rem; }
.table__bar .bar { flex: 1; }

/* --- quotes, facts, stats, breakdowns, trends -------------------------- */

.quote {
    padding: .1rem 0 .1rem .8rem;
    border-left: 3px solid var(--line-strong);
    color: var(--ink-soft);
    font-style: italic;
}

.facts {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: .35rem .9rem;
    margin: 0;
    font-size: .9rem;
}

.facts dt { color: var(--muted); }
.facts dd { margin: 0; font-weight: 500; }

.insights__cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: .7rem;
    margin: 0 0 var(--gap);
}

.stat {
    display: grid;
    gap: .1rem;
    padding: .85rem 1rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
}

.stat__label {
    color: var(--muted);
    font-size: .76rem;
    font-weight: 650;
    letter-spacing: .05em;
    text-transform: uppercase;
}

.stat__value { font-size: 1.75rem; font-weight: 680; letter-spacing: -.02em; line-height: 1.1; }
.stat__note { color: var(--muted); font-size: .82rem; }

.breakdown {
    display: grid;
    gap: .5rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.breakdown__row {
    display: grid;
    grid-template-columns: 7rem minmax(0, 1fr) auto;
    align-items: center;
    gap: .7rem;
    font-size: .9rem;
}

.breakdown__name { font-weight: 550; }
.breakdown__count { font-variant-numeric: tabular-nums; white-space: nowrap; }

.trend {
    display: flex;
    align-items: flex-end;
    gap: .4rem;
    margin: 0;
    padding: 0;
    list-style: none;
    overflow-x: auto;
}

.trend__bar {
    display: block;
    width: 1.5rem;
    min-height: 2px;
    height: var(--fill, 0%);
    border-radius: 3px 3px 0 0;
    background: var(--accent);
}

.trend__label { color: var(--muted); font-size: .72rem; white-space: nowrap; }
.trend__week { color: var(--muted); font-size: .72rem; }

/* --- history ------------------------------------------------------------ */

.history { margin: 0 0 var(--gap); }
.history__list { display: grid; gap: .4rem; margin: 0; padding: 0; list-style: none; }

.history__row {
    display: grid;
    grid-template-columns: 9rem minmax(0, 1fr) auto;
    align-items: center;
    gap: .7rem;
    padding: .5rem .7rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface);
    font-size: .9rem;
}

.history__when { color: var(--muted); font-size: .84rem; }
.history__range { font-weight: 600; }
.history__rock { color: var(--ink-soft); }
.history__counts { color: var(--muted); font-variant-numeric: tabular-nums; }
.history__done { color: var(--accent-ink); font-weight: 600; }
.history__row.is-now { border-color: var(--accent); background: var(--accent-soft); }

/* --- settings ----------------------------------------------------------- */

.settings-list {
    margin: 0 0 .8rem;
    padding: 0;
    list-style: none;
}

.settings-row {
    display: flex;
    align-items: center;
    gap: .7rem;
    padding: .5rem 0;
    border-bottom: 1px solid var(--line);
}

.settings-row:last-child { border-bottom: 0; }
.settings-row__name { font-weight: 550; }
.settings-row__form { display: flex; align-items: center; gap: .5rem; margin: 0 0 0 auto; }
.settings-row .dot { --role: var(--muted); }
.settings-group {
    margin: 1rem 0 .3rem;
    color: var(--muted);
    font-size: .78rem;
    font-weight: 700;
    letter-spacing: .05em;
    text-transform: uppercase;
}

/* --- review and rollover ------------------------------------------------ */

.rollover { margin: 0; }

.rollover__hint {
    color: var(--muted);
    font-size: .89rem;
}

.rollover__week {
    margin: 0 0 1rem;
    padding: .7rem .8rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface);
}

.rollover__list { display: grid; gap: .35rem; margin: .4rem 0 0; padding: 0; list-style: none; }

.rollover__row {
    display: flex;
    align-items: baseline;
    gap: .5rem;
    font-size: .91rem;
}

.rollover__title { font-weight: 550; }

.rollover__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.rollover__buttons { display: flex; gap: .5rem; }

/* --- auth --------------------------------------------------------------- */

.auth {
    width: 100%;
    max-width: 24rem;
    margin: 0 auto;
}

.auth--wide { max-width: 34rem; }

.auth__card {
    padding: 1.6rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: var(--shadow);
}

.auth__title { margin-bottom: .25rem; font-size: 1.35rem; }
.auth__sub { margin-bottom: 1.1rem; color: var(--muted); font-size: .92rem; }

.auth__form { display: grid; gap: .75rem; }
.auth__form .btn { margin-top: .2rem; }

.auth__error {
    margin: 0 0 .9rem;
    padding: .55rem .7rem;
    border: 1px solid color-mix(in srgb, var(--danger) 45%, var(--line));
    border-left: 3px solid var(--danger);
    border-radius: var(--radius-sm);
    background: var(--danger-soft);
    color: var(--danger-ink);
    font-size: .9rem;
}

.auth__note {
    margin-top: 1.1rem;
    padding-top: .9rem;
    border-top: 1px solid var(--line);
    color: var(--muted);
    font-size: .86rem;
}

.auth__foot {
    margin-top: 1rem;
    color: var(--muted);
    font-size: .9rem;
    text-align: center;
}

.auth__foot .btn { margin-bottom: .5rem; }
.auth__foot .linkish + .linkish { margin-left: 1rem; }

.codes {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
    gap: .35rem;
    margin: 0 0 1rem;
    padding: 0;
    list-style: none;
}

.codes__item {
    padding: .4rem .55rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-sunk);
    font: 500 .95rem/1.4 var(--mono);
    letter-spacing: .04em;
    text-align: center;
}

.codes__actions { display: flex; gap: .5rem; margin-bottom: 1rem; }

/* --- forms -------------------------------------------------------------- */

.field {
    display: grid;
    gap: .2rem;
    min-width: 0;
    font-size: .88rem;
}

.field > span {
    color: var(--muted);
    font-size: .78rem;
    font-weight: 650;
    letter-spacing: .03em;
    text-transform: uppercase;
}

.field--grow { flex: 1 1 12rem; }
.field--wide { flex: 1 1 100%; }
.field--tight { flex: 0 1 9rem; }
.field--tiny { flex: 0 1 5rem; }
.field--name { flex: 1 1 10rem; }
.field--goal { flex: 1 1 16rem; }
.field--swatch { flex: 0 0 5rem; }

.field__note { color: var(--muted); font-size: .8rem; }

/* --- the days a task can be put on -------------------------------------- */

/* Seven buttons for the week on screen, and one date box for anywhere else. A
   task on several days is one task, so the buttons are a set rather than a
   list: picking a second day does not replace the first. */
.days {
    border: 0;
    padding: 0;
    margin: 0;
}

.days > legend {
    padding: 0;
    color: var(--muted);
    font-size: .78rem;
    font-weight: 650;
}

.days__row {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
    margin: .25rem 0 .45rem;
}

.days__chip {
    position: relative;
    display: block;
}

.days__chip input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}

.days__face {
    display: grid;
    justify-items: center;
    min-width: 2.6rem;
    padding: .3rem .4rem .25rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-sunk);
    color: var(--ink-soft);
    font-size: .74rem;
    font-weight: 700;
    line-height: 1.1;
    text-align: center;
}

.days__num { font-size: .68rem; font-weight: 500; color: var(--muted); }

/* The tick is drawn on the face rather than on the box, so the button and its
   state are one thing - and picked days are the accent, which is what "here"
   means everywhere else in the app. */
.days__chip input:checked + .days__face {
    border-color: var(--accent);
    background: var(--accent-soft);
    color: var(--accent-ink);
}

.days__chip input:checked + .days__face .days__num { color: var(--accent-ink); }

.days__chip input:focus-visible + .days__face {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

.days__other { margin-bottom: .35rem; }

/* The rest of the calendar: a month of boxes, one per day, with the arrows that
   step through it. It takes several days the same way the week's chips do, so it
   is drawn as the same kind of thing - a set of days rather than a string of
   dates - and it borrows their face so a day picked here looks like a day picked
   above it. Seven columns, because that is what a month is. */
.calendar {
    border: 0;
    padding: 0;
    margin: .25rem 0 .5rem;
}

.calendar__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    margin-bottom: .3rem;
}

.calendar__month {
    font-size: .78rem;
    font-weight: 650;
    color: var(--ink-soft);
}

.calendar__step {
    display: grid;
    place-items: center;
    width: 1.7rem;
    height: 1.7rem;
    padding: 0;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-sunk);
    color: var(--ink-soft);
    font-size: .95rem;
    line-height: 1;
    cursor: pointer;
}

.calendar__step:hover { color: var(--ink); }

.calendar__step:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

/* Two grids rather than one, because the weekday row never changes and the days
   below it are rebuilt when the month does. Both are seven columns wide, which is
   what keeps them lined up with each other. */
.calendar__weekdays,
.calendar__days {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: .2rem;
}

.calendar__weekday {
    padding-bottom: .15rem;
    color: var(--muted);
    font-size: .64rem;
    font-weight: 700;
    text-align: center;
    text-transform: uppercase;
    letter-spacing: .02em;
}

.calendar__day {
    position: relative;
    display: grid;
    place-items: center;
    min-height: 1.9rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-sunk);
    color: var(--ink-soft);
    font-size: .72rem;
    font-weight: 600;
    cursor: pointer;
}

/* A day that belongs to the month either side of this one holds the shape of the
   grid and is not offered: it is the calendar's edge, not a day of the answer. */
.calendar__day.is-blank {
    border-color: transparent;
    background: none;
    cursor: default;
}

.calendar__day input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}

.calendar__num { pointer-events: none; }

.calendar__day:has(input:checked) {
    border-color: var(--accent);
    background: var(--accent-soft);
    color: var(--accent-ink);
}

.calendar__day:has(input:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

/* The calendar is one more way of naming a day, so its boxes go with the chips
   when the week itself is not on the page to be pressed. */
.calendar__held { display: none; }

/* The hidden attribute has to win. A field is a grid, and a display declaration
   beats the attribute, so the repeating-task form would reshape itself in the markup
   and not on the screen. */
.field[hidden] { display: none; }

.field-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: .75rem;
    margin: 0 0 .6rem;
}

.field-row .btn { margin-bottom: 1px; }

/* The quick task's "how long" lights. They are the traffic light the form is
   asked with: the dot is the colour and the fill is the answer that is standing.
   A light writes the end time and nothing else, so the three are only ever one
   answer between them - which is what the pressed state is. */
.lights {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .35rem;
}

.light {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .34rem .7rem;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    background: var(--surface-sunk);
    color: var(--ink-soft);
    font: 600 .85rem/1.3 var(--sans);
    cursor: pointer;
}

.light::before {
    content: "";
    width: .6rem;
    height: .6rem;
    border-radius: 50%;
    background: var(--light, var(--muted));
}

.light--go { --light: var(--kind-commitment); }
.light--warn { --light: var(--sun); }
.light--stop { --light: var(--hot); }

.light:hover { border-color: var(--muted); color: var(--ink); }

.light[aria-pressed="true"] {
    border-color: color-mix(in srgb, var(--light, var(--accent)) 55%, var(--line));
    background: color-mix(in srgb, var(--light, var(--accent)) 18%, var(--surface));
    color: var(--ink);
}

/* How much a note matters, as four buttons. The radio is the answer and the pill
   is only how it is drawn, so the chosen one wears the colour its tag will wear
   on the note - and with the script off, or in a browser that does not know
   :checked, all four are still there to be tapped. */
.rank {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem;
}

.rank__pick { position: relative; display: inline-flex; }

.rank__pick > span {
    display: inline-flex;
    align-items: center;
    padding: .3rem .66rem;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    background: var(--surface-sunk);
    color: var(--ink-soft);
    font-size: .85rem;
    font-weight: 650;
    cursor: pointer;
}

.rank__pick:hover > span { border-color: var(--muted); color: var(--ink); }

/* Out of sight but still the thing that is focused, so the arrow keys move the
   choice the way a radio group should. Named with its type because the rule for
   radios above says `width: auto`, and it is later in the file than this. */
.rank__pick > input[type="radio"] {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    opacity: 0;
}

.rank__pick > input[type="radio"]:focus-visible + span { outline: 2px solid var(--accent); outline-offset: 2px; }

.rank__pick > input[type="radio"]:checked + span {
    border-color: color-mix(in srgb, var(--rank, var(--muted)) 55%, var(--line));
    background: color-mix(in srgb, var(--rank, var(--muted)) 18%, var(--surface));
    color: var(--ink);
}

.rank__pick--urgent { --rank: var(--danger); }
.rank__pick--high { --rank: var(--warn); }
.rank__pick--medium { --rank: var(--accent); }
.rank__pick--low { --rank: var(--muted); }

input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="time"],
input[type="color"],
input[type="search"],
select,
textarea {
    width: 100%;
    min-width: 0;
    padding: .38rem .5rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--ink);
    font: inherit;
    font-size: 1rem;
}

/* A phone zooms a page in when the field it is about to type into draws its words
   smaller than 16px - and it does not zoom back out when the typing is done, so
   the page you are left holding is not the page you started on. Nothing here is
   ever drawn smaller than the document's own size, which is 16px unless the reader
   has asked for the larger words.

   The rule above names the controls this app writes. This one is the floor for
   every control there is, including the ones a browser draws for itself - a date
   box is drawn at a size of its own choosing, and that size is under 16px. Only
   the size of the words is set here: what a control looks like is still the rule
   above, and a control it does not name keeps its own shape. */
input,
select,
textarea {
    font-size: 1rem;
}

textarea { min-height: 4.5rem; resize: vertical; }

input[type="color"] { padding: .15rem; height: 2.15rem; cursor: pointer; }
input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); width: auto; }

input:focus, select:focus, textarea:focus {
    border-color: var(--accent);
    outline: 2px solid color-mix(in srgb, var(--accent) 25%, transparent);
    outline-offset: 0;
}

/* A hint is dimmer than a value, but it still has to be read on a dark field. */
input::placeholder, textarea::placeholder { color: var(--muted); }

/* --- the adders (progressively-disclosed forms) ------------------------ */

.adder { margin-top: .8rem; }

.adder__open {
    display: inline-block;
    padding: .3rem .1rem;
    color: var(--accent-ink);
    font-size: .89rem;
    cursor: pointer;
    list-style: none;
}

.adder__open::-webkit-details-marker { display: none; }
.adder__open::before { content: "+ "; font-weight: 700; }
.adder[open] > .adder__open::before { content: "– "; }
.adder__open:hover { text-decoration: underline; }

.adder__form {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: .7rem;
    margin-top: .5rem;
    padding: .8rem;
    border: 1px dashed var(--line-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-sunk);
}

.adder__form .btn { margin-bottom: 1px; }
.adder__note { margin: .4rem 0 0; color: var(--muted); font-size: .84rem; }

.adder--inline .adder__form { flex-wrap: nowrap; overflow-x: auto; }
.adder--slim .adder__form { padding: .6rem; gap: .5rem; }
.adder--slim { margin: .2rem 0 1rem; }

/* --- the foot of the week, and the names beside you --------------------- */

.week__foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem 1rem;
    margin: 0 0 var(--gap);
    padding-top: .9rem;
    border-top: 1px solid var(--line);
}

.week__members { margin: 0 0 var(--gap); font-size: .88rem; }

/* --- turning the page ---------------------------------------------------- */

/* The wheel swaps one section's body for another's, and the incoming body
   slides a little in the direction of travel. The wheel is not part of this: it
   is drawn by the shell, outside the page, so nothing here can carry it off. */
.week.is-entering > * {
    animation: turn-in .26s ease-out both;
}

@keyframes turn-in {
    from {
        opacity: .25;
        transform: translateX(var(--turn-shift, 0));
    }
}

/* --- marks on the grid -------------------------------------------------- */

/* A flagged block says why in its own corner, so a clash is never a mystery. */
.chip {
    align-self: flex-start;
    padding: 0 .3rem;
    border: 1px solid var(--line-strong);
    border-radius: 3px;
    background: var(--surface-sunk);
    color: var(--ink-soft);
    font-size: .68rem;
    font-weight: 650;
    letter-spacing: .03em;
    text-transform: uppercase;
    white-space: nowrap;
}

.chip--overlap { border-color: color-mix(in srgb, var(--danger) 45%, var(--line)); background: var(--danger-soft); color: var(--danger-ink); }
.chip--invalid-time { border-color: color-mix(in srgb, var(--warn) 45%, var(--line)); background: var(--warn-soft); color: var(--warn-ink); }
.chip--outside-grid { border-color: var(--line-strong); background: var(--surface-sunk); color: var(--muted); }

/* Today is the warm colour everywhere, so the one day that is happening now is
   never mistaken for a selected one. */
.day.is-today { background: color-mix(in srgb, var(--hot-soft) 65%, transparent); }
.day.is-today .day__name { color: var(--hot-ink); }

/* Repeating rules drawn on the grid: visible where they happen, changed on their
   own page - hence the dashed edge and no tick. */
.entry--repeat {
    --entry: var(--hot-ink);
    border-style: dashed;
    background: var(--accent-soft);
    box-shadow: none;
}

.entry--repeat.is-done .entry__title { text-decoration: none; }

.entry__tag {
    color: var(--muted);
    font-size: .68rem;
    font-weight: 650;
    letter-spacing: .04em;
    text-transform: uppercase;
}

/* --- repeating tasks and work ------------------------------------------ */

.recurring__list {
    display: grid;
    gap: .5rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.recurring {
    padding: .6rem .75rem;
    border: 1px solid var(--line);
    border-left: 3px solid var(--accent);
    border-radius: var(--radius-sm);
    background: var(--surface);
}

.recurring.is-paused { border-left-color: var(--line-strong); background: var(--surface-sunk); }
.recurring.is-paused .recurring__title { color: var(--muted); }

.recurring__head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: .1rem .8rem;
}

.recurring__title { margin: 0; font-size: .98rem; font-weight: 650; }
.recurring__when { margin: 0; color: var(--muted); font-size: .85rem; }

.recurring__dates {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem .9rem;
    margin: .4rem 0 0;
    padding: 0;
    list-style: none;
}

.recurring__dates li { display: flex; align-items: center; gap: .3rem; }

.recurring__date {
    color: var(--ink-soft);
    font-size: .85rem;
    font-variant-numeric: tabular-nums;
}

.recurring__dates form { margin: 0; }

.recurring__none,
.recurring__notes { margin: .4rem 0 0; color: var(--muted); font-size: .86rem; }

.recurring__tools {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .2rem .9rem;
    margin-top: .35rem;
}

.recurring__tools form { margin: 0; }

.recurring__edit { margin-top: .35rem; }

.recurring__edit > summary {
    color: var(--muted);
    font-size: .85rem;
    cursor: pointer;
}

.recurring__edit > summary:hover { color: var(--accent-ink); }
.recurring__edit[open] > summary { color: var(--ink-soft); }
.recurring__edit .adder__form { margin-top: .5rem; }

/* --- tomorrow, ready to send ------------------------------------------- */

.brief { display: grid; gap: .6rem; }

.brief__day { margin: 0; font-size: 1rem; }

.brief__when {
    margin-right: .4rem;
    color: var(--accent-ink);
    font-size: .78rem;
    font-weight: 700;
    letter-spacing: .09em;
    text-transform: uppercase;
}

.brief__space { color: var(--muted); font-weight: 400; }

.brief__text {
    margin: 0;
    padding: .9rem 1rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface-sunk);
    font: 400 .92rem/1.55 var(--mono);
    white-space: pre-wrap;
    overflow-x: auto;
}

.brief__tools { display: flex; flex-wrap: wrap; gap: .5rem; }

.brief__status {
    min-height: 1.4em;
    margin: 0;
    color: var(--muted);
    font-size: .86rem;
}

/* --- the pop-up input screens ------------------------------------------ */

.sheets { margin: 0; }

.sheet {
    width: min(36rem, calc(100vw - 1.6rem));
    max-height: min(88vh, 46rem);
    padding: 0;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
    box-shadow: var(--shadow-lift);
    overflow: auto;
}

.sheet::backdrop { background: var(--scrim); }

.sheet__form { display: grid; gap: .8rem; padding: var(--pad); }

.sheet__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    padding-bottom: .5rem;
    border-bottom: 1px solid var(--line);
}

.sheet__title { margin: 0; font-size: 1.05rem; }

.sheet__close {
    padding: 0 .3rem;
    border: 0;
    background: none;
    color: var(--muted);
    font-size: 1.2rem;
    line-height: 1;
    cursor: pointer;
}

.sheet__close:hover { color: var(--danger); }

.sheet__fields {
    display: grid;
    gap: .55rem;
}

.sheet__fields .field { width: 100%; }

.sheet__fields .btn { justify-self: start; }

.sheet__foot {
    display: flex;
    justify-content: flex-end;
    padding-top: .6rem;
    border-top: 1px solid var(--line);
}

/* --- the front door: today ---------------------------------------------- */

/* The page you land on answers one question - what am I doing today - so it is
   built from a single column of whole things: who and where (hero), the day
   itself, and then the rest of the week underneath it. */
.home { display: grid; gap: var(--gap); }

.home__hero {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: .6rem 1rem;
    padding: 1.15rem 1.3rem;
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: linear-gradient(115deg, var(--accent-soft) 0%, var(--surface) 45%, var(--hot-soft) 130%), var(--surface);
    box-shadow: var(--shadow);
}

.home__hero-text {
    flex: 1 1 14rem;
    min-width: 0;
}

.home__hello {
    margin: 0 0 .1rem;
    color: var(--accent-ink);
    font-size: .82rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
}

.home__hero h1 { margin: 0 0 .3rem; font-size: 1.8rem; line-height: 1.15; }

.home__note { margin: 0 0 .9rem; color: var(--muted); font-size: .93rem; }

/* --- the tick switch ----------------------------------------------------- */

/* One switch, in the corner of the hero, and no words around it: whether a task
   wears a tick to press. It sits at the foot of the hero's right-hand side,
   which is the corner a thumb reaches without crossing the page - and it is the
   state that is read, not a caption, so the two poses are told apart by colour
   alone at arm's length: the off pose is red and the on pose is green, and the
   thumb, which is the part that moves, is a raised white button rather than a
   flat dot, because the thing a hand expects to move should look like a thing
   that can be moved. */
.task-ticks {
    display: flex;
    flex: 0 0 auto;
    align-items: flex-end;
    justify-content: flex-end;
    align-self: flex-end;
    margin-inline-start: auto;
}

.switch {
    --switch-w: 2.35rem;
    --switch-h: 1.25rem;
    --switch-inset: .12rem;
    position: relative;
    display: block;
    flex: 0 0 auto;
    width: var(--switch-w);
    height: var(--switch-h);
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: var(--switch-off);
    /* The track is a groove rather than a stripe: the inset shadow is what gives
       the recess the thumb sits in. */
    box-shadow:
        inset 0 .12rem .22rem rgb(0 0 0 / 45%),
        inset 0 -.05rem 0 rgb(255 255 255 / 18%),
        0 1px 0 rgb(255 255 255 / 8%);
    cursor: pointer;
    transition: background-color .26s ease;
}

/* A raised button, not a disc: a soft radial highlight, a lit top edge, and a
   drop shadow under it that says it stands off the track. */
.switch__thumb {
    position: absolute;
    top: 50%;
    left: var(--switch-inset);
    width: calc(var(--switch-h) - (var(--switch-inset) * 2));
    height: calc(var(--switch-h) - (var(--switch-inset) * 2));
    border-radius: 50%;
    background: var(--switch-thumb);
    box-shadow:
        0 .09rem .16rem rgb(0 0 0 / 45%),
        inset 0 1px 0 rgb(255 255 255 / 85%);
    transform: translateY(-50%) translateX(0);
    transition: transform .26s cubic-bezier(.22, .61, .36, 1);
}

.switch[aria-checked="true"] { background: var(--switch-on); }

.switch[aria-checked="true"] .switch__thumb {
    transform: translateY(-50%) translateX(calc(var(--switch-w) - var(--switch-h)));
}

.switch.is-inert {
    cursor: default;
    filter: saturate(.72);
    opacity: .8;
}

/* Named as closely as the rules it undoes, so asking for less movement really
   gets less movement rather than a slower slide. */
@media (prefers-reduced-motion: reduce) {
    .switch,
    .switch[aria-checked="true"],
    .switch .switch__thumb,
    .switch[aria-checked="true"] .switch__thumb { transition: none; }
}

/* --- whether the ticks are shown at all ---------------------------------- */

/* Every tick the app can draw is printed whether or not the space has asked for
   them, because the switch that asks is answered without the page reloading: the
   shell writes the answer on the document (data-ticks), and this one rule - the
   only place in the stylesheet that knows the question was ever asked - takes
   them off the page while the answer is no. Nothing else about a tick, and
   nothing at all about a task, depends on it. @see app/Views/layout.php */
html[data-ticks="off"] [data-tick] { display: none; }

.planner-strip {
    padding: var(--pad);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: var(--shadow);
}

.planner-strip__head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: .6rem 1rem;
    margin-bottom: .9rem;
}

/* Tomorrow, at the top of the days ahead of today: the brief is the one page of
   the week that leaves the app, so it is offered where the days it is about are
   read. A small door rather than a button, because it is a place to go and not a
   thing to do. */
.week-ahead__brief {
    display: flex;
    gap: .45rem;
    align-items: center;
    max-width: 15rem;
    padding: .4rem .6rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface-sunk);
    color: var(--ink);
    text-decoration: none;
}

.week-ahead__brief:hover { border-color: var(--accent); }
.week-ahead__brief-icon { font-size: 1.05rem; line-height: 1; }
.week-ahead__brief-label { display: block; font-size: .86rem; font-weight: 650; }
.week-ahead__brief-note { display: block; color: var(--muted); font-size: .76rem; }

.planner-strip__eyebrow {
    margin: 0;
    color: var(--accent-ink);
    font-size: .74rem;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
}

.planner-strip__title { margin: .1rem 0 .3rem; font-size: 1.25rem; }
.planner-strip__title a { color: var(--ink); text-decoration: none; }
.planner-strip__title a:hover { color: var(--accent-ink); text-decoration: underline; }

.planner-strip__rock {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .2rem .4rem;
    margin: 0;
    max-width: 48ch;
    color: var(--ink-soft);
    font-size: .94rem;
}

.planner-strip__label {
    display: inline-block;
    margin-right: .4rem;
    padding: .05rem .45rem;
    border-radius: 999px;
    background: var(--sun-soft);
    color: var(--sun);
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
}

.planner-strip__rock-text.is-empty { color: var(--muted); }

/* The one press that names the week's one thing. It is deliberately small - most
   weeks the Rock is already there and this is not what the eye should land on -
   and it is round because a round thing beside a line of text reads as a way of
   adding to that line rather than as another thing on it. The hit area is larger
   than the drawing, which is what makes it pressable at the size it is. */
.rock-add {
    position: relative;
    display: inline-flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: center;
    width: 2.1rem;
    height: 2.1rem;
    margin: -.35rem 0 -.35rem .1rem;
    padding: 0;
    border: 1px dashed var(--line-strong);
    border-radius: 999px;
    background: transparent;
    color: var(--muted);
    font-size: .95rem;
    line-height: 1;
    cursor: pointer;
    transition: border-color .2s ease, color .2s ease, background-color .2s ease;
}

/* The drawing is small on purpose, the press is not: the reach extends past the
   circle to the size a thumb is measured in, so what is discreet to look at is
   still a 44px target to hit. */
.rock-add::after {
    content: "";
    position: absolute;
    inset: -.4rem;
    border-radius: 999px;
}

.rock-add:hover,
.rock-add:focus-visible {
    border-color: var(--accent);
    border-style: solid;
    background: var(--accent-soft);
    color: var(--accent-ink);
}

/* The rest of this week, under today. It is the same planner strip - the week's
   own header at the top - with the seven days ahead of today listed inside it, in
   the order the grid draws them, and the line that says where one week ends and
   the next begins. Today is never said twice. */
.week-ahead__quiet { margin: 0; color: var(--muted); font-size: .9rem; }

.week-ahead__day .week-ahead__quiet { font-size: .86rem; }

/* Where the seven days cross into the next week. The rule runs to the end of the
   line so the days below it are read as a different week rather than as more of
   the same one. */
.week-ahead__split {
    display: flex;
    align-items: center;
    gap: .6rem;
    margin: .1rem 0 0;
    color: var(--muted);
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
}

.week-ahead__split::after {
    content: "";
    flex: 1 1 auto;
    height: 1px;
    background: var(--line);
}

.week-ahead__days {
    display: grid;
    gap: .9rem;
    padding-top: .8rem;
    border-top: 1px solid var(--line);
}

.week-ahead__name {
    display: flex;
    align-items: baseline;
    gap: .4rem;
    margin: 0 0 .4rem;
    font-size: .95rem;
    font-weight: 650;
}

.week-ahead__date { color: var(--muted); font-size: .82rem; font-weight: 600; }

/* Each day of the week carries its own hue, so six days in a column are told
   apart at a glance. The wash is behind everything the day holds and the hue is
   the rule down its side and the name at the top of it, which is enough to pick
   a day out and not enough to be the thing you notice first. */
.week-ahead__day {
    --day: var(--line-strong);
    --day-soft: transparent;

    padding: .55rem .7rem .7rem;
    border: 1px solid var(--line);
    border-left: 4px solid var(--day);
    border-radius: var(--radius-sm);
    background: var(--day-soft);
}

.week-ahead__day--1 { --day: var(--day-1); --day-soft: var(--day-1-soft); }
.week-ahead__day--2 { --day: var(--day-2); --day-soft: var(--day-2-soft); }
.week-ahead__day--3 { --day: var(--day-3); --day-soft: var(--day-3-soft); }
.week-ahead__day--4 { --day: var(--day-4); --day-soft: var(--day-4-soft); }
.week-ahead__day--5 { --day: var(--day-5); --day-soft: var(--day-5-soft); }
.week-ahead__day--6 { --day: var(--day-6); --day-soft: var(--day-6-soft); }
.week-ahead__day--7 { --day: var(--day-7); --day-soft: var(--day-7-soft); }

.week-ahead__day .week-ahead__name { color: var(--day); }

/* The wash is the day's, so the blocks on it let it through: opaque cards on a
   tinted ground read as a tinted border and nothing more. */
.week-ahead__day .plan__item { background: color-mix(in srgb, var(--surface) 84%, transparent); }

/* --- when, without an hour ---------------------------------------------- */

/* "When" is answered one of two ways and never both, so the hours travel in a
   wrapper that takes no room of its own: the fields inside it are children of
   whatever grid they were in before the wrapper existed.
   `display: contents` is authored, so it beats the user agent's own rule for
   [hidden] - the second line is the one that actually hides them. */
.when { display: contents; }
.when[hidden] { display: none; }

.plan__band {
    display: inline-block;
    padding: .05rem .4rem;
    border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--line));
    border-radius: 999px;
    background: var(--accent-soft);
    color: var(--accent-ink);
    font-size: .78rem;
    font-weight: 650;
    letter-spacing: .01em;
    white-space: nowrap;
}

.today {
    padding: var(--pad);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: var(--shadow);
}

.today__head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: .2rem 1rem;
}

.today__head h2 { margin: 0 0 .2rem; font-size: 1.3rem; }
.today__note { margin: 0 0 .8rem; color: var(--muted); font-size: .9rem; }

.today__aim {
    margin: 0 0 .9rem;
    padding: .5rem .8rem;
    border-left: 3px solid var(--hot);
    border-radius: var(--radius-sm);
    background: var(--hot-soft);
    font-size: .95rem;
}

.today__aim-label {
    display: block;
    color: var(--hot-ink);
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
}

.today__empty {
    padding: 1.1rem;
    border: 1px dashed var(--line-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-sunk);
    text-align: center;
}

.today__empty-title { margin: 0 0 .2rem; font-weight: 650; }

.plan { display: grid; gap: .4rem; margin: 0; padding: 0; list-style: none; }

/* Three lines for what a row *is*, read downwards: everything that answers when and
   which - the hour, how often, how many days, what kind of thing it is, whose lane
   it is in, that it has already been moved once - then the task's own words, then,
   only when somebody wrote one, the description.

   The words are on a line of their own rather than on the line of the hour, which
   is the whole of this shape: a title that shares a line with its hour is a title
   given whatever the hour and the chips leave it, and on a phone that was half a
   row to write a title in. Read down the page a row is three lines either way; read
   across it, the words now have all of the width.

   The action corner has not moved: it is the end of the first line, where the whole
   of what can be done with the row is read - and it is given the first two rows so
   that the height of a control, and a tick is a whole 2.75rem because a thumb is
   not, is spread across them instead of setting the height of a line of words. */
.plan__item {
    --entry: var(--muted);
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    column-gap: .7rem;
    row-gap: .12rem;
    align-items: start;
    padding: .45rem .55rem .5rem .65rem;
    border: 1px solid var(--line);
    border-left: 4px solid var(--entry);
    border-radius: var(--radius-sm);
    background: var(--surface);
}

.plan__item.is-done { background: var(--surface-sunk); }
.plan__item.is-done .plan__title { color: var(--muted); text-decoration: line-through; }

/* The hour, and - on a repeating row - how often it comes back, which is an answer
   to "when" rather than a remark about the words. The two wrap rather than being
   forced side by side, because a row that cannot wrap is a row that makes the whole
   page as wide as itself: beside each other while there is room, one over the other
   when there is not. It holds to the top of the line it shares with the move, so the
   hour and the move are level whatever the chips beside them do. */
.plan__when {
    grid-column: 1;
    grid-row: 1;
    align-self: start;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .15rem .35rem;
    color: var(--ink-soft);
    font-size: .88rem;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    white-space: nowrap;
}

.plan__clock { white-space: nowrap; }
.plan__dash { margin: 0 .15rem; color: var(--muted); }

/* How often a thing comes back is an answer to "when", so a repeating row says it
   beside its hour rather than in the line of chips underneath - the two are read
   as one answer, and the row gains a line it did not need. */
.plan__cadence {
    padding: .05rem .4rem;
    border: 1px dashed color-mix(in srgb, var(--hot-ink) 45%, var(--line));
    border-radius: 999px;
    background: color-mix(in srgb, var(--hot-soft) 55%, transparent);
    color: var(--hot-ink);
    font-size: .72rem;
    font-weight: 600;
    letter-spacing: .01em;
}

/* The words, and the chips that answer for them, in one wrapping row: a long title
   takes the line it needs and its chips follow it onto the next one, which is a
   narrower row rather than a taller one. */
.plan__meta {
    grid-column: 2;
    grid-row: 1;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem;
    min-width: 0;
}

/* The task's own words, on the line the chips above it have cleared for them. They
   stop at the action corner rather than running under it: a title is what the row
   is *about*, and a title with a tick sitting in the middle of it is a title
   crossing out its own last word. */
.plan__title {
    grid-column: 1 / 3;
    grid-row: 2;
    font-weight: 600;
    overflow-wrap: anywhere;
}

.plan__notes {
    grid-column: 1 / -1;
    grid-row: 3;
    color: var(--muted);
    font-size: .87rem;
    overflow-wrap: anywhere;
}

/* What can be done with a row, at the end of the line it is read on - the move, and
   the tick. The cell is two rows deep so that the height of a control is spread
   across them rather than setting the height of a line, and it is held to the top of
   that pair so the move stands on the first line, beside the hour, as it is asked
   for: what hangs below it is the tick's own hit area, which is dead space around a
   circle rather than a line of its own.

   The two controls sit beside each other and wrap only when the row has nothing
   left to give them: a cell that cannot wrap is a cell whose width the whole page
   has to grow to hold, and on a phone that is the difference between a page that
   fits and a page that slides sideways. */
.plan__acts {
    grid-column: 3;
    grid-row: 1 / span 2;
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: .1rem;
    justify-self: end;
    align-self: start;
}

/* With no script the move form unfolds in the row it belongs to, and a form the
   width of a corner is not a form: the corner it was opened from steps down and
   takes the whole row. Only ever seen with no script - a script hands the form to
   the pop-up and it never unfolds here. @see setUpMove() in assets/js/app.js */
.plan__acts:has(.plan__move[open]) {
    grid-column: 1 / -1;
    grid-row: 4;
    justify-self: stretch;
    align-self: stretch;
}

.plan__acts:has(.plan__move[open]) .plan__move[open] { flex: 1 1 100%; }

.plan__item--repeat { --entry: var(--hot-ink); border-style: dashed; background: color-mix(in srgb, var(--hot-soft) 45%, var(--surface)); }

/* --- the tick on a task -------------------------------------------------- */

/* The question "is this done?" is asked while reading a list, so the answer is
   on the row itself - the same button on a task on the home page, a task in the
   grid, and a repeating thing that has come round again. It is drawn like the
   other ticks in the app (the Sharpen dots, a commitment): an empty circle
   until it is pressed, then filled with the accent and a tick. The hit area is
   a whole 2.75rem even though the circle is small, because a thumb is not.

   On a reading list it stands in the row's **action corner**, at the end of the
   line the row is read on and beside the move: the corner holds what can be done
   with the row, so the pair of them read as that rather than as part of the title
   they happen to be level with. */
.plan__tick-form { display: contents; }

.plan__tick,
.entry__tick {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    align-self: flex-end;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--muted);
    font-size: 1.05rem;
    line-height: 1;
    cursor: pointer;
}

/* On a row of a reading list the tick stands in the row's action corner, beside the
   move, and the two are read as one thing the row can do - the end of a line is
   where a hand goes to finish it. Its box is a whole 2.75rem square, because a
   thumb is not, so it is folded into the row's own padding rather than being
   allowed to set the height of a row of words. */
.plan__tick {
    align-self: center;
    margin: -.35rem -.2rem -.35rem 0;
}

/* A band off the clock is a column, and `margin-top: auto` is what carries the
   tick to the floor of it. */
.band__form > .entry__tick { margin: auto -.2rem -.15rem 0; }

/* A block on the grid has no floor to spare: half an hour is 58px, and a control
   laid out in that column pushes everything else out of the block it belongs to.
   So there the tick is pinned to the corner instead - inside the block's own
   bounds, so the block's `overflow: hidden` never takes it away - and its circle
   is drawn a little smaller to match the room it has. */
.entry > .entry__tick {
    position: absolute;
    right: .1rem;
    bottom: .1rem;
    width: 1.9rem;
    height: 1.9rem;
    margin: 0;
}

.entry > .entry__tick > span[aria-hidden] {
    width: 1.35rem;
    height: 1.35rem;
    font-size: .85rem;
}

/* The circle is the button's own face, drawn inside the hit area rather than
   being the hit area: a 2.75rem circle would shout, and a 1.75rem one would be
   missed. */
.plan__tick > span[aria-hidden],
.entry__tick > span[aria-hidden] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    border: 1px solid var(--line-strong);
    border-radius: 50%;
    background: var(--surface);
    transition: background .15s ease-out, border-color .15s ease-out, color .15s ease-out;
}

.plan__tick:hover > span[aria-hidden],
.entry__tick:hover > span[aria-hidden] { border-color: var(--accent); color: var(--accent); }

.plan__tick.is-on > span[aria-hidden],
.entry__tick.is-on > span[aria-hidden] { border-color: var(--accent); background: var(--accent); color: var(--on-accent); }

.plan__tick:focus-visible,
.entry__tick:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* --- the home deck: a second page to turn to ----------------------------- */

/* A page of two sides: the home page is today and the week on the front, the
   month and the year on the back, and the week is the week on the front and the
   balance of it on the back. With no script they are simply stacked - the second
   side is the page below the first, which is all there was before there was a
   deck - and the deck is the polish laid over that.

   The turn is driven by one number the script writes on the deck: how far
   through the turn it is, 0 on the first side and 1 on the second (--deck-turn).
   The rail travels by exactly one width of itself for that one whole turn, and
   the two leaves take their angles from the same number, so a drag that is let
   go of halfway is still one consistent picture. The rail's height is measured
   by the script as it goes, because the two sides are not the same length.

   Which side is up is written on the deck as a position rather than a name
   (data-deck-at), because everything the deck says about itself - which way the
   tab points, which whispered line is shown - is a question about where the page
   is, not about what it is called. */
.deck { position: relative; }

/* The deck is a cell of the page's own grid, and a cell may not be smaller than what
   is in it unless it is told so. What is in it is a rail of leaves, each one the
   whole width of the rail, so its smallest possible width is the sum of them - a
   number that has nothing to do with the screen. Left alone, that number is what the
   page is drawn at: the day side fits a phone only just, and the reader who asks for
   the larger words is handed a page that slides sideways. The stage is given no
   minimum of its own, so the rail is exactly as wide as the room it has and the
   leaves shrink into it instead of pushing the page open. @see .deck__leaf */
.deck__stage { min-width: 0; }

.deck__rail { display: grid; gap: var(--gap); }
.deck__leaf { display: grid; gap: var(--gap); align-content: start; }

/* The way across for anyone without the turn - a plain pair of anchors. */
.deck__foot {
    display: flex;
    flex-wrap: wrap;
    gap: .45rem .8rem;
    align-items: center;
    justify-content: center;
}

.deck__marks {
    display: flex;
    gap: .2rem;
    padding: .18rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--surface-sunk);
}

.deck__mark {
    padding: .22rem .8rem;
    border-radius: 999px;
    color: var(--muted);
    font-size: .8rem;
    font-weight: 650;
    text-decoration: none;
    white-space: nowrap;
}

.deck__mark:hover { color: var(--ink); }
.deck__mark.is-here { background: var(--accent-soft); color: var(--accent-ink); }

/* The tab and the whispered line are the script's own: without one the marks
   above already say there is a second page. */
.deck__peek, .deck__hint { display: none; }

.deck.is-live .deck__stage {
    perspective: 1800px;

    /* The rail is what moves, so the stage is what clips. A rail that clipped
       itself would carry its own clip along with it, and the side arriving
       would be clipped away with everything else: the two sides are one width
       apart, and the clip would have moved that far out of the way.

       It sits on the stage rather than on the deck because the wheel is drawn
       inside the deck, and a box that is fixed to the corner of the screen is
       placed by the nearest ancestor holding a perspective or a clip: the wheel
       would be clipped in at the top of the page and carried down it with the
       rail, instead of staying where a thumb already is. */
    overflow: clip;
}

.deck.is-live .deck__rail {
    display: flex;
    align-items: flex-start;
    height: var(--deck-height, auto);

    /* One turn is one width of the rail, so the sides sit edge to edge: any
       gap between them would be a gap in the middle of the page at rest. */
    gap: 0;

    transform: translate3d(calc(var(--deck-turn, 0) * -100%), 0, 0);
    transform-style: preserve-3d;
    transition: transform var(--deck-turn-time, .5s) cubic-bezier(.22, .68, .3, 1),
        height var(--deck-turn-time, .5s) cubic-bezier(.22, .68, .3, 1);
    touch-action: pan-y;
}

/* Both leaves hinge on the left spine. The first one lifts its right edge as the
   rail carries it away; the second stands off the spine and falls flat. */
.deck.is-live .deck__leaf {
    flex: 0 0 100%;
    min-width: 0;
    transform: rotateY(calc(var(--deck-turn, 0) * -16deg)) scale(calc(1 - var(--deck-turn, 0) * .04));
    transform-origin: left center;
    transition: transform var(--deck-turn-time, .5s) cubic-bezier(.22, .68, .3, 1),
        opacity var(--deck-turn-time, .5s) linear;
    backface-visibility: hidden;
}

.deck.is-live .deck__rail > .deck__leaf:nth-child(2) {
    transform: rotateY(calc((1 - var(--deck-turn, 0)) * 16deg)) scale(calc(.96 + var(--deck-turn, 0) * .04));
    opacity: calc(.25 + .75 * var(--deck-turn, 0));
}

/* While a finger or the mouse has hold of the deck it follows exactly, with no
   easing of its own, and it does not drag a selection out of the page. */
.deck.is-live.is-dragging .deck__rail,
.deck.is-live.is-dragging .deck__leaf { transition: none; }

.deck.is-live.is-dragging { cursor: grabbing; user-select: none; }

.deck.is-live .deck__peek {
    display: flex;

    /* Fixed to the edge of the screen, not to the edge of the page: the tab is
       how you get from one side of the home page to the other, and a day's list
       is longer than a screen, so a tab that stayed where the page began would
       be behind you by the time you wanted it. The two ancestors that could
       claim it - the deck, which is relative, and the stage, which clips - are
       both avoided by this: neither carries a transform, and the tab is drawn
       as a sibling of the stage rather than inside it. */
    position: fixed;
    top: 50%;
    right: 0;
    z-index: 3;
    flex-direction: column;
    gap: .3rem;
    align-items: center;
    padding: .55rem .3rem .55rem .45rem;
    border: 1px solid var(--line-strong);
    border-right: 0;
    border-radius: var(--radius) 0 0 var(--radius);
    background: linear-gradient(90deg, var(--surface-sunk), var(--surface));
    box-shadow: var(--shadow-lift);
    color: var(--ink-soft);
    cursor: pointer;
    transform: translateY(-50%);
    transition: color .2s ease, box-shadow .2s ease, opacity .3s ease, visibility .3s ease;
}

.deck.is-live .deck__peek:hover { color: var(--accent-ink); }
.deck.is-live .deck__peek-label {
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    writing-mode: vertical-rl;
    transform: rotate(180deg);
}

.deck.is-live .deck__peek-arrow { font-size: 1rem; line-height: 1; }
/* On the second side the tab would point at nothing, so it goes - and with it
   its place in the tab order, which `opacity` alone would leave behind. The way
   back is excluded here and given its own rule below, because the two tabs are
   never both on the screen and neither is ever absent from the page. */
.deck.is-live[data-deck-at="1"] .deck__peek:not(.deck__peek--back) {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
}

/* The way back is the same tab, mirrored onto the left edge: a page turned is a
   page you may want to turn back, and the marks underneath are a row you have
   to find. It belongs to the second side - on the first it would point at the
   page already up - and, like the tab it mirrors, it takes its place in the tab
   order with it. */
.deck.is-live .deck__peek--back {
    right: auto;
    left: 0;
    padding: .55rem .45rem .55rem .3rem;
    border-right: 1px solid var(--line-strong);
    border-left: 0;
    border-radius: 0 var(--radius) var(--radius) 0;
    background: linear-gradient(270deg, var(--surface-sunk), var(--surface));
}

.deck.is-live:not([data-deck-at="1"]) .deck__peek--back {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
}

.deck.is-live .deck__hint {
    display: block;
    margin: 0;
    color: var(--muted);
    font-size: .8rem;
}

/* The whispered line says what the swipe would do from here, so it changes with
   the side. Nothing depends on it: the marks above say the same thing twice. */
.deck.is-live .deck__hint > [data-deck-hint="1"] { display: none; }
.deck.is-live[data-deck-at="1"] .deck__hint > [data-deck-hint="0"] { display: none; }
.deck.is-live[data-deck-at="1"] .deck__hint > [data-deck-hint="1"] { display: inline; }

/* --- the month, as a page of the home deck ------------------------------- */

.month, .year {
    padding: var(--pad);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: var(--surface);
    box-shadow: var(--shadow);
}

.month__head { margin-bottom: .85rem; }

.month__eyebrow {
    margin: 0;
    color: var(--accent-ink);
    font-size: .74rem;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
}

.month__name { margin: .15rem 0 .3rem; font-size: 1.3rem; }
.month__note { margin: 0; color: var(--muted); font-size: .92rem; }

.month__weekdays,
.month__grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: .3rem;
}

.month__weekdays { margin-bottom: .3rem; }

.month__weekday {
    color: var(--muted);
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-align: center;
    text-transform: uppercase;
}

.month__grid { margin: 0; padding: 0; list-style: none; }
.month__day { min-width: 0; }

.month__cell {
    display: grid;
    gap: .18rem;
    align-content: start;
    justify-items: center;
    min-height: 3.2rem;
    padding: .35rem .2rem .3rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface-sunk);
    color: var(--ink-soft);
    text-align: center;
    text-decoration: none;
}

a.month__cell:hover { border-color: var(--accent); color: var(--ink); }
.month__day.is-outside .month__cell { border-color: transparent; background: none; opacity: .3; }

.month__num {
    font-size: .9rem;
    font-variant-numeric: tabular-nums;
    font-weight: 650;
}

.month__day.is-heavy .month__num { color: var(--ink); }

.month__day.is-here .month__cell {
    border-color: color-mix(in srgb, var(--accent) 55%, var(--line));
    background: var(--accent-soft);
}

.month__day.is-today .month__cell {
    border-color: var(--hot);
    background: color-mix(in srgb, var(--hot-soft) 70%, var(--surface));
    color: var(--ink);
}

.month__dots {
    display: flex;
    flex-wrap: wrap;
    gap: .14rem;
    align-items: center;
    justify-content: center;
}

.month__dot {
    width: .32rem;
    height: .32rem;
    border-radius: 999px;
    background: var(--kind-task);
}

.month__dot--appointment { background: var(--kind-appointment); }
.month__dot--commitment { background: var(--kind-commitment); }
.month__dot--block { background: var(--kind-block); }
.month__dot--focus { background: var(--kind-focus); }
.month__dot--repeat { background: var(--hot-ink); }
.month__dot.is-done { opacity: .35; }
.month__more { color: var(--muted); font-size: .68rem; font-weight: 700; }

/* --- the year, one small square a day ------------------------------------ */

.year__head { margin-bottom: .85rem; }

.year__eyebrow {
    margin: 0;
    color: var(--accent-ink);
    font-size: .74rem;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
}

.year__name { margin: .15rem 0 .3rem; font-size: 1.3rem; }
.year__note { margin: 0; color: var(--muted); font-size: .92rem; }

.year__months {
    display: grid;
    /* A month may never be the thing that makes the page wider than the screen:
       `min(10rem, 100%)` is the floor that still gives way when it has to. */
    grid-template-columns: repeat(auto-fit, minmax(min(10rem, 100%), 1fr));
    gap: .9rem .8rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.year__label {
    margin: 0 0 .3rem;
    color: var(--muted);
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
}

.year__month.is-here .year__label { color: var(--accent-ink); }

/* A month of the year is a month, small: the seven letters of the week over the
   seven squares under them, in the planner's own order, so the square a date falls
   on can be read as the weekday it is. A strip of blank squares is a stripe - it
   says how busy a fortnight was and nothing about when. */
.year__weekdays,
.year__grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 2px;
}

.year__weekdays { margin-bottom: .25rem; }

.year__weekday {
    color: var(--muted);
    font-size: .58rem;
    font-weight: 700;
    letter-spacing: .01em;
    text-align: center;
}

.year__grid {
    margin: 0;
    padding: 0;
    list-style: none;
}

.year__day {
    display: grid;
    place-items: center;
    aspect-ratio: 1;
    border-radius: 2px;
    background: var(--surface-sunk);
}

/* The number of the day, in the square the day is: it is what makes a month of
   these a calendar rather than a heat map. It rides inside the door where the day
   has one, so it grows and rings with the day it belongs to. */
.year__day-num {
    color: color-mix(in srgb, var(--ink) 78%, transparent);
    font-size: .62rem;
    font-variant-numeric: tabular-nums;
    line-height: 1;
}

.year__day.is-quiet .year__day-num { color: var(--muted); }

.year__day.is-blank { background: none; }
.year__day--1 { background: color-mix(in srgb, var(--violet) 30%, var(--surface-sunk)); }
.year__day--2 { background: color-mix(in srgb, var(--violet) 55%, var(--surface-sunk)); }
.year__day--3 { background: color-mix(in srgb, var(--violet) 85%, var(--surface-sunk)); }
.year__day.is-today { box-shadow: 0 0 0 2px var(--hot); }

/* A day with something on it is a door and wears the fact: the whole square is the
   target, and the ring is what says so. A day with nothing on it stays a mark,
   because a door that opens onto nothing is a door you learn not to press - and a
   year of 365 of them is a year of stops for anyone reading with their fingers. */
.year__day-link {
    display: grid;
    place-items: center;
    width: 100%;
    height: 100%;
    border-radius: 2px;
    cursor: pointer;
    transition: box-shadow .15s ease, transform .15s ease;
}

.year__day-link:hover,
.year__day-link:focus-visible {
    box-shadow: 0 0 0 2px var(--accent);
    transform: scale(1.18);
}

/* --- the day behind a date ----------------------------------------------- */

/* The lines of a day, as the list they are: the hour down the left where a hand
   can run down it, the words beside, and whose lane it is - or how often it comes
   back - in the quiet voice, because that is a note about the row rather than the
   row itself. */
/* The day's pop-up is read rather than filled in, so its one action sits at the
   left where the list ends and the way out stays where the other sheets keep it. */
.sheet--day .sheet__foot {
    justify-content: space-between;
    align-items: center;
    gap: .6rem;
}

.day-sheet { gap: .35rem; }

.day-sheet__heading {
    margin: .4rem 0 .1rem;
    color: var(--accent-ink);
    font-size: .72rem;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
}

.day-sheet__heading:first-child { margin-top: 0; }

.day-sheet__row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .15rem .55rem;
    margin: 0;
    padding: .35rem .5rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface-sunk);
}

.day-sheet__when {
    flex: 0 0 auto;
    min-width: 5.4rem;
    color: var(--muted);
    font-size: .8rem;
    font-variant-numeric: tabular-nums;
}

.day-sheet__what { flex: 1 1 8rem; font-size: .93rem; }
.day-sheet__who { flex: 0 1 auto; margin-left: auto; color: var(--muted); font-size: .78rem; }

.day-sheet__row.is-done .day-sheet__when { color: var(--accent-ink); }
.day-sheet__row.is-done .day-sheet__what { text-decoration: line-through; opacity: .7; }

.day-sheet__quiet { margin: 0; color: var(--muted); font-size: .92rem; }

/* Said beside the list rather than in it: the four are promises made for the day,
   and the list above is what is written into it. */
.day-sheet__four {
    margin: .5rem 0 0;
    padding-top: .5rem;
    border-top: 1px solid var(--line);
    color: var(--muted);
    font-size: .84rem;
}

/* --- a task that has been moved ------------------------------------------ */

/* Not a warning and not an error: a small lamp that says this one has already
   been picked up once, and that picking it up again is the second time. */
.plan__moved {
    display: inline-flex;
    gap: .32rem;
    align-items: center;
    padding: .08rem .42rem .08rem .3rem;
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    background: var(--surface-sunk);
    color: var(--muted);
    font-size: .76rem;
    font-weight: 600;
}

.plan__moved-light {
    width: .44rem;
    height: .44rem;
    border-radius: 999px;
    background: var(--muted);
}

.plan__moved--once {
    border-color: color-mix(in srgb, var(--sun) 45%, var(--line));
    color: var(--sun);
}

.plan__moved--once .plan__moved-light {
    background: var(--sun);
    box-shadow: 0 0 0 2px var(--sun-soft);
}

.plan__moved--twice {
    border-color: color-mix(in srgb, var(--danger) 50%, var(--line));
    color: var(--danger-ink);
}

.plan__moved--twice .plan__moved-light {
    background: var(--danger);
    box-shadow: 0 0 0 2px var(--danger-soft);
}

@media (prefers-reduced-motion: no-preference) {
    .plan__moved--twice .plan__moved-light { animation: lamp-blink 2.6s ease-in-out infinite; }
}

@keyframes lamp-blink {
    0%, 100% { opacity: 1; }
    50% { opacity: .45; }
}

/* --- the move: a button in the row, a form in the pop-up ------------------ */

/* The control is a `<details>` because that is what works with no script at all -
   the summary unfolds the row's own form where it stands. With a script the form
   is handed to the page's move dialog before it is ever opened, so what a press
   does is open a pop-up and the row stays the height of one line.

   The summary is drawn as a small button, because that is what it is. */
.plan__move { flex: none; }

/* On a repeating row the move is not drawn until the row is pressed. A rule comes
   back every week whether or not anything is done about it, so the row is read far
   more often than it is moved, and a list of reads should not be a list of buttons.
   The press is the row's own - the row carries a tabindex for it, and the focus that
   reaches the control is the focus that reveals it, so nothing has to be guessed at
   and no script is needed. A pointer that rests on the row is a reader who has found
   the row, so that reveals it too, and on a phone the press that reveals it is also
   the hover that keeps it there - which is how a thumb sees it at all.

   Hidden rather than removed, so the corner keeps the width it will need: what a
   press changes is what the corner says, not where the tick beside it stands or how
   the title above it is wrapped. Nothing hidden can be pressed by accident either. */
.plan__item--repeat .plan__move { visibility: hidden; }

.plan__item--repeat:focus-within .plan__move,
.plan__item--repeat:hover .plan__move,
.plan__item--repeat .plan__move[open] { visibility: visible; }

.plan__move-open {
    display: inline-flex;
    align-items: center;
    padding: .22rem .5rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-sunk);
    color: var(--ink-soft);
    font-size: .78rem;
    font-weight: 600;
    line-height: 1.2;
    white-space: nowrap;
    cursor: pointer;
    list-style: none;
    transition: border-color .15s ease-out, color .15s ease-out;
}

.plan__move-open::-webkit-details-marker { display: none; }
.plan__move-open:hover { border-color: var(--accent); color: var(--accent-ink); }
.plan__move[open] > .plan__move-open { border-color: var(--accent); color: var(--accent-ink); }

.plan__move-form {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem .7rem;
    align-items: end;
    margin-top: .6rem;
}

.plan__move-form .field { flex: 0 1 8rem; }

/* The day may be one of the week's own or any date at all, and a date box needs
   the room a date needs rather than the room a label needs - ten characters and a
   calendar button - so it is held to a floor of its own. The media query below
   narrows every other field on a phone; this one keeps the width it needs. */
.plan__move-form .field--other { min-width: 9.5rem; }

/* A repeating row is moved one of two ways, and the two are drawn one under the
   other with each saying its own name: "just this day" takes one date off the day
   its rule says, "every time from now on" changes the rule itself, and a reader who
   cannot tell them apart is a reader who moves a year of Wednesdays by accident. */
.plan__move-shape {
    display: flex;
    flex: 1 1 100%;
    flex-wrap: wrap;
    gap: .5rem .7rem;
    align-items: end;
}

.plan__move-shape + .plan__move-shape {
    padding-top: .6rem;
    border-top: 1px dashed var(--line);
}

.plan__move-head {
    flex: 1 1 100%;
    margin: 0;
    color: var(--ink-soft);
    font-size: .8rem;
    font-weight: 700;
}

/* The sentence that says which of the two days wins belongs to the pair of them,
   so it takes the line under them rather than sitting beside them. */
.plan__move-form .field__note { flex: 1 1 100%; }

/* Borrowed by the pop-up, the same form is the fields of a form like any other:
   one under another, at the width of the sheet. */
.sheet--move .plan__move-form {
    display: grid;
    gap: .55rem;
    margin-top: 0;
}

/* --- the bar at the foot of the screen ---------------------------------- */

/* The way round the app, and the way in, in one place a thumb already rests:
   the week and today on the left, the + in the middle, the notes and you on the
   right. It is drawn by the shell on every page, so it is one control rather
   than five copies of one, and it is always there rather than something opened
   first - which is the whole difference between this and the wheel it replaces.

   Fixed to the foot of the window, clear of a phone's own home bar, and never
   hidden behind anything: a page keeps the room for it at the bottom of itself,
   so the last row of a list is never under the bar. It is the same five doors on
   every screen, a monitor included - only its width changes there, because five
   slots stretched across a desk are five slots nobody can miss and nobody needs. */
.tabbar {
    --tabbar-face: var(--surface);
    --tabbar-tone: var(--accent);

    /* How far the bar floats off the foot of the screen, how much room a page
       keeps for it, and how tall the motto under it is: the pill is 3.9rem tall (a
       row of 3.4rem inside .25rem of its own padding), it stands .7rem clear of the
       bottom edge, and the motto is one small line under it. All of them are written
       here because three things have to agree with them - the page's own bottom
       padding, where the notice about the connection sits, and where a panel opens.
       @see --tabbar-clear, .tabbar__panel */
    --tabbar-gap: .7rem;
    --tabbar-row: 3.4rem;
    --tabbar-motto: 1.7rem;
    --tabbar-clear: 7.9rem;

    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 0 var(--tabbar-gap) calc(var(--tabbar-gap) + env(safe-area-inset-bottom, 0px));
    border: 0;
    background: none;
    box-shadow: none;
    /* The strip around the pill is not a target: it is the page, with a bar
       floating over it. */
    pointer-events: none;
    -webkit-tap-highlight-color: transparent;
}

/* The motto, under the bar and above the phone's own home bar. It is the page's
   own words rather than a control, so it takes no presses and no taps: it is the
   one thing on the screen that is only ever read. */
.tabbar__motto {
    margin: .35rem 0 0;
    color: var(--tabbar-ink-quiet);
    font-size: .78rem;
    font-style: italic;
    line-height: 1.25;
    text-align: center;
    text-wrap: balance;
}

/* The bar itself: a rounded capsule, floating clear of the bottom edge, glassy
   enough to show the page moving under it and solid enough to read against
   anything. The panels and the page keep clear of it rather than it of them. */
.tabbar__row {
    display: flex;
    align-items: stretch;
    justify-content: space-around;
    width: 100%;
    max-width: 32rem;
    margin: 0;
    padding: .25rem;
    list-style: none;
    border: 1px solid var(--glass-line);
    border-radius: 999px;
    background: var(--glass);
    -webkit-backdrop-filter: blur(14px) saturate(150%);
    backdrop-filter: blur(14px) saturate(150%);
    box-shadow: 0 16px 36px var(--glass-shadow), inset 0 1px 0 var(--glass-glow);
    overflow: hidden;
    pointer-events: auto;
}

/* A browser that cannot blur has to be told to be solid instead: glass over a
   page of text with nothing behind it is a bar you cannot read. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .tabbar__row { background: var(--surface); }
    .tabbar__panel { background: var(--surface); }
}

.tabbar__slot { display: flex; flex: 1 1 0; min-width: 0; }

/* A slot is a whole area, not a word: the 44px a thumb needs is the button's
   own height, and the label sits inside it rather than beside the icon. */
.tabbar__link {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .15rem;
    min-height: var(--tabbar-row, 3.25rem);
    padding: .4rem .15rem .35rem;
    border: 0;
    border-radius: 999px;
    background: none;
    color: var(--tabbar-ink);
    font: 650 .66rem/1 var(--sans);
    letter-spacing: .02em;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
}

/* The slot you are standing on wears the accent twice over - the filled icon and
   a soft capsule behind it - so the bar reads as a dial with you on it. The
   accent as *ink* rather than as a fill, which is the deep blue on a light page
   and the pale blue on a dark one. */
.tabbar__link.is-current { color: var(--accent-ink); background: var(--accent-soft); }
.tabbar__link:hover, .tabbar__link:focus-visible { background: var(--wash); }
.tabbar__link.is-current:hover, .tabbar__link.is-current:focus-visible { background: var(--accent-soft); }
.tabbar__link:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tabbar__label { display: block; }

/* One icon drawn twice: the rim of the shape while the slot is resting, and the
   whole of it once you are there. The detail strokes are cut in the bar's own
   colour, so a filled week is still a week rather than a solid square. */
.tabbar__icon { display: block; width: 1.75rem; height: 1.75rem; }

.tabbar__icon-line path {
    fill: none;
    stroke: currentColor;
    stroke-width: 1.7;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.tabbar__icon-fill { display: none; }

.tabbar__icon-fill path { fill: currentColor; }

.tabbar__icon-fill path:not(:first-child) {
    fill: none;
    stroke: var(--tabbar-face);
    stroke-width: 1.9;
    stroke-linecap: round;
}

.tabbar__link.is-current .tabbar__icon-line { display: none; }
.tabbar__link.is-current .tabbar__icon-fill { display: block; }

/* The way in is the one slot that is lit whatever page you are on, because it is
   the one thing you can do here rather than somewhere you can go. */
/* The + is the one slot that is a thing to do rather than a place to be, so it
   wears the same shape a size larger and always filled. */
.tabbar__link--add { color: var(--accent-ink); }
.tabbar__link--add .tabbar__icon { width: 1.95rem; height: 1.95rem; }
.tabbar__link--add .tabbar__icon-fill { display: block; }
.tabbar__link[aria-expanded="true"] { color: var(--ink); }
.tabbar__link[aria-expanded="true"] .tabbar__icon-fill path:not(:first-child) { stroke: var(--tabbar-face); }

/* You, in the corner where your own things are: an initial in a circle, which is
   the only picture the app has of anybody, filled in when the panel is out. */
.tabbar__face {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 1.55rem;
    height: 1.55rem;
    border-radius: 50%;
    background: var(--surface-sunk);
    color: var(--tabbar-ink);
    font: 700 .78rem/1 var(--sans);
}

.tabbar__link--me.is-current .tabbar__face,
.tabbar__link--me[aria-expanded="true"] .tabbar__face { background: var(--accent); color: var(--on-accent); }

/* --- what the + and Me open --------------------------------------------- */

/* A panel stands on the bar rather than over the page: what it holds belongs to
   where you are standing, and a sheet that covered the page would be a page you
   have to get back from. It is a plain list, each row a link the script may turn
   into the dialog the app already has for that thing. */
.tabbar__panel {
    position: absolute;
    left: var(--tabbar-gap, .7rem);
    right: var(--tabbar-gap, .7rem);
    bottom: calc(100% + .4rem);
    max-height: min(70vh, 26rem);
    overflow: auto;
    padding: .6rem .6rem .5rem;
    border: 1px solid var(--line-strong);
    border-radius: 1.15rem;
    background: var(--glass-solid);
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
    box-shadow: var(--shadow-lift);
    pointer-events: auto;
}

.tabbar__panel[hidden] { display: none; }

.tabbar__panel-title {
    margin: 0 0 .45rem;
    padding: 0 .4rem;
    color: var(--muted);
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
}

.tabbar__list { display: grid; gap: .2rem; margin: 0; padding: 0; list-style: none; }

.tabbar__item {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .5rem .55rem;
    border-radius: var(--radius-sm);
    color: var(--ink);
    text-decoration: none;
}

.tabbar__item:hover,
.tabbar__item:focus-visible { background: var(--surface-sunk); }
.tabbar__item.is-current { background: var(--surface-sunk); }

.tabbar__item-icon {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 1.9rem;
    height: 1.9rem;
    border-radius: 50%;
    background: var(--surface-sunk);
    font-size: 1rem;
}

.tabbar__item-label { display: block; font-size: .92rem; font-weight: 600; }
.tabbar__item-note { display: block; color: var(--muted); font-size: .78rem; }

.tabbar__out { margin: .35rem 0 0; padding: .45rem .55rem 0; border-top: 1px solid var(--line); text-align: left; }

/* The space you are in, said rather than offered: pressing the space you are
   already standing in is a journey that goes nowhere, so it is written as a fact.
   The name is the loud half and the role is the quiet one, because "owner" is a
   note about the space rather than the space itself. */
.tabbar__me-space { margin: 0; padding: .1rem .55rem .5rem; border-bottom: 1px solid var(--line); }

.tabbar__me-name { display: block; font-size: 1rem; font-weight: 650; }
.tabbar__me-note { display: block; color: var(--muted); font-size: .78rem; }

/* Another space, asked for at the foot of Me. It is the one thing in the panel
   that makes something rather than goes somewhere, and it stands under the list of
   spaces it will join - which is also where the new one will appear. */
.tabbar__new {
    display: flex;
    gap: .4rem;
    margin: .5rem 0 0;
    padding: .5rem .55rem 0;
    border-top: 1px solid var(--line);
}

.tabbar__new input { flex: 1 1 auto; min-width: 0; }

/* The page keeps the room for the bar, so the last row of a list is never
   underneath it - on every screen, because the bar is on every screen, motto and
   float measured in (--tabbar-clear is where that arithmetic lives). The safe area
   is added twice on purpose: once inside the bar for the phone's own home bar, once
   here for the room the two make together. */
.page { padding-bottom: calc(var(--tabbar-clear, 7.9rem) + env(safe-area-inset-bottom, 0px)); }

/* The footer is only on the pages that have no bar - nobody is signed in, so there
   is nothing to hang a motto from - and so it keeps the ordinary space a footer
   keeps rather than the room a fixed bar needs. */
.footer { padding-bottom: calc(2.5rem + env(safe-area-inset-bottom, 0px)); }

/* The bar is the only way round the app, so nothing here hides or re-shows a row
   of doors: the capsule is the same width everywhere and carries the same five
   places on a phone and on a monitor. */

/* The connection going: the notice sits above the bar rather than under it, so
   both can be read at once - clear of the capsule, the motto under it, and the
   float under that. */
html.is-offline .offline {
    bottom: calc(var(--tabbar-row, 3.4rem) + var(--tabbar-motto, 1.7rem) + 1.15rem + env(safe-area-inset-bottom, 0px));
}

@media (prefers-reduced-motion: reduce) {
    .tabbar__panel { animation: none; }
}


/* --- notes -------------------------------------------------------------- */

/* The library. A note is a thing that exists between weeks, so nothing here is
   tied to the grid: the page is a column, the folders are chips along the top,
   and each note opens onto its own doors. */

.notes__write .sheet__form { padding: 0; }

/* The one way into a note, on the page where notes are read: the fields behind
   it are the same fields the dialog on the week pages carries, so a note written
   here and a note written there are written in one place.

   The button opens a dialog, so it is only drawn where a dialog can open: with
   no script the form underneath it is what is offered, and the two are never on
   the screen together. */
.notes__add {
    align-self: center;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.6rem;
    height: 2.6rem;
    padding: 0;
    border: 1px solid var(--accent);
    border-radius: 50%;
    background: var(--accent);
    box-shadow: var(--shadow);
    color: var(--on-accent);
    font: 400 1.45rem/1 var(--sans);
    cursor: pointer;
    transition: background .18s ease, border-color .18s ease, transform .18s ease;
}

.notes__add:hover {
    background: var(--accent-strong);
    border-color: var(--accent-strong);
    transform: translateY(-1px);
}

.notes__add:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
.notes__add > span { line-height: 1; }

html.has-js .notes__write { display: none; }
html:not(.has-js) .notes__add { display: none; }

.notes__search { align-items: flex-end; }

.notes__chips {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
    margin: 0 0 .9rem;
}

/* The chips are links here rather than labels, so they take a finger's worth of
   room and stop shouting in capitals - they are read, not scanned past. */
.chip--link {
    padding: .18rem .5rem;
    color: var(--ink-soft);
    font-size: .78rem;
    text-transform: none;
    text-decoration: none;
}

.chip--link:hover {
    border-color: color-mix(in srgb, var(--accent) 55%, var(--line-strong));
    color: var(--accent-ink);
}

.chip--link.is-current {
    border-color: var(--accent);
    background: var(--accent-soft);
    color: var(--accent-ink);
}

/* Standing in a folder names it once more above its own list, with what it is
   holding and the two ways out of it: the notes handed back to the inbox, or
   taken with the folder. It is drawn quieter than a block because it belongs to
   the list under it rather than to the page. */
.notes__standing {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: .45rem .8rem;
    margin: 0 0 1rem;
    padding: .5rem .65rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface-sunk);
}

.notes__standing-what {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .45rem;
    margin: 0;
    font-size: .9rem;
}

.notes__standing-tools {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4rem;
}

.notes {
    display: grid;
    gap: .55rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

.note {
    padding: .6rem .7rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface-sunk);
}

/* A done note is still here, just out of the way. */
.note--shelved {
    border-style: dashed;
    opacity: .72;
}

/* Finishing a note is one press on the note itself, so it sits at the end of the
   line the note is read on rather than behind the fold of "Open it". */
.note__done {
    margin-left: auto;
}

.note__done-btn {
    white-space: nowrap;
}

.note__head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .45rem;
}

.note__title {
    flex: 1 1 12rem;
    min-width: 0;
    margin: 0;
    font-size: .97rem;
    overflow-wrap: anywhere;
}

.note__folder { color: var(--ink-soft); font-size: .78rem; font-weight: 650; }

.note__body {
    margin: .35rem 0 0;
    color: var(--ink-soft);
    font-size: .88rem;
    overflow-wrap: anywhere;
}

.note__where {
    display: flex;
    flex-wrap: wrap;
    gap: .3rem .5rem;
    margin: .35rem 0 0;
    font-size: .76rem;
}

.note__became { color: var(--accent-ink); font-weight: 650; }

.note__when { margin-left: auto; font-variant-numeric: tabular-nums; }

.note__fold { margin-top: .4rem; }

.note__fold > summary {
    color: var(--muted);
    font-size: .82rem;
    cursor: pointer;
}

.note__fold > summary:hover { color: var(--accent-ink); }
.note__fold[open] > summary { color: var(--ink-soft); }

.note__panels {
    display: grid;
    gap: .8rem;
    margin-top: .6rem;
}

/* Each door is drawn as a ruled-off room, so the four forms read as four
   answers rather than as one long page of fields. */
.note__panel {
    padding-top: .6rem;
    border-top: 1px solid var(--line);
}

.note__panel .subhead { margin: 0 0 .5rem; font-size: .72rem; }

.note__panel .sheet__form { padding: 0; }

.note__tools {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
}

.note__tools form { margin: 0; }

/* A note opened: the four doors as four tabs in one pop-up, the note's own
   panels moved in behind them. The sheet is a shell - a title, the row of tabs,
   the one door that is up - so it is laid out like a form even though it holds
   no form of its own. */
.sheet--note { width: min(40rem, calc(100vw - 1.6rem)); }

.note__sheet {
    display: grid;
    gap: .75rem;
    padding: var(--pad);
}

.note__tabs {
    display: flex;
    flex-wrap: wrap;
    gap: .15rem;
    border-bottom: 1px solid var(--line);
}

.note__tab {
    padding: .35rem .6rem .4rem;
    border: 0;
    border-bottom: 2px solid transparent;
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    background: none;
    color: var(--muted);
    font: 650 .82rem/1.2 var(--sans);
    white-space: nowrap;
    cursor: pointer;
}

.note__tab:hover { color: var(--accent-ink); }

.note__tab[aria-selected="true"] {
    border-bottom-color: var(--accent);
    background: linear-gradient(180deg, var(--accent-soft), transparent 85%);
    color: var(--accent-ink);
}

.note__tab:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* In the sheet a door is a tab, so it loses the ruled-off room it is drawn with
   in the note, and the heading the tab has taken over is not said twice. */
.note__sheet .note__panels { margin-top: 0; }

.note__sheet .note__panel {
    padding-top: 0;
    border-top: 0;
}

.note__sheet .note__panel[hidden] { display: none; }
.note__sheet .note__panel > .subhead { display: none; }

/* --- print -------------------------------------------------------------- */

/* Paper is white whatever the screen is, so the palette is turned inside out
   here rather than rule by rule: the tokens are re-declared light and the whole
   page follows, because nothing is coloured outside the token block. */
@media print {
    :root {
        color-scheme: light;

        --ink: #111827;
        --ink-soft: #374151;
        --muted: #4b5563;
        --line: #d4d8de;
        --line-strong: #9aa3ad;

        --paper: #fff;
        --surface: #fff;
        --surface-sunk: #f3f4f6;

        --accent-ink: #1d4ed8;
        --hot-ink: #9a3412;

        --kind-task: #475569;
        --kind-appointment: #6d28d9;
        --kind-commitment: #047857;
        --kind-block: #0369a1;
        --kind-focus: #b45309;

        --day-1: #1d4ed8; --day-1-soft: #eef2ff;
        --day-2: #0e7490; --day-2-soft: #ecfeff;
        --day-3: #047857; --day-3-soft: #ecfdf5;
        --day-4: #b45309; --day-4-soft: #fffbeb;
        --day-5: #c2410c; --day-5-soft: #fff7ed;
        --day-6: #6d28d9; --day-6-soft: #f5f3ff;
        --day-7: #be185d; --day-7-soft: #fdf2f8;

        --warn-ink: #92400e;
        --danger-ink: #991b1b;
        --on-accent: #fff;

        --glass: #fff;
        --glass-solid: #fff;
        --glass-line: #d4d8de;
        --glass-glow: transparent;
        --glass-shadow: transparent;
        --wash: transparent;
        --scrim: transparent;

        --shadow: none;
        --shadow-lift: none;
    }

    .topbar, .adder, .entry__kill, .day__add, .footer, .hero__links, .composer, .deck__peek { display: none !important; }
    .tabbar, .sheets, .prefs, .brief__tools, .week__foot, .loader, .offline { display: none !important; }
    .deck__peek, .deck__foot { display: none !important; }

    /* On paper both sides of the deck are printed, one under the other: the
       deck stops clipping and the rail goes back to being a column. */
    .deck.is-live .deck__stage { overflow: visible; }
    .deck.is-live .deck__rail { display: grid; height: auto !important; gap: var(--gap); transform: none; }
    .deck.is-live .deck__leaf,
    .deck.is-live .deck__leaf[data-deck-leaf="month"] { transform: none; opacity: 1; }

    body { background: #fff; }
    .block { box-shadow: none; break-inside: avoid; }
    .grid { overflow: visible; }
    .brief__text { border: 0; background: none; padding: 0; }
    .tag--urgent, .chip--overlap, .auth__error, .linkish--danger { color: #991b1b; }
    .tag--high, .chip--invalid-time { color: #92400e; }
    .plan__moved--once { color: #92400e; }
    .plan__moved--twice { color: #991b1b; }
}

/* --- narrow screens ----------------------------------------------------- */

@media (max-width: 44rem) {
    .hero { grid-template-columns: minmax(0, 1fr); align-items: start; }
    .notes__add { justify-self: end; margin-top: .35rem; }
    .hero__links { margin-top: .4rem; }
    .grid { grid-template-columns: 2.9rem repeat(7, 8rem); }
    .history__row, .breakdown__row { grid-template-columns: minmax(0, 1fr); }
    .slot { grid-template-columns: 1.4rem minmax(0, 1fr); }
    .slot__side { grid-column: 2; justify-content: flex-start; }
    .settings-row { flex-wrap: wrap; }
    .settings-row__form { margin-left: 0; }
    /* What gives way first on a phone is the size of the hour and the cadence beside
       it: the two wrap rather than widening the column they are in, because the
       width belongs to the words on the line below. */
    .plan__when { font-size: .82rem; }
    .plan__item { column-gap: .5rem; }
    .home__hero h1 { font-size: 1.5rem; }
    .deck__peek-label { font-size: .62rem; letter-spacing: .06em; }
    .deck__peek { padding: .5rem .22rem .5rem .3rem; }
    .month, .year { padding: 1rem; }
    .month__name { font-size: 1.1rem; }
    .month__cell { min-height: 2.6rem; padding: .25rem .12rem .2rem; }
    .month__weekdays, .month__grid { gap: .22rem; }
    .year__months { grid-template-columns: repeat(auto-fit, minmax(min(9rem, 100%), 1fr)); }
    .plan__move-form .field { flex: 1 1 7rem; }
    .note__title { flex-basis: 100%; }
    .note__when { margin-left: 0; }
    .notes__chips { gap: .3rem; }
}


/* --- the turn between the places of the app ------------------------------- */

/*
    Moving between the places of the app is a turn rather than a load.

    The page that is leaving slides out and the page that arrives comes in after
    it, which is the same sideways movement the home page's own two sides make when
    the deck turns - the same easing, a little quicker, because a place is bigger
    than a page of a calendar and a long turn between two screens is a wait with a
    picture on it. It is the browser's own transition, so nothing about the app
    changes to have it: every bar slot is still a link, every form still posts, the
    back button still goes back, and the pages still work with no script at all.
    A browser that has never heard of any of this navigates the way it always did.

    The bar and the header are named so that they are drawn once and stand still:
    they are the frame the page moves inside, and a frame that slides with the page
    would read as the whole screen being dragged. The direction is written on the
    document by the server, from the page that sent us here, so moving right along
    the bar turns one way and moving back turns the other. @see App\Support\PageOrder
*/
@view-transition { navigation: auto; }

:root {
    --turn-time: .4s;
    --turn-ease: cubic-bezier(.22, .68, .3, 1);
}

#main { view-transition-name: page; }
.topbar { view-transition-name: shell-head; }
.tabbar { view-transition-name: shell-bar; }
.loader { view-transition-name: shell-cover; }
.offline { view-transition-name: shell-offline; }

/* Everything that is the frame is drawn as it is: no fade, no movement, nothing
   to notice. */
::view-transition-old(shell-head),
::view-transition-new(shell-head),
::view-transition-old(shell-bar),
::view-transition-new(shell-bar),
::view-transition-old(shell-cover),
::view-transition-new(shell-cover),
::view-transition-old(shell-offline),
::view-transition-new(shell-offline) { animation: none; }

/* And the page itself is the only thing that moves, so the ground it moves over
   is never shown moving underneath it. */
::view-transition-old(root),
::view-transition-new(root) { animation: none; }

/* The turn is drawn in three dimensions rather than as a flat slide, as the deck's
   is, so the two pages pass each other rather than one covering the other. */
::view-transition-group(page) { perspective: 1400px; }

::view-transition-old(page) { animation: page-turn-out var(--turn-time) var(--turn-ease) both; }
::view-transition-new(page) { animation: page-turn-in var(--turn-time) var(--turn-ease) both; }

html[data-turn="right"]::view-transition-old(page) { animation-name: page-turn-out-right; }
html[data-turn="right"]::view-transition-new(page) { animation-name: page-turn-in-right; }

/* A page with no direction - one that arrived from nowhere in particular, or a
   move between two pages that stand in the same place - simply changes over. */
html:not([data-turn])::view-transition-old(page) { animation-name: page-fade-out; }
html:not([data-turn])::view-transition-new(page) { animation-name: page-fade-in; }

@keyframes page-turn-out {
    to { transform: translate3d(-12%, 0, 0) rotateY(6deg); opacity: 0; }
}

@keyframes page-turn-in {
    from { transform: translate3d(12%, 0, 0) rotateY(-6deg); opacity: 0; }
}

@keyframes page-turn-out-right {
    to { transform: translate3d(12%, 0, 0) rotateY(-6deg); opacity: 0; }
}

@keyframes page-turn-in-right {
    from { transform: translate3d(-12%, 0, 0) rotateY(6deg); opacity: 0; }
}

@keyframes page-fade-out {
    to { opacity: 0; }
}

@keyframes page-fade-in {
    from { opacity: 0; }
}

/* Asking for less movement gets none of it: the page is simply there. */
@media (prefers-reduced-motion: reduce) {
    ::view-transition-old(page),
    ::view-transition-new(page),
    ::view-transition-old(root),
    ::view-transition-new(root) { animation: none; }
}