/* SELF-HOSTED FONTS, and why they are not loaded from Google.
   ---------------------------------------------------------------------------
   These two typefaces used to come from fonts.googleapis.com, which meant every
   visitor to any of the 1733 pages had their IP address sent to Google in the
   United States before they had agreed to anything. privacy.html disclosed it
   honestly, and disclosure is not a lawful basis: it is the single most
   litigated GDPR issue there is for a small site.

   Serving them from this domain removes the transfer completely. It is also
   faster - one fewer DNS lookup, one fewer TLS handshake, and no render-blocking
   request to a third party.

   Only the latin and latin-ext subsets are kept, 12 files of the 33 Google
   offers. The interface is English and latin-ext covers the accented country
   names (Turkiye, Cote dIvoire, Curacao). Greek, Cyrillic and Vietnamese would
   be weight for nothing. unicode-range means a browser downloads latin-ext only
   when a page actually uses one of those characters.

   font-display:swap matches what the Google link requested, so text is readable
   immediately in the fallback and reflows once when the face arrives. */
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:400;font-display:swap;
  src:url(fonts/jetbrains-mono-400-latin-ext.woff2) format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:400;font-display:swap;
  src:url(fonts/jetbrains-mono-400-latin.woff2) format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:500;font-display:swap;
  src:url(fonts/jetbrains-mono-500-latin-ext.woff2) format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:500;font-display:swap;
  src:url(fonts/jetbrains-mono-500-latin.woff2) format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:700;font-display:swap;
  src:url(fonts/jetbrains-mono-700-latin-ext.woff2) format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'JetBrains Mono';font-style:normal;font-weight:700;font-display:swap;
  src:url(fonts/jetbrains-mono-700-latin.woff2) format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;}
@font-face{font-family:'Oswald';font-style:normal;font-weight:400;font-display:swap;
  src:url(fonts/oswald-400-latin-ext.woff2) format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Oswald';font-style:normal;font-weight:400;font-display:swap;
  src:url(fonts/oswald-400-latin.woff2) format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;}
@font-face{font-family:'Oswald';font-style:normal;font-weight:500;font-display:swap;
  src:url(fonts/oswald-500-latin-ext.woff2) format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Oswald';font-style:normal;font-weight:500;font-display:swap;
  src:url(fonts/oswald-500-latin.woff2) format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;}
@font-face{font-family:'Oswald';font-style:normal;font-weight:600;font-display:swap;
  src:url(fonts/oswald-600-latin-ext.woff2) format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
@font-face{font-family:'Oswald';font-style:normal;font-weight:600;font-display:swap;
  src:url(fonts/oswald-600-latin.woff2) format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;}

/* ==========================================================================
   inmycalendar - site.css
   Shared shell: design tokens, ribbon, controls strip, footer, content pages.
   Loaded FIRST on every page. app.css loads second and wins where they overlap.

   THEME: light / near-black. The chrome is deliberately neutral so that every
   colour left on screen carries meaning - category fills, holiday stripes and
   kanban status colours. An accent hue here would collide with all three.
   ========================================================================== */
:root{
  --sans:ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
  --mono:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,monospace;
  --disp:Oswald,"Arial Narrow",sans-serif;

  --page:#f6f7f9;      /* app background          */
  --card:#ffffff;      /* panels, columns, cells  */
  --card2:#f3f4f6;     /* hover / subtle fill     */
  --ink:#111318;       /* primary text            */
  --soft:#6b7280;      /* secondary text          */
  --faint:#9ca3af;     /* tertiary text           */
  --rule:#e5e7eb;      /* hairline border         */
  --rule2:#f0f1f3;     /* faintest border         */

  --accent:#18181b;    /* chrome, today ring, active */
  --accent2:#52525b;
  --accentBg:#f4f4f5;
  --onAccent:#ffffff;
  --danger:#b91c1c;

  --todo:#2563eb;  --todoBg:#eff5ff;  --todoE:#dbe6fe;
  --doing:#d97706; --doingBg:#fffaf0; --doingE:#fae2c2;
  --done:#059669;  --doneBg:#ecfdf4;  --doneE:#c6efdd;

  /* holiday markers - shown as a stripe, never a fill, so they never
     collide with a user's category colour on the same day */
  --holNat:#dc2626;
  --holReg:#2563eb;

  /* COLOURS THAT USED TO BE WRITTEN INTO RULES.
     Each of these was a literal hex somewhere below, which is fine while there
     is one theme and fatal the moment there are two: a rule that hard-codes
     #2b2f38 text keeps painting near-black on a near-black page. They are
     tokens now so the dark block at the foot of this file can answer for all
     of them in one place. */
  --bodyInk:#2b2f38;        /* long-form reading text on content pages */
  --dangerBg:#fdf3f3;       /* the wash behind a destructive control   */
  --dangerLine:#f0d5d5;
  --dangerInk:#991b1b;      /* the confirmed-delete button             */
  --wkInk:#c3c7ce;          /* week numbers down the calendar's left   */
  --cellInk:#374151;        /* the date inside a calendar cell         */
  --wkndBg:#fafbfc;         /* the faint Saturday/Sunday tint          */
  --outInk:#c9ced6;         /* a day belonging to the neighbouring year*/
  --chipLine:rgba(0,0,0,.12);
  /* The toast is deliberately the INVERSE of the page in both themes - that
     is what makes it read as a system message rather than as another panel -
     so it cannot be built from --ink and --card. */
  --toastBg:#111318;
  --toastInk:#ffffff;
  --toastLine:rgba(255,255,255,.45);
  --toastHover:rgba(255,255,255,.14);
  --toastSoft:rgba(255,255,255,.6);
  --overlay:rgba(17,19,24,.4);
  --shadow:rgba(17,19,24,.14);
  --shadowStrong:rgba(0,0,0,.22);

  --wrap:1320px; --gut:20px; --rail:240px;
  --hBar:52px;
  --wIso:96px; --wMeta:150px; --wYrs:88px;
  /* How tall a column may grow before it scrolls.

     This used to be a flat 326px, sized for exactly ten single-line tasks. Two
     things were wrong with that. It ignored the screen: the same 326px on a
     13-inch laptop and a 27-inch monitor wasted most of the larger one. And it
     was described in TASKS, which stopped being meaningful once a task can run
     to three lines - a column of long tasks and a column of short ones share
     one height, so the tall ones simply show fewer. That is the correct
     behaviour and it falls out of measuring in pixels rather than in rows.

     32vh is about eight single-line tasks on a typical laptop and more on a
     large monitor. The clamp stops it collapsing on a short window or running
     away on a very tall one. */
  --laneMax:clamp(180px, 32vh, 460px);
  --hYear:30px; --hDow:22px;
  --r:10px;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{background:var(--page);color:var(--ink);font-family:var(--mono);font-size:13px;line-height:1.45}
button{font:inherit;cursor:pointer}
input,select,textarea{font:inherit;color:inherit}
.hidden{display:none !important}
/* Read by screen readers and search engines, occupies no space on screen.
   The app page needs an h1 for SEO but has no room for a visible heading. */
.sronly{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
.wrap{width:min(100% - (var(--gut) * 2), var(--wrap));margin-left:auto;margin-right:auto}

/* ---------------- ribbon: white, hairline, three fluid zones --------------- */
.bar{position:sticky;top:0;z-index:60;background:var(--card);border-bottom:1px solid var(--rule)}
/* The ribbon gets MORE width than the content column below it.
   This was the actual bug behind "the top row keeps wrapping". .wrap caps
   everything at 1320px, so on a 1900px monitor the ribbon still only had
   1320px to work with, and the contents need about 1330 once the date meta is
   showing. Every breakpoint I wrote keyed off viewport width, which was never
   the number that mattered - the container cap was. Widening the bar alone
   lets a large monitor actually be a large monitor, which is what was being
   asked for, while the reading column below stays a comfortable 1320. */
.bar .wrap{display:flex;align-items:center;gap:12px;flex-wrap:wrap;
  width:min(100% - (var(--gut) * 2), 1640px);
  min-height:var(--hBar);padding-top:7px;padding-bottom:7px}
.brand{display:inline-flex;align-items:center;gap:8px;font-family:var(--disp);font-size:15px;
  letter-spacing:.07em;white-space:nowrap;color:var(--ink);text-decoration:none;flex:none}
.brand b{font-weight:500;color:var(--soft)}
.brand svg{display:block;border-radius:6px}
.brand .wordmark{white-space:nowrap}
.appzone{display:flex;align-items:center;gap:7px;flex-wrap:wrap;min-width:0;flex:1 1 auto}
.sitenav{display:flex;gap:1px;align-items:center;flex-wrap:wrap;flex:none;margin-left:auto}
.sitenav a{color:var(--soft);text-decoration:none;font-family:var(--disp);font-size:11px;
  letter-spacing:.08em;text-transform:uppercase;padding:6px 10px;border-radius:7px;white-space:nowrap}
.sitenav a:hover{background:var(--accentBg);color:var(--ink)}
.sitenav a.on{background:var(--accentBg);color:var(--ink);font-weight:500}
.sitenav .gap{width:1px;height:15px;background:var(--rule);margin:0 7px;flex:none}

/* Above the phone breakpoint the ribbon must stay on ONE line. It wraps by
   default so a narrow screen can reflow, but on a desktop that wrapping put
   the account badge and Sign out on a second row, costing a whole row of
   vertical space and dropping the settings panel where half of it fell off
   the left edge. Everything shrinks instead; nothing wraps.

   THIS BLOCK MUST STAY BELOW the .appzone and .sitenav rules above. A media
   query carries no extra specificity, so when it sat higher in the file the
   plain .appzone{flex-wrap:wrap} underneath simply won and the whole thing
   did nothing. Position in the file is the only reason it applies. */
/* ---- one ribbon line, WITHOUT overlapping -------------------------------
   The first attempt set nowrap plus min-width:0 on these zones. min-width:0
   lets a flex item shrink BELOW its own contents, so the contents did not
   shrink with it - they spilled out and painted over the neighbour. "Month"
   sat on top of "Kanban Board". Overlapping is strictly worse than wrapping,
   which is what it replaced.

   Nothing may shrink below its content now. Instead, at each width, the least
   useful thing is removed until the rest genuinely fits:

     < 1500px   the date meta goes ("Tue · Week 28"), worth 150 fixed pixels
                and duplicated by the date field two controls to its left
     < 1250px   the nav labels shorten, "Kanban Board" becomes "Board"
     <  901px   the ribbon is allowed to wrap again, because on a small screen
                two honest rows beat cramming

   wrap stays the base everywhere, so if a future addition overflows the line
   the ribbon grows a second row rather than overlapping. Ugly beats broken. */
/* NOTE: no flex-wrap:nowrap anywhere. An earlier attempt set it here, and
   nowrap means that when the contents do not fit they OVERLAP instead of
   moving down - which is exactly what happened, at every width below 1280px.
   Wrapping is the safety net: if the breakpoints below ever fail to free up
   enough room, the ribbon grows a second row, which is untidy but readable.
   Overlapping text is neither. The breakpoints keep it to one line in
   practice; wrap makes the failure mode survivable. */
@media (min-width:901px){
  .appzone{flex:0 1 auto}
  .sitenav{flex:0 0 auto}
  .authslot{flex:none}
}
@media (max-width:1499px){
  .meta{display:none}
}
/* The BASE state, and it must stay ABOVE the media query below.
   It used to sit underneath it. A media query carries no extra specificity, so
   an equally-specific plain rule written later simply wins - which meant
   .navshort was display:none at EVERY width, and under 1250px .navlong was
   hidden too. The result: on every phone, the main "Board" link in the ribbon
   was an empty 16px box with no text in it at all. The same ordering trap is
   already written up a few rules above for .appzone; it caught this one too. */
.navshort{display:none}

@media (max-width:1249px){
  .sitenav a{padding:6px 7px;letter-spacing:.05em}
  .sitenav .gap{margin:0 3px}
  .navlong{display:none}
  .navshort{display:inline}
}

/* ---------------- shared controls ---------------- */
.btn{background:var(--card);border:1px solid var(--rule);color:var(--ink);padding:5px 11px;
  font-size:11.5px;border-radius:7px;white-space:nowrap}
.btn:hover{background:var(--card2)}
.btn:disabled{opacity:.4;cursor:not-allowed}
.btn.danger{color:var(--danger);border-color:var(--dangerLine)}
.btn.danger:hover{background:var(--dangerBg)}
.sel{background:var(--card);border:1px solid var(--rule);color:var(--ink);border-radius:7px;
  padding:4px 8px;font-size:11.5px;height:29px}

footer{border-top:1px solid var(--rule);margin-top:26px;padding:16px 0 30px;
  text-align:center;font-size:11.5px;color:var(--soft)}
footer a{color:var(--soft);margin:0 9px;text-decoration:none}
.fdata{display:flex;justify-content:center;flex-wrap:wrap;gap:8px;margin-top:14px}
.fdata .btn{font-size:11px;padding:5px 12px}
footer a:hover{color:var(--ink)}

/* ---------------- content pages (about / contact / privacy) ---------------- */
.pagebody{padding:30px 0 50px}
.pagebody .body{max-width:660px;font-family:var(--sans);font-size:14.5px;line-height:1.7;color:var(--bodyInk)}
.pagebody h1{font-family:var(--disp);font-weight:500;font-size:27px;letter-spacing:.04em;
  text-transform:uppercase;color:var(--ink);margin:0 0 6px}
.pagebody h2{font-family:var(--disp);font-weight:500;font-size:15px;letter-spacing:.09em;
  text-transform:uppercase;color:var(--ink);margin:28px 0 7px}
.pagebody .eyebrow{font-family:var(--mono);font-size:11.5px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--soft);margin-bottom:22px}
.pagebody p{margin:0 0 12px}
.pagebody ul{margin:0 0 12px;padding-left:20px}
.pagebody li{margin-bottom:5px}
.pagebody a{color:var(--ink);text-decoration:underline;text-underline-offset:2px}
.pagebody .card{background:var(--card);border:1px solid var(--rule);border-radius:var(--r);
  padding:14px 16px;margin:18px 0}
.pagebody .card.warn{border-color:var(--doingE);background:var(--doingBg)}

/* ===========================================================================
   SIGN-IN WIDGET - lives here, in site.css, NOT in app.css.

   It used to live in app.css, which only index.html loads. The widget itself
   is injected by auth.js into every page, so on guide / contact / privacy it
   rendered completely unstyled and the name badge sat on top of the Sign out
   button. Exactly the "shared shell fixed in one file only" trap the README
   warns about, shipped anyway because nothing asserted it across all four
   pages. There is now a test that does.
   =========================================================================== */
.authslot{display:inline-flex;align-items:center;gap:7px;flex:none;margin-left:4px}
.authslot .btn{padding:5px 12px;font-size:11px;font-family:var(--disp);letter-spacing:.06em;
  text-transform:uppercase}
.authslot .signin{background:var(--accent);border-color:var(--accent);color:var(--onAccent)}
.authslot .signin:hover{background:var(--accent2);border-color:var(--accent2)}
/* Was a 26px circle holding one letter, which read as a placeholder rather
   than as "you are signed in". It now carries the first name, so it has to be
   a pill that grows with the text instead of a fixed circle. */
.authslot .who{width:28px;height:28px;border-radius:50%;background:var(--accentBg);color:var(--ink);
  display:inline-flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;
  letter-spacing:.02em;border:1px solid var(--rule);flex:none;cursor:default}

.authslot{position:relative}
.authmenu{position:absolute;right:0;top:calc(100% + 8px);z-index:80;width:250px;background:var(--card);
  border:1px solid var(--rule);border-radius:var(--r);box-shadow:0 12px 30px var(--shadow);
  padding:10px;display:flex;flex-direction:column;gap:6px;text-align:left}
.amrow{background:var(--card);border:1px solid var(--rule);border-radius:8px;padding:9px 11px;
  font-size:12.5px;color:var(--ink);text-align:left}
.amrow:hover{background:var(--card2)}
.amsep{font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);
  text-align:center;margin:4px 0 2px}
.aminput{width:100%;border:1px solid var(--rule);border-radius:8px;padding:8px 10px;font-size:12.5px}
.aminput:focus{outline:none;border-color:var(--accent)}
.amsend{background:var(--accent);border:1px solid var(--accent);color:var(--onAccent);
  border-radius:8px;padding:8px 11px;font-size:12px}
.amsend:hover{background:var(--accent2)}
.amsend:disabled{opacity:.5}
.amnote{font-size:11px;color:var(--soft);line-height:1.4;min-height:1px}

/* On a phone the ribbon wraps, so the slot can end up near the left edge and a
   250px menu anchored to its right edge runs off the screen. Pin it to the
   viewport instead of to the button.

   top MUST be a real length, never auto. position:fixed with BOTH top and
   bottom auto falls back to the box's STATIC position - where it would have
   sat inside the ribbon - which measured -121.55px on a 375x812 screen. That
   put Continue with Google and Continue with Microsoft entirely above the top
   of the screen and clipped GitHub in half, and since the menu was not
   overflowing there was nothing to scroll to reach them: OAuth sign-in was
   impossible on a phone. dvh rather than vh so that the on-screen keyboard
   shrinking the viewport leaves the menu scrollable instead of cut off. */
@media (max-width:640px){
  .authmenu{position:fixed;left:12px;right:12px;top:12px;bottom:auto;width:auto;
            max-height:calc(100vh - 24px);max-height:calc(100dvh - 24px);overflow-y:auto}
  .amrow,.amsend{min-height:44px;font-size:15px}
  .aminput{font-size:16px;min-height:44px}
  .authslot .btn{min-height:36px}
}

@media (max-width:640px){
  :root{--gut:12px}
  .brand{font-size:13.5px}
  .sitenav{margin-left:0;width:100%;order:5}
  .sitenav a{padding:6px 8px;font-size:10.5px}
  .sitenav .gap{margin:0 4px}
  .pagebody .body{font-size:14px}

  /* THUMB TARGETS ON EVERY PAGE, not just the app.
     A phone pass was done a while ago and every line of it went into app.css,
     which ONLY index.html loads. The other 1,732 pages load this file and got
     none of it. Measured at 390px on about.html: 24 controls under 32px, all of
     them navigation. The board had none, which is exactly why the problem kept
     being reported as fixed and kept not being.

     32px is the floor used throughout the app's own phone pass. Padding rather
     than a fixed height, so a link that wraps still grows instead of clipping. */
  .sitenav a{min-height:32px;display:inline-flex;align-items:center}
  .brand{min-height:32px;display:inline-flex;align-items:center;gap:6px}
  footer a{min-height:32px;display:inline-flex;align-items:center;padding:2px 0}
}

/* account settings panel, hung off the initials */
.authslot .who{cursor:pointer;font-family:inherit}
.authslot .who:hover{border-color:var(--accent)}
.authslot .who:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.authmenu.profile{width:270px}
.pfemail{font-size:12px;color:var(--ink);word-break:break-all;margin:-2px 0 4px}
.pflabel{font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--faint);margin-top:2px}
.signoutrow{margin-top:8px;text-align:center;font-weight:600}
.signoutrow:hover{background:var(--dangerBg);color:var(--danger);border-color:var(--dangerLine)}

/* Deleting the account. Deliberately NOT styled like the primary action: it
   reads as a plain row until it is opened, so it cannot be hit on the way to
   Sign out, which sits directly above it. */
.deleterow{text-align:center;color:var(--danger);border-color:var(--dangerLine)}
.deleterow:hover{background:var(--dangerBg)}
.delbox{margin-top:8px;display:flex;flex-direction:column;gap:8px}
.delbox .amsend.deletego{background:var(--danger);border-color:var(--danger)}
.delbox .amsend.deletego:hover{background:var(--dangerInk);border-color:var(--dangerInk)}
.delbox .amsend.deletego:disabled{opacity:.45}

/* reminder controls in the account panel */
.pfcheck{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--ink);
  padding:2px 0;cursor:pointer}
.pfcheck input{width:16px;height:16px;flex:none;cursor:pointer}
.pffreq{width:100%;height:32px;font-size:12.5px}
.pffreq:disabled{opacity:.5}

/* ==========================================================================
   DARK MODE

   Every colour above is a token and every rule below reads one, so the whole
   theme is this one block: nothing is restyled, the values are simply
   answered differently. That is also why it took a pass through both
   stylesheets first - a rule holding a literal #2b2f38 would have gone on
   painting near-black text on a near-black page, and no amount of theming
   here would have reached it.

   Keyed to the OS setting rather than to a switch in the app. The phone and
   the laptop already know whether it is night, both platforms expect an app
   to follow, and a switch is one more thing to find, set and store.

   Three rules held while choosing the values:

   1. Not inverted. A true inversion makes white text on pure black, which
      smears on an OLED phone. The page is #0f1115 and the text #e8eaee.
   2. The chrome stays neutral. The whole point of the light theme is that
      every colour left on screen means something - a category, a holiday, a
      status - so the dark chrome is grey too, and the meaningful colours are
      LIFTED rather than dimmed: a #2563eb that reads well on white is muddy
      on #0f1115, so it becomes #60a5fa.
   3. Surfaces get lighter as they come forward. --page is darkest, --card
      sits above it, --card2 above that. In light they run the other way.
      This is what replaces the shadows, which do almost nothing on a dark
      ground.
   ========================================================================== */
@media (prefers-color-scheme: dark){
  :root{
    --page:#0f1115;
    --card:#181b21;
    --card2:#242830;
    --ink:#e8eaee;
    --soft:#a2a9b6;
    --faint:#79808d;
    --rule:#2c313b;
    --rule2:#232830;

    /* Inverted deliberately: --accent is the "active" fill and --onAccent the
       text on it, so in the dark they swap ends rather than staying put. */
    --accent:#e8eaee;
    --accent2:#a2a9b6;
    --accentBg:#242830;
    --onAccent:#0f1115;
    --danger:#f87171;

    /* Lifted, not dimmed - see rule 2 above. */
    --todo:#60a5fa;  --todoBg:#141c2b;  --todoE:#22314c;
    --doing:#fbbf24; --doingBg:#241a0e; --doingE:#412f13;
    --done:#34d399;  --doneBg:#0e2019;  --doneE:#17392b;

    --holNat:#f87171;
    --holReg:#60a5fa;

    --bodyInk:#c8cede;
    --dangerBg:#2a1a1c;
    --dangerLine:#4b2c30;
    --dangerInk:#ef4444;
    --wkInk:#5b626e;
    --cellInk:#c4cad6;
    --wkndBg:#14171d;
    --outInk:#484e59;
    --chipLine:rgba(255,255,255,.16);

    /* The toast is the inverse of the page in BOTH themes, which is what makes
       it read as a system message rather than as one more panel. So in the
       dark it is the light one, and everything drawn on it flips with it. */
    --toastBg:#e8eaee;
    --toastInk:#0f1115;
    --toastLine:rgba(0,0,0,.32);
    --toastHover:rgba(0,0,0,.10);
    --toastSoft:rgba(0,0,0,.55);

    --overlay:rgba(0,0,0,.62);
    --shadow:rgba(0,0,0,.55);
    --shadowStrong:rgba(0,0,0,.65);
  }

  /* Tell the browser the page is dark, so form controls, scrollbars and the
     text-selection colour come from the dark set rather than being drawn light
     on top of it. One line, and it fixes every native widget at once. */
  :root{color-scheme:dark}

  /* The logo is a white tile with near-black artwork, drawn inline in the
     markup where no token can reach it. On a dark page a bright white tile is
     the loudest thing on screen, which is not what a wordmark is for. */
  .brand svg{opacity:.9}
}

/* ==========================================================================
   SAFE AREAS - the notch, the rounded corners and the home indicator

   The viewport meta now carries viewport-fit=cover, which is what an installed
   app needs to reach the edges of the screen instead of being letterboxed
   inside white bars. It also means the operating system's own furniture is now
   ON TOP of the page rather than beside it, so anything at an edge has to keep
   out of its way itself.

   env(safe-area-inset-*) is exactly zero on a desktop, in a normal mobile
   browser tab, and on any phone without a notch - the browser's own chrome is
   already doing the job there. It only becomes a real number in an installed,
   full-screen app on a device that needs it, which is precisely where these
   rules should act and nowhere else. So this block cannot move a desktop
   pixel: the arithmetic is "+ 0".
   ========================================================================== */
.bar{padding-top:env(safe-area-inset-top)}
.wrap{padding-left:env(safe-area-inset-left);padding-right:env(safe-area-inset-right)}
footer{padding-bottom:calc(30px + env(safe-area-inset-bottom))}

/* ==========================================================================
   CONTENT PAGES ON A PHONE

   Everything here is inside a max-width query. The desktop reading experience
   is not part of this pass and does not change: a rule inside a max-width
   query is never applied above that width, so there is nothing to override.
   ========================================================================== */

/* The contents list is phone-only. On a desktop these pages are two or three
   screens and the eye does the navigating; on a phone the guide is 7.8
   screens of sixteen sections, privacy 4.2 of nine, terms 3.6 of ten, and
   there was no way to reach a section except to scroll for it. */
.toc{display:none}

@media (max-width:640px){
  /* THE HEADER SCROLLS AWAY, as it already does in the app.
     It is 128px on a phone because seven nav links wrap onto two rows, and
     sticky it took 15% of every screen of a page that exists to be read. The
     app page fixed this in app.css, which only index.html loads, so the two
     halves of the same site behaved differently. */
  .bar{position:static}

  .toc{display:block;margin:18px 0 8px;padding:12px 14px;
    background:var(--card);border:1px solid var(--rule);border-radius:var(--r)}
  .tochead{margin:0 0 8px;font-family:var(--disp);font-size:10px;letter-spacing:.14em;
    text-transform:uppercase;color:var(--soft)}
  .toc ol{margin:0;padding:0;list-style:none;counter-reset:toc}
  .toc li{counter-increment:toc;border-top:1px solid var(--rule2)}
  .toc li:first-child{border-top:0}
  /* 44px rows. These are the only links on the page that are not inside a
     sentence, so they are the only ones a target-size rule applies to. */
  .toc a{display:flex;align-items:center;gap:10px;min-height:44px;
    font-family:var(--sans);font-size:14px;line-height:1.35;
    color:var(--ink);text-decoration:none;padding:4px 0}
  .toc a::before{content:counter(toc);flex:none;min-width:20px;
    font-family:var(--mono);font-size:11px;color:var(--faint)}
  .toc a:hover{color:var(--accent)}

  /* Landing on a section should not put its heading against the very top edge
     of the screen. Harmless where nothing is sticky; correct if it ever is. */
  .pagebody h2{scroll-margin-top:12px}
}
