/* ==========================================================================
   inmycalendar - app.css
   The app itself: date controls, controls strip, board, calendar, rail, modal.
   Loads AFTER site.css, so rules here win where the two overlap.

   Everything is fluid: the layout reflows from a wide desktop down to a narrow
   side-panel window without clipping. No fixed pixel widths on anything that
   has to survive a resize.
   ========================================================================== */

/* A <button> carries a browser default border. Every button-as-cell below must
   zero it explicitly, or a heavy outline appears on the three sides you did not
   set. That is what put black boxes around every date. */
.wg .dc,.wg .wk,.rr{border:0}

/* ---------------- date controls in the ribbon ---------------- */
.nub{background:var(--card);border:1px solid var(--rule);color:var(--ink);border-radius:7px;
  height:29px;min-width:29px;padding:0 8px;font-size:12px;line-height:1;flex:none}
.nub:hover{background:var(--card2)}
.iso{width:var(--wIso);text-align:center;flex:none;font-family:var(--mono);font-size:13px;
  font-weight:700;letter-spacing:.01em;color:var(--ink);background:transparent;border:0;
  border-bottom:1px dashed transparent;padding:2px 0}
.iso:hover{border-bottom-color:var(--faint)}
/* fixed width: without it the meta text ("Mon · Week 33" vs "Aug 2026 ·
   read-only") changes length and pushes the Day/Week/Month buttons sideways
   as you click them. */
.meta{width:var(--wMeta);flex:none;font-size:11px;color:var(--soft);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.dpick{position:absolute;opacity:0;width:29px;height:29px}
.seg{display:inline-flex;border:1px solid var(--rule);border-radius:7px;overflow:hidden;flex:none;
  background:var(--card)}
.seg button{background:transparent;border:0;color:var(--soft);height:27px;padding:0 12px;
  font-family:var(--disp);font-size:11px;letter-spacing:.06em;text-transform:uppercase}
.seg button+button{border-left:1px solid var(--rule)}
.seg button.on{background:var(--accent);color:var(--onAccent)}
.seg button:hover:not(.on){background:var(--card2);color:var(--ink)}

/* ---------------- controls strip: no hidden settings menu ----------------
   Week start and the data actions used to live behind a gear. They are the
   two things people actually need, so they sit in the open on one thin row. */
/* week start moved into the ribbon; data actions moved to the foot of the rail.
   The old full-width controls strip cost ~44px of vertical space on every screen. */

.wkpick{display:inline-flex;align-items:center;gap:6px;flex:none}
.wkpick .lbl{font-family:var(--disp);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--soft)}
.wkpick .sel{height:27px;font-size:11.5px;padding:2px 6px}
.databox{display:flex;flex-wrap:wrap;gap:6px}
.databox .btn{flex:1 1 calc(50% - 3px);padding:4px 6px;font-size:10.5px}
.ctl{display:inline-flex;align-items:center;gap:6px;font-size:11px;color:var(--soft);
  text-transform:uppercase;letter-spacing:.06em;font-family:var(--disp);white-space:nowrap}
.ctlsep{width:1px;height:18px;background:var(--rule);flex:none}
.grow{flex:1 1 auto}

/* ---------------- shell ---------------- */
main{padding:16px 0 0}
.shell{display:grid;grid-template-columns:minmax(0,1fr) var(--rail);gap:16px;align-items:start}
.pane{min-width:0}
.rail{min-width:0;display:flex;flex-direction:column;gap:12px;
  /* the rail must not dictate page height - it stays within the main column's
     span and scrolls internally if a user adds a long list of countdowns */
  position:sticky;top:calc(var(--hBar) + 12px);max-height:calc(100vh - var(--hBar) - 24px);
  overflow-y:auto;overflow-x:hidden;scrollbar-width:thin}
.rail::-webkit-scrollbar{width:6px}
.rail::-webkit-scrollbar-thumb{background:var(--rule);border-radius:3px}
/* flex:none is load-bearing, and its absence was a real bug twice.
   .rail is a flex COLUMN with a max-height. Flex children shrink by default,
   so once the rail's contents were taller than the viewport every panel was
   squeezed - and because .rbox also clips with overflow:hidden, the squeezed
   part vanished with no scrollbar and nothing to indicate it existed. The
   symptom was "Add countdown disappears once I have a few countdowns":
   measured, the panel was 307px tall holding 417px of content, and the 110px
   cut off the bottom was exactly the add form. The same missing flex:none
   squashed tasks in .lane earlier. Panels keep their natural height; the RAIL
   scrolls. */
.rbox{background:var(--card);border:1px solid var(--rule);border-radius:var(--r);
  overflow:hidden;flex:none}
/* Countdowns scroll inside their own panel once there are more than about
   five, so a long list never pushes the add form out of reach again. */
/* padding-right keeps the delete button clear of the scrollbar. Measured: the
   scrollbar is 10px and the x ended one pixel from it, so aiming for a 20px
   target at the very edge of a scrolling list hit the scrollbar instead - the
   countdown could not be deleted once the list was long enough to scroll,
   which is exactly when you want to delete one. */
#tkList{max-height:290px;overflow-y:auto;overflow-x:hidden;scrollbar-width:thin;
  padding-right:10px}
#tkList::-webkit-scrollbar{width:6px}
#tkList::-webkit-scrollbar-thumb{background:var(--rule);border-radius:3px}
.rbox>h3{font-family:var(--disp);font-size:10px;letter-spacing:.14em;text-transform:uppercase;
  color:var(--soft);padding:10px 12px 2px;margin:0;font-weight:500}
.rhint{font-size:10px;color:var(--faint);padding:0 12px 6px;font-style:italic;line-height:1.35}
.rbody{padding:2px 12px 10px}
/* The name field is HALF width now. The space it gave up carries the count
   of days marked with that colour in whatever span is on screen, which is the
   question the colours exist to answer - "how much leave have I taken" -
   without opening the export. */
.cat{display:flex;align-items:center;gap:8px;margin-bottom:6px;cursor:grab;
  border-radius:6px;padding:1px 2px}
.cat:active{cursor:grabbing}
.cat.dragging{opacity:.45}
.cat.catover{box-shadow:inset 0 -2px 0 var(--accent)}
.catn{flex:none;min-width:34px;text-align:right;font-family:var(--mono);
  font-size:10.5px;color:var(--faint);font-variant-numeric:tabular-nums}
/* Sits in the heading, so hiding the colours costs no extra height. */
.hidecats{float:right;background:transparent;border:1px solid var(--rule);
  border-radius:5px;color:var(--faint);font-family:var(--disp);font-size:9px;
  letter-spacing:.08em;text-transform:uppercase;padding:1px 6px;margin-top:-2px}
.hidecats:hover{color:var(--ink);border-color:var(--accent)}
.hidecats.on{background:var(--accent);color:var(--onAccent);border-color:var(--accent)}
.cat i{width:11px;height:11px;border-radius:3px;flex:none}
.cat input[type=text]{flex:1;min-width:0;max-width:108px;background:transparent;border:0;
  border-bottom:1px solid transparent;font-size:12px;padding:2px 0;color:var(--ink)}
.cat input:hover{border-bottom-color:var(--rule)}
.cat input:focus{border-bottom-color:var(--accent);outline:none}
.cat .pen{color:var(--faint);font-size:10px;flex:none;opacity:0;transition:opacity .12s}
.cat:hover .pen{opacity:1}

/* tracked dates
   Two lines, not three: the date and the name on the first, how long to go on
   the second. Three lines meant only two countdowns fitted before the list
   started scrolling. */
.tk{border-top:1px solid var(--rule2);padding:7px 0;font-size:11.5px}
.tk:first-child{border-top:0}
/* The "next up" marker is an INSET shadow, not a border with a negative
   margin. The old rule pulled the row 12px left, out past the panel's padding,
   where .rbox's overflow:hidden clipped it - measured at 3px of the label
   gone, which is why the first letter of that one countdown looked chopped.
   An inset shadow paints the same stripe without moving anything. */
.tk.next{box-shadow:inset 2px 0 0 var(--accent);padding-left:8px}
.tkhead{display:flex;align-items:center;gap:6px;min-width:0}
/* an input styled as text - same pattern as the Day colours rename, which is
   reliable. An overlay editor that swapped nodes on click was not. */
.tkl{flex:1;min-width:0;font-weight:700;color:var(--ink);font-size:11.5px;
  background:transparent;border:0;border-bottom:1px solid transparent;padding:1px 0;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tkl:hover{border-bottom-color:var(--rule)}
.tkl:focus{outline:none;border-bottom-color:var(--accent)}
.tkr{display:flex;align-items:center;gap:4px;margin-top:1px}
.tkc{flex:1;min-width:0;color:var(--soft);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px}
.tkc.fut{color:var(--doing)}
.tk select{background:transparent;border:0;font-size:10px;color:var(--faint);max-width:58px}

/* The date, as a compact button rather than a native date input. The native
   control is ~110px wide with its own calendar icon, which in this rail left
   almost no room for the name. The real input is still there, hidden behind
   it, doing the picking. */
.tkdw{position:relative;display:inline-flex;flex:none}
.tkdate{background:var(--card2);border:1px solid var(--rule);border-radius:6px;
  font-family:var(--mono);font-size:10.5px;color:var(--ink);padding:2px 5px;
  line-height:1.35;white-space:nowrap}
.tkdate:hover{border-color:var(--accent)}
.tknat{position:absolute;left:0;top:0;width:100%;height:100%;opacity:0;pointer-events:none}
.tk .x{flex:none;background:transparent;border:0;color:var(--faint);
  padding:0;width:20px;height:20px;font-size:14px;line-height:1;border-radius:5px}
.tk .x:hover{color:var(--danger);background:var(--card2)}
.tform{display:flex;flex-direction:column;gap:5px;border-top:1px solid var(--rule2);
  margin-top:6px;padding-top:8px}
.tform input[type=text],.tform select{width:100%;background:var(--card);border:1px solid var(--rule);
  padding:5px 8px;font-size:11.5px;height:30px;border-radius:7px}
.fld{display:flex;flex-direction:column;gap:3px;margin-bottom:7px}
.fld>span{font-family:var(--disp);font-size:9.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--soft)}
.fld>span em{font-style:normal;text-transform:none;letter-spacing:0;font-size:9px;color:var(--faint)}
.fld>span em::before{content:"\00b7";margin:0 4px}
.fld .sel{width:100%;height:29px}
.chk{display:flex;align-items:center;gap:6px;font-size:10.5px;color:var(--soft);margin:2px 0 6px;white-space:nowrap}
.hkey{display:flex;gap:12px;font-size:10px;color:var(--soft);padding-top:2px}
.hkey span{display:inline-flex;align-items:center;gap:5px}
.hkey .sw{width:12px;height:3px;border-radius:2px;display:inline-block}
.hkey .sw.nat{background:var(--holNat)}
.hkey .sw.reg{background:var(--holReg)}

.dfield{position:relative;display:flex;align-items:center}
.dfield input[type=text]{flex:1;min-width:0;padding-right:30px}
.dbtn{position:absolute;right:5px;background:transparent;border:0;padding:2px;font-size:12px;
  line-height:1;color:var(--soft)}
.dbtn:hover{color:var(--ink)}
.dfield .dpick{right:5px;width:24px;height:24px}
.tform label{font-size:11px;color:var(--soft);display:flex;align-items:center;gap:6px}
.go{background:var(--accent);border:1px solid var(--accent);color:var(--onAccent);height:30px;
  padding:0 12px;border-radius:7px;font-size:11.5px}
.go:hover{background:var(--accent2);border-color:var(--accent2)}
.err{color:var(--danger);font-size:11px}
.none{font-size:11px;color:var(--faint);font-style:italic;padding:3px 0}

/* ---------------- kanban ----------------
   Status colour is carried by a top rule and a dot, not a heavy filled bar -
   keeps the colour budget for categories and holidays. */
.kb{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}
.col{min-width:0;display:flex;flex-direction:column;background:var(--card);
  border:1px solid var(--rule);border-radius:var(--r);overflow:hidden}
.col{border-top:2px solid var(--rule)}
.col[data-s=todo]{border-top-color:var(--todo)}
.col[data-s=doing]{border-top-color:var(--doing)}
.col[data-s=done]{border-top-color:var(--done)}
.ch{display:flex;align-items:center;gap:7px;height:36px;padding:0 12px;
  font-family:var(--disp);font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--ink)}
.ch::before{content:"";width:7px;height:7px;border-radius:50%;flex:none;background:var(--rule)}
.c-todo::before{background:var(--todo)} .c-doing::before{background:var(--doing)} .c-done::before{background:var(--done)}
.ch .n{margin-left:auto;font-family:var(--mono);font-size:11px;color:var(--faint)}
/* A phone keyboard has no reliable Enter, so the add field carries a visible
   button. The row is the flex parent; the input flexes, the button does not. */
/* align-items:flex-start, not center: the field grows downwards as you type
   and the + button must stay level with its FIRST line rather than drifting to
   the middle of a four-line box. */
.addrow{display:flex;gap:6px;align-items:flex-start;margin:0 10px 8px;min-width:0}
/* A textarea, not an input - see the comment in app.js. resize:none because
   the field sizes itself; a drag handle in the corner of every column is
   clutter that also fights the automatic height. min-height matches the old
   input exactly so a single-line field looks unchanged. */
.cadd{flex:1;min-width:0;background:var(--card);border:1px solid var(--rule);
  border-radius:7px;padding:5px 9px;font-size:12px;font-family:inherit;
  min-height:30px;height:30px;max-height:150px;line-height:1.5;
  resize:none;overflow-y:auto;display:block}
.addgo{flex:none;width:30px;height:30px;border:1px solid var(--rule);border-radius:7px;
  background:var(--card);color:var(--soft);font-size:16px;line-height:1;padding:0;cursor:pointer}
.addgo:hover{background:var(--accent);color:var(--onAccent);border-color:var(--accent)}
.addgo:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.cadd::placeholder{color:var(--faint)}
.cadd:focus{outline:none;border-color:var(--accent)}
/* Cap the lane so a long list scrolls inside the column instead of pushing
   "Year at a glance" off the screen. The board's footprint stays predictable. */
.lane{display:flex;flex-direction:column;gap:4px;min-width:0;min-height:24px;padding:0 10px 10px;
  max-height:var(--laneMax);overflow-y:auto;overflow-x:hidden;scrollbar-width:thin}
.lane::-webkit-scrollbar,.rlist::-webkit-scrollbar{width:6px}
.lane::-webkit-scrollbar-thumb,.rlist::-webkit-scrollbar-thumb{background:var(--rule);border-radius:3px}
/* the week/month lists scroll at the SAME physical height as a kanban lane, so
   "Year at a glance" sits in the same place whichever scope you are in */
.rlist{max-height:var(--laneMax);overflow-y:auto;overflow-x:hidden;scrollbar-width:thin}
.lane.over{outline:2px dashed var(--accent);outline-offset:-4px}
/* A block, not a flex row. Flex would size the text to the space left over by
   the controls and narrow EVERY line by their width, which is what left line
   two stopping short of the right edge for no reason. As a block with the
   controls floated right, only the first line is shortened; lines two and
   three run the full width of the card underneath them. */
/* flex:none is load-bearing, not tidiness. .lane is a flex column with a
   max-height, and a flex item defaults to flex-shrink:1, so once the column
   was full the browser compressed EVERY task to make them fit - a three-line
   task got squashed to one line with its text clipped mid-word. The lane is
   supposed to scroll instead. Without this line, adding a task silently
   shrinks all the others. */
.t{display:block;flex:none;min-width:0;min-height:28px;padding:4px 8px;
  border:1px solid var(--rule);border-radius:8px;background:var(--card)}
.t::after{content:"";display:block;clear:both}
.t.s-todo{background:var(--todoBg);border-color:var(--todoE)}
.t.s-doing{background:var(--doingBg);border-color:var(--doingE)}
.t.s-done{background:var(--doneBg);border-color:var(--doneE)}
.t .grip{float:left;margin:1px 6px 0 0;color:var(--faint);font-size:11px;cursor:grab;letter-spacing:-1px}
/* Three lines, then clip. One line hid all but the first few words; unlimited
   lines would let a single task eat the lane. Three fits about 150 characters,
   which covers a real sentence-length task.

   Two traps here, both of which silently produced the OLD broken layout where
   every line was narrowed instead of just the first, and both of which were
   caught by measuring the live page rather than by reading the CSS:

   1. NOT -webkit-line-clamp. It needs display:-webkit-box, and a -webkit-box
      does not flow around a float at all.
   2. overflow:CLIP, never overflow:hidden. overflow:hidden establishes a block
      formatting context, and a block formatting context refuses to overlap a
      float - it shrinks its whole box to sit beside it. That narrowed every
      line to 113px of a 307px card. overflow:clip does not create one, so the
      line boxes flow around the float exactly as intended.

   line-height 1.6 gives a 20px line box, which must stay >= the floated
   controls' height or the float spills into line two and narrows that as
   well. See .op below: the two numbers are a pair. */
/* pre-wrap, so a task written with Shift+Enter keeps its line breaks. It still
   wraps normally and is still clipped at three lines - a card is a summary,
   not the document. */
/* The character clamp that used to live here is gone. It was wrong at 90, wrong
   at 55, and still wrong split per breakpoint - 27 controls for 2 genuinely
   clipped tasks. Overflow is measured now, once per render, by markOverflowing()
   in app.js. See the note there. */

.t .txt{display:block;font-size:12.5px;line-height:1.6;cursor:text;white-space:pre-wrap;
  max-height:calc(1.6em * 2);overflow:clip;overflow-wrap:anywhere}
/* THE FIRST LINE IS THE TASK, THE REST IS DETAIL.
   The card used to render the whole task and clip it at three lines, so a long
   one was cut off mid-sentence with nothing to say it had been cut. Now the
   first line is shown and the rest waits behind a control. Two lines rather
   than three, because a single long sentence with no newline still needs to be
   readable at a glance and three lines of a wall is not more useful than two. */
.t.open .txt{max-height:none}
.t .detail{display:none}
.t.open .detail{display:block;font-size:12px;line-height:1.55;color:var(--soft);
  white-space:pre-wrap;overflow-wrap:anywhere;margin-top:6px;padding-top:6px;
  border-top:1px solid var(--rule2)}
/* Hidden unless the text really is cut. markOverflowing() in app.js measures
   that once per render and adds .overflowing; a character count was tried and
   was wrong in both directions. */
.t:not(.overflowing) .more{display:none}
.t .more{display:block;background:transparent;border:0;padding:3px 0 0;margin:0;
  font-family:var(--disp);font-size:10px;letter-spacing:.07em;text-transform:uppercase;
  color:var(--soft);cursor:pointer}
.t .more:hover{color:var(--ink)}
/* While renaming, the textarea holds the WHOLE task, so a detail block below it
   would be the same words twice. */
.t.editing .detail,.t.editing .more{display:none}
.t.s-done .txt{text-decoration:line-through;color:var(--soft)}
/* Controls in the open, not behind a ⋯. Tried the menu, and one tap to reach
   Rename on a control used constantly was the wrong trade. The cost is that
   the first line of a task is ~113px instead of ~210px, which the three-line
   layout below absorbs.
   Floated right and BEFORE the text in the source, so only the first line
   shortens around them. visibility rather than display, so the text does not
   reflow when they appear on hover. */
.t .ops{float:right;display:flex;gap:2px;margin:0 0 0 8px;visibility:hidden}
.t:hover .ops,.t:focus-within .ops{visibility:visible}
/* A touch screen cannot hover, so hiding the controls behind :hover made them
   unreachable on a phone. Anything that reports no hover gets them always on. */
@media (hover:none){.t .ops{visibility:visible}}
.t.dragging{opacity:.4}
/* height pairs with .t .txt line-height above: the floated controls must fit
   inside ONE line box (17px + 2px border = 19px, against a 20px line box) or
   they spill into line two and narrow it, which is the bug this layout exists
   to fix. Change one of these two numbers and you must change the other. */
.op{background:var(--card);border:1px solid var(--rule);width:20px;height:17px;border-radius:5px;
  font-size:10.5px;line-height:1;padding:0;color:var(--soft)}
/* THE MENU BUTTON IS A PHONE CONTROL ONLY.
   On a wide card all seven controls are in the open, which is the right trade
   for a mouse, so a button whose whole job is to reveal them has nothing to do
   and would just be an eighth thing to look at. The phone block turns it on. */
.op.menu{display:none}
.op:hover:not(:disabled){background:var(--card2);color:var(--ink)}
.op:disabled{opacity:.3;cursor:not-allowed}
.op.x:hover{background:var(--dangerBg);color:var(--danger);border-color:var(--dangerLine)}
/* While editing, the floated controls are hidden so the field gets the whole
   card. Sharing the row with them left the field about a third width, which is
   unusable for a task that renders over three lines. */
.t.editing .ops,.t.editing .grip{display:none}
.t .edit{display:block;width:100%;box-sizing:border-box;font-family:inherit;
  font-size:12.5px;line-height:1.6;resize:vertical;min-height:calc(1.6em * 3);
  border:1px solid var(--accent);border-radius:6px;padding:3px 6px;background:var(--card)}
.t .edit:focus{outline:none;box-shadow:0 0 0 2px var(--accentBg)}

/* read-only week / month */
.ro{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}
.rc{min-width:0;border:1px solid var(--rule);background:var(--card);border-radius:var(--r);overflow:hidden;
  border-top:2px solid var(--rule)}
.rc[data-s=todo]{border-top-color:var(--todo)}
.rc[data-s=doing]{border-top-color:var(--doing)}
.rc[data-s=done]{border-top-color:var(--done)}
.rc>.rh{height:36px;display:flex;align-items:center;gap:7px;padding:0 12px;
  font-family:var(--disp);font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--ink)}
.rc>.rh::before{content:"";width:7px;height:7px;border-radius:50%;flex:none;background:var(--rule)}
.rc>.rh.c-todo::before{background:var(--todo)}
.rc>.rh.c-doing::before{background:var(--doing)}
.rc>.rh.c-done::before{background:var(--done)}
.rc>.rh .n{margin-left:auto;font-family:var(--mono);font-size:11px;color:var(--faint)}
.rr{display:flex;gap:8px;align-items:center;padding:5px 12px;border:0;border-top:1px solid var(--rule2);
  min-width:0;width:100%;background:transparent;text-align:left;font-size:12px;color:var(--ink)}
.rr:hover{background:var(--card2)}
.rr .d{color:var(--faint);font-size:10.5px;white-space:nowrap;flex:none;font-family:var(--mono)}
.rr .t2{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rr.now .d{color:var(--accent);font-weight:700}
.rc .none{padding:8px 12px}

/* ---------------- calendar / glance ---------------- */
/* Day note under the board. One line tall until it is used or focused, so it
   costs almost no vertical space - the calendar below must not move down. */
.bnotewrap .bnbtns{display:none;gap:6px;flex:none;align-self:flex-end}
.bnotewrap.editing .bnbtns{display:flex}
.bnotewrap .bnbtns .btn{padding:4px 12px;font-size:11px}
.bnx{display:none;background:transparent;border:0;color:var(--faint);font-size:17px;line-height:1;
  padding:0 2px;flex:none;align-self:flex-start}
.bnx:hover{color:var(--ink)}
.bnotewrap.editing .bnx{display:block}
.bnotewrap{display:flex;align-items:flex-start;gap:10px;margin-top:12px;padding:7px 12px;
  background:var(--card);border:1px solid var(--rule);border-radius:var(--r)}
.bnotewrap label{font-family:var(--disp);font-size:9.5px;letter-spacing:.11em;text-transform:uppercase;
  color:var(--soft);padding-top:5px;flex:none}
.bnotewrap textarea{flex:1;min-width:0;border:0;background:transparent;resize:none;font-size:12.5px;
  color:var(--ink);line-height:1.45;padding:3px 0;height:24px;overflow:hidden}
.bnotewrap textarea:focus{outline:none;height:64px;overflow:auto}
.bnotewrap.filled textarea{height:44px;overflow:auto}
.bnotewrap.filled textarea:focus{height:76px}
.bnotewrap textarea::placeholder{color:var(--faint)}
.bnoteacts{display:none;gap:6px;flex:none;align-self:flex-end}
.bnotewrap.filled .bnoteacts,.bnotewrap:focus-within .bnoteacts{display:flex}
.bnoteacts .btn{padding:4px 12px;font-size:11px}
.bnoteacts .done{background:var(--accent);border-color:var(--accent);color:var(--onAccent)}
.bnoteacts .done:hover{background:var(--accent2);border-color:var(--accent2)}

.gridbox{border:1px solid var(--rule);background:var(--card);border-radius:var(--r);
  overflow:hidden;margin-top:16px}
.gridbox:first-child{margin-top:0}
.gtitle{display:flex;align-items:center;gap:8px;min-height:38px;padding:6px 12px;
  border-bottom:1px solid var(--rule);flex-wrap:wrap}
.gtitle h2{font-family:var(--disp);font-size:12px;letter-spacing:.12em;text-transform:uppercase;
  color:var(--ink);margin:0;font-weight:500}
.gtitle .yrs{width:var(--wYrs);text-align:center;flex:none;font-size:11.5px;font-weight:700;
  color:var(--ink);font-family:var(--mono)}
.gtitle .sp{flex:1 1 auto}
.fold,.yarr{background:var(--card);border:1px solid var(--rule);color:var(--soft);border-radius:6px;
  height:24px;min-width:24px;font-size:10px;line-height:1;padding:0 5px;flex:none}
.fold:hover,.yarr:hover:not(:disabled){background:var(--card2);color:var(--ink)}
.yarr:disabled{opacity:.3;cursor:not-allowed}
.gridbox.folded .glance{display:none}
/* Height follows the actual viewport, so a large monitor shows more weeks
   instead of stopping at an arbitrary fraction. 190px covers the ribbon,
   the grid header and the footer padding. */
.calbox{overflow:auto;max-height:calc(100vh - 190px);min-height:320px}
/* years wrap onto new rows instead of forcing a horizontal scrollbar */
.calrail{display:flex;align-items:flex-start;flex-wrap:wrap;gap:14px;padding:12px}
.glance{display:flex;align-items:flex-start;flex-wrap:wrap;gap:14px;padding:12px}
.calrail>.wg,.glance>.wg{flex:1 1 300px;min-width:260px}

/* NO overflow:hidden here. It was present to clip the rounded corners, and it
   silently killed the sticky year and day-of-week headers inside: an ancestor
   with overflow:hidden becomes the containing block for a sticky child, and
   since .wg does not itself scroll, the headers had no range to stick over and
   scrolled away with the grid. This is the exact trap the README records, hit
   a second time. The corners are rounded on the header and footer rows
   instead, which needs no clipping. */
.wg{display:grid;grid-template-columns:26px repeat(7,minmax(0,1fr));
  border:1px solid var(--rule2);border-radius:8px;background:var(--card)}
.wg .yh{border-radius:7px 7px 0 0}
.wg>*:nth-last-child(8){border-bottom-left-radius:7px}
.wg>*:last-child{border-bottom-right-radius:7px}
.wg .yh{grid-column:1/-1;min-height:var(--hYear);position:sticky;top:0;z-index:7;background:var(--card);
  color:var(--ink);font-family:var(--disp);font-weight:600;font-size:12.5px;letter-spacing:.02em;
  display:flex;align-items:baseline;gap:7px;padding:7px 11px 5px;border-bottom:1px solid var(--rule2)}
.wg .yh .mo{color:var(--faint);font-family:var(--mono);font-weight:400;font-size:10px;letter-spacing:0}
.wg .yh .sub{color:var(--faint);font-family:var(--mono);font-weight:400;font-size:9.5px;
  margin-left:auto;padding-left:8px}
.wg.c .yh{position:static}
/* sits directly under the year row, which is why the offset is --hYear */
.wg .dh{min-height:var(--hDow);position:sticky;top:var(--hYear);z-index:6;background:var(--card);
  color:var(--faint);font-family:var(--mono);font-size:9px;font-weight:700;letter-spacing:.02em;
  display:flex;align-items:center;justify-content:center;border-bottom:1px solid var(--rule2)}
.wg .dh.wknd{color:var(--soft)}
.wg.c .dh{position:static}
.wg .wk{font-size:9px;color:var(--wkInk);background:transparent;border:0;border-top:1px solid var(--rule2);
  display:flex;align-items:center;justify-content:center;padding:0;font-family:var(--mono)}
.wg .wk:hover{color:var(--accent);background:var(--card2)}

.wg .dc{min-height:23px;border:0;border-top:1px solid var(--rule2);display:flex;align-items:center;
  justify-content:center;font-size:10px;background:transparent;padding:0;position:relative;
  font-family:var(--mono);color:var(--cellInk);border-radius:0}
.wg.c .dc{min-height:21px;font-size:9.5px}
.wg .dc.wknd{background:var(--wkndBg)}
.wg .dc:hover{background:var(--accentBg);color:var(--ink)}
.wg .dc.out{color:var(--outInk)}
/* four independent visual channels so nothing ever collides:
   fill = category · stripe = holiday · ring = today · dot = has tasks */
/* Driven from JS so the four day colours can actually be changed. The values
   below are the defaults and are also the fallback if the variables are ever
   missing - a day must never render as unstyled white when it has been marked.
   applyCatColours() in app.js sets --k0b/--k0f and friends from cfg.catColors,
   deriving the pale fill and the readable text colour from the one hex the
   person picked, so they choose a colour rather than a colour scheme. */
/* A selected day. An inset ring rather than a background, so it reads clearly
   ON TOP of a category fill, a holiday stripe and the today ring instead of
   replacing any of them - you need to see what a day already is while you are
   deciding to recolour it. */
.wg .dc.selected{box-shadow:inset 0 0 0 2px var(--accent);border-radius:4px;
  font-weight:700}
/* Dragging across days should select them, not smear a text selection across
   half the calendar. */
.wg.picking,.wg.picking .dc{user-select:none;-webkit-user-select:none}

.wg .dc.k0{background:var(--k0b,#fde8e6);color:var(--k0f,#9b2c20)}
.wg .dc.k1{background:var(--k1b,#fdf0d9);color:var(--k1f,#8a5a10)}
.wg .dc.k2{background:var(--k2b,#e2f4e9);color:var(--k2f,#1f6640)}
.wg .dc.k3{background:var(--k3b,#e4edfd);color:var(--k3f,#1e4694)}
/* Categories are addable now, capped at eight. Beyond that the panel stops
   being scannable and the colours stop being distinguishable at a glance,
   which is the entire job of a colour. Four through seven have no literal
   fallback because they only exist once someone adds them, and
   applyCatColours() always sets the variable before a cell can use the class. */
.wg .dc.k4{background:var(--k4b);color:var(--k4f)}
.wg .dc.k5{background:var(--k5b);color:var(--k5f)}
.wg .dc.k6{background:var(--k6b);color:var(--k6f)}
.wg .dc.k7{background:var(--k7b);color:var(--k7f)}
.wg .dc.hol-nat::after,.wg .dc.hol-reg::after{content:"";position:absolute;left:4px;right:4px;
  bottom:2px;height:2px;border-radius:2px}
.wg .dc.hol-nat::after{background:var(--holNat)}
.wg .dc.hol-reg::after{background:var(--holReg)}
/* TODAY: a ring, never a fill - stays visible on white, on a category fill
   and on a holiday stripe alike. The old white-on-white pill was invisible. */
.wg .dc.now{font-weight:700;color:var(--accent);
  box-shadow:inset 0 0 0 2px var(--accent);border-radius:6px}
/* SELECTED DAY: the ring again, in grey. Deliberately not a new colour - the
   calendar already carries four independent channels (category fill, holiday
   stripe, today ring, task dot) and a fifth hue would tip it into noise. */
.wg .dc.picked{font-weight:700;box-shadow:inset 0 0 0 2px var(--soft);border-radius:6px}
.wg .dc .task{position:absolute;right:3px;top:3px;width:3px;height:3px;border-radius:50%;background:var(--soft)}
.wg .dc .pen{position:absolute;left:2px;top:1px;font-size:7px;color:var(--faint)}

/* ---------------- ad slots (reserved, off by default) ---------------- */
.ad{border:1px dashed var(--rule);background:var(--card);color:var(--faint);border-radius:var(--r);
  display:flex;align-items:center;justify-content:center;text-align:center;
  font-size:10.5px;letter-spacing:.06em;text-transform:uppercase}
.ad-rail{min-height:600px}
.ad-foot{min-height:90px;margin-top:16px}
.ad-anchor{display:none;position:sticky;bottom:0;z-index:50;min-height:50px}

/* ---------------- modal ---------------- */
.ov{position:fixed;inset:0;background:var(--overlay);z-index:100;display:flex;
  align-items:flex-start;justify-content:center;padding:32px 14px;overflow:auto}
.md{width:min(900px,100%);background:var(--page);border:1px solid var(--rule);border-radius:12px;overflow:hidden}
.mh{display:flex;align-items:center;gap:10px;background:var(--card);color:var(--ink);padding:12px 14px;
  border-bottom:1px solid var(--rule);flex-wrap:wrap}
.mh .d{font-family:var(--disp);font-size:16px;letter-spacing:.06em}
.mh .w{font-size:11.5px;color:var(--soft)}
.mh button{margin-left:auto;background:var(--card);border:1px solid var(--rule);color:var(--soft);
  width:27px;height:25px;border-radius:6px;line-height:1}
.mh button:hover{background:var(--card2);color:var(--ink)}
.mh button .mdone{display:none}
.mb{padding:14px}
.mrow{display:flex;flex-wrap:wrap;gap:8px;align-items:center}
.mnlabel{display:block;font-family:var(--disp);font-size:10px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--soft);margin:12px 0 5px}
.mnlabel em{font-style:normal;text-transform:none;letter-spacing:0;font-size:10.5px;color:var(--faint)}
.mnlabel em::before{content:"\00b7";margin:0 5px}
/* search overlay */
.md.search{width:min(680px,100%)}
.sinput{width:100%;border:1px solid var(--rule);border-radius:8px;padding:10px 12px;font-size:14px;
  background:var(--card)}
.sinput:focus{outline:none;border-color:var(--accent)}
.sout{margin-top:10px;max-height:52vh;overflow-y:auto}
.scount{font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--faint);padding:6px 2px}
.srow{display:flex;align-items:baseline;gap:10px;width:100%;text-align:left;background:transparent;
  border:0;border-top:1px solid var(--rule2);padding:8px 4px;font-size:12.5px;color:var(--ink)}
.srow:hover{background:var(--card2)}
.sdate{font-family:var(--mono);font-size:11px;color:var(--soft);flex:none}
.skind{font-family:var(--disp);font-size:9.5px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--faint);flex:none;min-width:64px}
.stext{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.op.on{background:var(--accentBg);color:var(--ink);border-color:var(--accent)}

.mnote{width:100%;min-height:60px;background:var(--card);border:1px solid var(--rule);border-radius:8px;
  padding:8px;font-size:12.5px;resize:vertical;margin:10px 0 12px}
.dab{width:24px;height:24px;border-radius:6px;border:1px solid var(--chipLine);padding:0}
.mfoot{display:flex;justify-content:flex-end;gap:8px;margin-top:14px;padding-top:12px;
  border-top:1px solid var(--rule)}
.mfoot .btn{padding:7px 22px;font-size:12.5px}
.mfoot .primary{background:var(--accent);border-color:var(--accent);color:var(--onAccent)}
.mfoot .primary:hover{background:var(--accent2);border-color:var(--accent2)}
.dab.on{box-shadow:0 0 0 2px var(--accent)}

/* ==========================================================================
   RESPONSIVE - the app reflows all the way down to a narrow side panel.
   ========================================================================== */
@media (max-width:1100px){ :root{--rail:210px} }
@media (max-width:900px){
  /* Single column, and the order is deliberate. Left alone, the rail lands
     AFTER the whole year grid, so a phone user scrolls past a calendar to
     reach the colours and the country picker. display:contents flattens the
     wrappers so every block can be ordered individually.
     .hidden still wins, so switching views keeps working. */
  /* align-items:start is inherited from the base grid rule; in a column flex
     container it stops children stretching, which squeezed the board to ~70%
     of the screen. Must be reset explicitly. */
  .shell{display:flex;flex-direction:column;gap:14px;align-items:stretch}
  .pane,#boardView{display:contents}
  #carryHost{order:1}
  #scopeHost{order:2}          /* the board itself, first */
  #bnoteWrap{order:3}
  #calView{order:3}
  .rail{order:4;flex-direction:row;flex-wrap:wrap;position:static;max-height:none;overflow:visible}
  #glanceBox{order:5}          /* the big year grid goes last */
  .ad-foot{order:6}
  .rbox{flex:1 1 260px}
  .ad-rail{display:none}
}
@media (max-width:760px){
  .kb,.ro{grid-template-columns:1fr}
  .calrail>.wg,.glance>.wg{flex:1 1 100%}
  /* Three stacked years is unusable on a phone, and reordering them produced a
     nonsense sequence (2026, 2025, 2027). Show ONE year; the arrows still pan. */
  .calrail>.wg:not(.focusyear){display:none}
}
@media (max-width:640px){
  :root{--wIso:88px}
  main{padding-top:10px}
  .appzone{width:100%;order:4}
  .meta{display:none}
  #dPick{display:none}
  .ctrls .wrap{gap:6px}
  .ad-foot{display:none}
  .ad-anchor{display:flex}
  .t .grip{display:none}
  .t .ops{visibility:visible}
}
/* ===========================================================================
   PHONE PASS

   Three separate faults, all of which made the app feel broken on a phone
   rather than merely cramped:

   1. iOS Safari force-zooms the page whenever you focus an input whose text is
      under 16px. EVERY field here was smaller, so tapping anything lurched the
      layout and left the user pinched in. This is the single most damaging one
      because it happens on the very first tap.
   2. Tap targets. Calendar days measured 26x21. Apple and Google both put the
      minimum at 44x44. Missing the day you aimed at was the normal case.
   3. Seven controls at 20px wide competed with the task text for a 343px row.
      They now sit on their own line under the text, at a size a thumb can hit.
   =========================================================================== */
@media (max-width:640px){
  /* 16px is not a design choice, it is the exact threshold below which iOS
     zooms. Anything smaller here and the page lurches on first tap. */
  /* The countdown fields are styled by id, which outranks a bare element
     selector, so they have to be named here or they stay at 11.5px and keep
     zooming the page. Checked on the live site: these four were the survivors. */
  .cadd,.sel,.dpick,.tinput,.sinput,#bnote,#mNote,#tLabel,#tDate,#tUnit,
  input[type=text],input[type=email],input[type=date],input[type=number],select,textarea{
    font-size:16px
  }
  #tLabel,#tDate,#tUnit{min-height:38px}
  /* a checkbox does not trigger the zoom, but 10.5px is still too small to hit */
  /* 16px on the checkbox too. It is the last input that still triggered the
     iOS zoom - width and height were set on a previous pass but not the font
     size, and it is the font size Safari looks at. Measured, not assumed. */
  #holReg{width:20px;height:20px;font-size:16px}

  /* ---- READABILITY -----------------------------------------------------
     Measured on a real 375px viewport: 421 pieces of text were rendering
     below 11px, some at 9px. That is the single most common complaint about
     this app on a phone and it is not a layout bug, it is simply too small
     to read. Desktop keeps the compact sizes; the phone gets legible ones. */
  .sitenav a{font-size:12px}
  .fld>span{font-size:11px}
  .fld>span em{font-size:10.5px}
  .chk{font-size:12.5px}
  .rbox>h3{font-size:11.5px}
  .rhint{font-size:11.5px}
  .hkey{font-size:11px}
  .wg .dh{font-size:10.5px}
  .wg .yh .mo{font-size:11px}
  .wg .yh .sub{font-size:10.5px}
  .tkl,.tkc{font-size:12.5px}
  .cat input{font-size:13px}
  .databox .btn{font-size:12px;padding:7px 8px}
  footer{font-size:12.5px}

  /* ---- VERTICAL SPACE --------------------------------------------------
     The board began 215px down the page: a quarter of the screen used before
     any content. The ribbon has to wrap on a phone, but it does not have to
     be padded like a desktop while it does. */
  .bar .wrap{gap:7px;padding-top:5px;padding-bottom:5px}
  main{padding-top:8px}
  .shell{gap:10px}
  .gridbox{margin-top:10px !important}
  .cadd{min-height:38px;height:38px}
  .addgo{width:38px;height:38px;font-size:19px}
  .sel{min-height:38px;padding-right:26px}

  /* thumb-sized calendar days */
  .wg .dc{min-height:34px;font-size:11px}
  .wg.c .dc{min-height:30px;font-size:10.5px}
  /* 26px wide was the last control under 32 on a phone. Eight columns share
     the width, so taking it to 32 costs each of the seven day cells under a
     pixel - measured at 344px, the narrowest screen tested, with no overflow. */
  .wg .wk{min-height:34px;min-width:32px;font-size:10px}
  .wg .dh{min-height:22px}

  /* The row controls get their own line. Trying to fit seven buttons and the
     task text on one 343px line gives neither of them enough room. */
  /* On a phone the float is the wrong shape: seven 34px controls would leave
     about 60px for the first line. So here the row becomes a column, text
     first, controls on their own row underneath. Flex order does that without
     changing the source order the desktop float depends on. */
  /* A PHONE CARD IS A LINE OF TEXT, NOT A CONTROL PANEL.

     It used to be a flex column: text, then a full-width row of seven buttons
     underneath. Measured with 30 realistic tasks at 390px, that made every card
     137px tall for two lines of text and put the whole board at 5.4 screens.
     Two tasks visible at a time.

     So the card goes back to the desktop arrangement - a block with the
     controls floated right, text flowing around them - and only two controls
     are in the open. The card is then as tall as its text and nothing more.

     Renaming still costs one tap: it has always been on the text itself. */
  .t{padding:8px 10px}
  /* THREE LINES, NOT TWO.
     At two lines, 27 of 30 realistic tasks were clipped and each grew a "more"
     row - a full 32px on almost every card, which gave back most of the height
     the redesign had just saved. A phone card is about 27 characters a line, so
     a third line covers the ordinary case and leaves "more" for the genuinely
     long ones, which is what it is for. */
  .t .txt{float:none;font-size:14px;line-height:1.45;max-height:calc(1.45em * 3)}
  .t.open .txt{max-height:none}
  .t .detail{font-size:13.5px}
  .t .more{min-height:32px;font-size:11px}
  /* WRAP, DO NOT SHRINK.
     Eight controls at 34px with seven 8px gaps needs 328px. On the narrowest
     phones the card is about 294px wide, and flex's default shrink quietly
     took every button down to 27.8px - under the size this whole block exists
     to guarantee. Measured at 344px wide, which is a folded Galaxy Flip.
     A second row of buttons costs a few pixels of height; shrinking them costs
     the thumb, so the row wraps and flex:none holds the size. */
  /* Collapsed: a compact pair, floated right, sharing the first line with the
     text exactly as on desktop. */
  .t .ops{float:right;width:auto;gap:6px;margin:0 0 2px 8px;flex-wrap:nowrap}
  .t .ops .op{display:none}
  .t .ops .op.adv,.t .ops .op.menu{display:inline-flex;align-items:center;justify-content:center}
  .t .ops .op.menu{font-size:16px;line-height:1}

  /* The .acts rules that used to live here are gone with the class. They
     unfolded the eight controls into a full-width row inside the card, which
     was an improvement on seven of them being always-on and still left a grid
     of unlabelled glyphs - meaning conveyed by a tooltip, on a device that has
     none. The menu opens a labelled sheet now; see the action sheet block at
     the foot of this file. */
  .op{width:34px;height:32px;font-size:13px;flex:none}
  .t .edit{order:1;width:100%;font-size:16px;padding:6px 8px}
  .t::after{display:none}

  /* THE APP HEADER IS TOO TALL ON A PHONE.
     Measured at 390x844: the header was 217px and the first task started at
     319px, 38% of the screen gone before anything you came for. The navigation
     alone was 73px because seven links wrap onto two rows, leaving PRIVACY
     alone on a line of its own.

     The five content links come out on phones. Board and Calendar are what the
     app itself needs; About, Guide, Holidays, Contact and Privacy are all still
     in the footer of every page, one scroll away. Simulated before committing:
     header 217px to 179px, first task 319px to 281px, nav back to one row.

     Inside a max-width query, so it does not exist on desktop at all - the
     desktop header is not overridden, this rule is simply never applied. */
  .sitenav a.page,.sitenav .gap{display:none}

  /* everything else a thumb has to hit */
  .nub,.iso,.seg button,.sitenav a,.authslot .btn{min-height:36px}
  .seg button{padding-left:12px;padding-right:12px}
  .swatch,.sw{min-width:32px;min-height:32px}
  /* .go was 30px, under the 32 this block sets everywhere else */
  .go{min-height:32px}
  .amrow,.amsend{min-height:42px;font-size:15px}
  .aminput{font-size:16px;min-height:42px}
  .op.x{margin-left:auto}

  /* the day note was 12.5px, which is small to read and small to type into */
  #bnote,#mNote{font-size:16px;line-height:1.45}
  .none{font-size:13px}
}
@media (prefers-reduced-motion:reduce){*{transition:none !important}}

/* sync status, left of the account controls. Deliberately quiet: it is
   reassurance, not a control, and it must never push the ribbon around, so it
   has a fixed width and truncates rather than growing. */
.syncst{font-size:10px;font-family:var(--disp);letter-spacing:.06em;text-transform:uppercase;
  color:var(--faint);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:120px;
  flex:none;align-self:center}
.syncst.ok{color:var(--done)}
.syncst.warn{color:var(--doing)}
@media (max-width:640px){.syncst{max-width:70px;font-size:9px}}

/* ---------------- selecting several days ----------------
   The bar that appears once days are selected on the calendar. Fixed to the
   bottom of the viewport rather than placed in the layout: the selection is
   made by dragging around the calendar, and the controls have to stay put and
   stay reachable wherever that drag ended up. */
.selbar{position:fixed;left:50%;transform:translateX(-50%);bottom:16px;z-index:70;
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;justify-content:center;
  background:var(--card);border:1px solid var(--rule);border-radius:12px;
  padding:9px 14px;box-shadow:0 6px 24px var(--shadowStrong);max-width:calc(100vw - 24px)}
.selbar.hidden{display:none}
.selcount{font-family:var(--disp);font-size:11px;letter-spacing:.06em;
  text-transform:uppercase;color:var(--soft);white-space:nowrap}
.selsw{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.selsw .dab{width:22px;height:22px;border-radius:6px;border:1px solid var(--chipLine);
  cursor:pointer;padding:0}
.selsw .dab:hover{transform:scale(1.12)}
.selsw .btn{padding:3px 9px;font-size:10.5px}
/* Not styled as a primary action: it sits in a title bar next to the year
   arrows and should read as a tool, not as the thing to press. */
.selstart{padding:3px 9px;font-size:10.5px;margin-left:8px}

/* The colour picker in the Day colours panel. A native input[type=color], sized
   to match the swatch it replaced, so the row looks unchanged until clicked. */
.catdot{width:18px;height:18px;flex:none;padding:0;border:1px solid var(--rule);
  border-radius:4px;background:transparent;cursor:pointer}
.catdot::-webkit-color-swatch-wrapper{padding:1px}
.catdot::-webkit-color-swatch{border:0;border-radius:3px}
.catdot::-moz-color-swatch{border:0;border-radius:3px}

@media (max-width:640px){
  /* Above the anchor ad slot, and wide enough to hit with a thumb. */
  .selbar{left:8px;right:8px;transform:none;bottom:8px;padding:10px}
  .selsw .dab{width:34px;height:34px}
  .selsw .btn{min-height:34px}
  .selstart{min-height:32px}
  .catdot{width:26px;height:26px}
}

/* ---------------- undo ----------------
   Deleting a task is instant, and the delete button is one of seven controls
   on a row at about 34px each on a phone. A confirmation on every delete would
   be worse than the accident it prevents, because deleting is something people
   do many times a day. This is the alternative: let it happen, then offer it
   back. Bottom-left rather than centred, so it does not sit under the thumb
   that just pressed delete. */
.toast{position:fixed;left:16px;bottom:16px;z-index:75;
  display:flex;align-items:center;gap:10px;
  background:var(--toastBg);color:var(--toastInk);border-radius:10px;
  padding:9px 10px 9px 14px;box-shadow:0 6px 24px var(--shadowStrong);
  max-width:calc(100vw - 32px);font-size:12.5px}
.toast.hidden{display:none}
/* When the day-selection bar is up, stack above it instead of overlapping. */
.toast.above{bottom:76px}
.toasttext{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
.toast .undogo{flex:none;background:transparent;border:1px solid var(--toastLine);
  color:var(--toastInk);font-family:var(--disp);font-size:10.5px;letter-spacing:.08em;
  text-transform:uppercase;padding:4px 10px;border-radius:7px;cursor:pointer}
.toast .undogo:hover{background:var(--toastHover)}
.toastx{flex:none;background:transparent;border:0;color:var(--toastSoft);
  font-size:16px;line-height:1;padding:0 2px;cursor:pointer}
.toastx:hover{color:var(--toastInk)}

@media (max-width:640px){
  /* Full width, clear of the anchor ad slot, and thumb-sized. */
  .toast{left:8px;right:8px;bottom:8px;max-width:none}
  .toast.above{bottom:96px}
  .toast .undogo{min-height:34px;padding:6px 14px}
  .toastx{min-width:30px;min-height:30px}
}

/* ---------------- phone: controls you can actually hit ----------------
   An audit at 375px found 95 interactive elements under 32px. The earlier
   mobile work had sized four things - the add field, its button, the task row
   controls and the calendar cells - and left everything else at desktop size,
   which is how a "mobile pass" can be done and still leave the app awkward to
   use with a thumb.

   The dense calendar grid is deliberately NOT in here. Its cells are 26x34 and
   making them 44px would turn a year that fits on one screen into a scrolling
   list, which loses the entire point of the view. Everything else is chrome,
   and chrome has no such excuse. */
@media (max-width:640px){
  /* ribbon: the day arrows are the most-used controls on a phone and were
     29px wide */
  .nub{min-width:38px;height:38px}
  .brand{min-height:34px;display:inline-flex;align-items:center}
  .dpick{width:38px;height:38px}
  .iso{min-height:38px}

  /* the year and fold arrows on the calendar and the year-at-a-glance */
  .yarr,.fold{min-width:34px;min-height:34px;font-size:13px}

  /* footer links were 17px tall - two lines of them, tightly packed */
  footer .wrap a{display:inline-block;padding:8px 10px;min-height:34px}
  /* the data buttons live in .fdata in the footer, not .databox - the
     class was renamed when they moved out of the rail. */
  .databox .btn,.fdata .btn{min-height:36px;font-size:11.5px}

  /* the rail: countdown controls, the regional-holiday checkbox, the
     add-countdown date picker */
  .tk .x{width:32px;height:32px;font-size:17px}
  .tk select{min-height:30px;font-size:11.5px}
  .tkdate{min-height:30px;padding:5px 8px;font-size:11.5px}
  .tkl{min-height:30px}
  .tform .dbtn,#tPick{min-width:34px;min-height:34px}
  /* The LABEL is the tap target, not the box: clicking anywhere on it
     toggles the checkbox, which gives a 34px-tall full-width target without
     fighting the browser over how big a native checkbox may be. */
  .chk{min-height:34px;display:flex;align-items:center;gap:8px}
  .cat input[type=text]{min-height:32px}
  /* 30px, and every other control in this block is at least 32 */
  .catdot{width:32px;height:32px}

  /* Found by measuring at 440x956 and again at 360x800: both were under 30px.
     #bnote is the day note, which starts one row tall and grows as you type;
     .hidecats is the hide-colours control that sits in the panel heading. */
  #bnote{min-height:34px}
  .hidecats{padding:5px 10px;font-size:10px;min-height:32px}

  /* the day popup's colour swatches and its close button */
  .dab{min-width:36px;min-height:36px}
  .mx,.bnx{min-width:36px;min-height:36px}
}

/* Adding and removing day colours. The delete button is hidden until the row
   is hovered or focused: it is the only destructive control in a panel that is
   otherwise all editing, and a permanently visible x next to four rows invites
   exactly the mis-click it should not. */
.catx{flex:none;width:18px;height:18px;padding:0;background:transparent;border:0;
  color:var(--faint);font-size:14px;line-height:1;border-radius:4px;
  opacity:0;transition:opacity .12s}
.cat:hover .catx,.catx:focus-visible{opacity:1}
.catx:hover:not(:disabled){color:var(--danger);background:var(--card2)}
.catx:disabled{opacity:0 !important;cursor:default}
.catadd{margin-top:2px}
.catadd .btn{width:100%;padding:4px 8px;font-size:10.5px;color:var(--soft)}
.catadd .btn:hover{color:var(--ink);border-color:var(--accent)}

@media (max-width:640px){
  /* No hover on a touch screen, so the delete control has to be permanent
     there - and big enough to hit deliberately rather than by accident. */
  .catx{opacity:1;width:32px;height:32px;font-size:17px}
  .catadd .btn{min-height:36px}
}

/* ===========================================================================
   TOUCH ON A BIG SCREEN
   The phone pass above is keyed to max-width:640px, so a tablet in portrait
   gets desktop density with a finger driving it. Measured at 768x1024: the
   year arrows were 24px, the colour swatches 18px, the delete-colour buttons
   18px. All fine under a mouse, all too small for a thumb.

   Keyed to (hover:none) instead of to a width, so it follows the input device
   rather than a guess about screen size. Deliberately sizes ONLY: a tablet has
   room for the desktop LAYOUT and should keep it. The calendar day cells and
   the week column are left alone on purpose - they are a dense grid where
   growing one cell shrinks six others.
   =========================================================================== */
@media (hover:none) and (min-width:641px){
  .nub,.iso,.fold,.yarr,.addgo,.dbtn,.catx,.hidecats{min-width:32px;min-height:32px}
  .sitenav a,.seg button,.btn,.go,.cadd,.tkl,.tkdate{min-height:32px}
  .catdot{width:32px;height:32px}
  .chk{min-height:32px;display:flex;align-items:center;gap:8px}
}

/* ===========================================================================
   PHONE: ONE SCROLL, ONE COLUMN, AND A BAR THAT EARNS ITS PLACE

   Measured on the live site at 400x710, which is a common phone once the
   browser's own chrome is taken off the top and bottom:

     the board            6.0 screens long
     first task           290px down - 41% of the screen used before content
     To do lane           227px tall, holding 392px of tasks
     the calendar box     520px tall, holding 1,882px of weeks
     Year at a glance     2,069px, about half the whole board page

   One cause under all of it. The desktop layout is PANELS: fixed-height boxes
   that scroll inside themselves, sitting side by side so the page itself
   barely moves. That is correct on a desktop. On a phone the panels stack, so
   there is no side-by-side arrangement left to protect, and every internal
   scroll becomes a trap - a box that scrolls inside a page that also scrolls,
   where a thumb cannot tell which of the two it is moving. Three were live at
   once.

   The whole block is inside max-width:640px. A rule inside a max-width query
   is not overridden above that width, it is never applied at all, so nothing
   here can reach the desktop board or the desktop calendar. That is checked by
   measurement rather than trusted - see the phone section of tests/app.test.js
   and the before/after numbers in the commit.
   =========================================================================== */

/* The column switcher and the calendar density control are phone furniture.
   They are declared hidden here, at desktop width, so that adding them to the
   markup cannot change the desktop layout even for one frame. */
.colpick,.densepick{display:none}

@media (max-width:640px){

  /* ---- 1. THE LANE IS A SCREEN, NOT A LETTERBOX AND NOT A ROPE ---------
     This went wrong in both directions before it landed here.

     The desktop cap is clamp(180px, 32vh, 460px), which is 227px on a phone -
     about three cards. With twenty tasks in To do that is a 227px window onto
     1,664px, and reaching task four meant working a scrollbar the height of
     three cards inside a page that was also scrolling. So the cap came off.

     Off was worse. Measured at 322x710 with a real twenty-task To do list:
     the lane became 1,664px and the page 5,400px - 7.6 screens, where it had
     been 6.0 before any of this work. Everything below the board, which is
     the calendar and the year, went a very long way down.

     So the cap is back, sized to the SCREEN rather than to a fraction of it.
     The tabs pin at the top and take 46px, the add field 38px; the lane gets
     what is left, so it fills the phone exactly once and the next section
     begins just below the fold. About seven cards on a 710px screen instead
     of three, and one obvious scroller rather than a letterbox.

     The floor matters for a phone held sideways, where 100vh is about 380px
     and the arithmetic would otherwise leave a lane too short to use. */
  .lane,.rlist{max-height:max(260px, calc(100vh - 104px));overflow-y:auto}

  /* The calendar is the exception, and deliberately: a year should scroll
     continuously, and with no box scrolling inside it the grid's own year
     header sticks to the page instead of to the inside of a box. */
  .calbox{max-height:none;overflow:visible}

  /* ---- 2. THE STICKY THING IS THE CONTEXT, NOT THE CHROME ---------------
     The app bar is 180px on a phone because the ribbon wraps onto three rows:
     a quarter of the screen, permanently, for controls used a few times a
     session. It also hid whatever scrolled under it - the calendar's title
     row, carrying the year and the arrows, sat 24px behind it and could not
     be read while browsing the very thing it labels.

     So the bar scrolls away. Nothing is lost: the week grid already has a
     sticky year header of its own (.wg .yh, and .dh under it), and with the
     calendar box no longer a scrollport those now stick to the page instead
     of to the inside of a box. You keep the year and the weekday letters
     pinned while you scroll, which is the context that actually matters, and
     the bar is one short flick back up. */
  .bar{position:static}

  /* And the year header has to be ABLE to stick. .gridbox clips with
     overflow:hidden so the grid cannot escape its rounded corners, but a box
     with overflow:hidden is a scroll container, and a scroll container is what
     position:sticky measures against. With .calbox no longer scrolling, the
     nearest one became .gridbox - which never moves - so the header quietly
     stopped sticking at all. Measured: at 1200px down it was at -945, i.e.
     scrolled off. overflow:clip clips identically and is NOT a scroll
     container, so the header sticks to the page as intended. */
  .gridbox{overflow:clip}

  /* ---- 3. ONE COLUMN AT A TIME -----------------------------------------
     Three stacked columns pay the same 36px title and 38px add row THREE
     times - 222px of overhead before a single task, on a 710px screen. Side
     by side on a desktop that overhead is paid once, which is why it was
     never a problem there.

     So a phone shows one column and a switcher, which is what every mobile
     Kanban does. Nothing is hidden from you: the switcher carries the count
     for all three, and moving a task between them never needed drag anyway -
     the forward arrow is one of the two controls kept in the open on a phone
     card. */
  /* The host stops being a grid when it is showing one column: as a grid the
     switcher is a grid ITEM and collects the 12px row gap under it, which put
     the first task further down than the column header it replaced. */
  .kb[data-only]{display:block}
  .colpick{display:flex;gap:6px;position:sticky;top:0;z-index:30;
    /* --page, not --bg: there is no --bg, and a background that resolves to
       nothing is transparent, which let task text scroll through the 6px gaps
       between the pinned tabs. Caught in a screenshot, not in a rule. */
    background:var(--page);padding:4px 0;margin:0 0 6px}
  .colpick button{flex:1 1 0;min-height:38px;min-width:0;
    display:flex;align-items:center;justify-content:center;gap:6px;
    background:var(--card);color:var(--soft);cursor:pointer;
    border:1px solid var(--rule);border-radius:9px;
    font-family:var(--disp);font-size:11px;letter-spacing:.06em;
    text-transform:uppercase;padding:0 6px}
  .colpick button .n{font-size:11px;font-weight:700;opacity:.75}
  .colpick button[aria-selected=true]{background:var(--card);color:var(--ink);
    border-color:var(--ink)}
  /* The status colour lives on the top edge, matching the column border it
     stands for, so the tab and the column read as the same thing. */
  .colpick button[data-s=todo]{border-top:2px solid var(--todo)}
  .colpick button[data-s=doing]{border-top:2px solid var(--doing)}
  .colpick button[data-s=done]{border-top:2px solid var(--done)}

  .kb[data-only] .col{display:none}
  .kb[data-only=todo]  .col[data-s=todo],
  .kb[data-only=doing] .col[data-s=doing],
  .kb[data-only=done]  .col[data-s=done]{display:flex}

  /* The column's own title row is now the switcher's job. Measured: it was
     36px repeating the name, the count and the status colour that the selected
     tab is already showing, directly under it. Leaving both in cost more than
     the switcher saved, and put the first task 66px FURTHER down than before -
     which is the opposite of the point. The colour moves to the column's top
     edge so the panel still reads as To do, In progress or Done. */
  .kb[data-only] .col .ch{display:none}
  .kb[data-only] .col{border-top-width:3px;padding-top:2px}

  /* ---- 4. A YEAR YOU CAN ACTUALLY LOOK AT -------------------------------
     53 week rows at the 34px thumb size is 1,882px - the longest scroll in
     the app, on the view whose whole promise is seeing a year. No layout fits
     a legible year on one phone screen, so this does not pretend to: it is a
     density switch. Comfortable stays the 34px default for picking a day;
     dense drops the row to 23px, which takes a year to about 1.2 screens and
     keeps every colour, holiday stripe and week number intact.

     Only the row height changes. The cell keeps its date text, so nothing has
     to be re-rendered to switch and the JS knows nothing about it. */
  .densepick{display:inline-flex;align-items:center;justify-content:center;
    min-height:32px;min-width:64px}
  #calView.dense .wg .dc{min-height:23px;font-size:9.5px}
  #calView.dense .wg .wk{min-height:23px;font-size:9px}
  #calView.dense .wg .dc .task{width:3px;height:3px;top:2px;right:2px}
  #calView.dense .wg .dc .pen{font-size:6px}
}

/* ==========================================================================
   PHONE: A TAB BAR AT THE BOTTOM, AND SETTINGS IN A SHEET

   What the board page actually measured at 393x852 before this:

     header      180px    5%    four stacked rows of controls
     THE BOARD   496px   12%    the thing the app is for
     day note     50px    1%
     settings    848px   21%    country picker, countdowns, colour rows
     year grid  2069px   52%    too small to read, and not foldable by default
     footer      254px    6%
                -----
                4120px  = 4.8 screens

   The board was an eighth of the board page. Settings were nearly twice the
   board. That is the finding this block answers, and none of it is a styling
   problem - it is a question of what belongs in the main scroll.

   Declared hidden at desktop width first, so adding the markup cannot change
   the desktop layout even for one frame.
   ========================================================================== */
.tabbar,.sheethead{display:none}

@media (max-width:640px){

  /* ---- THE TAB BAR ------------------------------------------------------
     Four destinations, at the bottom, where the thumb is. 56px plus whatever
     the home indicator needs on the device it is running on. */
  .tabbar{display:flex;position:fixed;left:0;right:0;bottom:0;z-index:80;
    background:var(--card);border-top:1px solid var(--rule);
    padding-bottom:env(safe-area-inset-bottom)}
  .tabbar .tab{flex:1 1 0;min-width:0;min-height:56px;
    display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;
    background:transparent;border:0;text-decoration:none;color:var(--faint);
    font-family:var(--disp);font-size:10px;letter-spacing:.06em;text-transform:uppercase;
    cursor:pointer;padding:6px 2px}
  .tabbar .tab .ti{font-size:17px;line-height:1}
  .tabbar .tab.on{color:var(--ink)}
  .tabbar .tab:active{background:var(--card2)}

  /* The page has to end above the bar, or the last task sits under it. */
  body{padding-bottom:calc(56px + env(safe-area-inset-bottom))}
  /* and anything already pinned to the bottom moves up by the same amount */
  .toast{bottom:calc(64px + env(safe-area-inset-bottom))}
  .toast.above{bottom:calc(152px + env(safe-area-inset-bottom))}
  .selbar{bottom:calc(64px + env(safe-area-inset-bottom))}

  /* Board and Calendar are in the tab bar now, so the header loses its fourth
     row - the one that carried them - and 36px with it. */
  .sitenav{display:none}

  /* ---- SETTINGS AS A SHEET ----------------------------------------------
     The rail is not restyled into a sheet so much as lifted out of the page:
     same markup, same panels, taken out of the scroll and put behind a tab.
     visibility, not display, so the transform has something to animate and so
     a screen reader does not see a hidden panel. */
  /* Below the tab bar's z-index, and stopping short of it, so Settings is a
     DESTINATION rather than a modal: the bar stays visible and usable, the
     Settings tab stays lit while you are in it, and you leave by tapping
     another tab exactly as you would in any app. "Done" is the second way
     out, not the only one. */
  .rail{position:fixed;left:0;right:0;top:0;z-index:70;margin:0;padding:0 14px 24px;
    bottom:calc(56px + env(safe-area-inset-bottom));
    background:var(--page);overflow-y:auto;-webkit-overflow-scrolling:touch;
    max-height:none;display:block;
    transform:translateY(100%);visibility:hidden;
    transition:transform .22s ease, visibility .22s}
  body.sheet .rail{transform:none;visibility:visible}
  /* The tab lights up like any other destination. */
  body.sheet #tabSettings{color:var(--ink)}
  body.sheet{overflow:hidden}          /* the board must not scroll behind it */

  .sheethead{display:flex;align-items:center;gap:10px;
    position:sticky;top:0;z-index:2;background:var(--page);
    padding:calc(10px + env(safe-area-inset-top)) 0 10px;
    border-bottom:1px solid var(--rule);margin-bottom:12px}
  .sheethead h2{flex:1 1 auto;margin:0;font-family:var(--disp);font-size:15px;
    letter-spacing:.1em;text-transform:uppercase;color:var(--ink);font-weight:500}
  /* "Done" rather than a 27px glyph in a corner: it says what it does, it is
     the width of a word, and it stays put because the header is sticky. */
  .sheetdone{min-height:40px;padding:0 16px;border:1px solid var(--ink);
    border-radius:9px;background:var(--accent);color:var(--onAccent);
    font-family:var(--disp);font-size:11px;letter-spacing:.08em;
    text-transform:uppercase;cursor:pointer;flex:none}

  /* The rail's own phone rules from the 900px block turned it into a wrapping
     row of panels beside the board. Inside a sheet it is a column again. */
  .rail{flex-direction:column}
  .rail .rbox{flex:none;margin-bottom:12px}
}

/* ==========================================================================
   THE TASK ACTION SHEET, phone only

   Replaces the eight-glyph row that used to unfold inside the card. Same
   actions, in the same order, with the words that were only ever in a tooltip
   - and delete separated, in red, at the end, instead of six pixels from the
   arrow that moves a task forward.
   ========================================================================== */
.actsheet{display:none}

@media (max-width:640px){
  .actsheet{display:block;position:fixed;inset:0;z-index:110}
  .actsheet.hidden{display:none !important}
  .actback{position:absolute;inset:0;background:var(--overlay)}
  .actpanel{position:absolute;left:0;right:0;bottom:0;
    background:var(--card);border-top:1px solid var(--rule);
    border-radius:16px 16px 0 0;
    padding:8px 10px calc(10px + env(safe-area-inset-bottom));
    max-height:82vh;overflow-y:auto;
    box-shadow:0 -8px 30px var(--shadow)}

  /* Which task this is about. Two lines at most - it is a reminder of what you
     tapped, not the task itself. */
  .acttitle{margin:6px 8px 10px;font-size:13px;line-height:1.4;color:var(--soft);
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}

  .actlist{display:flex;flex-direction:column;gap:2px}
  /* 48px, above the 44 Apple asks for, because these are the rows that were
     34x32 glyphs and this is the whole point of the change. */
  .actrow{display:flex;align-items:center;gap:14px;width:100%;min-height:48px;
    padding:0 10px;background:transparent;border:0;border-radius:10px;
    color:var(--ink);font-size:15px;text-align:left;cursor:pointer}
  .actrow:active{background:var(--card2)}
  .actrow[disabled]{opacity:.35;cursor:default}
  .actrow .ai{flex:none;width:24px;text-align:center;font-size:15px;color:var(--soft)}
  .actrow.danger{color:var(--danger)}
  .actrow.danger .ai{color:var(--danger)}
  /* A rule above delete, so the destructive one is never the row your thumb
     lands on by momentum. */
  .actrow.danger{margin-top:6px;border-top:1px solid var(--rule);border-radius:0 0 10px 10px}

  .actcancel{width:100%;min-height:48px;margin-top:8px;
    background:var(--card2);border:1px solid var(--rule);border-radius:12px;
    color:var(--ink);font-size:15px;cursor:pointer}
}

/* ==========================================================================
   THE DAY POPUP AS A SHEET, phone only

   Measured at 393x852: the panel was 891px tall - taller than the screen it
   was on - so it overflowed and the whole overlay scrolled instead. The way
   out was a 27x25 button in its top corner, the smallest control in the app,
   and scrolling to the bottom of the panel took that button off the top of
   the screen. Tapping the backdrop does close it and always did - but the only
   backdrop left was a 32px strip above the panel, and that scrolled away with
   everything else. So at the bottom of a day there was no visible way out, and
   a phone keyboard has no Escape.

   As a sheet it is anchored to the bottom, never taller than 88vh, and the
   header holds still while the body scrolls under it - so the way out is
   always on screen, and it is a word rather than a glyph.
   ========================================================================== */
@media (max-width:640px){
  /* The overlay stops being the scroller. Its only jobs now are the dim and
     the tap-to-dismiss target above the sheet. */
  .ov{align-items:flex-end;padding:0;overflow:hidden}
  .ov .md{width:100%;max-height:88vh;display:flex;flex-direction:column;
    border-radius:16px 16px 0 0;border-bottom:0;
    box-shadow:0 -8px 30px var(--shadow)}

  /* Sticky, so scrolling the note or the board inside cannot take the exit
     away with it. */
  /* No flex-wrap:nowrap here, deliberately: the suite bans it because on the
     ribbon it turned a tight fit into overlapping text, and the reasoning is
     about a row of unknown width, which this is. If the date, the week and
     Done cannot share a line on a very narrow phone, a second line is the
     correct answer - the header is sticky either way. */
  .mh{position:sticky;top:0;z-index:2;
    padding:12px 12px calc(12px);border-radius:16px 16px 0 0}
  .mh .w{white-space:nowrap}
  /* 27x25 to a labelled 44px control. It reads "Done" because that is what it
     does - the note and the colours save as you go, so there is nothing to
     confirm and nothing to cancel. */
  .mh button{margin-left:auto;flex:none;width:auto;height:auto;min-height:44px;
    padding:0 14px;background:var(--accent);color:var(--onAccent);
    border-color:var(--ink);border-radius:10px;
    font-family:var(--disp);font-size:11px;letter-spacing:.08em;text-transform:uppercase}
  .mh button:hover{background:var(--accent);color:var(--onAccent)}
  .mh button .mx{display:none}
  .mh button .mdone{display:inline}

  .mb{flex:1 1 auto;overflow-y:auto;-webkit-overflow-scrolling:touch;
    padding:14px 12px calc(14px + env(safe-area-inset-bottom))}

  /* The search overlay is a different shape - it belongs at the top, under the
     thumb's reach but next to the keyboard that opens with it. */
  #sov{align-items:flex-start;padding:0}
  .ov .md.search{max-height:100vh;border-radius:0;box-shadow:none}
}

/* ==========================================================================
   CALENDAR CELLS ON A PHONE: THE DAY, NOT THE DATE

   Every cell printed MM-DD. That is the month repeated seven times across a
   row and 371 times down a year, to say something that changes twelve times,
   and it is what set the cell's width: five characters of monospace at a size
   small enough to fit seven of them across a 393px screen.

   So on a phone the month comes out of the cell and is printed once, in the
   week column, on the week where it changes. Two characters instead of five,
   which is what buys the room for a bigger tap target and stops the date
   crowding the holiday stripe beneath it.

   Desktop is untouched: the cells there still read MM-DD in full, and the
   data-mo attribute the week button carries is simply never drawn.
   ========================================================================== */
@media (max-width:640px){
  .wg .dc .cm{display:none}
  /* Two characters where there were five, so the cell can be read at a size
     worth tapping rather than merely fitted. */
  .wg .dc{font-size:13px}
  .wg.c .dc{font-size:11px}
  #calView.dense .wg .dc{font-size:10.5px}

  /* The month, once, where the week number already is. Stacked under it so it
     costs no width - the week column is the narrowest thing on the grid and
     seven day cells are waiting on every pixel of it. */
  .wg .wk{flex-direction:column;justify-content:center;line-height:1.05}
  .wg .wk[data-mo]::after{content:attr(data-mo);display:block;
    font-family:var(--disp);font-size:8px;letter-spacing:.04em;
    text-transform:uppercase;color:var(--soft)}
  /* A rule across the grid at the boundary, so the month reads as a division
     of the year rather than as a label stuck on one cell. The class is set in
     JS on that week's own seven cells: a "~" sibling selector would have
     matched every cell after it, which on a year grid is the whole rest of the
     year, because .wg is one flat grid and not a row per week. */
  .wg .wk[data-mo],.wg .dc.mstart{border-top-color:var(--rule)}
}
