﻿html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

/* ---------------------------------------------------------------------------
   One blue, named once.

   Bootstrap ships its own blue (#0d6efd); Taskplan's is a little deeper. Fixing
   that per rule is how a screen ends up with two blues that nobody chose: the
   Save button was ours while its hover state was Bootstrap's, every checkbox
   ticked in Bootstrap blue, and the account navigation was Bootstrap's
   throughout. The values below are the whole of Taskplan's blue, and everything
   drawn in it reads them from here.

   Where Bootstrap exposes a custom property, the override sets that property
   rather than the rule -- hover, focus, active and disabled then keep working
   exactly as they were built to, in our colour. Only where Bootstrap hard-codes
   a value (a ticked checkbox, a focused border) is a rule overridden, and those
   are marked below.

   *** Repainted on 2026-09-20, and the names did not change -- only what they
   hold. That is the whole return on "one blue, named once": the direction was a
   handoff document and the change was this block.

   Away from a saturated corporate blue, toward slate navy for the furniture and
   a muted blue used only where something is active.

   One value from the handoff was moved. The secondary text it proposed,
   #687681, reads 4.47:1 on #f9fafb -- just under the bar. One step darker at
   #65737e is 4.67:1 and looks the same.

   *** Repainted again the same evening, and again only the values (D-163). ***
   The app icon is #1878D2 and the interface was #3478a5: the same corner of the
   wheel, one of them pale and pulled toward grey, which is why the icon looked
   as though it came from another set. The blues below are now the icon's own
   hue and saturation.

   The fill is one step darker than the icon, and that step is the only thing
   here worth arguing about. White on #1878D2 is 4.51:1, and D-096 asks for
   4.5:1 -- a hundredth of a point of headroom, for the colour that carries every
   button in the product. #1773c9 is 4.85:1 and the eye cannot tell the two
   apart. **A bar is not a target.**

   Measured, all of it: the fill against white 4.85:1 -- which is both "white on
   the button" and "the link as words on white", because a contrast ratio is
   symmetric -- the active entry against the sidebar 3.13:1 (shapes want 3), and
   what is written on the sidebar 13.97:1.
   --------------------------------------------------------------------------- */
:root {
    /* The fill: buttons, the page you are on, a ticked box. */
    --tp-blue: #1773c9;
    --tp-blue-border: #1568b7;

    /* Hover and active, shaded from the fill by the same steps Bootstrap uses
       (15% and 20% toward black), so a button behaves as a Bootstrap button. */
    --tp-blue-hover: #1568b7;
    --tp-blue-hover-border: #135ea4;
    --tp-blue-active: #135ea4;
    --tp-blue-active-border: #115392;

    /* The focus ring, which Bootstrap wants as bare rgb numbers. It is the
       *icon's* blue rather than the fill: a ring is a glow around a control and
       wants to be lighter than the control, not the same. */
    --tp-blue-tint-rgb: 24, 120, 210;

    /* Text, and the same colour as the fill, which is what it has been since the
       repaint: the fill reads 4.85:1 as words on white, so there is no reason for
       a second blue, and one fewer blue on screen is worth having.

       Written out because it is easy to get wrong twice: a contrast ratio is
       symmetric. "White on the fill" and "the fill as words on white" are the
       same measurement, not two, and a darker link here would buy nothing that
       the number does not already say. */
    --tp-link: #1773c9;
    --tp-link-hover: #1568b7;

    /* Backgrounds that hint rather than state: a hovered pager cell. The same
       lightness as the pair they replaced, in the new hue -- this is furniture
       and had no reason to move. */
    --tp-blue-wash: #e9f2fb;
    --tp-blue-wash-border: #d3e5f7;

    /* Half-way to white, the same relation Bootstrap's #86b7fe has to its own
       blue, so a focused field reads as focused and not as broken. */
    --tp-blue-soft: #9cc3ea;

    /* ---- The furniture, which had no names at all before -------------------

       The dark surfaces were written into the rules that used them, so the
       sidebar's navy lived in one place, the brand row's in another and the
       phone's top bar in a third -- three literals that had to agree and no way
       to say so. They are one colour now, and it is said here. */

    /* Every dark chrome surface: the sidebar, the top bar, the PWA's own edge.

       It was a neutral slate and is now navy, in the icon's own hue (D-163). The
       name stayed, because what it is *for* did not change -- and because three
       other files say this number and all three were changed with it: the
       `theme-color` in App.razor and `theme_color` in the manifest (D-161), and
       nothing else, since every rule that paints something dark reads it here. */
    --tp-slate: #0f2740;

    /* The same, a touch lighter, for what sits *on* the dark: a hovered entry. */
    --tp-slate-soft: #17334f;

    /* ---- The brand surface (D-193) ------------------------------------------

       What the sidebar, the phone's top bar and the app's bar are painted with
       since 2026-10-06: not the navy above but the blue of the app icon, running
       left to right the way the top edge of the calendar in the icon does.

       *** Three values, and they are the whole of the brand. *** Two colours and
       the direction between them. Nothing that paints the surface names a colour
       of its own; everything reads `--tp-brand`. That is what makes the surface
       exchangeable later -- an OEM's own colours, or a person's own -- by one
       more `:root` rule after this file that sets the same three names. No rule
       below has to change for it, and none may start naming a blue directly.

       One step darker than the icon itself, and the step is arithmetic rather
       than taste (D-096): the icon's light end is #2699EF, on which white is
       3.05:1. On these two white is 5.68:1 and 8.16:1, and the quieter writing
       at 85% white still 4.58:1 at the light end -- which is why nothing on this
       surface is written fainter than that. BrandTests reads the numbers back
       from this file and does the sum again. */
    --tp-brand-from: #1568b7;
    --tp-brand-to: #104f95;
    --tp-brand-angle: 180deg;

    --tp-brand: linear-gradient(var(--tp-brand-angle), var(--tp-brand-from), var(--tp-brand-to));

    /* What is written on it, and the quieter voice beside it. */
    --tp-on-brand: #ffffff;
    --tp-on-brand-quiet: rgba(255, 255, 255, .85);

    /* "You are here" on the brand surface: the ground a shade darker, and the
       word in white and heavier. A blue fill, which is what marks the current
       page everywhere else, cannot be seen on a blue ground.

       It was a white entry with a blue word for one afternoon (2026-10-06), and
       Jan's word for that was "too harsh": a white block in a blue panel is the
       loudest thing on the screen, and all it says is where one already is. So
       the mark is quiet now -- black at a quarter strength over whatever the
       ground is, which follows any brand's colours without naming one -- and
       the weight of the word does the rest. White on it is 8.49:1 at the least.

       The strength has to stay a plain number between 0 and 1: BrandTests reads
       it from here and does the sum. */
    --tp-brand-current: rgba(0, 0, 0, .25);
    --tp-brand-current-hover: rgba(0, 0, 0, .32);
    --tp-on-brand-current: #ffffff;

    /* What is written on it. Not pure white: 13.97:1 is plenty, and a fraction
       off white stops a long list of entries glaring. A hair of the ground's own
       blue in it, so it reads as belonging to the surface rather than lying on
       top of it. */
    --tp-on-slate: #f2f6fa;

    /* ---- And the light side -------------------------------------------- */

    /* The ground a page stands on, against the white of its cards. */
    --tp-canvas: #f9fafb;
    --tp-surface: #ffffff;

    --tp-ink: #1f2933;
    --tp-ink-soft: #65737e;
    --tp-line: #dce3e8;
}

a, .btn-link {
    color: var(--tp-link);
}

.btn-primary {
    --bs-btn-bg: var(--tp-blue);
    --bs-btn-border-color: var(--tp-blue-border);
    --bs-btn-hover-bg: var(--tp-blue-hover);
    --bs-btn-hover-border-color: var(--tp-blue-hover-border);
    --bs-btn-active-bg: var(--tp-blue-active);
    --bs-btn-active-border-color: var(--tp-blue-active-border);
    --bs-btn-disabled-bg: var(--tp-blue);
    --bs-btn-disabled-border-color: var(--tp-blue-border);
    --bs-btn-focus-shadow-rgb: var(--tp-blue-tint-rgb);
}

/* The ring stays its own brighter blue. A focus indicator is not decoration --
   it has to be findable against everything around it, including our own fill --
   so it is deliberately not one of the tokens above. The select was missing from
   this list and drew Bootstrap's ring while every field beside it drew ours. */
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus,
.form-control:focus, .form-select:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

/* Hard-coded in Bootstrap, so overridden as rules rather than as properties. */
.form-control:focus, .form-select:focus, .form-check-input:focus {
    border-color: var(--tp-blue-soft);
}

.form-check-input:checked {
    background-color: var(--tp-blue);
    border-color: var(--tp-blue-border);
}

/* The account pages navigate with pills. Left alone they draw Bootstrap's blue
   twice over -- once for the links, once for the pill you are standing on -- a
   hand's width from buttons in ours. The sidebar is a plain `.nav` and keeps its
   own colours, which is why this is scoped to `.nav-pills`. */
.nav-pills {
    --bs-nav-link-color: var(--tp-link);
    --bs-nav-link-hover-color: var(--tp-link-hover);
    --bs-nav-pills-link-active-bg: var(--tp-blue);
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* Bootstrap's pager ships in its own blue, which is a different blue from the one
   the buttons above the list use. Two blues on one screen read as two systems, so
   the pager is told to use ours: the link colour for the numbers and the primary
   button's fill for the page you are on. Set through Bootstrap's own variables
   rather than by overriding its rules, so hover, focus and disabled keep working
   as they were built to. */
.pagination {
    --bs-pagination-color: var(--tp-link);
    --bs-pagination-hover-color: var(--tp-link-hover);
    --bs-pagination-hover-bg: var(--tp-blue-wash);
    --bs-pagination-hover-border-color: var(--tp-blue-wash-border);
    --bs-pagination-focus-color: var(--tp-link-hover);
    --bs-pagination-focus-bg: var(--tp-blue-wash);
    --bs-pagination-focus-box-shadow: 0 0 0 .2rem rgba(27, 110, 194, .25);
    --bs-pagination-active-bg: var(--tp-blue);
    --bs-pagination-active-border-color: var(--tp-blue-border);
}

/* ---------------------------------------------------------------------------
   Accent marks (D-057)

   Global rather than scoped, and that is the point: the same colours are drawn
   on the dark sidebar, on the white work list and in the picker. Three copies in
   three component stylesheets would be three things to keep in step, which is
   the fault the structure page taught us. This is a token set, not one
   component's private styling.

   Twelve hues in two tones. Every value clears 3.6:1 against BOTH grounds -- the
   sidebar's dark blue and a white page -- because each lightness was chosen by
   maximising the *worse* of its two contrasts. Twelve rather than sixteen
   because the wheel is not perceptually even: the hues are spent where the eye
   can use them, five between red and lime and three from purple to pink, and
   only three across the whole green-to-cyan span where everything reads as "some
   green".

   Each class publishes its colour twice: as background-color for anything that
   is simply a coloured shape, and as --tp-mark for anything whose own rule wins
   on specificity and has to read the value instead -- the masked icons in the
   tree.
   --------------------------------------------------------------------------- */

:root {
    --tp-accent-red: #eb4c4c;
    --tp-accent-red-soft: #c66c6c;
    --tp-accent-orange: #d86518;
    --tp-accent-orange-soft: #b87447;
    --tp-accent-amber: #ae7b13;
    --tp-accent-amber-soft: #a2813f;
    --tp-accent-yellow: #938610;
    --tp-accent-yellow-soft: #8f8638;
    --tp-accent-lime: #679310;
    --tp-accent-lime-soft: #728f38;
    --tp-accent-green: #119c34;
    --tp-accent-green-soft: #3b9752;
    --tp-accent-teal: #109388;
    --tp-accent-teal-soft: #39938b;
    --tp-accent-cyan: #148fb8;
    --tp-accent-cyan-soft: #428fa9;
    --tp-accent-blue: #4783eb;
    --tp-accent-blue-soft: #6184c2;
    --tp-accent-purple: #bf55ec;
    --tp-accent-purple-soft: #ab6cc6;
    --tp-accent-magenta: #e935bc;
    --tp-accent-magenta-soft: #c365ab;
    --tp-accent-pink: #eb477e;
    --tp-accent-pink-soft: #c46887;
}

/* The rail beside a heading or a list row. Always rendered, transparent when
   there is no mark, so that names line up whether or not somebody has coloured
   something -- the same rule as the note's empty checkbox slot.

   Not used in the tree: there the rows are indented, so a rail at the left edge
   floats away from the thing it belongs to. The mark goes on the icon instead. */
.tp-accent {
    flex: 0 0 auto;
    align-self: stretch;
    width: 3px;
    min-height: 1rem;
    border-radius: 2px;
    background-color: transparent;
}

/* No margin of its own: every place it is used is a flex row with a gap, and a
   margin on top of that would space this one child differently from the rest. */

/* Beside a page heading, where 3px next to 2rem of type would read as a slip of
   the mouse rather than a mark. */
.tp-accent-lg {
    width: 5px;
    border-radius: 3px;
}

.tp-accent-red { background-color: var(--tp-accent-red); --tp-mark: var(--tp-accent-red); }
.tp-accent-red-soft { background-color: var(--tp-accent-red-soft); --tp-mark: var(--tp-accent-red-soft); }
.tp-accent-orange { background-color: var(--tp-accent-orange); --tp-mark: var(--tp-accent-orange); }
.tp-accent-orange-soft { background-color: var(--tp-accent-orange-soft); --tp-mark: var(--tp-accent-orange-soft); }
.tp-accent-amber { background-color: var(--tp-accent-amber); --tp-mark: var(--tp-accent-amber); }
.tp-accent-amber-soft { background-color: var(--tp-accent-amber-soft); --tp-mark: var(--tp-accent-amber-soft); }
.tp-accent-yellow { background-color: var(--tp-accent-yellow); --tp-mark: var(--tp-accent-yellow); }
.tp-accent-yellow-soft { background-color: var(--tp-accent-yellow-soft); --tp-mark: var(--tp-accent-yellow-soft); }
.tp-accent-lime { background-color: var(--tp-accent-lime); --tp-mark: var(--tp-accent-lime); }
.tp-accent-lime-soft { background-color: var(--tp-accent-lime-soft); --tp-mark: var(--tp-accent-lime-soft); }
.tp-accent-green { background-color: var(--tp-accent-green); --tp-mark: var(--tp-accent-green); }
.tp-accent-green-soft { background-color: var(--tp-accent-green-soft); --tp-mark: var(--tp-accent-green-soft); }
.tp-accent-teal { background-color: var(--tp-accent-teal); --tp-mark: var(--tp-accent-teal); }
.tp-accent-teal-soft { background-color: var(--tp-accent-teal-soft); --tp-mark: var(--tp-accent-teal-soft); }
.tp-accent-cyan { background-color: var(--tp-accent-cyan); --tp-mark: var(--tp-accent-cyan); }
.tp-accent-cyan-soft { background-color: var(--tp-accent-cyan-soft); --tp-mark: var(--tp-accent-cyan-soft); }
.tp-accent-blue { background-color: var(--tp-accent-blue); --tp-mark: var(--tp-accent-blue); }
.tp-accent-blue-soft { background-color: var(--tp-accent-blue-soft); --tp-mark: var(--tp-accent-blue-soft); }
.tp-accent-purple { background-color: var(--tp-accent-purple); --tp-mark: var(--tp-accent-purple); }
.tp-accent-purple-soft { background-color: var(--tp-accent-purple-soft); --tp-mark: var(--tp-accent-purple-soft); }
.tp-accent-magenta { background-color: var(--tp-accent-magenta); --tp-mark: var(--tp-accent-magenta); }
.tp-accent-magenta-soft { background-color: var(--tp-accent-magenta-soft); --tp-mark: var(--tp-accent-magenta-soft); }
.tp-accent-pink { background-color: var(--tp-accent-pink); --tp-mark: var(--tp-accent-pink); }
.tp-accent-pink-soft { background-color: var(--tp-accent-pink-soft); --tp-mark: var(--tp-accent-pink-soft); }


/* --- the icons ----------------------------------------------------------- */

/* One icon set for everything Taskplan draws itself: Material Symbols, at
   FILL 0, wght 200, GRAD 0, opsz 24 (D-150). The four axes are set once, here,
   and nowhere else -- that is the whole of why the icons look like one family.

   An icon in the markup is its own name as text, because the font carries
   ligatures: <span class="tp-i">calendar_month</span>. Readable, greppable, and
   a good deal shorter than the eighty characters of SVG path data each of these
   used to be.

   The file is subsetted to exactly the names listed below. To add one, fetch the
   subset again with the name in it -- the list is here so that nobody has to
   open a font to find out what is inside it.

   *** And bump `?v=` on the three lines that ask for the file: here, on the
   preload in App.razor, and on the one in the copy's index.html (Taskplan.Wasm).
   *** The name never changes, so to a browser that has the
   old one there is nothing to fetch -- it goes on using it, the new names are not
   in it, and a page then spells out `settings` and `more_horiz` in plain letters
   where the icons should be. That happened on an iPhone on 2026-09-19, and it is
   the only way this can go wrong: the glyphs a reader already had kept working,
   so the fault looked like two broken icons rather than one stale file.

   *** The list is two lists, and the split is deliberate (2026-09-20). ***
   Everything above the rule is drawn somewhere today. Everything below it is on
   stock: Jan asked for the symbols the next few months will want, so that adding
   one is an edit rather than an errand -- each new name costs a fetch, and five
   fetches in a fortnight is how a subsetted font stops being worth having.

   The split has to stay visible. A list that mixes the used with the intended is
   a list nobody can prune, and this file's whole purpose is that nobody has to
   open a font to find out what is inside it. **When one of the second group is
   first used, move its name up.** When a year has passed and it has not been,
   take it out on the next fetch.

   Drawn today:

     add · add_photo_alternate · arrow_back · calendar_month · chat_bubble · check · check_circle
     checklist · chevron_left · chevron_right · close · cloud_off · content_copy · event_repeat
     expand_more · filter_alt · folder · format_list_bulleted · grid_view
     group · inbox · layers · link · lock · login · logout · more_horiz
     notifications · person · person_add · schedule · search · settings
     star · target · today · translate · visibility · visibility_off

   ---

   On stock, not yet drawn anywhere:

     archive · attach_file · credit_card · delete · description
     download · drag_indicator · edit · expand_less · flag · groups · help
     home · how_to_reg · info · label · mail · push_pin · refresh · reply
     send · share · unarchive · undo · upload · view_week · warning

   Where each of them is expected, so that the list can be argued with rather
   than merely trusted: `send`, `mail`, `reply` for the messages page that is a
   placeholder today; `push_pin`, `label`, `star`, `flag` for marking and
   sorting; `description`, `attach_file` for what hangs off a work item (`link` is drawn since D-271: an entry
   linked to the telephone's calendar);
   `download`, `upload`, `share` for getting things in and out; `edit`, `close`,
   `undo`, `delete`, `archive`, `unarchive` for the ordinary verbs, and the last
   three because deleting here means archiving (D-032) and both directions need a
   mark; `schedule` for a time or a duration; `groups` and `how_to_reg` for teams
   and for an offer that was accepted (D-048); `drag_indicator` for ordering areas
   and projects, which TODO 13 is about; `expand_less` as the other half of
   `expand_more`; `home`; `view_week` for the calendar's shapes, which are words
   today; `credit_card` for plans; `warning`, `info` and `help` for the sentences
   a page has to say about itself; `refresh` for trying again after a moment
   without a server (TODO 23b, the friendly retry).

   *** The `FILL` axis is now a range rather than a pin. *** It used to be fetched
   as `FILL@0`, which meant the file physically could not draw a filled glyph --
   and a filled mark is how every interface in the world says "this one is
   pinned", "this one is starred". Asking for `FILL@0..1` costs a few kilobytes
   and saves the fetch that discovering this later would have cost. `.tp-i` still
   rests at 0; a class that sets `'FILL' 1` is all a filled state needs.

   `workspaces` is off this list because nothing uses it any more -- the sidebar's
   workspace row asks for `layers` now (Jan, 2026-09-20). It is *not* off it for
   being broken: that row was blank for two days and the font was blamed twice,
   wrongly, for what turned out to be a colour. See `.tp-i-plain` below.

   *** And if the button is a `btn-link`, give it `text-decoration: none`. ***
   Bootstrap underlines one, and that line never showed while the mark inside was
   an <svg> -- an image is not text and takes no underline. A glyph is text, so
   the line appears beneath it the moment a drawing becomes a name, close enough
   to read as part of the icon. It has caught us three times now: the floating
   plus, and both buttons in the phone calendar's header. It is not fixed from
   here -- the line is painted by the button across everything on its row, and a
   rule on the glyph cannot reach it.

   Self-hosted and not linked from fonts.googleapis.com: a link there sends every
   visitor's IP address to Google, which LG Muenchen I held to be a GDPR breach
   in 2022 (3 O 17493/20). Taskplan is a German product with German customers. */

/* `font-display: block` and this is the one setting that must not be changed.
   The rule this replaced argued against an icon font on the grounds that a font
   arriving late leaves a row of empty boxes -- a fair objection, and worse than
   it sounds with ligatures: the fallback would spell out `calendar_month` in the
   sidebar in plain letters. `block` says draw nothing until the font is here.
   With 3 KB, preloaded in App.razor, "until" is the same paint. */
/* The weight is a *range*, and 200 is only where it rests. The bar at the
   bottom of a telephone marks the page one is on by drawing its icon heavier,
   which is what the stroke width did before this (see MobileBar.razor.css) --
   and a font pinned to exactly 200 has nothing to thicken. Everything else
   takes 200 from `.tp-i` below and never asks for anything else. */
@font-face {
    font-family: 'Material Symbols Outlined';
    font-style: normal;
    font-weight: 200 400;
    font-display: block;
    src: url('fonts/material-symbols.woff2?v=10') format('woff2');
}

.tp-i {
    font-family: 'Material Symbols Outlined';
    font-weight: normal;
    font-style: normal;
    font-size: 1.25rem;
    line-height: 1;
    letter-spacing: normal;
    text-transform: none;
    display: inline-block;
    flex: 0 0 auto;
    white-space: nowrap;
    direction: ltr;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    font-feature-settings: 'liga';
    font-variation-settings: 'FILL' 0, 'wght' 200, 'GRAD' 0, 'opsz' 24;

    /* A glyph sits on the baseline like any letter, and an icon beside a word
       wants its middle on the word's middle. */
    vertical-align: -.25em;
}

/* A mark that is set draws filled: a rated star, a pinned pin (D-175). The other three axes as
   `.tp-i` has them -- `font-variation-settings` is one declaration, and naming one axis alone would
   reset the rest. */
.tp-i.tp-i-filled {
    font-variation-settings: 'FILL' 1, 'wght' 200, 'GRAD' 0, 'opsz' 24;
}

/* The row of a structure node, one size down: these stand indented under the
   entries above them, and an icon as large as a navigation entry's would make a
   child row shout louder than its parent. */
.nav-item .tp-i-row {
    font-size: 1rem;
    margin-right: .4rem;
    vertical-align: -.2em;
}

/* The accent (D-057), and the two lines are not interchangeable.

   `color`, because a glyph is painted in the text colour -- where the masked
   version this replaced used background-color, the mask being the shape.

   And `background: none` because .tp-accent-red sets a background-color of its
   own: on a mask that WAS the icon, on a glyph it would be a coloured block
   behind it. Two classes here outrank the one there, which is what makes this
   work at all -- see the note above the accent list. */
.nav-item .tp-i {
    background: none;
    color: var(--tp-mark, var(--tp-on-brand-quiet));
}

/* On the entry one is on, the ground is white since D-193 and the off-white above would be a mark
   nobody can see. It takes the entry's own blue there; a node's accent still wins, as it does
   everywhere else. Three classes, so it outranks the rule above and the plain one below without
   depending on which comes later in the file. */
.nav-item a.active .tp-i {
    color: var(--tp-mark, var(--tp-on-brand-current));
}

/* The navigation's own icons take the colour of the row they sit in, like the
   word beside them. Only the nodes carry an accent.

   *** Only for a mark inside a `.nav-link`. *** "The row it sits in" is a link,
   and a link has a colour; a `div.nav-item` does not, so a glyph put straight
   into one inherits the body's near-black and disappears against the sidebar.
   That is exactly what happened to the workspace row (WorkspaceTree), and it
   cost two days: a mark drawn in an invisible colour and a mark whose glyph is
   missing look identical, so the font was suspected twice. A mark that is not
   inside a link leaves this class off and takes the off-white above. */
.nav-item .tp-i-plain {
    color: inherit;
}

/* The tree's own folding arrow. Smaller than a row's mark, because it is not one
   of the things the tree lists -- it is the handle on one. */
.nav-item .tp-i-fold {
    font-size: 1rem;
    vertical-align: -.2em;
}

/* An icon standing alone in a button, wherever that button is.

   These replaced typographic characters -- a gear, a midline ellipsis, single
   and double angle quotes -- which every system drew at its own weight and which
   Windows drew as a colour emoji (D-150). A glyph at the font's own 1.25rem is a
   shade large inside a small button, and this is the one number that says so for
   all of them at once. */
.btn > .tp-i:only-child,
.page-link > .tp-i:only-child {
    font-size: 1.125rem;
    vertical-align: -.2em;
}


/* --- the picker ---------------------------------------------------------- */

/* Deliberately WITHOUT a background-color of its own. The first version set one
   to transparent here, and because .tp-swatch and .tp-accent-red have the same
   specificity the later rule won: every colour rendered as an empty circle. A
   base class that resets the property its modifiers exist to set is a trap. */
.tp-swatch {
    display: inline-block;
    width: 1.35rem;
    height: 1.35rem;
    padding: 0;
    border: 1px solid rgba(0, 0, 0, .15);
    border-radius: 50%;
    cursor: pointer;
}

.tp-swatch:hover {
    transform: scale(1.12);
}

/* The chosen one is named by a ring outside it, not by a tick drawn on top:
   a mark on the swatch would hide the very colour it is confirming. */
.tp-swatch.tp-chosen {
    box-shadow: 0 0 0 2px #fff, 0 0 0 4px #495057;
}

/* "No colour" has to look like an absence and still be a target. */
.tp-swatch-none {
    background-color: #fff;
    background-image: linear-gradient(45deg, transparent 45%, #adb5bd 45%, #adb5bd 55%, transparent 55%);
}

.tp-swatch-sm {
    width: 1rem;
    height: 1rem;
}

/* The circle on its own, as a button: beside a heading there is no room for a bordered control saying
   "Keine Farbe", and the circle already says it. Bootstrap's own padding and border are taken away so
   that the target is the swatch and the line it sits in does not grow -- what makes it findable is
   that it is the only round thing in the row, and what makes it pressable is the pointer. */
.tp-swatch-button {
    padding: 0;
    border: 0;
    line-height: 0;
    background: none;
}

/* A popover, not a row of two dozen circles across the form. The grid is twelve
   wide and two deep: the top row is the vivid tones, the bottom the soft ones,
   so a hue sits in the same column in both. */
.tp-accent-popover {
    position: absolute;
    z-index: 1051;
    top: calc(100% + .35rem);
    left: 0;
    padding: .6rem;
    border: 1px solid rgba(0, 0, 0, .15);
    border-radius: .5rem;
    background-color: #fff;
}

.tp-accent-grid {
    display: grid;
    grid-template-columns: repeat(12, 1.35rem);
    gap: .4rem;
}

/* Closing by clicking anywhere else, without a line of JavaScript: an invisible
   sheet under the popover and over everything else. */
.tp-accent-backdrop {
    position: fixed;
    inset: 0;
    z-index: 1050;
}

/* ---------------------------------------------------------------------------
   The vendor's theme, tuned to Taskplan's (D-028).

   Loaded after the Kendo theme on purpose -- see App.razor. Both declare on
   `:root`, so this only works second.

   Almost the whole palette is derived: Kendo computes hover, active, subtle and
   the rest with oklch() from one variable. Setting that one to Bootstrap's own
   blue re-tunes twenty shades at once, and there is nothing to keep in step by
   hand. That is why this block is four lines and not forty.
   --------------------------------------------------------------------------- */
:root {
    /* Taskplan's own blue, not Bootstrap's. `--tp-blue` is a little deeper than
       `#0d6efd`, which is exactly why the picker's "Übernehmen" and the page's
       "Speichern" still read as two different blues after the first attempt --
       the vendor had been given Bootstrap's value rather than ours.

       Referenced rather than copied: it is declared a few lines above in this same
       file, and a custom property substitutes textually, so Kendo's
       oklch(from var(--kendo-color-primary) ...) sees whatever `--tp-blue` holds
       and derives the rest from it. One blue in one place -- which is why the
       repaint of 2026-09-20 reached the vendor's controls without touching this
       line. */
    --kendo-color-primary: var(--tp-blue);

    /* One notch down from the vendor's 1rem. Kendo sizes padding and heights from
       its font size, so this is what makes a picker and a "+ Neuer Termin" sit
       beside our own btn-sm instead of towering over them -- Bootstrap's small
       button is 0.875rem, and now so is theirs. */
    --kendo-font-size: 0.875rem;
    --kendo-font-family: inherit;
}

/* The scheduler keeps the page's own radius, so a calendar does not read as a
   different application pasted into the middle of one. */
.k-scheduler,
.k-datepicker,
.k-datetimepicker,
.k-timepicker,
.k-calendar {
    border-radius: var(--bs-border-radius, 0.375rem);
}

/* The vendor's links -- the month name in a picker's header, "Heute" -- come from
   its own link colour rather than from the primary, so they need saying as well.
   Taskplan's link blue is a shade lighter than its button blue on purpose (see
   `--tp-link` above); the two must not be collapsed into one. */
:root {
    --kendo-color-link: var(--tp-link);
    --kendo-color-link-hover: var(--tp-link-hover);
}

/* The week-number column in a picker's calendar, quieter than the days beside it (D-076).

   The vendor already draws it a size down and in the subtle colour, and it was still read as a
   date -- because at that size the only difference from the day next to it is the colour, and a
   column of numbers on the left of a calendar is a column of numbers. Smaller again, and lighter,
   so it reads as a label on the row rather than as one of the days in it.

   Here and not in a scoped stylesheet: the picker hangs its calendar in a popup appended to the
   body, outside every component's scope. Same reason the theme tuning above lives here (D-077). */
.k-calendar-td.k-alt {
    font-size: .7rem;
    font-weight: 400;
    opacity: .65;
}

/* The sidebar shell, shared by both layouts that draw one: the application's `MainLayout` and the
   `AccountLayout` the Identity pages got when they were taken out of the workspace boundary (D-083).
   Unscoped for exactly that reason -- a component stylesheet reaches only its own component, and the
   alternative was a second copy of every rule below.

   *** The long fade is gone as of 2026-09-20. ***

   It ran from one navy into a darker one, and both were chosen to answer the brand row above it. The
   repaint answers a different question: the dark surfaces are one colour now, said once as
   `--tp-slate`, and a gradient down 100vh was the thing stopping the sidebar and the phone's top bar
   from being visibly the same thing. Flat is also what the direction asks for -- "minimal gradients"
   -- and a fade nobody can name the ends of is decoration rather than information.

   What it replaced, for whoever wonders: `linear-gradient(180deg, rgb(5, 39, 103), #03173e 70%)`,
   which had in turn replaced the template's purple -- a colour Taskplan never chose and used nowhere
   else, so the bottom half of the sidebar was the one place on screen speaking a different language. */
.page {
    position: relative;
    display: flex;
    flex-direction: column;
}

/* Scoped to `.page` rather than left as a bare `main`, because this file is global and `main` is a
   tag anybody may reach for later. */
.page main {
    flex: 1;
}

/* ---------------------------------------------------------------------------
   `.tp-echo` stood here from 2026-09-20 to 2026-09-22: a heading hidden on a
   telephone where it only repeated the word in the bottom bar.

   Nobody uses it any more, so it is gone rather than kept "in case". Today and
   the Inbox took it off the evening it arrived (D-165); the calendar, the last
   page that wore it, now names the whole workspace instead of saying "Kalender"
   again, and puts that name on a line of its own (D-164, amended). A rule no
   markup reaches is a rule the next person reads, believes and builds on.

   If the idea comes back, D-165 has the part worth keeping: a heading may give
   its place to something that does not repeat -- and where there is nothing to
   give it to, it keeps it.
   --------------------------------------------------------------------------- */

/* Below the breakpoint a fixed row of four sits at the bottom of the screen (D-098). Without this the
   last line of every page is behind it, and the page that suffers most is the one somebody scrolled
   to the end of -- which is the one they were reading. */
/* ---------------------------------------------------------------------------
   A name that is changed where it stands (D-169).

   A field that looks like a heading: the work item's title since D-113, and
   since 2026-09-22 the area's while its settings are open and the workspace's
   on its own page. Here and not in any one page's stylesheet, because three
   pages draw it and a scoped rule reaches only the markup in its own file.

   It inherits the heading's type rather than declaring its own: the two must
   not drift, or renaming something would change how big its name is -- and on
   a telephone that includes the step `TitleFit` chose for it.

   The frame appears on hover and while typing: that is what says "this can be
   changed" without a second control saying it out loud. Everywhere the same,
   the area's included -- for one afternoon it was framed from the moment its
   settings opened, and Jan asked for it to wait for the click like the other
   two (2026-09-22). `min-width: 0`
   because an input has a width of its own and would otherwise push the row
   wider than a telephone -- the fault the title row was built to prevent.
   --------------------------------------------------------------------------- */
.tp-title-edit {
    display: block;
    width: 100%;
    min-width: 0;
    padding: .1rem .35rem;
    margin-left: -.35rem;

    font: inherit;
    color: inherit;
    line-height: inherit;

    border: 1px solid transparent;
    border-radius: .3rem;
    background: none;
}

.tp-title-edit:hover {
    border-color: var(--bs-border-color);
}

.tp-title-edit:focus {
    border-color: var(--tp-link, #0d6efd);
    outline: none;
    background-color: var(--bs-body-bg);
}


/* ---------------------------------------------------------------------------
   A page's title on a telephone: one line, and it gives way (Jan, 2026-09-22).

   The title shares its row with the page's symbols on all four pages -- day,
   calendar, Inbox, Browse -- and the row must never be pushed wider than the
   screen. So a long title first steps down in size, as far as the symbols
   beside it and no further, and is then cut with an ellipsis.

   Which step is decided on the server from the title's length (`TitleFit`),
   not measured here: CSS cannot see whether text overflows, and a script that
   measured it would redraw every title after it had been drawn once.

   `min-width: 0` on the heading and on the name is what lets any of this
   happen. A flex child will not be narrower than its text unless it is told it
   may, and without it the name simply pushes the row -- the calendar broke
   exactly that way on its first long area name, while Browse, which had these
   lines in its own file, did not. They live here now so all four have them.

   The size steps are `h1.` plus a class: Bootstrap sizes `h1` by element and
   `.h1` by class, and the step has to outweigh both.
   --------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
    .tp-page-title {
        min-width: 0;
    }

    .tp-page-title-text {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    h1.tp-fit-m {
        font-size: 1.5rem;
    }

    /* The size of a symbol in the row beside it, which is where Jan asked it
       to stop: smaller than the controls, a title stops reading as a title. */
    h1.tp-fit-s {
        font-size: 1.125rem;
    }
}

/* *** The bottom row's measurements, in one place at last. ***

   They were three numbers in two files that had to be changed together -- the row's height here, the
   floating button's offset and the page's bottom padding in `MobileBar.razor.css` -- with a comment
   in each saying so. Comments do not hold numbers together; this does. Custom properties are not
   scoped, so the component file reads them as happily as this one.

   `--tp-bar-body` is the row of touch targets: 3.5rem clears the 44px every platform names as the
   smallest thing worth aiming at, with a quarter rem for the caption under the symbol.

   `--tp-bar-foot` is the air below them, added on 2026-09-21 on Jan's word after the first look at
   an installed app: the words sat on the very edge of the glass, where the phone's own bars keep a
   finger's width.

   `--tp-bar-under` is what actually stands below the row (D-233). Where the page reaches under the
   home indicator -- the app on the iPhone, whose copy is laid out edge to edge -- the strip the phone
   keeps for it (`safe-area-inset-bottom`, 34 points) was *added* to the air until 2026-10-10, and
   the row sat a finger's width higher than in the web app's pages, which are not laid out under it
   (Jan, comparing the day with the backend). Now: the air, or the strip less `--tp-bar-into-strip`,
   whichever is larger. The indicator is a thin line low in that strip; the row may reach into its
   upper part, as the web app's pages show it does without harm. Where there is no strip (browsers,
   the web app) nothing changes: the air alone.

   Android has a strip as well, since its web view runs under the system's bar: 48 pixels with the
   three buttons. But that bar is drawn *over* the page, and what reaches into it is covered -- the
   captions were cut off at the bottom (Jan, 2026-10-10, D-235), whatever the air was set to, since
   the strip less 1rem was always the larger number. So on Android the row stays wholly above it, a
   quarter rem higher: `--tp-bar-into-strip` below zero. The class is set by the copy's index.html.

   `--tp-bar` is the whole of it, the strip included: everything that stands above the row or must not
   run under it reads this one number and adds nothing of its own. */
:root {
    --tp-bar-body: 3.5rem;
    --tp-bar-foot: .75rem;
    --tp-bar-into-strip: 1rem;
    --tp-bar-under: max(var(--tp-bar-foot), calc(env(safe-area-inset-bottom, 0px) - var(--tp-bar-into-strip)));
    --tp-bar: calc(var(--tp-bar-body) + var(--tp-bar-under));
}

/* `html.` and not `:root.`: one element more, so it wins over the block above wherever it stands. */
html.tp-android {
    --tp-bar-into-strip: -.25rem;
}

/* While the phone's workspace view is open (D-251): the page behind it stands still, and the round
   note button -- which floats above the bottom row and so above the view -- steps aside. The view
   is a checkbox (NavMenu), so the page can only be asked about it this way; `:has` is in every
   browser the app runs in (Safari 15.4, Chrome 105).

   `--tp-star` is the warm yellow of a set star, in the list and in the tree alike. */
:root {
    --tp-star: #ffd166;
}

@media (max-width: 767.98px) {
    body:has(.navbar-toggler:checked) {
        overflow: hidden;
    }

    body:has(.navbar-toggler:checked) .tp-mobile-note {
        visibility: hidden;
    }
}

@media (max-width: 767.98px) {
    /* The row's own height plus the space a last line needs not to look cut off. */
    .page main {
        padding-bottom: calc(var(--tp-bar) + 1.25rem);
    }

    /* The top bar stays put while the page moves under it (D-098).

       On the sidebar and not on the `.top-row` inside it, which is where one would reach first and
       where it does nothing: below the breakpoint the sidebar is a short block at the top of the
       column, so a bar stuck to the top of *it* is a bar that leaves with it after two lines of
       scrolling. Sticking the block itself is what keeps the brand, the hamburger and the bell on
       screen -- and with the drawer open the whole drawer stays with them, which is what somebody
       reading a long tree wants anyway.

       Under the bottom row's z-index rather than over it: the two never overlap, and if a popover
       ever makes them, the row a thumb is aiming at should win. */
    .sidebar {
        position: sticky;
        top: 0;
        z-index: 1025;
    }
}

/* The dark block at the top of a telephone, and the one that reaches into the status bar.

   `viewport-fit=cover` in App.razor lets the page fill a screen with a notch, and the iOS status bar
   is declared translucent, which together mean the clock and the battery are drawn *over* this block
   rather than above it. Without the padding the brand sits under them. With it, the slate runs to the
   very top edge and the status bar's white text stands on the same colour as the bar below it, which
   is the coloured top edge Jan asked for on 2026-09-20.

   Not inside a media query: `env()` is zero everywhere except in a display that has such an inset, so
   this is nought pixels in every browser window and the right number in an installed app. */
.sidebar {
    /* The brand surface since D-193, where it was `--tp-slate`. The plain colour first, for the
       moment before the gradient is resolved and for anything that cannot draw one.

       *** The colour the gradient starts with, not the one it ends with. *** An installed app on
       an iPhone colours the strip under the clock from the plain colour of the bar below it -- it
       does not read the gradient. With the end colour here the strip was the dark blue and the bar
       under it began in the light one: a seam across the top of the screen (Jan, 2026-10-06). */
    background-color: var(--tp-brand-from);
    background-image: var(--tp-brand);
    padding-top: env(safe-area-inset-top, 0px);
}

@media (min-width: 768px) {
    .page {
        flex-direction: row;
    }

    /* `position: sticky` creates a stacking context, which is what made the notification panel
       disappear behind the page: anything the sidebar contains is then ordered *inside* it, however
       high its own z-index, and the sidebar itself sits at level auto -- below `main`, which follows
       it in the document. Lifting the sidebar puts its popovers over the content again. Kept under
       1050, so the colour picker's own backdrop and popover still cover it while they are open. */
    .sidebar {
        width: 250px;
        height: 100vh;
        position: sticky;
        top: 0;
        z-index: 1040;
    }
}

/* The description editor's toolbar, folded away until its switch is pressed (D-173). Here rather
   than in a scoped file: the toolbar is the vendor's markup, which no component's scope reaches. */
.tp-rich:not(.tp-rich-open) .k-editor-toolbar {
    display: none;
}

.tp-rich-toggle,
.tp-rich-image {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 2.5rem;
    min-height: 2.25rem;
    padding: 0;
    color: var(--bs-secondary-color);
    text-decoration: none;
}

.tp-rich-open .tp-rich-toggle {
    color: var(--tp-link, #0d6efd);
}

/* Pictures in a description (D-281): never wider than the field, the height following, and marked
   when selected so a picture can be seen to be the thing a delete will take. The line under the field
   says what is under way, or what went wrong (richimages.js). */
/* The description drawn taller with the mouse (D-283): the browser's own handle at the lower right
   corner, where there is a mouse -- a finger has no corner to take. Not smaller than it opens with less
   a little, not taller than the window. richheight.js remembers the height in this browser. The editor's
   writing area grows with it (the vendor's editor is a column with the content filling the rest). */
@media (pointer: fine) {
    .tp-rich .k-editor {
        display: flex;
        flex-direction: column;
        resize: vertical;
        overflow: hidden;
        min-height: 8rem;
        max-height: 85vh;
    }

    .tp-rich .k-editor-content {
        flex: 1 1 auto;
        min-height: 0;
    }
}

.tp-rich .ProseMirror img {
    max-width: 100%;
    height: auto;
    border-radius: .375rem;
}

.tp-rich .ProseMirror img.ProseMirror-selectednode {
    outline: 2px solid var(--tp-link, #0d6efd);
    outline-offset: 2px;
}

.tp-rich-note {
    margin-top: .25rem;
    font-size: .875rem;
    color: var(--bs-secondary-color);
}

.tp-rich-busy .tp-rich-image {
    opacity: .5;
}

/* A finger's handles on a selected picture (D-285, richimages.js): a frame laid over the picture, in the
   box that holds the editor's text (so it scrolls with it), with four corners large enough to take with a
   finger (32px) and a dot to see. The frame lets every touch through except on the corners; dragging a
   corner does not scroll the page. */
.tp-image-frame {
    position: absolute;
    z-index: 1060;
    pointer-events: none;
    outline: 2px solid var(--tp-link, #0d6efd);
    outline-offset: 0;
}

.tp-image-frame,
.tp-image-handle {
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
}

/* And no "save picture" menu from holding a finger on a picture being sized. */
.tp-rich-finger .ProseMirror img {
    -webkit-touch-callout: none;
}

.tp-image-handle {
    position: absolute;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    margin: -16px;
    pointer-events: auto;
    touch-action: none;
}

.tp-image-handle::after {
    content: "";
    width: 14px;
    height: 14px;
    border: 2px solid var(--tp-link, #0d6efd);
    border-radius: 50%;
    background: var(--bs-body-bg, #fff);
}

.tp-image-handle-nw { top: 0; left: 0; cursor: nwse-resize; }
.tp-image-handle-ne { top: 0; right: 0; cursor: nesw-resize; }
.tp-image-handle-sw { bottom: 0; left: 0; cursor: nesw-resize; }
.tp-image-handle-se { bottom: 0; right: 0; cursor: nwse-resize; }

/* The vendor's eight handles, hidden while a finger has ours (D-285): they answer a mouse only, and on the
   iPhone four of them stood under ours and four beside them -- eight points, of which none worked. */
.tp-rich-finger [class*="k-editor-resize-handle"] {
    display: none !important;
}

/* Links in a description (D-280): drawn as links, and a tap opens the small menu beside them
   (richlinks.js). The menu hangs on the body, outside every component's scope, so it is styled here. */
.tp-rich .ProseMirror a {
    color: var(--tp-link, #0d6efd);
    text-decoration: underline;
    cursor: pointer;
}

.tp-link-menu {
    position: fixed;
    z-index: 1080;
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
    min-width: 14rem;
    max-width: min(22rem, calc(100vw - 1rem));
    padding: 0.5rem 0.625rem;
    background: var(--bs-body-bg);
    color: var(--bs-body-color);
    border: 1px solid var(--bs-border-color);
    border-radius: 0.5rem;
    box-shadow: 0 0.5rem 1.5rem rgba(0, 0, 0, 0.15);
}

.tp-link-href {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.875rem;
    color: var(--bs-secondary-color);
}

.tp-link-fields {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.tp-link-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem;
}

/* The editor's two colour tools (D-096).

   Here rather than in `RichTextField.razor.css`, for the reason the calendar rules above give: the
   palette hangs in a popup appended to the body, outside every component's scope. The toolbar half is
   written here too, so the two halves of one control are not in two files.

   The picker buttons sit outside the button groups on purpose -- they open a palette rather than
   toggling -- but that left them on white while everything around them had a grey field, and the pair
   read as two disabled controls rather than two tools. Same ground, same shape as the groups beside
   them. */
.k-editor-toolbar .k-colorpicker,
.k-editor-toolbar .k-picker {
    background-color: var(--kendo-color-base, #f5f5f5);
    border-radius: var(--kendo-border-radius-md, .25rem);
}

/* And the palette itself as circles, because Taskplan already has a colour picker -- the accent on an
   item -- and two pickers in one product that disagree about what a colour looks like are two
   products. The measurements are `.tp-swatch`'s, deliberately: the same 1.35rem, the same hairline,
   the same lift on hover.

   Both spellings of the tile class are given. The vendor renamed it between theme generations, and a
   selector that silently stops matching would leave squares behind with nothing to explain them. */
.k-colorpalette-tile,
.k-color-palette-tile {
    border-radius: 50%;
    border: 1px solid rgba(0, 0, 0, .15);
}

.k-colorpalette-tile:hover,
.k-color-palette-tile:hover {
    transform: scale(1.12);
}

/* Kendo lays the tiles out in a table, where circles would otherwise touch. */
.k-colorpalette-table,
.k-color-palette-table {
    border-spacing: .25rem;
    border-collapse: separate;
}

/* Which of a picker's two halves is on screen (D-127).

   `DateField`, `TimeField` and `DateTimeField` each render the vendor's picker and the browser's own
   field, and exactly one of them is ever visible: the vendor above the breakpoint, where it reads the
   workspace's culture and a mouse does the picking, and the platform's own below it, where a wheel
   with real momentum and a wrap-around beats anything a library draws.

   Here rather than in three scoped stylesheets, for the reason the Telerik rules above give and one
   more: the vendor's picker is a child component, so a scoped rule would have to reach it with
   `::deep` -- three files saying the same thing in the more awkward of the two spellings.

   `!important` because of what these two class names are up against. The vendor's root carries several
   of its own -- `.k-datetimepicker.k-input` is already (0,2,0) -- and the scoped stylesheet loads
   after this file with an attribute selector on every rule, so a page that sizes one of these fields
   from its own component would win too. A rule whose whole job is "not this one, here" should not be
   the one that quietly loses: a field that is hidden must be hidden. */
@media (max-width: 767.98px) {
    .tp-picker-wide {
        display: none !important;
    }
}

@media (min-width: 768px) {
    .tp-picker-touch {
        display: none !important;
    }
}

/* ---------------------------------------------------------------------------
   A work item's type, as a mark in front of its name (D-053).

   Here rather than in `WorkItems.razor.css`, where these four rules were written:
   the result list of a workspace search draws the same mark and is its own
   component, and scoped CSS reaches only the markup in the file it belongs to.
   The navigation icons made the same journey for the same reason -- see the note
   in `WorkspaceTree.razor.css`.
   --------------------------------------------------------------------------- */

/* The type, in front of the name, the way the tree marks a node's kind (D-053). Inline data URIs
   rather than an icon font, for the same reason as there: a font that arrives late leaves a column of
   empty boxes behind it.

   Dimmed to .75 on purpose. These rows are read by their names; the mark is what the eye skips along,
   and at full black it would compete with the text it is meant to label. */
.tp-type-icon {
    display: inline-block;
    flex: 0 0 auto;
    width: .95rem;
    height: .95rem;
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
    opacity: .75;
}

/* A sheet with a folded corner and two lines: a note is something written down and nothing more
   (D-040). It is the one type with no checkbox beside it, and the mark says why. */
.tp-type-note {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23495057'%3E%3Cpath fill-rule='evenodd' d='M3 1.5h6v4h4v9H3V1.5zM5 8h6v1H5V8zm0 2.5h5v1H5v-1z'/%3E%3C/svg%3E");
}

/* A flag, and emphatically not a ticked box -- see TypeIcon in the code-behind. The row already
   carries a checkbox two millimetres to the left, and a second box beside it said "done" about work
   nobody had started. */
.tp-type-task {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23495057'%3E%3Cpath d='M3.5 1.5h1v13h-1zM5 2h8l-2.2 3L13 8H5z'/%3E%3C/svg%3E");
}

/* A calendar, the same picture the Calendar entry uses in the sidebar. An appointment is the one
   work item whose whole point is when it happens. */
.tp-type-appointment {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23495057'%3E%3Cpath fill-rule='evenodd' d='M5 1h1.5v1.5H5V1zm4.5 0H11v1.5H9.5V1zM2.5 2.5h11v12h-11v-12zm1 3.5v7.5h9V6h-9z'/%3E%3C/svg%3E");
}

/* The magnifier's panel (D-166).

   Global rather than scoped to one page, because two pages draw it now -- the work list and the
   phone calendar -- and a scoped rule reaches only the markup in its own file.

   Drawn in the pale blue the rest of the product uses for "this is a note about what you are doing"
   rather than as another white card: it sits between a heading and a list of white rows, and a white
   panel among white rows is a panel nobody sees open. Text on it measures 13:1, well past D-096.

   No rule hides it. The markup draws nothing while the panel is shut, which is a state a stylesheet
   cannot get wrong -- and this one has to work on the account pages' width as well as a phone's. */
.tp-find-open {
    margin-bottom: .75rem;
    padding: .625rem .75rem;
    border: 1px solid var(--tp-blue-wash-border);
    border-radius: .5rem;
    background-color: var(--tp-blue-wash);
}
