/*
    PAGES01 — the one stylesheet.

    Commandment 2, and the reason is a measured one: the v1 grew in layers and
    every layer brought its own CSS, until nobody could say which rule won. Two
    files are already the first step towards twenty. There is one file, and the
    exit criteria count them.

    What this is trying to be: dense and legible. Filippo reads lists all day; a
    page that breathes like a poster is a page that shows eight rows where it
    could show twenty-five. Nothing here is decorative, and nothing here is a
    framework.

    Everything tunable is a variable, below. Changing how NANO looks should be
    changing ten values in one place, not hunting through twenty selectors.
*/

/*
    LOOK02 — THE LEGEND OF THE BUTTONS.

    Filippo, 2026-09-07: "i bottoni tutti dello stesso colore si confondono tra
    loro". They did: thirty `primary` and twenty-three `.button`, all blue or all
    neutral, with Remove the same colour as Save and Confirm the same colour as
    Show.

    Five classes. EVERY <button> and every a.button carries EXACTLY ONE of them,
    and a test reads the .cshtml files and fails if one carries none or two.

      .quiet    neutral, bordered   navigate, filter, open a form, back out
                                    Show, Next, New task, Edit, Clear, Cancel,
                                    Discard, Ignore, Search, Sign out

      .act      BLUE   --accent     the form's own action: it writes, or it sets
                                    something of NANO's going
                                    Save, Upload, Import, Add, Attach, Book,
                                    Move, Rename, Scan now, Back up now, Sign in

      .confirm  GREEN  --good       saying yes to a proposal, or switching
                                    something ON
                                    Confirm, Open it, Let it in, Switch it on

      .danger   RED    --bad        removing, or switching something OFF
                                    Remove, Removed, Close it, Stop it,
                                    Switch it off

      .ai       ORANGE --ai         it calls the model, and it costs money
                                    Ask, Ask NANO, Analyze now, Re-analyse

    Two rules that follow from it: a form has ONE filled button and everything
    else beside it is .quiet; and a two-state switch changes class with the state,
    green when it turns something on and red when it turns it off.

    A new button CHOOSES from this table. It does not invent a sixth colour, and
    it does not go without one. The same table is in SPECIFICA.md, under Look.

    Reading is not AI: "Read what is waiting" and "Read again" pull bytes out of
    a file and are .act. Orange means a paid call to the model — see docs/AI.md.
*/

:root {
    /* ── Colour ──────────────────────────────────────────────────────────────
       Two greys, one ink, one accent, and two signals. Deliberately few: a
       palette with fifteen entries is a palette nobody keeps consistent.

       LOOKC01 - the ink and the soft ink a shade deeper, because the type got
       a pixel smaller: --ink 16.51 : 1 on white, --ink-soft 6.68 : 1 on white
       and 6.17 : 1 on --paper-sunk. StylesheetGuardTests measures them. */
    --ink: #1a1f29;
    --ink-soft: #535d6b;
    --ink-faint: #8b939c;
    --paper: #ffffff;
    --paper-sunk: #f5f6f8;

    /* CAL03 - the hours and the deliveries on the calendar. Two colours of their
       own and NOT two of LOOK01's seven: those belong to STATES, and an hour has
       no state. Measured against white, against --paper-sunk and against their
       own tint: the worst is 6.17 : 1, well past the 4.5 : 1 asked for. Their
       nearest neighbour among the seven state inks is 54 units away in RGB,
       which is what keeps them from reading as "some state or other". */
    --time-ink: #8a2f6b;
    --time-soft: #fbeaf4;

    --upload-ink: #4b6116;
    --upload-soft: #eef4e0;

    /* CALFIX02 - and what a day OWES. A task is drawn in the same red as a
       refusal, deliberately: an overdue card and a thing that went wrong are
       the same kind of news. A milestone is a darker red still, because a date
       that must be met outranks a card that has slipped.

       Named for the calendar rather than reused as --bad, so that the day the
       calendar wants a different red the error colour does not move with it.
       Measured against white, against --paper-sunk and against their own tint:
       the worst is 6.81 : 1, past the 4.5 : 1 asked for. */
    --task-ink: #9b2226;
    --task-soft: #fbeaea;

    /* PROJ01 - the projection simulator's two projectors and its wall, the
       original's own colours (P1 blue, P2 orange, the wall olive), kept so a
       drawing on this page reads like the one Filippo validated. They are
       lines and fills in a drawing, never text on a coloured ground, and
       they mean nothing anywhere else in NANO. NANO has no dark theme, so
       the original's dark variants have nowhere to go. */
    --pj-p1: #2a78d6;
    --pj-p2: #eb6834;
    --pj-wall: #5d7a2a;

    --milestone-ink: #5c0f14;
    --milestone-soft: #f7e9ea;
    /* LOOKC01 - the rules a touch firmer, to hold shorter rows apart. */
    --rule: #dde1e7;
    --rule-strong: #c2c9d2;
    /* LOGO02 — the accent is the brand's navy, taken from the logo artwork.
       Two values and no selectors: the accent already reaches the links, the
       primary button, the outline of a focused field, the marker under the
       current page and the row under the pointer, so changing it here changes
       all of them at once. That is what the variables are for.
       On white it measures 15.66 : 1, and white on it measures the same — so
       the primary button reads either way round. */
    --accent: #001b64;
    --accent-soft: #eef0f7;
    --bad: #9b2226;
    --bad-soft: #fbeaea;
    --good: #2d6a4f;
    --good-soft: #e8f3ee;

    /* LOOK02 - orange, and it means one thing: this calls the model and it
       costs. A BUTTON orange has to carry white text, so it is nearer #b45309
       than #f97316: white on it measures 5.02 : 1, past the 4.5 asked for.
       It is 62 units from --bad in RGB, which is the yardstick CAL03 set for
       "not the same colour as that other one at a glance". */
    --ai: #b45309;
    --ai-soft: #fdf4ea;

    /* The four filled buttons, one tone down, for :hover.
       Measured values and not filter: brightness(), which LOGO02 already found
       lightens where it was meant to deepen. White on each of them:
       act 18.23, confirm 10.03, danger 11.04, ai 7.51 - all past 4.5. */
    --accent-deep: #00113f;
    --good-deep: #1f4a37;
    --bad-deep: #75181b;
    --ai-deep: #8a3f07;

    /* ── Spacing ─────────────────────────────────────────────────────────────
       One step of 4px, used in multiples. A scale with arbitrary values is how
       margins stop lining up.

       LOOKC01 - no longer multiples of 4: 4, 6, 10, 12, 18, 24. Filippo chose
       the compact variant on 2026-10-09 ("direi C") so that a page shows more
       rows; the scale is still ONE scale, and every margin still reads it from
       here, which is what keeps them lining up. */
    --gap-1: 4px;
    --gap-2: 6px;
    --gap-3: 10px;
    --gap-4: 12px;
    --gap-5: 18px;
    --gap-6: 24px;

    /* ── Type ────────────────────────────────────────────────────────────────
       The system stack: nothing is downloaded, so nothing can fail to arrive.
       15px because 16 is comfortable for prose and this is not prose.

       LOOKC01 - and 14px, with a tighter line, for the same reason taken one
       step further: lists, BOQs and the board are read by scanning, and a row
       of 28 pixels instead of 39 is some forty per cent more of them on the
       screen (measured on /documents, 2026-10-09). */
    --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, "Cascadia Mono", Consolas, monospace;
    --size: 14px;
    --size-small: 12px;
    --size-h1: 20px;
    --size-h2: 15px;
    --line: 1.35;

    --radius: 3px;

    /* NOTES03 - how wide a line of prose may get. Sixty to eighty characters is
       what a person reads without losing the line; a note two thousand pixels
       wide is a note nobody proof-reads. Used by the editor, and by nothing
       else: the lists and the tables want the whole page. */
    --measure: 78ch;

    /* LOOK03 - the measure for an ANSWER, which is read once and skimmed rather
       than proof-read. Wider than --measure because a chat answer is mostly
       short lines - lists, headings, tables - and at 78ch on a 1770px screen it
       read in a column with half the window empty beside it. Still a measure,
       though: the alternative was "the whole width", and a paragraph 1700 pixels
       long loses the line on every return.

       In PIXELS and not in ch, and that is a correction worth recording: `ch` is
       the width of the digit zero, which in this font is 8.1px, so the 90ch the
       lot asked for came out at 730px - narrower than the 800 the same lot set
       as its floor. 960px is the number that was actually wanted. */
    --measure-wide: 960px;

    /* LOOK01 — 1400 e non 1100. Chiesto il 2026-09-04 davanti al kanban: con
       quattro colonne e una tabella di progetti, 1100 lasciava un terzo dello
       schermo vuoto su ogni lato. Sotto i 1400 non cambia niente: main ha gia'
       i suoi margini e il contenuto prende quello che c'e'. */
    --page-width: 1400px;

    /* LOOKC01 - every other row of a table, a shade under white: on a wide
       BOQ the eye keeps its line across without a rule per cell. */
    --paper-zebra: #f9fafb;

    /* LOOKC01 - how long a hover or a focus takes to arrive. Short enough
       that nothing waits for it; switched off entirely, with every other
       transition, under prefers-reduced-motion at the end of this file. */
    --ease: 100ms ease-out;
}

/* ── Page frame ───────────────────────────────────────────────────────────── */

*, *::before, *::after { box-sizing: border-box; }

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

body {
    margin: 0;
    background: var(--paper-sunk);
    color: var(--ink);
    font-family: var(--font);
    font-size: var(--size);
    line-height: var(--line);
}

/* LOGO02 — a light bar, so the logo can be its own colours instead of a
   reversed copy of itself.
   The hairline underneath is load-bearing: the body of the page is --paper-sunk
   and the bar is --paper, which are close enough that without a rule between
   them the bar would dissolve into the page. */
header {
    display: flex;
    align-items: center;
    gap: var(--gap-5);
    padding: var(--gap-3) var(--gap-4);
    background: var(--paper);
    color: var(--ink);
    border-bottom: 1px solid var(--rule-strong);
}

/* LOGO01 — the company, then the product, with a hairline between them.
   Centred rather than on a baseline: an image has no baseline worth aligning
   to, and the two words next to it would sit a pixel off for their whole life.
   The nav keeps its own baseline alignment, which is what its links want. */
header .brand {
    display: flex;
    align-items: center;
    gap: var(--gap-3);
}

/* The height is set here and the width follows, so the file can be replaced
   with a bigger one without touching the stylesheet. */
header .brand img {
    display: block;
    height: 28px;
    width: auto;
}

header .brand strong {
    font-size: var(--size-h2);
    letter-spacing: 0.08em;
    border-left: 1px solid var(--rule-strong);
    padding-left: var(--gap-3);
}

/* MENU01 — flex-wrap, because the bar could not do it. Nine links, a name and
   a button in a row that had no instruction for what to do when the window is
   narrower than they are: they were squeezed until they touched. Wrapping is
   the honest answer; the row-gap is what keeps two lines from reading as one.

   The bar spaces GROUPS; a group spaces its own links. That is why the gap here
   is the wide one. */
header nav {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--gap-6);
    row-gap: var(--gap-2);
    flex: 1;
}

/* MENU01 — one group of the bar: Home | Companies Projects | Tasks Calendar
   Time | Notes Documents | Settings. Who and what, then the day, then what is
   left behind.

   It is an element and not a margin on purpose. A margin looks the same on one
   line and is wrong the moment the bar wraps - it will happily leave Notes on
   one line and Documents on the next, which is the one break that must not
   happen. A flex item cannot be split, so the browser breaks between groups
   because there is nowhere else to break. */
header nav .cluster {
    display: flex;
    align-items: baseline;
    gap: var(--gap-4);
}

/* LOGO02 — opacity is gone as a tool, here and below.
   On a dark bar it was a cheap way to make hierarchy. On a light one it produces
   a grey nobody chose and whose contrast nobody knows. The hierarchy is made by
   the two greys the palette already has: --ink-soft at 6.09 : 1 on white, and
   --ink at 16.91 : 1 for the page you are on (6.68 and 16.51 since LOOKC01). */
header nav a {
    color: var(--ink-soft);
    text-decoration: none;
    padding-bottom: 2px;
    border-bottom: 2px solid transparent;
}

header nav a:hover { color: var(--ink); }


/* The page you are on. Set by each page through a body class, so the marker is
   one rule rather than a conditional in every link.

   LOOK02 - a filled pill rather than a two-pixel line. The bar is not a folder
   and does not become one; it just has to say where you are from across the
   room, which a hairline did not. */
header nav a.here {
    color: var(--accent);
    background: var(--accent-soft);
    border-bottom-color: transparent;
    border-radius: var(--radius);
    padding: 2px var(--gap-2);
    font-weight: 600;
}

/* LOOK02 - and Ask is orange wherever it appears, because orange means the
   model. The same rule as the buttons, in the one place in the bar that leads
   to a paid call. --ai on --ai-soft measures 4.62 : 1. */
header nav a.ai.here {
    color: var(--ai);
    background: var(--ai-soft);
}

header nav a.ai:hover { color: var(--ai); }

/* Who is signed in, pushed to the right end of the bar. HOURS01 turned it from
   a label into a link — it is the way into the account settings — so the rule
   that moves it has to name a class rather than an element. */
/* --ink-soft and NOT --ink-faint, which is what it looks like it wants.
   --ink-faint measures 3.11 : 1 on white, under the 4.5 : 1 floor — it is a
   colour for an em dash standing in for a missing value, not for a word
   somebody reads. */
header nav a.who {
    margin-left: auto;
    font-size: var(--size-small);
    color: var(--ink-soft);
}

header nav form { margin: 0; }

/* LOGO02 — the ordinary button, which the stylesheet already has. Everything
   that used to be here was white-because-the-bar-was-dark; with that gone there
   was nothing left worth overriding except the size, and a bar is a place for a
   small button. */
header nav button {
    font-size: var(--size-small);
    padding: 2px var(--gap-2);
}

/* MENU01 — the first media query this stylesheet has ever had, and it is here
   because the bar is the one part of NANO whose width is not the page's to
   decide: it holds as many words as there are areas, and there will be more
   of them. Below this width the brand and the bar stop sharing a line, and the
   groups give back the air they were given: on two lines the wrapping already
   says where a group ends. */
@media (max-width: 900px) {
    header {
        flex-wrap: wrap;
        gap: var(--gap-3);
    }

    header nav {
        gap: var(--gap-5);
        row-gap: var(--gap-2);
    }

    header nav .cluster { gap: var(--gap-3); }
}

main {
    max-width: var(--page-width);
    margin: 0 auto;
    padding: var(--gap-5) var(--gap-4) var(--gap-6);
}

/* DIAGFIX02 — una pagina che E' uno strumento, e non un testo, si dichiara
   larga (ViewData["Wide"] nel layout): niente larghezza massima, e meno
   margine ai lati. Oggi e' solo l'editor degli schemi, dove i due pannelli di
   draw.io, trecento pixel in due, coprivano il disegno dentro i 1400 della
   pagina. */
main.wide {
    max-width: none;
    padding-left: var(--gap-3);
    padding-right: var(--gap-3);
    padding-bottom: var(--gap-3);
}

footer {
    max-width: var(--page-width);
    margin: 0 auto;
    padding: 0 var(--gap-4) var(--gap-5);
    color: var(--ink-faint);
    font-size: var(--size-small);
}

/* ── Text ─────────────────────────────────────────────────────────────────── */

h1 {
    font-size: var(--size-h1);
    font-weight: 600;
    margin: 0 0 var(--gap-4);
}

h2 {
    font-size: var(--size-h2);
    font-weight: 600;
    margin: var(--gap-6) 0 var(--gap-3);
}

p { margin: 0 0 var(--gap-3); }

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

small { font-size: var(--size-small); color: var(--ink-soft); }

code, .code { font-family: var(--font-mono); font-size: var(--size-small); }

/* LOOK03 - the one line under a page's title that says what the page is for.
   One line: the paragraph it replaces on the chat said the same thing three
   times over and was wrong about a number, which is what a paragraph nobody
   rereads becomes. Kept to the measure so it does not run the width of a
   1770px screen. */
.lede {
    max-width: var(--measure);
    color: var(--ink-soft);
}

/* Said once, used wherever a value is genuinely absent. An em dash is an answer;
   a blank cell is a question about whether the page worked. */
.none { color: var(--ink-faint); }

/* ── The bar under a heading: what this page offers ───────────────────────── */

.bar {
    display: flex;
    align-items: center;
    gap: var(--gap-3);
    flex-wrap: wrap;
    margin-bottom: var(--gap-4);
}

.bar form {
    display: flex;
    gap: var(--gap-2);
    margin: 0;
}

.bar .grow { flex: 1; }

/* LOOK01 — a control inside a bar is as wide as what it holds, not as wide as
   the line. The global rule gives every select and input width: 100%, which is
   right in a form laid out in fields and wrong in a row of filters: it put the
   board's project picker on a line of its own and pushed the date and the
   button onto two more. Scoped to a form that has declared itself a bar, so no
   field anywhere else changes. */
form.bar > select {
    width: auto;
    flex: 0 1 24ch;
}

form.bar > input[type="date"] { width: auto; }

/* ── Panels ───────────────────────────────────────────────────────────────── */

.panel {
    background: var(--paper);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    padding: var(--gap-4);
    margin-bottom: var(--gap-4);
}

.panel > :last-child { margin-bottom: 0; }

/* Two columns of label-and-value, for the card of one thing. */
.facts {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: var(--gap-2) var(--gap-4);
    margin: 0;
}

.facts dt { color: var(--ink-soft); font-size: var(--size-small); }
.facts dd { margin: 0; }

/* ── Tables ───────────────────────────────────────────────────────────────── */
/*
    One of the two building blocks that repeat everywhere, so it is worth doing
    properly once. Rules between rows and not around cells: a grid of boxes is
    harder to read across than a row is.
*/

table {
    width: 100%;
    border-collapse: collapse;
    background: var(--paper);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
}

caption {
    text-align: left;
    color: var(--ink-soft);
    font-size: var(--size-small);
    padding-bottom: var(--gap-2);
}

th, td {
    text-align: left;
    padding: var(--gap-2) var(--gap-3);
    border-bottom: 1px solid var(--rule);
    vertical-align: top;
}

thead th {
    font-size: var(--size-small);
    font-weight: 600;
    color: var(--ink-soft);
    background: var(--paper-sunk);
    border-bottom-color: var(--rule-strong);
    white-space: nowrap;
}

tbody tr:last-child td { border-bottom: none; }

tbody tr:hover { background: var(--accent-soft); }

/* FIX03 - the line just booked, on the Log tab. The same green the "Booked
   11:00-11:35" sentence above it uses, because they are the same fact said
   twice: one in words, one in place. It lasts exactly as long as the address
   that carries the id - a refresh keeps it, the next Book moves it, any other
   link loses it - so there is no timer and no script. */
tbody tr.just-saved,
tbody tr.just-saved:hover { background: var(--good-soft); }

tbody tr.just-saved td { border-bottom-color: var(--good); }

td.right, th.right { text-align: right; }

td.tight, th.tight { white-space: nowrap; width: 1%; }

/* Nothing there yet. A sentence, not an empty frame. */
.empty {
    background: var(--paper);
    border: 1px dashed var(--rule-strong);
    border-radius: var(--radius);
    padding: var(--gap-5);
    color: var(--ink-soft);
    text-align: center;
}

/* ── Forms ────────────────────────────────────────────────────────────────── */
/*
    The other building block. Labels above fields, one column: a form read top to
    bottom is a form nobody loses their place in, and the width is capped so a
    text box does not run the length of a wide screen.
*/

form { margin: 0 0 var(--gap-4); }

.field {
    display: flex;
    flex-direction: column;
    gap: var(--gap-1);
    margin-bottom: var(--gap-3);
    max-width: 520px;
}

.field label { font-size: var(--size-small); color: var(--ink-soft); }

.field .hint { font-size: var(--size-small); color: var(--ink-faint); }

/* NOTES01 — a field that wants the width of the page, not the width of a name.
   The body of a note is the only one so far. */
.field.wide { max-width: none; }

/* The buttons above the note box. They are drawn by the one script in NANO;
   with the script absent this is an empty div and the box below still works. */
.toolbar {
    display: flex;
    gap: var(--gap-2);
    margin-bottom: var(--gap-1);
}

.toolbar button {
    font-size: var(--size-small);
    padding: 2px var(--gap-2);
}

/* A rendered note. The only place in NANO where markup comes from a document
   rather than from a page, so it is the only place these elements appear. */
.prose > :first-child { margin-top: 0; }
.prose > :last-child { margin-bottom: 0; }

.prose h1, .prose h2, .prose h3 {
    font-size: var(--size-h2);
    font-weight: 600;
    margin: var(--gap-5) 0 var(--gap-2);
}

.prose ul, .prose ol { margin: 0 0 var(--gap-3); padding-left: var(--gap-5); }

.prose li { margin-bottom: var(--gap-1); }

.prose blockquote {
    margin: 0 0 var(--gap-3);
    padding-left: var(--gap-4);
    border-left: 2px solid var(--rule-strong);
    color: var(--ink-soft);
}

.prose pre {
    background: var(--paper-sunk);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    padding: var(--gap-3);
    overflow-x: auto;
}

/* Side by side where two fields are genuinely one thought — a pair of dates, or
   the five boxes of a timesheet line that are typed as one gesture. */
.row {
    display: flex;
    gap: var(--gap-3);
    flex-wrap: wrap;
}

.row .field { flex: 1; min-width: 180px; }

/* HOURS01 — a date, a time, a rate. Boxes whose content has a known width, kept
   from stretching across a row and making the line hard to scan. */
.field.narrow { flex: 0 0 auto; min-width: 0; max-width: 160px; }

/* And its opposite: the one field on a row that should take what is left. */
.row .field.grow { flex: 3; max-width: none; }

/* A checkbox reads as one thing with its words, so it sits on the line with
   them instead of under a label of its own. */
label.check {
    display: flex;
    align-items: center;
    gap: var(--gap-2);
    font-size: var(--size);
    color: var(--ink);
}

label.check input { width: auto; }

/* A total belongs at the right end of the heading it totals: the eye goes down
   the right edge looking for numbers, not into the middle of a sentence. */
.right-total {
    float: right;
    font-variant-numeric: tabular-nums;
}

/* Numbers line up under each other or they cannot be compared at a glance. */
td.right, th.right { font-variant-numeric: tabular-nums; }

/* RATES01 — where a number came from, said in one quiet word beside it. On its
   own line so it never pushes the amount out of the column it lines up in. */
.whence {
    display: block;
    color: var(--ink-faint);
    font-variant-numeric: normal;
}

/* Pushes whatever follows to the far end of a bar. */
.bar .grow { flex: 1; }

/* TIMEFILTER01 — the numbers at the right end of the period bar. Tabular so that
   two totals read one under the other when the period changes, rather than
   shifting sideways by a digit. */
.bar strong { font-variant-numeric: tabular-nums; }

input[type="text"],
input[type="search"],
input[type="email"],
input[type="url"],
input[type="tel"],
input[type="date"],
input[type="password"],
select,
textarea {
    font: inherit;
    color: var(--ink);
    background: var(--paper);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    padding: var(--gap-2);
    width: 100%;
}

textarea { min-height: 90px; resize: vertical; }

input:focus, select:focus, textarea:focus {
    outline: 2px solid var(--accent);
    outline-offset: -1px;
    border-color: var(--accent);
}

/* A field the server refused. The colour is a second signal, never the only one:
   the message underneath says what is wrong in words. */
input.wrong, select.wrong, textarea.wrong {
    border-color: var(--bad);
    background: var(--bad-soft);
}

.field .wrong-text { color: var(--bad); font-size: var(--size-small); }

/* ── Buttons ──────────────────────────────────────────────────────────────── */

/* LOOK02 - the shape every button shares. The COLOUR comes from one of the five
   classes below, and the legend at the top of this file says which. */
button, .button {
    font: inherit;
    display: inline-block;
    padding: var(--gap-2) var(--gap-4);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    background: var(--paper);
    color: var(--ink);
    text-decoration: none;
    cursor: pointer;
    white-space: nowrap;
}

/* ── The five ────────────────────────────────────────────────────────────────
   .quiet is the shape above and nothing else: it is what a button looks like
   when it only takes you somewhere. It is named all the same, so that "which of
   the five is this" always has an answer and the gate can ask it. */

button.quiet:hover, .button.quiet:hover {
    border-color: var(--ink-soft);
    background: var(--paper-sunk);
}

button.act, .button.act {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--paper);
}

button.act:hover, .button.act:hover {
    background: var(--accent-deep);
    border-color: var(--accent-deep);
}

button.confirm, .button.confirm {
    background: var(--good);
    border-color: var(--good);
    color: var(--paper);
}

button.confirm:hover, .button.confirm:hover {
    background: var(--good-deep);
    border-color: var(--good-deep);
}

button.danger, .button.danger {
    background: var(--bad);
    border-color: var(--bad);
    color: var(--paper);
}

button.danger:hover, .button.danger:hover {
    background: var(--bad-deep);
    border-color: var(--bad-deep);
}

button.ai, .button.ai {
    background: var(--ai);
    border-color: var(--ai);
    color: var(--paper);
}

button.ai:hover, .button.ai:hover {
    background: var(--ai-deep);
    border-color: var(--ai-deep);
}

/* Visible on all five, including the filled ones where a coloured outline would
   disappear: the ring is drawn OUTSIDE the button, in the ink of the page. */
button:focus-visible, .button:focus-visible {
    outline: 2px solid var(--ink);
    outline-offset: 2px;
}

/* MAIL01 — a button that cannot be pressed has to LOOK like one.
   Rendering `disabled` without styling it is worse than not disabling it: the
   button invites a click and then does nothing, and the person blames the page
   rather than the configuration it is telling them about.

   LOOK02 — and it beats all five, so a disabled Remove is not a red button that
   quietly does nothing. */
button:disabled,
button:disabled:hover {
    background: var(--paper-sunk);
    border-color: var(--rule);
    color: var(--ink-faint);
    cursor: not-allowed;
    filter: none;
}

.actions {
    display: flex;
    align-items: center;
    gap: var(--gap-3);
    margin-top: var(--gap-4);
}

/* AUTOSAVE01 — whether the laptop can be closed.
   Quiet, next to the Save button, and NOT quiet when it has something bad to
   say: .wrong-text is added by the script and turns it red. A silent automatism
   that has broken is worse than not having one. */
.save-status {
    font-size: var(--size-small);
    color: var(--ink-soft);
    font-variant-numeric: tabular-nums;
}

.save-status.wrong-text { color: var(--bad); }

/* UPLOAD01 — somewhere to drop twenty files.
   Hidden in the markup and revealed by the script, so with the script absent
   there is no dead rectangle inviting a drag that does nothing. */
.dropzone {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--gap-1);
    padding: var(--gap-5);
    margin-bottom: var(--gap-2);
    border: 2px dashed var(--rule-strong);
    border-radius: var(--radius);
    background: var(--paper-sunk);
    color: var(--ink-soft);
}

.dropzone.over {
    border-color: var(--accent);
    background: var(--accent-soft);
}

/* UPLOADFIX01 — the files about to go up, one row each, with the total above
   them. The browser's own box beside the picker is emptied after every choice,
   so this is the only honest answer to "what will Upload send". */
.upload-chosen { margin-bottom: var(--gap-3); }
.upload-summary { margin: 0 0 var(--gap-2); font-weight: 600; }
.upload-chosen td { vertical-align: middle; }
.upload-chosen td button { padding: 2px var(--gap-2); font-size: var(--size-small); }

/* UPLOADFIX02 — under a file's name, the folder it came out of, and a warning
   when another file in the list has the same name. */
.upload-from { display: block; color: var(--ink-faint); font-size: var(--size-small); }
.upload-from.wrong-text { color: var(--bad); }
.upload-summary.wrong-text { color: var(--bad); font-size: inherit; }

/* `button` is display: inline-block in this file, which beats the hidden
   attribute; "Clear the list" is hidden while there is nothing to clear. */
[data-upload] button[hidden] { display: none; }

/* The picker is hidden to the eye and not to the keyboard: tabbing to it rings
   the label that stands for it. */
.file-pick:focus-visible + label { outline: 2px solid var(--accent); outline-offset: 2px; }

/* A label dressed as a button, inside a field whose labels are small and grey
   and which stretches its children: it keeps the button's size and ink. */
.field label.button { align-self: flex-start; font-size: var(--size); color: var(--ink); }

/* The one bar there is: the whole request's, which the browser measures. */
.upload-progress { display: block; width: 100%; max-width: 520px; margin-top: var(--gap-2); }
.upload-progress[hidden] { display: none; }

/* The outcome is where the page goes when the upload ends; the box at its top
   is green or red, and nothing on it should look like a focused field. */
.upload-results { margin-top: var(--gap-5); }
.upload-results:focus { outline: none; }

/* ── Messages ─────────────────────────────────────────────────────────────── */

.notice, .problem {
    border: 1px solid;
    border-radius: var(--radius);
    padding: var(--gap-3) var(--gap-4);
    margin-bottom: var(--gap-4);
}

.notice { border-color: var(--good); background: var(--good-soft); color: var(--good); }
.problem { border-color: var(--bad); background: var(--bad-soft); color: var(--bad); }

.problem ul { margin: var(--gap-2) 0 0; padding-left: var(--gap-5); }

/* MEETNOTE01 - the editor's two messages stay out of the way until the script
   has something to say: the Mac changed the note, or this laptop holds text
   NANO never received. */
.writing [data-note-conflict][hidden],
.writing [data-note-restore][hidden] { display: none; }
.writing [data-note-restore] button { margin-left: var(--gap-2); }

/* ── The seven colours ────────────────────────────────────────────────────── */
/*
    LOOK01 — a state has a colour, and this is the only file that knows what a
    colour looks like.

    ── What this replaces, and why it is not a reversal ──────────────────────

    The rule here used to read: no colour coding by code, because the codes are
    Filippo's to add and a stylesheet that knows their names would have to be
    edited every time he adds one. That reasoning was right and still is. What
    changed is WHERE the colour lives.

    It is now a property of the STATE — a name Filippo picks from seven, on the
    Lists page, stored on the row. The page emits `hue-amber` because the row
    says amber; this file knows what amber is and has never heard of
    "delivered". Adding a state adds no line here, which is the whole of what
    the old comment was protecting.

    ── Two variables, four uses ──────────────────────────────────────────────

    Each name sets --hue-ink and --hue-soft and stops. Everything below reads
    those two — the pill, the board's column, its cards, the calendar — so a
    colour is decided once and spent four ways. Every element that carries no
    hue falls back through var(..., <the old value>) and looks exactly as it did.

    ── The numbers were measured ─────────────────────────────────────────────

    Each ink on its own tint, on white, and on --paper-sunk: twenty-one
    contrasts, all >= 4.5 : 1, the worst 5.63 : 1 (green on its tint). Computed
    with the WCAG formula, not judged by eye.
*/

.hue-slate  { --hue-ink: #41505f; --hue-soft: #eef1f4; }
.hue-blue   { --hue-ink: #17457e; --hue-soft: #e8eff8; }
.hue-teal   { --hue-ink: #0f5b5e; --hue-soft: #e2f0f0; }
.hue-green  { --hue-ink: #2d6a4f; --hue-soft: #e8f3ee; }
.hue-amber  { --hue-ink: #7a5300; --hue-soft: #fbf0d9; }
.hue-red    { --hue-ink: #9b2226; --hue-soft: #fbeaea; }
.hue-violet { --hue-ink: #5b3d8f; --hue-soft: #f0eaf9; }

/* The swatch beside a name in the Lists picker. Square, small, and the only
   place a colour is shown for its own sake. */
.swatch {
    display: inline-block;
    width: 14px;
    height: 14px;
    border-radius: 3px;
    background: var(--hue-soft, var(--paper-sunk));
    border: 2px solid var(--hue-ink, var(--rule-strong));
    vertical-align: -2px;
}

/* ── State pills ──────────────────────────────────────────────────────────── */

.pill {
    display: inline-block;
    font-size: var(--size-small);
    padding: 1px var(--gap-2);
    border: 1px solid var(--hue-ink, var(--rule-strong));
    border-radius: 10px;
    background: var(--hue-soft, var(--paper-sunk));
    color: var(--hue-ink, var(--ink-soft));
    white-space: nowrap;
}

/* Coloured but off. The tint and the full-strength word go; the outline keeps
   the colour, dashed, so a closed state is still recognisable as ITS state
   without competing with the open ones. Opacity is not used here for the same
   reason it is not used anywhere: it makes a grey nobody chose. */
.pill.terminal {
    background: var(--paper-sunk);
    color: var(--ink-soft);
    border-color: var(--hue-ink, var(--rule-strong));
    border-style: dashed;
}

/* ── PROJTABS01 — the project's card ──────────────────────────────────────── */
/*
    One heading, one line of facts, and a row of tabs that are LINKS: each tab is
    an address of its own, so the browser's back button and bookmarks work and no
    script is needed. The marker is the same one the site's own nav uses, for the
    same reason — you are somewhere, and one rule says where.
*/

.project-meta {
    display: flex;
    align-items: center;
    gap: var(--gap-3);
    flex-wrap: wrap;
    margin: calc(-1 * var(--gap-2)) 0 var(--gap-4);
    color: var(--ink-soft);
}

/* LOOK02 - tabs that look like tabs.

   Filippo, 2026-09-07: "i tab sono piatti, non si percepisce che sono tab".
   They were: text with a two-pixel line under the current one. Now they have a
   SHAPE - a border on three sides, the top corners rounded, a sunk ground for
   the ones you are not on - and the one you ARE on has the page's own ground
   and no bottom edge, so it runs into the content below it. The eye reads a
   folder, which is the whole point of the metaphor.

   The open bottom is done with a background-coloured line over the container's
   border rather than by removing it: removing it leaves a one-pixel gap where
   the two rules meet, and that gap is exactly what makes a tab look broken. */
.tabs {
    display: flex;
    gap: var(--gap-1);
    border-bottom: 1px solid var(--rule-strong);
    margin-bottom: var(--gap-5);

    /* Under 900px they scroll sideways rather than wrapping: five tabs on two
       lines stop reading as one row of a folder. */
    overflow-x: auto;
    scrollbar-width: thin;

    /* SETFIX01 - and NOT up and down. Asking for one axis gives the other one
       away: a box that is `auto` on x computes to `auto` on y as well, so the
       one pixel the tabs hang below the strip (their -1px bottom margin) became
       a scrollbar of its own beside a row 39 pixels tall. Filippo saw it at
       1600px on the settings, where ten tabs fit and nothing needed to scroll at
       all - a scrollbar for a pixel nobody can reach.

       `hidden` and not `clip`: both do the job here, and `hidden` is the word
       the other five places in this file already use. One less thing that reads
       as deliberate when it is only different. */
    overflow-y: hidden;
}

.tabs a {
    color: var(--ink-soft);
    text-decoration: none;
    padding: var(--gap-2) var(--gap-4);
    margin-bottom: -1px;
    border: 1px solid var(--rule);
    border-bottom-color: transparent;
    border-radius: var(--radius) var(--radius) 0 0;
    background: var(--paper-sunk);
    white-space: nowrap;
}

.tabs a:hover {
    color: var(--ink);
    background: var(--paper);
}

/* FIX03 - what pushes a link away from the tabs. Time's Import is a page of its
   own and not a third view of the hours, and the gap says so. */
.tabs .grow { flex: 1; }

.tabs a.here {
    color: var(--ink);
    font-weight: 600;
    background: var(--paper);
    border-color: var(--rule-strong);
    border-bottom-color: var(--paper);
}

/* A delivery that folds. The browser does the folding — details/summary — and
   only the most recent one is born open; thirty open deliveries are a page three
   metres long. The summary is the one line that says who, what and when. */
details.delivery {
    background: var(--paper);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    margin-bottom: var(--gap-3);
}

details.delivery > summary {
    display: flex;
    align-items: center;
    gap: var(--gap-3);
    flex-wrap: wrap;
    padding: var(--gap-3) var(--gap-4);
    cursor: pointer;
    list-style: none;
}

details.delivery > summary::-webkit-details-marker { display: none; }

details.delivery > summary::before {
    content: "▸";
    color: var(--ink-faint);
    font-size: var(--size-small);
}

details.delivery[open] > summary::before { content: "▾"; }

details.delivery[open] > summary { border-bottom: 1px solid var(--rule); }

details.delivery > .body { padding: var(--gap-3) var(--gap-4) var(--gap-4); }

details.delivery > .body > :last-child { margin-bottom: 0; }

/* ── LISTS01 — a list Filippo edits, one form per row ─────────────────────── */

/* The label takes what is left; the order is a number and gets a number's
   width; a retired row is drawn in the softer grey, never faded with opacity. */
.list-row { margin-bottom: var(--gap-2); }

.list-row input.grow { flex: 1; width: auto; min-width: 160px; }

.list-row input.order { width: 80px; font-variant-numeric: tabular-nums; }

/* LOOK01 — the colour picker sits between the code and the order, and it is as
   wide as the longest of seven short words. The general rule for a select in a
   bar would give it 24ch, which here is a hole. */
.list-row select.hue-pick { flex: 0 0 9em; width: auto; }

.list-row input[type="number"] {
    font: inherit;
    color: var(--ink);
    background: var(--paper);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    padding: var(--gap-2);
}

.list-row.retired input.grow { color: var(--ink-soft); }

.list-row code { color: var(--ink-faint); }

/* ── TAGS01 — a row of labels to tick ─────────────────────────────────────── */

/* On the upload form and beside every file: the boxes in a row that wraps, and
   a retired label the file still carries drawn in the softer grey. */
.tags {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--gap-2) var(--gap-4);
    margin: var(--gap-1) 0 0;
}

.tags label.check { font-size: var(--size-small); }

.tags label.check.retired { color: var(--ink-soft); }

.tags button { font-size: var(--size-small); padding: 2px var(--gap-2); }

/* ── Sign-in ──────────────────────────────────────────────────────────────── */

/* CAL02 — FullCalendar, dressed as NANO.
   =========================================================================

   The library ships no stylesheet: FullCalendar 6 injects its own CSS from the
   JavaScript, so there is nothing for wwwroot/css/vendor/ to hold and nothing
   missing from it. What it DOES expose is a set of --fc-* variables, and those
   are the whole of our contact with it — commandment 2: if a library's rule is
   in the way it gets overridden here, and here is the only place to look for a
   change of ours.

   FIX01 — this block used to end "nothing below copies one of the library's own
   selectors", and now two rules do. Said out loud rather than quietly stopped
   being true: a headline in the month view had to be allowed to wrap, and the
   library says `white-space: nowrap` on it. There is no --fc-* variable for
   that. The two rules are at the end of this block, named, and they are the
   only ones. */
#calendar {
    --fc-border-color: var(--rule);
    --fc-page-bg-color: var(--paper);
    --fc-neutral-bg-color: var(--paper-sunk);
    --fc-small-font-size: var(--size-small);

    /* The one that makes it NANO's: the brand navy for an event and for the
       block you drag out to make one. */
    --fc-event-bg-color: var(--ink);
    --fc-event-border-color: var(--ink);
    --fc-event-text-color: var(--paper);

    --fc-today-bg-color: var(--paper-sunk);
    --fc-now-indicator-color: var(--bad);
    --fc-highlight-color: var(--paper-sunk);

    font-size: var(--size-small);
}

/* The three species, told apart by the class the FEED sends. The feed says what
   a thing IS; this says how it looks. A colour in the JSON would be a
   stylesheet nobody could find. */
#calendar .nano-appointment {
    --fc-event-bg-color: var(--ink);
    --fc-event-border-color: var(--ink);
}

/* A date that must be met. CALFIX02 - dark red, and the darkest thing on the
   grid: nothing else on a calendar is a promise.

   It no longer takes its state's colour. LOOK01 gave it --hue-ink so that a
   milestone read as its stage; a grid where four kinds of thing are told apart
   by colour cannot spend one of those colours on a stage as well, and the
   stage is still on the card, on the pill, where there is room to read it. */
#calendar .nano-milestone {
    --fc-event-bg-color: var(--milestone-soft);
    --fc-event-border-color: var(--milestone-ink);
    --fc-event-text-color: var(--milestone-ink);
    font-weight: 600;
}

/* A reminder of work that lives on the board. CALFIX02 - red, because a task
   on a calendar is a date somebody has not met yet. It still does not move:
   its deadline belongs to its card. */
#calendar .nano-task {
    --fc-event-bg-color: var(--task-soft);
    --fc-event-border-color: var(--task-ink);
    --fc-event-text-color: var(--task-ink);
}

/* CAL03 - what a day COST. A filled but quiet bar: it is not a thing to do, it
   is a thing already done, and it must not compete with the appointments above
   it. It does not move - moving an hour is booking a different one, which is a
   page and a command. */
#calendar .nano-time {
    --fc-event-bg-color: var(--time-soft);
    --fc-event-border-color: var(--time-ink);
    --fc-event-text-color: var(--time-ink);
}

/* CAL03 - what ARRIVED that day. A fact about a day that has happened, so it is
   drawn like the hours and it moves even less. */
#calendar .nano-upload {
    --fc-event-bg-color: var(--upload-soft);
    --fc-event-border-color: var(--upload-ink);
    --fc-event-text-color: var(--upload-ink);
}

/* CALSYNC01 - an event of Filippo's own Google calendar, read in and never
   moved. Its colour is the one chosen in Settings -> Calendar, and it arrives
   the way every colour here does: as a hue-* class beside this one, whose
   --hue-ink and --hue-soft are drawn on a tint like the hours and the
   deliveries. A dashed edge says "this is the diary's, not NANO's". */
#calendar .nano-external {
    --fc-event-bg-color: var(--hue-soft);
    --fc-event-border-color: var(--hue-ink);
    --fc-event-text-color: var(--hue-ink);
    border-style: dashed;
}

/* APPTTIME02 - an appointment or an event that hours were logged from: a tick
   before its title, in the ink the event already wears, so it reads on every
   species' ground. The feed sends the class; the line under the calendar says
   what the tick means. Only here: nothing of it goes to Google. */
#calendar .nano-logged .fc-event-title::before {
    content: "\2713\00a0";
    font-weight: 700;
}

/* MEETNOTE01 - an appointment or an event that has meeting notes: a pencil
   after its title, so it sits beside the tick of the hours and never in its
   place. Said under the calendar like the tick. */
#calendar .nano-noted .fc-event-title::after {
    content: "\00a0\270E";
}

/* CALSYNC01b - Settings -> Calendar, what Google answered. The one thing to do
   on Google's side is a block of its own under the calendar it is about, never
   a box laid over the id; and an id is one long word, so it breaks where it
   must instead of pushing the panel out and being cut on the right. */
.probes dd { min-width: 0; }
.probes code { overflow-wrap: anywhere; }
.probes .problem { margin: var(--gap-2) 0 0; }

#calendar .fc-event { cursor: pointer; }

#calendar .fc-toolbar-title { font-size: var(--size-h2); }

/* FIX01 — a title in the month view wraps, instead of being cut after three
   words.

   Filippo: "I find it all a bit squashed." An appointment whose name carried a
   path showed as "Verify C:\Dev\NANO_INBOX hold…" on one line, because the
   library sets `white-space: nowrap` on a day-grid event and exposes no
   variable for it. So this overrides that selector, which is the first time
   anything here has — see the note at the top of the block.

   TWO lines and not more: three would make one busy Tuesday taller than the
   week around it, and `dayMaxEvents` already decides how many events a day
   shows before it says "+2 more". */
#calendar .fc-daygrid-event { white-space: normal; }

#calendar .fc-daygrid-event .fc-event-title {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* TASKS01 — the board: columns side by side, scrolling sideways when there are
   more than fit. A grid and not a table, because a column is a stack of cards
   and a card is not a row. DND01 — a card drags now, and the form on it stayed:
   see .dragging and .drop-target at the end of this block. */
.board {
    display: flex;
    gap: var(--gap-3);
    align-items: flex-start;
    overflow-x: auto;
    padding-bottom: var(--gap-2);
}

/* LOOK01 — the columns share the room instead of each taking 260px and leaving
   the rest of the page empty. Four columns on a wide screen fill it; a fifth
   makes them all narrower; below about 280px each they stop shrinking and the
   board scrolls sideways, which is what it did before. */
.board .column {
    flex: 1 1 280px;
    background: var(--paper-sunk);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    padding: var(--gap-2);
}

/* LOOK01 — the column's head is the band of colour, and the cards under it only
   carry a stripe. The colour has to say WHERE something is; saying it once,
   loudly, at the top is what makes four columns tell themselves apart across a
   room. */
.board .column > h2 {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin: calc(-1 * var(--gap-2)) calc(-1 * var(--gap-2)) var(--gap-2);
    padding: var(--gap-2);
    border-radius: var(--radius) var(--radius) 0 0;
    background: var(--hue-soft, transparent);
    color: var(--hue-ink, var(--ink));
    font-size: var(--size-small);
    text-transform: uppercase;
    letter-spacing: .04em;
}

/* The count takes the column's colour rather than the faint grey it used to
   have: --ink-faint measures 3.11 : 1 on white and less than that on a tint,
   which is under the floor. A number nobody can read is not a quieter number. */
.board .column .count {
    color: var(--hue-ink, var(--ink-faint));
    font-variant-numeric: tabular-nums;
}

.board .card {
    background: var(--paper);
    border: 1px solid var(--rule);
    border-left: 3px solid var(--hue-ink, var(--rule));
    border-radius: var(--radius);
    padding: var(--gap-2);
    margin-bottom: var(--gap-2);
}

/* A closed card is still a card, just quieter: it is there to be recognised,
   not to be worked on. Its stripe goes grey with it. */
.board .card.is-done {
    color: var(--ink-soft);
    border-left-color: var(--rule-strong);
}

.board .card-meta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap-2);
    margin-top: var(--gap-1);
    font-size: var(--size-small);
}

.board .move { margin-top: var(--gap-2); gap: var(--gap-1); }

.board .move select { flex: 1; min-width: 0; font-size: var(--size-small); }

.board .move button { font-size: var(--size-small); padding: 2px var(--gap-2); }

.board .empty { font-size: var(--size-small); margin: 0 0 var(--gap-2); }

/* DND01 — the two states a drag has, and nothing else. board.js puts the
   classes on and takes them off; what they look like is decided here, because a
   colour chosen in a script is a stylesheet nobody can find.

   The card being carried goes translucent and lifts; the column that would take
   it draws a dashed edge in ITS OWN colour, which is the same thing the board's
   colours already say — the colour is WHERE something is. The column the card
   started in never gets the class, so it never looks like it would accept. */
.board .card.dragging {
    opacity: .5;
    box-shadow: 0 2px 6px rgb(0 0 0 / .18);
    cursor: grabbing;
}

.board .column.drop-target {
    border-style: dashed;
    border-color: var(--hue-ink, var(--rule-strong));
    background: var(--hue-soft, var(--paper-sunk));
}

/* FIX01 — where the card can be dragged, the picker and its button are noise.
   board.js puts `by-hand` on the board when the primary pointer is fine; on a
   phone and a tablet the class never arrives and the form stays, which is the
   only way to move a card with a finger. The form is always in the HTML: this
   hides it, and hiding is a decision about a screen. */
.board.by-hand .move { display: none; }

/* The prompt allowed the title "a shade more room" on the shorter card. It
   needs none: nothing here ever clamped it, so a long title already wraps to
   as many lines as it wants, and adding a clamp to give it "more" space would
   take space away. What the card gains is the form's three lines of height,
   which is the point. */

/* DASH01 — the home page: six cards, and every row is a way out of it.
   =========================================================================

   Two columns above 900px, one below. Not a fixed grid of six: the cards are
   different heights — "Overdue" is often absent altogether — and a masonry of
   them would leave holes. Two columns of panels that flow is what a person
   reads top to bottom on a phone and left to right on a screen.

   `.wide` spans both, for the two cards that are lists of lists: Latest is two
   columns of its own, and the news is a column of sentences. */
.dash {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--gap-3);
    align-items: start;
}

@media (min-width: 900px) {
    .dash { grid-template-columns: 1fr 1fr; }
    .dash .wide { grid-column: 1 / -1; }
}

.dash > .panel { margin: 0; }

.dash h2 {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-top: 0;
}

/* The day headings inside "Next 14 days", and the two halves of "Latest". They
   are subheadings inside a card, so they are quieter than the card's own name. */
.dash h3 {
    margin: var(--gap-3) 0 var(--gap-1);
    font-size: var(--size-small);
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--ink-soft);
}

.dash h3:first-of-type { margin-top: 0; }

/* The card that has to hurt, and only when it is there. An empty Overdue is not
   drawn at all — an absence here is the good news, and a card saying "nothing
   overdue" would be a card asking to be read every morning for no reason. */
.dash .late {
    border-left: 3px solid var(--bad);
}

/* One row of a card: a fixed-width first column so the times, the durations and
   the "3 days" line up down the card, then the thing itself, then whatever is
   quiet about it. */
.rows {
    list-style: none;
    margin: 0;
    padding: 0;
}

.rows li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--gap-2);
    padding: var(--gap-1) 0;
    border-top: 1px solid var(--rule);
}

.rows li:first-child { border-top: none; }

/* LOOK01 — a row carrying a state takes its colour as a stripe, the same way a
   card on the board does: the colour says WHERE something is. */
.rows li[class*="hue-"] {
    border-left: 3px solid var(--hue-ink, var(--rule));
    padding-left: var(--gap-2);
}

.rows .when {
    flex: 0 0 5.5rem;
    color: var(--ink-soft);
    font-size: var(--size-small);
    font-variant-numeric: tabular-nums;
}

.rows .when .pill { font-size: var(--size-small); }

/* The quiet half of a row: which project, which company, how many files. */
.rows .whence { font-size: var(--size-small); }

/* "Latest" is two lists side by side above 700px and stacked below: two columns
   of five rows each are unreadable on a phone. */
.dash .two {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--gap-3);
}

@media (min-width: 700px) {
    .dash .two { grid-template-columns: 1fr 1fr; }
}

/* NEWS01 — the headlines, on the home card and on /news. A list and not a
   table, because a headline is a sentence: it wraps, and a table cell that
   wraps beside five other columns is a table nobody reads across.

   No bullets and no left padding: the marker would compete with the headline,
   which is the only thing on the line that matters. The rule between items is
   what separates them. */
/* AGENT01 - the chat. Two columns: the conversations kept on the left, the one
   being had on the right. One column under 900px, with the list first, because
   on a narrow screen "which conversation" comes before "what it said".

   LOOK03 - the left column is a fixed 260px and no longer 18em. It holds one
   line of title and one small line under it, and a column that grows with the
   font is a column that steals room from the reading for no reason. The right
   one takes everything else, up to the measure. */
.chat {
    display: grid;
    grid-template-columns: 260px minmax(0, 1fr);
    gap: var(--gap-5);
    align-items: start;
}

@media (max-width: 899px) {
    .chat { grid-template-columns: minmax(0, 1fr); }
}

/* LOOK03 - the list is a fold, and above 900px the fold is invisible: the
   summary is hidden and what is left is the plain list of two columns. */
.chat .conversations .fold > summary { display: none; }

@media (max-width: 899px) {
    .chat .conversations .fold > summary {
        display: list-item;
        cursor: pointer;
        margin-bottom: var(--gap-3);
        font-weight: 600;
    }

    /* A height of its own, because here the list sits ON TOP of the conversation
       and thirty entries would be thirty entries of scrolling before the first
       word of the answer. */
    .chat .conversations .fold[open] > :not(summary) { max-width: 100%; }

    .chat .conversations .fold[open] .rows { max-height: 40vh; overflow-y: auto; }
}

/* LOOK03 - the day headings in the list. Small, quiet, and sticky to nothing:
   they are separators, not titles competing with the page's own. */
.chat .conversations h3.when {
    margin: var(--gap-4) 0 var(--gap-1);
    font-size: var(--size-small);
    font-weight: 600;
    color: var(--ink-soft);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.chat .conversations h3.when:first-of-type { margin-top: var(--gap-3); }

/* One entry: ONE line of title, cut, with the whole of it in the tooltip. The
   ellipsis is the point - the title is the first question, and a first question
   is a sentence. Four-line entries made the list as tall as the conversation. */
.chat .conversations .rows li > a {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The conversation being read. The tint has to reach the edges of the row, and
   the row is padded rather than the highlight pulled outwards with negative
   margins: inside the scrolling box the narrow layout gives this list, a row
   wider than its container is a sideways scrollbar under the list. Measured. */
.chat .conversations .rows li {
    padding-left: var(--gap-2);
    padding-right: var(--gap-2);
}

.chat .conversations li.here {
    background: var(--accent-soft);
    border-radius: var(--radius);
}

.chat .conversations li.here > a { font-weight: 600; }

/* LOOK03 - everything in this column reads at the same width, and the question
   box is exactly as wide as the answer above it. They were different widths,
   which is the kind of thing nobody names and everybody sees. */
.chat .exchange {
    display: flex;
    flex-direction: column;
    gap: var(--gap-4);
}

.chat .exchange > * { max-width: var(--measure-wide); width: 100%; }

.chat .exchange .about { margin-bottom: 0; }

/* One turn. A question is a block with the accent down its left edge; an answer
   is the page's own text, because the answer is what somebody came to read.

   LOOK03 - the question stopped being an indented grey aside. Scrolling back
   through a long conversation, the thing being looked for is the question, and
   it has to be findable without reading. */
.chat-turn { max-width: 100%; }

.chat-turn.asked {
    padding: var(--gap-2) var(--gap-3);
    background: var(--paper-sunk);
    border-left: 3px solid var(--accent);
    border-radius: 0 var(--radius) var(--radius) 0;
}

.chat-turn.asked p { margin: 0; }

/* LOOK03 - the model's own headings, made smaller than the page's. A `###` in
   an answer used to render at the size of "Ask NANO" at the top, so an answer
   with three sections had three titles shouting over the one that names the
   page. h3 in the markup, h5 to the eye. */
.chat-turn .reply h1,
.chat-turn .reply h2,
.chat-turn .reply h3,
.chat-turn .reply h4 {
    font-size: 14px;
    font-weight: 600;
    margin: var(--gap-4) 0 var(--gap-1);
}

.chat-turn .reply > :first-child { margin-top: 0; }

.chat-turn .reply ul,
.chat-turn .reply ol { margin-bottom: var(--gap-2); }

.chat-turn .reply li { margin-bottom: 2px; }

/* LOOK03 - a citation that goes somewhere. [#123] to the document the block was
   read in, [news #1033] to the article itself. The accent colour and no
   underline until the pointer is on it: they sit inside sentences, and thirty
   underlined ids in a paragraph is a paragraph nobody can read. */
.cite {
    color: var(--accent);
    text-decoration: none;
    white-space: nowrap;
}

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

/* LOOK03 - the folds and the tally on ONE line under the answer.

   Called `reply` and not `answer` because `.answer` was already taken: it is the
   answer FORM on the Drive findings, a flex row of controls, and naming the chat
   answer the same thing turned every paragraph and heading into a flex item
   sitting beside its neighbour. Found by measuring, not by reading. Three stacked
   blocks made every answer look like it had a three-storey footer. An open fold
   takes the whole row, so its content is not squeezed into a flex item. */
.reply-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--gap-1) var(--gap-3);
    margin-top: var(--gap-3);
    padding-top: var(--gap-2);
    border-top: 1px solid var(--rule);
    font-size: var(--size-small);
}

.reply-meta > details[open] { flex: 1 0 100%; }

.reply-meta > .whence { margin-left: auto; }

/* LOOK03 - the question box is as wide as the answer above it.
   The global field is capped at 520px, which is right for a name and a date and
   wrong for the one box on this page: a question typed into a box half the width
   of the answer it produces looks like it belongs to a different page. Scoped to
   this form, so no field anywhere else changes. */
#ask .field { max-width: none; }

/* An answer that stopped on a bound is not a wrong answer, but it is not a
   finished one either, and the edge says so without shouting. */
.chat-turn.answered.stopped { border-left: 3px solid var(--bad); padding-left: var(--gap-3); }

/* What NANO actually opened. Folded, because five sources under every answer is
   a page of provenance and nobody reads a page of provenance - but one click
   away, because provenance nobody can reach is decoration. */
.chat-turn .sources { font-size: var(--size-small); }

.chat-turn .sources > summary { cursor: pointer; color: var(--ink-soft); }

.chat-turn .sources ul {
    list-style: none;
    margin: var(--gap-2) 0 0;
    padding: 0;
}

.chat-turn .sources li {
    padding: var(--gap-1) 0;
    border-top: 1px solid var(--rule);
}

.chat-turn .sources .extract {
    display: block;
    color: var(--ink-soft);
}

/* AGENTFIX01 - the second fold: what NANO ran, with the arguments and the first
   line of what came back. Same shape as the sources because it answers the
   neighbouring question - the sources say what was READ, this says what was
   ASKED FOR - and monospaced because it is a call and not a sentence. */
.chat-turn .tools { font-size: var(--size-small); }

.chat-turn .tools > summary { cursor: pointer; color: var(--ink-soft); }

.chat-turn .tools ul {
    list-style: none;
    margin: var(--gap-2) 0 0;
    padding: 0;
}

.chat-turn .tools li {
    padding: var(--gap-1) 0;
    border-top: 1px solid var(--rule);
}

.chat-turn .tools .ran {
    display: block;
    font-family: var(--font-mono);
    word-break: break-word;
}

.chat-turn .tools .whence,
.chat-turn .tools .wrong-text { display: block; }

/* AGENT02 - a proposal: something to DECIDE, so it does not look like the folds
   above it. A left edge in the accent colour and a filled ground, because the
   one thing that must never happen is a person scrolling past a card without
   noticing there was a question in it. */
.proposal {
    margin-top: var(--gap-3);
    padding: var(--gap-3);
    border: 1px solid var(--rule);
    border-left: 3px solid var(--accent);
    border-radius: var(--radius);
    background: var(--paper-sunk);
}

.proposal .bar { margin-bottom: var(--gap-2); }

/* Decided: it stays, and it stops asking. Rule 6 in a colour - the card is the
   evidence that the chat suggested this and somebody answered. */
.proposal.confirmed { border-left-color: var(--good); }
.proposal.rejected { border-left-color: var(--rule); opacity: 0.65; }

.proposal .fields {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: var(--gap-1) var(--gap-3);
    margin: 0 0 var(--gap-2);
    font-size: var(--size-small);
}

.proposal .fields dt { color: var(--ink-soft); }
.proposal .fields dd { margin: 0; }

/* Two forms side by side, so neither button can be reached by accident. */
.proposal .actions { display: flex; gap: var(--gap-2); align-items: center; }
.proposal .actions form { margin: 0; }

/* Everything still waiting, wherever it came from. Open by default: a fold that
   hides a decision nobody has made is a fold that makes the decision. */
.chat .pending,
.pending {
    margin-bottom: var(--gap-4);
    padding: var(--gap-3);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
}

.pending > summary { cursor: pointer; font-weight: 600; }

/* == ALERTS01: what NANO has to say this morning =============================

   Above everything on the home page, and drawn only when there is something.
   The panel is the ordinary one; what it gets of its own is a left edge in the
   accent colour and, on the rows that are about NOW, in the bad red - because
   "due in six days" and "was due on Friday" are two different feelings and a
   list where they look the same is a list that gets skimmed.
*/
.panel.alerts {
    border-left: 3px solid var(--accent);
    margin-bottom: var(--gap-4);
}

.panel.alerts li {
    display: flex;
    align-items: baseline;
    gap: var(--gap-3);
}

/* A thing that is late or is today. The whole row, not the pill: it has to be
   visible without being read. */
.panel.alerts li.now { background: var(--bad-soft); }
.panel.alerts li.now > a { color: var(--bad); }

/* Which rule raised it, so a glance can tell a milestone from a stuck reading
   without reading the sentence. Monospaced because it is a code. */
.pill.rule {
    font-family: var(--font-mono);
    font-size: 11px;
    color: var(--ink-soft);
    border-color: var(--rule);
    background: var(--paper-sunk);
}

.panel.alerts li > a { flex: 1; }

.panel.alerts li form { margin: 0; }

.panel.alerts .clear-all { margin-top: var(--gap-3); }

/* CHATFIX01 - the About picker. A bar like the others, and the select is as
   wide as it needs to be rather than as wide as the page: a project code and a
   name is a short line. */
.chat .bar.about {
    display: flex;
    align-items: center;
    gap: var(--gap-2);
    flex-wrap: wrap;
    margin-bottom: var(--gap-3);
}

.chat .bar.about select { width: auto; max-width: 24rem; }

.chat .bar.about label { margin: 0; }

/* CHATFIX01 - Ask NANO in a project's header, beside the state. Pushed to the
   end of the line so it does not sit between the client and the state, which
   read as one phrase. LOOK02 gave it its colour: it is orange, like everything
   that calls the model. */
.project-meta .button.ai { margin-left: auto; }

/* What NANO is doing while it reads. Beside the button, not over the page: a
   spinner that covers the question is a spinner that hides the question. */
.chat .saying {
    color: var(--ink-soft);
    font-size: var(--size-small);
}

/* NEWS05 - the news as cards, the shape HUB's page had. Two columns on a wide
   screen, one under 900px, and the same card half as wide inside the home
   panel. Grid rather than flex: the cards must line up in rows of equal height
   whatever length the model's paragraph came out. */
.news-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--gap-4);
    margin-bottom: var(--gap-4);
}

@media (max-width: 899px) {
    .news-grid { grid-template-columns: minmax(0, 1fr); }
}

.news-card {
    display: flex;
    flex-direction: column;
    gap: var(--gap-2);
    padding: var(--gap-4);
    background: var(--paper);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
}

/* A top pick wears its judgement on its edge, not in a colour over the whole
   card: a page of nine coloured cards is a page with no emphasis in it. */
.news-card.top { border-left: 3px solid var(--good); }

.news-card > header {
    display: flex;
    align-items: center;
    gap: var(--gap-2);
    flex-wrap: wrap;
}

.news-card > header .grow { flex: 1; }

/* The headline. Written after .dash h3 on purpose and with the same weight of
   selector plus one class: inside the home panel a bare h3 is a section label -
   small and upper case - and a headline set in small capitals is a headline
   nobody reads. */
.news-card h3,
.dash .news-card h3 {
    margin: 0;
    font-size: var(--size);
    line-height: 1.35;
    text-transform: none;
    letter-spacing: normal;
    color: var(--ink);
}

.news-card h3 a { font-weight: 600; }

.news-card .card-meta {
    display: flex;
    gap: var(--gap-3);
    flex-wrap: wrap;
}

/* What the model made of it, set apart from the headline it is about. */
.news-card .analysis {
    margin-top: auto;
    padding: var(--gap-3);
    background: var(--paper-sunk);
    border-radius: var(--radius);
}

/* Nothing to show - the feed's own blurb, and one quiet word saying why. */
.news-card .analysis.none { background: transparent; padding: 0; }

.news-card .reasoning,
.news-card .todo {
    margin: 0;
    color: var(--ink-soft);
    font-size: var(--size-small);
}

.news-card .todo { margin-top: var(--gap-2); color: var(--ink); }

.news-card a.pill.sector { text-decoration: none; }

/* NEWS05 - the score, in the corner and readable.

   Four bands, HUB's thresholds: 0.70, 0.55, 0.40. Measured on the ground each
   one sits on: 5.63, 6.70, 5.63 and 6.09 to one - all past 4.5. (LOOKC01:
   the last one is --ink-soft on white, 6.68 since the soft ink deepened.)

   The lowest band is --ink-soft and NOT --ink-faint, which is what the lot
   asked for: --ink-faint on --paper-sunk measures 2.88 : 1, and a number
   nobody can read is not a quiet number, it is a broken one. It stays the
   quietest of the four by losing its fill instead of its contrast. */
.news-score {
    padding: 1px var(--gap-2);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    font-size: var(--size-small);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    white-space: nowrap;
}

.news-score.score-high {
    color: var(--good);
    background: var(--good-soft);
    border-color: var(--good);
}

/* The teal of hue-teal, written out: a score band is not a state, and reading
   --hue-ink here would take whatever colour the row it sits in happens to set. */
.news-score.score-top {
    color: #0f5b5e;
    background: #e2f0f0;
    border-color: #0f5b5e;
}

.news-score.score-mid {
    color: var(--ink-soft);
    background: var(--paper-sunk);
    border-color: var(--rule-strong);
}

.news-score.score-low {
    color: var(--ink-soft);
    background: transparent;
    border-color: var(--rule);
}

/* The home card: the same card, narrower, with the model's paragraph clamped to
   two lines. A card that grew to fit a long answer would push the five out of
   one screen, which is the whole point of the home page. */
.news-grid.compact { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }

.news-card.compact { padding: var(--gap-3); }

.news-card.compact .analysis { padding: var(--gap-2); }

.news-card.compact .reasoning {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}


/* And the one pill that is not a state: "there is something to do about this".

   LOOK02 - it was the good green, on the grounds that it is an opportunity
   rather than a warning. Now it is the AI orange, on better grounds: a MODEL
   wrote that sentence, and the reader ought to know which of the two of them is
   suggesting the thing. Green is reserved for "yes, do it". */
.pill.action {
    border-color: var(--ai);
    background: var(--ai-soft);
    color: var(--ai);
}

/* LOOK02 - what a question costs, under the box. Money going to the model, so
   it is the model's colour and not a grey nobody reads. */
.ai-cost { color: var(--ai); }

/* API01 — a key, shown once, meant to be selected in one go. Its own line,
   monospaced and wrapping, because it is 48 characters that must survive a
   copy: a key broken across a layout is a key pasted wrong. */
.key {
    display: block;
    margin: var(--gap-2) 0;
    padding: var(--gap-2) var(--gap-3);
    background: var(--paper-sunk);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    font-size: var(--size-small);
    word-break: break-all;
    user-select: all;
}

.signin {
    max-width: 340px;
    margin: var(--gap-6) auto;
}

/* LOGO02 — the mark above the form, and nothing else with it. No tagline, and no
   company name written again underneath an image that already contains it. */
.signin-mark {
    text-align: center;
    margin: var(--gap-5) 0 var(--gap-6);
}

.signin-mark img {
    height: 64px;
    width: auto;
}

.signin .field { max-width: none; }


/* == DRIVE03: the questions Drive and the database raise =====================

   A number in the bar and a number in a heading are the same thing said twice,
   so they are one rule. It is small, round and quiet: it says how many, and it
   is not a button. Scoped to the bar and to headings on purpose - the board
   already has a count of its own, and it is a plain number by design.

   The answers sit in a table cell, several forms to a cell, because a finding
   has more than one possible answer and each answer is its own command. They
   wrap rather than overflow: two selects, a field and a button do not fit a
   narrow column on one line, and a cell that scrolls sideways is a cell nobody
   reads.
*/

header nav .count,
h2 .count {
    display: inline-block;
    margin-left: var(--gap-1);
    padding: 0 0.4em;
    min-width: 1.4em;
    border-radius: 999px;
    background: var(--accent-soft);
    color: var(--accent);
    font-size: var(--size-small);
    font-variant-numeric: tabular-nums;
    text-align: center;
}

.lead {
    color: var(--ink-soft);
    max-width: 60ch;
}

small.quiet { color: var(--ink-faint); }

.answer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--gap-1);
    margin-bottom: var(--gap-1);
}

.answer select,
.answer input[type="text"] { max-width: 14rem; }

/* A label the page still needs and the eye does not: the select beside it says
   what it is, and a form control with no label is a form nobody can hear. */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}


/* == NOTES03: the note page IS the editor ===================================

   Filippo, 2026-09-06: "lo spazio per scrivere e' piccolo". It was — an
   eighteen-row textarea in half a panel, under five labelled fields, inside a
   card. So the card is gone, the labels are gone, the second panel is gone, and
   what is left takes the window.

   NOTES02's two panels went with them, and its reason went too rather than
   being overruled: there is no preview any more because there is nothing to
   preview — the text on screen IS the formatted note. What that reason
   protected, the format staying the truth, is now protected by a test instead
   of by a layout. See tools/editor/roundtrip and NoteRoundTripTests.
*/

.writing {
    display: flex;
    flex-direction: column;
    gap: var(--gap-3);

    /* The window, minus the header, the two footers and main's own padding. dvh
       and not vh: on a phone the address bar comes and goes, and vh does not
       know it.

       The number is measured, not guessed: at 1600x1000 the editor comes to
       71 % of the window, and the exit criterion asked for 70. */
    min-height: calc(100dvh - 8rem);
}

/* This one page gives its padding to the text. :has() rather than a flag in the
   layout, so the layout does not have to learn about a page. */
main:has(.writing) { padding-top: var(--gap-3); padding-bottom: var(--gap-3); }

body:has(.writing) > footer { display: none; }

/* The first line of the page, and it looks like one: no box, no label. */
.writing .title {
    font-size: var(--size-h1);
    font-weight: 600;
    line-height: 1.2;
    border: none;
    border-radius: 0;
    background: none;
    padding: 0;
    width: 100%;
}

.writing .title:focus {
    outline: none;
    box-shadow: 0 2px 0 var(--accent);
}

.writing .title::placeholder { color: var(--ink-faint); font-weight: 400; }

/* Day, kind, project, who was there: one thin line. A note is written far more
   often than its filing is changed, so the filing gets one line and the writing
   gets the rest. */
.writing .about {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap-2);
    align-items: center;
    padding-bottom: var(--gap-2);
    border-bottom: 1px solid var(--rule);
}

.writing .about input,
.writing .about select {
    font-size: var(--size-small);
    padding: var(--gap-1) var(--gap-2);
    width: auto;
}

.writing .about input[type="text"] { flex: 1 1 14rem; min-width: 10rem; }

@media (max-width: 899px) {
    .writing .about > * { flex: 1 1 100%; }
}

/* The editor, and the textarea it stands in for. Both take everything left. */
.writing .milkdown-host,
.writing > textarea {
    flex: 1 1 auto;
    min-height: 24rem;
}

.writing > textarea { width: 100%; resize: vertical; }

/* Crepe scrolls INSIDE itself, so the page does not scroll while writing and
   the footer with the save line stays where it is. */
.writing .milkdown-host {
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.writing .milkdown-host .milkdown {
    flex: 1 1 auto;
    overflow-y: auto;
    background: var(--paper);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
}

/* The measure. Sixty to eighty characters is what a person reads without
   losing the line, and a note two thousand pixels wide is a note nobody
   proof-reads. Centred, so the space around it is quiet rather than empty. */
.writing .milkdown-host .milkdown .ProseMirror {
    max-width: var(--measure);
    margin: 0 auto;
    padding: var(--gap-4) var(--gap-4) var(--gap-6);
}

/* Ours, not the library's: the vendor sheets are never edited, and what we want
   different we say here. Crepe's own font stack names families nobody has
   installed; NANO's is the one every other page uses. */
.writing .milkdown-host .milkdown {
    --crepe-font-default: var(--font);
    --crepe-font-title: var(--font);
    --crepe-font-code: var(--font-mono);
    --crepe-color-background: var(--paper);
    --crepe-color-on-background: var(--ink);
}

.writing .footer {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap-3);
    align-items: center;
}

.writing .footer .save-status { margin-left: auto; }

/* A screenshot is as wide as the text and no wider, in the editor and on the
   note's own page. */
.prose img,
.writing .milkdown-host img {
    max-width: 100%;
    height: auto;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
}

/* The pictures kept inside a note, on the note's page and on the project's
   documents tab. Small on purpose: it is a way back to a screenshot, not a
   gallery. */
.thumbs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap-3);
    margin: 0 0 var(--gap-3);
    padding: 0;
    list-style: none;
}

.thumbs li {
    width: 12rem;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    padding: var(--gap-2);
    background: var(--paper);
}

.thumbs img {
    display: block;
    width: 100%;
    height: 7rem;
    object-fit: cover;
    border-radius: var(--radius);
    margin-bottom: var(--gap-1);
    background: var(--paper-sunk);
}

.thumbs small { display: block; color: var(--ink-faint); }

/* CAL03 - a checkbox with its word, on one line, in a bar of buttons and
   selects. The label is the target, so the word is clickable too. */
.tick {
    display: inline-flex;
    align-items: center;
    gap: var(--gap-1);
    font-size: var(--size-small);
    color: var(--ink-soft);
    white-space: nowrap;
}

/* == TIME02: the hours, drawn ==============================================

   Two SVGs the server writes. Everything about their shape is an attribute -
   coordinates are coordinates - and everything about their colour is here.

   FIX03 - the eight series are a CATEGORICAL palette, not a ramp.

   TIME02 chose eight tints of one navy, so that nobody would read a meaning
   into which colour a job got. Filippo looked at a day with two projects in it
   and could not tell them apart, which settles the argument: a chart that does
   not SEPARATE has failed at the only thing it is for. Reading a meaning into a
   colour is a small risk; not being able to see the bars is the whole point
   missed.

   Okabe-Ito, the eight-colour set drawn to stay distinguishable to the common
   forms of colour blindness. Adjacent series - which is where confusion
   actually happens, because the colours are assigned by hours descending - are
   at worst 62 units apart in RGB, and the closest pair anywhere in the set is
   the same 62. None of them is any of the calendar's inks or the seven state
   hues; the nearest of those is 72 away.

   The yellow does not hold text on white, so NO text is ever coloured with a
   series: the fill is the colour, the words stay --ink. The ninth project and
   everything after share the last step, because by then the difference is a
   rounding error.
*/

.charts { margin: 0 0 var(--gap-4); }

.charts summary {
    cursor: pointer;
    color: var(--ink-soft);
    font-size: var(--size-small);
    margin-bottom: var(--gap-2);
}

.charts h3 {
    font-size: var(--size-small);
    font-weight: 600;
    color: var(--ink-soft);
    margin: var(--gap-3) 0 var(--gap-1);
}

.chart-days {
    width: 100%;
    height: 280px;
}

/* A row is 24 units tall in the viewBox, and the viewBox has no fixed height:
   the picture grows with the number of projects rather than squashing them. */
.chart-projects {
    width: 100%;
    height: auto;
}

.chart-days .grid,
.chart-days .axis-line {
    stroke: var(--rule);
    stroke-width: 1;
}

.chart-days .axis-line { stroke: var(--rule-strong); }

.charts .axis {
    font-size: 11px;
    fill: var(--ink-faint);
}

.chart-projects a text { fill: var(--ink-soft); }
.chart-projects a:hover rect { opacity: 0.85; }

.charts .series-1 { fill: #e69f00; }   /* orange */
.charts .series-2 { fill: #56b4e9; }   /* sky blue */
.charts .series-3 { fill: #009e73; }   /* bluish green */
.charts .series-4 { fill: #f0e442; }   /* yellow */
.charts .series-5 { fill: #0072b2; }   /* blue */
.charts .series-6 { fill: #d55e00; }   /* vermillion */
.charts .series-7 { fill: #cc79a7; }   /* reddish purple */
.charts .series-8 { fill: #999999; }   /* grey */
.charts .series-other { fill: var(--rule-strong); }

/* A hairline round every filled shape: on a pale series against white paper the
   edge of a short bar would otherwise be a guess. */
.charts rect { stroke: var(--rule-strong); stroke-width: 0.5; }

/* AI01 - what NANO has spent against what it may spend. A <progress>, because
   a bar whose length is data belongs in an attribute: a percentage in a style
   attribute would be the one thing the pages are not allowed to carry. */
.meters {
    display: grid;
    grid-template-columns: 10em 1fr;
    gap: var(--gap-2) var(--gap-4);
    align-items: center;
    margin: 0;
}

.meters dt { color: var(--ink-soft); }

.meters dd { margin: 0; }

.meters dd.meter {
    display: flex;
    align-items: center;
    gap: var(--gap-3);
}

.meters progress {
    flex: 0 0 18em;
    height: 10px;
    accent-color: var(--accent);
}

.meters .meter span { font-variant-numeric: tabular-nums; }

/* FIX03 - the legend under the first chart. A square of the series and the
   project's code, in the order the colours were handed out, so a bar can be
   named without hovering it. */
.chart-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap-1) var(--gap-3);
    margin: var(--gap-1) 0 0;
    padding: 0;
    list-style: none;
    font-size: var(--size-small);
    color: var(--ink-soft);
}

.chart-legend li {
    display: inline-flex;
    align-items: center;
    gap: var(--gap-1);
}

.chart-legend svg {
    width: 12px;
    height: 12px;
    flex: none;
}

/* Under seven hundred pixels a day label every other one: thirty dates under a
   chart that is now four hundred pixels wide is a smudge. */
@media (max-width: 699px) {
    .chart-days { height: 220px; }

    /* One day label in two: which ones is decided by the server, which is the
       only place that knows the index. */
    .chart-days .wide-only { display: none; }
}

/* ── READ01 — a bill of quantities read back ──────────────────────────────────

   Three surfaces: the sheets to choose before a penny is spent, the columns as a
   person corrects them, and the reading itself. The rule that shapes all three
   is that a number NANO worked out and a number the file states must never look
   alike — so a difference is red and a match is green, and neither is a shade of
   the body text somebody has to compare by eye. */

/* The reconciliation, at the top of the reading, because it is the answer. The
   grid is .facts's — this only makes the figures tabular, so that two totals one
   above the other line up digit under digit, which is the whole reason for
   putting them one above the other. */
.reconciliation dd { font-variant-numeric: tabular-nums; }

.reconciliation code {
    background: var(--paper-sunk);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    padding: 0 var(--gap-1);
}

.reconciliation .actions { margin-top: var(--gap-4); }

/* What did not add up, in words, before the six hundred lines of evidence. The
   red is on the left edge and not on the text: a paragraph in red is a paragraph
   nobody finishes reading. */
.failures li {
    border-left: 3px solid var(--bad);
    padding-left: var(--gap-3);
}

.failures code {
    background: var(--paper-sunk);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    padding: 0 var(--gap-1);
}

/* A rule's own name, said once. Not a state, so it does not take a state's
   colour — it is the label of the check, in the grey of a label. */
.pill.rule {
    font-variant: normal;
    letter-spacing: 0.02em;
}

.pill.good { --hue-ink: var(--good); --hue-soft: var(--good-soft); }
.pill.bad  { --hue-ink: var(--bad);  --hue-soft: var(--bad-soft); }

/* The lines. Numbers right, tabular, and narrow: six hundred rows only work if
   a column of totals reads as a column. */
.lines td.right, .lines th.right { font-variant-numeric: tabular-nums; }

.lines small { display: block; }

/* A line whose quantity times its unit price is not its total. The tint is on
   the whole row, because the wrong number is in one cell but the line is what
   somebody has to go and look at. It survives the hover, or the row somebody is
   pointing at would be the one row that stops saying it is wrong. */
.lines tr.wrong-row,
.lines tr.wrong-row:hover { background: var(--bad-soft); }

.lines tr.wrong-row td { border-bottom-color: var(--bad); }

/* The number NANO worked out, under the number the file states. */
.wrong-text { color: var(--bad); font-size: var(--size-small); }

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

/* The sheets to read. A sheet NANO does not think is a priced list is still
   there and still tickable — the guess is a guess — but it steps back. */
.sheets tr.quiet-row td { color: var(--ink-soft); }

/* The columns as a person corrects them: eight little boxes of one or two
   letters, laid out side by side as they are read off the spreadsheet open
   beside the page. .field stacks a label over its input already; all this does
   is put the eight of them in a row and stop each one being 520px wide. */
.columns {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap-3);
    align-items: flex-end;
}

/* Five characters. A column letter is one or two; a box that could hold a
   sentence invites one. */
.columns .field {
    margin-bottom: 0;
    max-width: none;
}

.columns .field input {
    width: 6ch;
    text-transform: uppercase;
}

/* The heading row is a number, and numbers are not upper case. */
.columns .field input[inputmode="numeric"] { text-transform: none; }

.columns .actions { margin: 0; }

/* READFIX01 — the rows a sheet adds to its own total after the lines are done.
   Inside the reconciliation, because that is what they explain: a footer bigger
   than the lines, and by exactly how much. */
.reconciliation .summaries {
    list-style: none;
    margin: 0;
    padding: 0;
}

.reconciliation .summaries li {
    display: flex;
    gap: var(--gap-2);
    align-items: baseline;
    justify-content: flex-end;
}

.reconciliation .summaries .what { color: var(--ink-soft); }

.reconciliation .summaries .worth { font-variant-numeric: tabular-nums; }

/* The failures past the tenth. Ten is what a person reads; the rest is what a
   person checks. The browser does the folding. */
details.more-failures > summary {
    cursor: pointer;
    color: var(--ink-soft);
    font-size: var(--size-small);
    padding: var(--gap-2) 0;
}

details.more-failures > summary:hover { color: var(--ink); }

details.more-failures > .rows { margin-top: var(--gap-2); }

/* ── LOG01b / LOGFIX01 — the logbook ──────────────────────────────────────────

   The narrow layout is the BASE and not a media query, because that is the order
   the study asked for: the useful notes are written on a site and in a meeting,
   on a phone.

   LOGFIX01 changed what "the base" means. LOG01b drew an entry as three stacked
   lines with a rule under it — a hundred pixels each, which reads as a diary in
   pages the moment there are ten of them. Filippo, in front of it: «la data della
   nota, con il giorno completo, e il progetto di riferimento devono essere a
   fianco alla nota; tutto deve essere sequenziale». So it is a REGISTER: a table,
   one row an entry, thirty pixels each, and the columns turn into blocks under
   600px so a phone still gets a list. */

/* ── The one line of filters, at the top ──────────────────────────────────── */

.log-filters {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--gap-2);
    max-width: var(--measure-wide);
    margin-bottom: var(--gap-3);
}

/* Every control in NANO is a block a hundred per cent wide, because everywhere
   else it sits in a stacked field. On a LINE that rule is the line: the two date
   boxes came out 960px each and the row was four rows tall. */
.log-filters input, .log-filters select { width: auto; }

.log-filters input[type="search"] { flex: 1 1 10rem; min-width: 7rem; max-width: 22rem; }

.log-filters select { flex: 0 1 14rem; }

.log-filters input[type="date"] { flex: 0 0 auto; width: 9.5rem; }

/* The count, and it is the last thing on the line because it is the answer to
   the question the line asks. */
.log-filters .written {
    margin-left: auto;
    color: var(--ink-soft);
    font-size: var(--size-small);
    font-variant-numeric: tabular-nums;
}

.log-filters .clear { font-size: var(--size-small); }

/* ── The box, with its button beside it ───────────────────────────────────── */

.log-box {
    max-width: var(--measure-wide);
    margin-bottom: var(--gap-3);
}

.log-box .line {
    display: flex;
    align-items: flex-start;
    gap: var(--gap-2);
}

/* One row to start with, and logbook.js grows it. The min-height is what keeps
   it a line rather than a form even with the script switched off. */
.log-box textarea {
    flex: 1 1 auto;
    min-height: 2.4rem;
    resize: vertical;
    font-family: var(--font);
}

.log-box .hint { display: block; margin-top: var(--gap-1); }

.log-box .saying { margin-left: var(--gap-2); }

/* A label that is read aloud and not drawn. The filters and the box are lines of
   controls, and a caption over each is not a line any more; every one of them
   still says its own name to a screen reader. */
.away {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* The names that drop under the cursor. Positioned in the flow rather than
   floated over the page: at four hundred pixels an overlay covers the very
   thing being typed. */
.log-suggest {
    list-style: none;
    margin: 0 0 var(--gap-3);
    padding: 0;
    max-width: var(--measure-wide);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    background: var(--paper);
}

.log-suggest li {
    display: flex;
    gap: var(--gap-2);
    align-items: baseline;
    padding: var(--gap-2) var(--gap-3);
    cursor: pointer;
}

.log-suggest li + li { border-top: 1px solid var(--rule); }

.log-suggest li.here { background: var(--accent-soft); }

.log-suggest li small { color: var(--ink-soft); }

/* ── The register ─────────────────────────────────────────────────────────── */

table.log {
    width: 100%;
    max-width: var(--measure-wide);
    border-collapse: collapse;
}

/* Alternate rows and no rules: at thirty pixels a row, a hairline under each one
   is a page of hairlines. The tint is the faintest thing in the palette. */
table.log tr:nth-child(even) { background: var(--paper-sunk); }

table.log td {
    padding: var(--gap-1) var(--gap-2);
    vertical-align: top;
}

/* The two columns that line up. Fixed, and tabular figures, so the eye can run
   down them: that is the whole difference between a register and a list. */
table.log td.when {
    width: 12.5rem;
    white-space: nowrap;
    color: var(--ink-soft);
    font-size: var(--size-small);
    font-variant-numeric: tabular-nums;
}

table.log td.project {
    width: 5.5rem;
    font-size: var(--size-small);
}

/* The text takes the rest, and a long entry wraps inside its own cell. */
table.log td.text { width: auto; }

table.log td.text .said > :first-child { margin-top: 0; }

table.log td.text .said > :last-child { margin-bottom: 0; }

table.log td.text .said p { display: inline; margin: 0; }

/* What the parser found, in the tail of the text. Quiet on purpose: it is a
   reading of the line and not part of it. */
table.log td.text .pill {
    margin-left: var(--gap-1);
    font-size: var(--size-small);
}

table.log td.edit {
    width: 5.5rem;
    text-align: right;
    white-space: nowrap;
    font-size: var(--size-small);
}

table.log td.edit a.quiet { color: var(--ink-faint); }

table.log tr:hover td.edit a.quiet { color: var(--accent); }

table.log td.edit .edited { color: var(--ink-faint); font-style: italic; }

table.log .log-edit textarea { width: 100%; font-family: var(--font); }

table.log .log-edit .actions {
    display: inline-flex;
    gap: var(--gap-2);
    align-items: baseline;
    margin-top: var(--gap-1);
}

.log-more { margin-top: var(--gap-4); }

/* ── On a phone the register is a list ────────────────────────────────────── */

@media (max-width: 599px) {
    /* The filters keep the same six controls and pack them in pairs: the two
       questions about words on one line, the two about days on the next, and the
       verbs with the count under them. Three lines and not two, which the lot
       asked for: two is only reachable by breaking the line between «from» and
       «to», and a date pair split across a wrap reads worse than a third line. */
    .log-filters input[type="search"] { flex: 1 1 8rem; max-width: none; }

    .log-filters select { flex: 1 1 8rem; }

    .log-filters input[type="date"] { flex: 1 1 6.5rem; width: auto; min-width: 0; }

    .log-filters .written { margin-left: var(--gap-2); }

    table.log, table.log tbody, table.log tr, table.log td { display: block; width: auto; }

    table.log tr { padding: var(--gap-2) var(--gap-2) var(--gap-3); }

    /* The day and the job on one line above the text, which is the order they
       are read in when there is no room for columns. */
    table.log td.when, table.log td.project, table.log td.edit {
        display: inline;
        padding: 0;
        width: auto;
        text-align: left;
    }

    table.log td.project::before { content: " · "; color: var(--ink-faint); }

    table.log td.edit::before { content: " · "; color: var(--ink-faint); }

    table.log td.text { padding: var(--gap-1) 0 0; }
}

/* AGENTFIX02 — the call that asked for one project and read another. Quiet, and
   in the fold where the calls already are: it is a fact about one call and not a
   warning about the answer, which the anchor made correct. */
.chat-turn .tools .pill.ignored {
    background: var(--ai-soft);
    color: var(--ai);
    font-size: var(--size-small);
}

/* ── LOG02 — what a line became, and what it might become ─────────────────────

   The cards under a line are the chat's own, and they carry their own style from
   there. What is new is the tail of the text: «→ appointment #12», a link, quiet
   — it is a footnote to the line and not a second sentence. */

table.log td.text .log-cards { display: block; }

table.log td.text .became {
    display: inline-block;
    margin-left: var(--gap-1);
    font-size: var(--size-small);
    color: var(--good);
}

table.log td.text .became:hover { color: var(--good-deep); }

/* A card inside a register row: the same card, given room to breathe and kept
   inside the column so a table of them still lines up. */
table.log td.text .proposal {
    display: block;
    margin: var(--gap-2) 0 var(--gap-1);
    max-width: var(--measure);
}

/* == LOG03: the timeline of a job ==========================================

   Seven kinds of thing on one table, newest first. It is the register's own
   shape with one column more, on purpose: the two tables answer the same
   question at two scales - "what happened" over the diary and over a job - and
   a reader who has learnt to run down the left edge of one should not have to
   learn the other.

   ========================================================================== */

table.timeline {
    width: 100%;
    max-width: var(--measure-wide);
    border-collapse: collapse;
}

table.timeline tr:nth-child(even) { background: var(--paper-sunk); }

table.timeline td {
    padding: var(--gap-1) var(--gap-2);
    vertical-align: top;
}

/* The same 12.5rem the register uses, so a job's history and the diary line up
   when they are open in two tabs. Tabular figures for the same reason. */
table.timeline td.when {
    width: 12.5rem;
    white-space: nowrap;
    color: var(--ink-soft);
    font-size: var(--size-small);
    font-variant-numeric: tabular-nums;
}

/* Fixed, because a column of pills that changed width per row would undo the
   line the column is there to draw. */
table.timeline td.kind {
    width: 7.5rem;
    white-space: nowrap;
}

table.timeline td.what { width: auto; }

/* Who did it, after what was done: it is provenance (rule 4), and provenance is
   a footnote to a fact and not part of the sentence. */
table.timeline td.what .whence {
    margin-left: var(--gap-1);
    font-size: var(--size-small);
    color: var(--ink-faint);
}

/* ── The seven kinds ──────────────────────────────────────────────────────────

   Six tints and one plain, and every one of them is a colour the palette ALREADY
   holds, next to the thing it names: an hour on this table is the colour an hour
   is on the calendar. Seven new colours would be a second palette, and the file
   says at the top why there is only one.

   The diary keeps the plain pill deliberately: it is the commonest row here, and
   the loudest colour should not belong to the commonest thing. */
.pill.kind-hours      { --hue-ink: var(--time-ink);      --hue-soft: var(--time-soft); }
.pill.kind-documents  { --hue-ink: var(--upload-ink);    --hue-soft: var(--upload-soft); }
.pill.kind-task       { --hue-ink: var(--task-ink);      --hue-soft: var(--task-soft); }
.pill.kind-milestones { --hue-ink: var(--milestone-ink); --hue-soft: var(--milestone-soft); }
.pill.kind-events     { --hue-ink: var(--accent);        --hue-soft: var(--accent-soft); }
.pill.kind-proposals  { --hue-ink: var(--ai);            --hue-soft: var(--ai-soft); }

/* ── On a phone it is a list, like the register ───────────────────────────── */

@media (max-width: 599px) {
    table.timeline, table.timeline tbody, table.timeline tr, table.timeline td {
        display: block;
        width: auto;
    }

    table.timeline tr { padding: var(--gap-2) var(--gap-2) var(--gap-3); }

    /* The day and the kind on one line above what happened, which is the order
       they are read in when there is no room for columns. */
    table.timeline td.when, table.timeline td.kind {
        display: inline;
        padding: 0;
        width: auto;
    }

    table.timeline td.kind::before { content: " "; }

    table.timeline td.what { padding: var(--gap-1) 0 0; }
}

/* == DIAG01: gli schemi ====================================================

   Due superfici: la lista delle miniature di un progetto, e la pagina-editor.
   Di draw.io qui non c'e' NIENTE: vive dentro un iframe e si porta dietro il
   proprio foglio di stile, che e' meta' della ragione per cui e' in un iframe.
   Quel che sta qui e' la cornice - la barra sopra e la griglia delle schede.

   ========================================================================== */

/* -- La lista: schede con la miniatura ----------------------------------- */

ul.diagrams {
    list-style: none;
    margin: var(--gap-3) 0 0;
    padding: 0;

    /* auto-fill e non auto-fit: con un solo schema la scheda resta della sua
       misura invece di allargarsi per tutta la pagina, che e' quello che fa
       sembrare una lista di uno un errore. */
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: var(--gap-3);
}

ul.diagrams > li {
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    background: var(--paper);
    overflow: hidden;
}

/* La miniatura ha un rapporto FISSO: le schede si allineano in griglia anche
   quando i disegni dentro sono di forme diversissime, che e' il caso normale
   fra una planimetria e uno schema a blocchi. */
ul.diagrams .sheet {
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 4 / 3;
    background: var(--paper-sunk);
    border-bottom: 1px solid var(--rule);
    overflow: hidden;
}

ul.diagrams .sheet img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

/* «render pending» a parole. Non un rettangolo grigio che sembra un'immagine:
   il disegno e' al sicuro su Drive, e un segnaposto che sembra una figura e'
   una bugia. */
ul.diagrams .sheet .pending {
    font-size: var(--size-small);
    color: var(--ink-faint);
    font-style: italic;
}

ul.diagrams .what {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--gap-2);
}

ul.diagrams .what .whence {
    font-size: var(--size-small);
    color: var(--ink-soft);
    font-variant-numeric: tabular-nums;
}

ul.diagrams .exports {
    display: flex;
    gap: var(--gap-2);
    margin-top: var(--gap-1);
    font-size: var(--size-small);
}

/* -- La pagina-editor ----------------------------------------------------- */

/* La forma di NOTES03: una barra sottile, e poi il disegno che si prende il
   resto della finestra. */
.diagram-bar {
    display: flex;
    align-items: center;
    gap: var(--gap-2);
    flex-wrap: wrap;
    margin-bottom: var(--gap-2);
}

.diagram-bar .title {
    flex: 0 1 22rem;
    font-size: var(--size-h2);
    font-weight: 600;
    border: 1px solid transparent;
    background: transparent;
    padding: var(--gap-1) var(--gap-2);
}

/* Il bordo compare al passaggio: il nome e' un campo, ma e' anche il titolo
   della pagina, e un titolo con una casella intorno tutto il giorno e' un
   modulo. */
.diagram-bar .title:hover,
.diagram-bar .title:focus {
    border-color: var(--rule-strong);
    background: var(--paper);
}

.diagram-bar .whence {
    font-size: var(--size-small);
    color: var(--ink-soft);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* L'iframe. L'altezza e' calcolata perche' un iframe non sa quanto e' alto il
   suo contenuto: senza un'altezza esplicita collassa a 150px, che e' il valore
   che i browser hanno scelto nel 1996 e non hanno piu' cambiato.

   DIAGFIX02 — 9rem e non 14: sopra ci sono la testata e UNA riga di barra, e
   i cinque rem in piu' erano disegno tolto a ogni apertura. Su uno schermo
   basso tiene il min-height. */
iframe.drawio {
    display: block;
    width: 100%;
    height: calc(100vh - 9rem);
    min-height: 30rem;
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    background: var(--paper);
}

/* Senza JavaScript non c'e' editor - dichiarato in SPECIFICA, come il
   calendario. Resta la figura, che e' abbastanza per LEGGERE uno schema. */
img.drawio-still {
    display: block;
    max-width: 100%;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
}

/* La pastiglia nella lista dei documenti: uno schema e' un documento, e li'
   dentro dev'essere riconoscibile a colpo d'occhio. Il navy del marchio, come
   gli appuntamenti sulla timeline. */
.pill.kind-diagram { --hue-ink: var(--accent); --hue-soft: var(--accent-soft); }

/* -- Su uno schermo stretto ----------------------------------------------- */

@media (max-width: 599px) {
    /* Una colonna sola: due miniature affiancate su un telefono sono due
       miniature che non si guardano. */
    ul.diagrams { grid-template-columns: 1fr; }

    .diagram-bar .title { flex: 1 1 100%; }

    /* Meno cornice attorno: su un telefono lo schema non si disegna, si
       guarda, e ogni riga di barra e' altezza tolta al disegno. */
    iframe.drawio { height: calc(100vh - 18rem); }
}

/* == DIAG02: uno schema scritto invece che disegnato ========================

   Due segni soltanto. La pastiglia che dice da dove viene un disegno, e
   l'anteprima sulla card di una proposta - l'unica proposta che non si puo'
   controllare leggendola.

   ========================================================================== */

/* Grigia e non colorata: e' provenienza, non uno stato. Non compete con la
   pastiglia `diagram` della lista documenti, che dice una cosa diversa. */
.pill.from-text {
    --hue-ink: var(--ink-soft);
    --hue-soft: var(--paper-sunk);
    font-variant: normal;
}

/* L'anteprima di uno schema proposto. Sopra i campi, perche' e' la cosa su cui
   il bottone Confirm decide davvero: nove nomi di forme non dicono se il flusso
   e' giusto. */
.proposal img.preview {
    display: block;
    max-width: 100%;
    max-height: 22rem;
    margin: var(--gap-2) 0;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    background: var(--paper);
    object-fit: contain;
}

/* == DOCPAGE01: la pagina di un documento ==================================

   Poca roba: la riga di intestazione sotto il titolo, la figura di uno schema,
   e la piega dei blocchi. Tutto il resto è già vestito - `dl.fields` viene
   dalle card delle proposte, `table.log` dal registro, `.bar` da ovunque.

   ========================================================================== */

.doc-meta {
    display: flex;
    align-items: center;
    gap: var(--gap-2);
    flex-wrap: wrap;
    margin-bottom: var(--gap-3);
}

/* La figura di uno schema sulla sua pagina di documento. Limitata in altezza:
   una planimetria alta il doppio dello schermo spinge tutto il resto fuori. */
img.doc-still {
    display: block;
    max-width: 100%;
    max-height: 26rem;
    margin: var(--gap-3) 0 var(--gap-1);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    background: var(--paper);
    object-fit: contain;
}

/* La piega: cosa NANO ha letto davvero. Chiusa di default perché sono
   quaranta righe, e aperta è l'unico modo di rispondere a «ma cosa ha letto?» */
details.blocks { margin-top: var(--gap-4); }

details.blocks > summary {
    cursor: pointer;
    font-weight: 600;
}

details.blocks ol.rows {
    margin-top: var(--gap-2);
    max-height: 30rem;
    overflow-y: auto;
}

details.blocks ol.rows > li {
    display: flex;
    align-items: baseline;
    gap: var(--gap-2);
}

/* Il testo del blocco su una riga sola: quaranta blocchi a capo sono una
   pagina in cui non si trova più niente. */
details.blocks .said {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ── TIMEGRID01 — Log's three weeks ──────────────────────────────────────────
   Seven day columns and the week's total, three rows. Markup and CSS only: each
   cell is a link, choosing a day is an address. An empty day is an empty cell,
   on purpose — a gap in a week is the thing the grid exists to show. */

.time-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr)) auto;
    gap: 1px;
    background: var(--rule);
    border: 1px solid var(--rule);
    margin-bottom: var(--gap-4);
}

.time-grid .grid-column {
    background: var(--paper-sunk);
    color: var(--ink-soft);
    font-size: var(--size-small);
    padding: var(--gap-1) var(--gap-2);
}

.time-grid .grid-day {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--gap-1);
    min-height: 6.5rem;
    padding: var(--gap-2);
    background: var(--paper);
    color: var(--ink);
    text-decoration: none;
    min-width: 0;
}

.time-grid .grid-day:hover { background: var(--paper-sunk); }

/* The week in the address, told apart from its neighbours without a colour
   that means something else on this site. */
.time-grid .grid-day:not(.anchor) { background: var(--paper-sunk); }
.time-grid .grid-day:not(.anchor):hover { background: var(--paper); }

.time-grid .grid-day.today { box-shadow: inset 0 0 0 2px var(--accent); }

.time-grid .grid-day.selected { background: var(--accent-soft); }

.time-grid .grid-date {
    color: var(--ink-soft);
    font-size: var(--size-small);
}

.time-grid .grid-total { font-variant-numeric: tabular-nums; }

/* A pill says which project and how long; one that does not fit is cut, not
   wrapped, so the cells of a row stay the same height. */
.time-grid .grid-day .pill {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
}

.time-grid .grid-week {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    padding: var(--gap-2);
    background: var(--paper);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

.grid-detail h2:first-child { margin-top: 0; }

/* ── LINKS01 — the links somebody shared ─────────────────────────────────────
   One line each: the title is the link, the service and the pin beside it, the
   note under it in the quiet ink. No box: they sit on the card's page and on top
   of the Documents tab, and a panel around three lines is a frame around air. */

.shared-links ul.links {
    list-style: none;
    padding: 0;
    margin: 0 0 var(--gap-4);
}

.shared-links ul.links li {
    padding: var(--gap-2) 0;
    border-bottom: 1px solid var(--rule);
}

.shared-links ul.links li > a:first-child { font-weight: 600; }

.shared-links h2 .button { margin-left: var(--gap-3); vertical-align: middle; }

/* ── PROJ01 — the curved wall projection simulator ───────────────────────────
   The original's layout, kept: controls in a column on the left, drawings on
   the right, one column under 920 pixels; the charts side by side down to 1150.
   Its fonts are not here (nothing is downloaded) and its colours are NANO's,
   but for the two projectors and the wall, which are tokens at the top.

   The drawings arrive as shapes with colour NAMES, which the page turns into
   the classes pj-f-<name> (fill) and pj-s-<name> (stroke). Scoped under
   .pj-out svg so that they win over the default ink of an SVG label. */

.pj-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--gap-1) var(--gap-4); margin-bottom: var(--gap-3); }
.pj-head h2 { margin: 0; }
.pj-head p { margin: 0; color: var(--ink-soft); font-size: var(--size-small); }

.pj-layout { display: grid; grid-template-columns: 330px minmax(0, 1fr); gap: var(--gap-4); align-items: start; }
.pj-side { display: flex; flex-direction: column; gap: var(--gap-4); position: sticky; top: var(--gap-3); }

@media (max-width: 920px) {
    .pj-layout { grid-template-columns: minmax(0, 1fr); }
    .pj-side { position: static; }
}

.pj-controls, .pj-scenarios, .pj-card, .pj-kpi {
    background: var(--paper);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
}

.pj-controls { margin: 0; padding: var(--gap-3) var(--gap-4); display: flex; flex-direction: column; gap: var(--gap-4); }
.pj-controls fieldset { border: 0; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--gap-2); min-width: 0; }
.pj-controls fieldset[hidden] { display: none; } /* PROJ02: P2's group, hidden with one projector */
.pj-controls legend {
    padding: 0;
    margin-bottom: var(--gap-1);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--ink-faint);
}

.pj-row { display: grid; grid-template-columns: minmax(0, 1fr) 84px; gap: var(--gap-1) var(--gap-2); align-items: center; }
.pj-row label { font-size: var(--size-small); color: var(--ink-soft); }
.pj-row input[type="range"] { grid-column: 1 / -1; width: 100%; margin: 0; accent-color: var(--accent); }
.pj-row input[type="number"], .pj-row select {
    font: inherit;
    font-family: var(--font-mono);
    font-size: var(--size-small);
    width: 100%;
    padding: 3px 6px;
    color: var(--ink);
    background: var(--paper-sunk);
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    text-align: right;
}
.pj-row select { text-align: left; }
.pj-row .pj-wide { grid-column: 1 / -1; }
.pj-unit { font-size: 12px; color: var(--ink-faint); }

/* The original's two buttons, as two radios that post with the form. */
.pj-seg { display: flex; border: 1px solid var(--rule-strong); border-radius: var(--radius); overflow: hidden; }
.pj-seg label { flex: 1; text-align: center; padding: 5px 0; font-size: var(--size-small); color: var(--ink-soft); cursor: pointer; }
.pj-seg input { position: absolute; opacity: 0; pointer-events: none; }
.pj-seg label:has(input:checked) { background: var(--accent); color: var(--paper); }
.pj-seg label:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: -2px; }

.pj-hint { margin: 0; font-size: 12px; color: var(--ink-faint); }
.pj-derived { display: flex; flex-wrap: wrap; gap: var(--gap-1) var(--gap-3); font-family: var(--font-mono); font-size: 12px; color: var(--ink-soft); }

.pj-scenarios { padding: var(--gap-3) var(--gap-4); display: flex; flex-direction: column; gap: var(--gap-3); }
.pj-scenarios h3 { margin: 0; font-size: var(--size); }
.pj-scenarios form { margin: 0; }
.pj-save { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--gap-1) var(--gap-2); align-items: center; }
.pj-save label { grid-column: 1 / -1; font-size: var(--size-small); color: var(--ink-soft); }
.pj-list { list-style: none; margin: 0; padding: 0; }
.pj-list li { padding: var(--gap-1) 0; border-bottom: 1px solid var(--rule); }
.pj-list a.here { font-weight: 600; color: var(--ink); text-decoration: none; }

/* TOOLS01 - under Tools: the Project list of Save as takes the whole row, and
   the free scenarios and each project's are headed apart. */
.pj-save select { grid-column: 1 / -1; }
.pj-scenarios h4 { margin: 0; font-size: var(--size-small); color: var(--ink-soft); text-transform: uppercase; letter-spacing: .04em; }
.pj-scenarios h5 { margin: 0; font-size: var(--size-small); font-weight: 600; }

.pj-out { display: flex; flex-direction: column; gap: var(--gap-4); min-width: 0; }
.pj-status { margin: 0; padding: var(--gap-2) var(--gap-3); border-radius: var(--radius); background: var(--bad-soft); color: var(--bad); font-size: var(--size-small); }

.pj-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: var(--gap-2); }
.pj-kpi { padding: var(--gap-2) var(--gap-3); display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pj-kpi .k { font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: var(--ink-faint); }
.pj-kpi .v { font-family: var(--font-mono); font-size: 1.35rem; font-variant-numeric: tabular-nums; }
.pj-kpi .s { font-size: 12px; color: var(--ink-soft); }
.pj-chip { width: max-content; padding: 1px 7px; border-radius: 10px; font-size: 12px; font-weight: 600; }
.pj-chip.ok { background: var(--good-soft); color: var(--good); }
.pj-chip.no { background: var(--bad-soft); color: var(--bad); }

.pj-warns { display: flex; flex-direction: column; gap: var(--gap-2); }
.pj-warns:empty { display: none; }
.pj-warn { padding: var(--gap-2) var(--gap-3); border-radius: var(--radius); background: var(--bad-soft); color: var(--bad); font-size: var(--size-small); }

.pj-two { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--gap-4); }
@media (max-width: 1150px) { .pj-two { grid-template-columns: minmax(0, 1fr); } }

.pj-card { padding: var(--gap-3) var(--gap-4); min-width: 0; }
.pj-card h3 { margin: 0 0 2px; font-size: var(--size); }
.pj-sub { margin: 0 0 var(--gap-2); font-size: 12px; color: var(--ink-faint); }

.pj-out svg { display: block; width: 100%; height: auto; }
.pj-out svg text { font-family: var(--font-mono); fill: var(--ink-soft); }

.pj-legend { display: flex; flex-wrap: wrap; gap: var(--gap-1) var(--gap-3); margin-top: var(--gap-2); font-size: 12px; color: var(--ink-soft); }
.pj-legend i { display: inline-block; width: 14px; height: 3px; border-radius: 2px; margin-right: 5px; vertical-align: middle; }
.pj-key-wall { background: var(--pj-wall); }
.pj-key-p1 { background: var(--pj-p1); }
.pj-key-p2 { background: var(--pj-p2); }
.pj-key-ink { background: var(--ink); }
.pj-key-ink3 { background: var(--ink-faint); }

.pj-chart { position: relative; }
.pj-tip {
    position: absolute;
    pointer-events: none;
    padding: 6px 8px;
    background: var(--paper);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    box-shadow: 0 2px 8px rgba(0, 0, 0, .12);
    font-family: var(--font-mono);
    font-size: 12px;
    color: var(--ink);
    white-space: nowrap;
}

.pj-tablewrap { overflow-x: auto; }
.pj-tablewrap table { font-size: var(--size-small); }
.pj-tablewrap th, .pj-tablewrap td { text-align: right; padding: 5px 8px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.pj-tablewrap td { font-family: var(--font-mono); }
.pj-tablewrap th:first-child, .pj-tablewrap td:first-child { text-align: left; }

.pj-out svg .pj-f-p1 { fill: var(--pj-p1); }
.pj-out svg .pj-f-p2 { fill: var(--pj-p2); }
.pj-out svg .pj-f-wall { fill: var(--pj-wall); }
.pj-out svg .pj-f-ink { fill: var(--ink); }
.pj-out svg .pj-f-ink3 { fill: var(--ink-faint); }
.pj-out svg .pj-f-bad { fill: var(--bad); }
.pj-out svg .pj-f-panel { fill: var(--paper); }
.pj-out svg .pj-s-p1 { stroke: var(--pj-p1); }
.pj-out svg .pj-s-p2 { stroke: var(--pj-p2); }
.pj-out svg .pj-s-wall { stroke: var(--pj-wall); }
.pj-out svg .pj-s-ink { stroke: var(--ink); }
.pj-out svg .pj-s-ink3 { stroke: var(--ink-faint); }
.pj-out svg .pj-s-grid { stroke: var(--rule); }
.pj-out svg .pj-s-bad { stroke: var(--bad); }

/* ── LOOKC01 — compact, for the long lists ───────────────────────────────────

   Filippo, 2026-10-09, choosing among three variants drawn for him by COMO-29:
   "direi C". The tokens at the top did most of it - smaller type, a tighter
   line, a tighter scale. What is here is what tokens cannot say.

   Built for the long lists: BOQ, documents, the board. Rows are short, numbers
   line up, every other row is a shade darker so the eye keeps the line across a
   wide table, and the head of a table stays in view while it scrolls. */

header { padding: var(--gap-2) var(--gap-4); }
header .brand img { height: 24px; }

h1 { margin-bottom: var(--gap-3); letter-spacing: -0.005em; }
h2 { margin: var(--gap-5) 0 var(--gap-2); }

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

th, td { padding: var(--gap-1) var(--gap-2); }

thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    font-size: calc(var(--size-small) - 1px);
    text-transform: uppercase;
    letter-spacing: .05em;
}

tbody tr:nth-child(even) { background: var(--paper-zebra); }
tbody tr:hover { background: var(--accent-soft); }

.facts { gap: var(--gap-1) var(--gap-4); }

.board .card { padding: var(--gap-2) var(--gap-2) var(--gap-1); }
.board .card-meta { margin-top: 2px; }

/* LOOKC01 - the first transitions this sheet has had: short, CSS only, on
   colour, ground and border, and none at all for whoever asked the system
   for less motion - the block at the very end, which StylesheetGuardTests
   requires as soon as anything here moves.

   The buttons' transition sits in the rule whose selector is exactly
   `button, .button`: LookTests lets no other button rule mention a ground
   outside the five. */
a, header nav a, .tabs a {
    transition: color var(--ease), background-color var(--ease), text-decoration-color var(--ease);
}

button, .button {
    transition: background-color var(--ease), border-color var(--ease), color var(--ease);
}

input, select, textarea { transition: border-color var(--ease); }

/* LOOKC01 - focus that can be seen on the links, the summaries and the tabs
   too, not only on buttons and fields. */
a:focus-visible, summary:focus-visible, .tabs a:focus-visible, header nav a:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 2px;
}

input:focus, select:focus, textarea:focus {
    outline: 2px solid var(--accent);
    outline-offset: 1px;
    border-color: var(--accent);
}

/* INK01 - the pen probe (/tools/pen-probe). A stage the fingers cannot scroll
   or zoom on their own: ink.js does both, so that a palm does neither.
   touch-action: none is the one line that makes the palm rule possible. */
.ink-probe { display: flex; flex-direction: column; gap: var(--gap-3); }
.ink-probe:fullscreen { background: var(--paper-sunk); padding: var(--gap-3); overflow: auto; }

.ink-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--gap-2); }
.ink-group { display: inline-flex; flex-wrap: wrap; gap: var(--gap-1); }
/* The chosen tool, colour and width: a ring in the accent, not a ground - a
   button's colour is one of the five (LOOK02). */
.ink-bar [aria-pressed="true"] { border-color: var(--accent); box-shadow: inset 0 0 0 2px var(--accent); font-weight: 600; }

.ink-swatch::before {
    content: "";
    display: inline-block;
    width: 0.8em;
    height: 0.8em;
    margin-right: 0.4em;
    border-radius: 50%;
    vertical-align: -0.05em;
    background: currentColor;
}
.ink-black::before { background: #1a1f29; }
.ink-blue::before { background: #1f4fbf; }
.ink-red::before { background: #b3261e; }
.ink-green::before { background: #2d6a4f; }

.ink-stage {
    position: relative;
    height: 72vh;
    min-height: 420px;
    background: var(--paper-sunk);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    overflow: hidden;
    touch-action: none;
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
}
.ink-probe:fullscreen .ink-stage { height: calc(100vh - 7rem); }
.ink-layer { position: absolute; inset: 0; width: 100%; height: 100%; }

.ink-facts { display: grid; grid-template-columns: max-content 1fr; gap: var(--gap-1) var(--gap-3); margin: 0; }
.ink-facts dt { color: var(--ink-soft); }
.ink-facts dd { margin: 0; font-family: var(--font-mono); overflow-wrap: anywhere; }

/* On the tablet the buttons are for a pen nib and a fingertip, not a mouse. */
@media (pointer: coarse) {
    .ink-bar button, .ink-bar select { min-height: 48px; min-width: 48px; padding-left: var(--gap-3); padding-right: var(--gap-3); }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        transition: none !important;
        animation: none !important;
        scroll-behavior: auto !important;
    }
}
