/* TimeForest — design system.
 *
 * Everything here is measured against the app as it actually renders, on the
 * machine it actually runs on. A previous version of this file opened with a
 * paragraph citing Apple's HIG and its 4.5:1 contrast floor, above rules that
 * shipped 1.66:1; it declared `--radius: 8px` and never used it; it declared
 * five font weights that rendered as one. Rationale that isn't checked against
 * pixels is worse than no rationale, because it stops the next person looking.
 *
 * TYPE. Two weights, because Japanese has two.
 *   Measured (ink coverage of 歯医者の予約 at 24px, this machine):
 *     400 -> 222    500 -> 346    550 -> 346    600 -> 346
 *     620 -> 346    650 -> 346    700 -> 346
 *   Every weight from 500 up is the same pixels. CJK system faces ship
 *   Regular and one bold; the in-between numbers are wishes. Latin has one
 *   more step, but using it would mean the hierarchy changes when the text
 *   changes language, which is worse than having one step fewer.
 *
 *   The stack names ONE family per platform, and that family covers Latin too.
 *   The old stack (-apple-system, Segoe UI, Hiragino, …) set "2026年 7月" in
 *   TWO faces — Segoe UI for the digits, whatever CJK face the machine had for
 *   the kanji — with different x-heights and different colour. Which face you
 *   got depended on what was installed. That mixed-metrics shimmer is a large
 *   part of what "vibe-coded" felt like, and it is invisible until you go
 *   looking with CSS.getPlatformFontsForNode.
 *
 * HAIRLINES. --hair, not 0.5px. 0.5px is crisp at DPR 2 (a Mac) and paints as
 *   a 0.75-device-pixel smear at DPR 1.5 (this machine, and most of Windows).
 *   41 borders were doing that. The whole app was very slightly out of focus.
 *
 * SCALES. Radii and spacing come from a set, and the set is small. Given a
 *   free choice each time, this file previously used every integer from 2 to
 *   12 for border-radius and fifteen font sizes in 0.5px steps. That is not a
 *   system, that is the absence of one.
 */

:root {
  --bg: #ffffff;
  --bg-side: #f4f4f6;
  --bg-elev: #ffffff;
  --bg-hover: rgba(60, 60, 67, 0.06);
  --bg-active: rgba(60, 60, 67, 0.12);
  --label: #16171a;           /* not #000: pure black on white is a glare edge */
  --label-2: #62636b;         /* 5.9:1 on --bg. was rgba(60,60,67,.6) = 4.4:1 */
  --label-3: #74757e;         /* 4.6:1 — still passes AA for body text */
  --sep: rgba(60, 60, 67, 0.16);
  --sep-strong: rgba(60, 60, 67, 0.28);
  --accent: #12a45f;          /* darkened from #2ecc87 (1.9:1) to 3.4:1 on white */
  --accent-fg: #ffffff;
  --accent-soft: rgba(46, 204, 135, 0.14);
  --accent-wash: rgba(46, 204, 135, 0.07);
  --red: #d70015;
  --blue: #0064d2;
  --shadow: 0 0 0 1px rgba(0, 0, 0, 0.06), 0 8px 24px -6px rgba(0, 0, 0, 0.16),
            0 18px 48px -12px rgba(0, 0, 0, 0.14);

  color-scheme: light;
}

:root[data-theme="dark"] {
  --bg: #1c1c1e;
  --bg-side: #161618;
  --bg-elev: #2c2c2e;
  --bg-hover: rgba(235, 235, 245, 0.07);
  --bg-active: rgba(235, 235, 245, 0.13);
  --label: #f2f2f5;
  --label-2: #a1a1aa;         /* 6.2:1 on --bg */
  --label-3: #8a8a93;         /* 4.7:1 */
  --sep: rgba(120, 120, 128, 0.32);
  --sep-strong: rgba(140, 140, 150, 0.5);
  --accent: #30d158;
  --accent-fg: #06210f;
  --accent-soft: rgba(48, 209, 88, 0.16);
  /* The "today" wash is a backdrop for a whole column, not a highlighter pen.
     At 0.16 it was the loudest object on the week grid, to say something the
     date badge already said. */
  --accent-wash: rgba(48, 209, 88, 0.05);
  --red: #ff5257;
  --blue: #3aa0ff;
  --shadow: 0 0 0 1px rgba(255, 255, 255, 0.06), 0 8px 24px -6px rgba(0, 0, 0, 0.5),
            0 18px 48px -12px rgba(0, 0, 0, 0.45);

  color-scheme: dark;
}

:root {
  /* One family per platform, Latin and CJK from the same face.
   *
   * "Yu Gothic", NOT "Yu Gothic UI". The UI variant looks like the obvious
   * choice and is the one variant that ships no `palt` table — measured, by
   * setting font-feature-settings and reading the width back:
   *     Yu Gothic UI   定例ミーティング（オンライン）   0.0% narrower
   *     Yu Gothic      同上                          16.0% narrower
   *     Meiryo         同上                           0.0%
   * `palt` is proportional kana metrics: without it every ひらがな sits in a
   * full em box and the title truncates ~10% sooner for nothing.
   *
   * "Hiragino Kaku Gothic ProN" is deliberately NOT here even though it was
   * winning on the dev machine: it turned out to be a per-user W5 (medium)
   * install, i.e. this app was being designed against a heavier Japanese than
   * any user would ever see, on a font shipped with macOS.
   */
  --ui: "Yu Gothic", "Hiragino Sans", "Noto Sans JP", sans-serif;

  /* Hairlines that are one device pixel wherever they're drawn. */
  --hair: 1px;

  /* Radii: four. Everything is a chip, a panel, a sheet, or a pill. */
  --r-sm: 4px;
  --r-md: 8px;
  --r-lg: 12px;
  --r-pill: 999px;

  /* Spacing: a 4px grid, named. */
  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 24px;

  /* Type: six sizes. CJK floors at 11px — below that kanji are mush, and the
     grids are exactly where the dense text lives. */
  --t-xs: 11px;
  --t-sm: 12px;
  --t-md: 13px;
  --t-lg: 15px;
  --t-xl: 17px;
  --t-2xl: 22px;

  --toolbar-h: 46px;
  --sidebar-w: 208px;
}

/* True hairlines only where they actually land on one device pixel. */
@media (-webkit-min-device-pixel-ratio: 2) {
  :root { --hair: 0.5px; }
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* Icons are svg with stroke:currentColor, so they take the colour of whatever
   text they sit with. `block` kills the inline-baseline gap that makes an svg
   sit 3px low next to text — the thing that reads as "not quite aligned". */
.i { display: block; flex: none; }

html, body {
  height: 100%;
  overflow: hidden;
}

body {
  font-family: var(--ui);
  font-size: var(--t-md);
  line-height: 1.45;
  color: var(--label);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  user-select: none;
  cursor: default;

  /* Proportional kana. ~10-16% narrower Japanese titles for one line of CSS —
     the difference between 「定例ミーティング（オンライン）」 fitting and
     being cut. Applied at the root and turned OFF for the note body below,
     where it's long-form reading rather than a tight label. */
  font-feature-settings: "palt" 1;

  /* A gap between Japanese and Latin/digits — 「予約14時」. The spec says this
     is on by default; Chromium 150 does not do it unless asked (measured:
     `default` renders identically to `no-autospace`). The alternative is
     typing spaces into the strings, which W3C says in as many words not to do.
     Chromium inserts 1/8 em, which is on the tight side of JLReq's ¼ em —
     right for a dense grid. */
  text-autospace: normal;
}

button, input, select { font: inherit; color: inherit; }
button { background: none; border: 0; cursor: default; text-align: left; }

/* One focus ring, everywhere, and only for keyboard.
 *
 * The event rows, chips and blocks used to be <div onclick>: not focusable, so
 * a keyboard user could create an event and then never open, edit or delete
 * one. They're buttons now, which only helps if you can see where you are —
 * and the text fields had `outline: 0` with a hairline border-colour as the
 * sole cue, measured at 2.92:1 against the field. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-radius: var(--r-sm);
}
.f-title:focus-visible, .f-cl-t:focus-visible, .palette input:focus-visible {
  outline-offset: -2px;
}

#app { height: 100%; display: flex; flex-direction: column; }

/* --- toolbar ------------------------------------------------------------ */

.toolbar {
  height: var(--toolbar-h);
  flex: none;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 12px;
  border-bottom: var(--hair) solid var(--sep);
  background: var(--bg-side);
  -webkit-app-region: drag;
}
.toolbar > * { -webkit-app-region: no-drag; }

.tb-title {
  font-size: var(--t-lg);
  font-weight: 700;
  letter-spacing: -0.01em;
  min-width: 130px;
  font-variant-numeric: tabular-nums;
}

.tb-spacer { flex: 1; }

.nav { display: flex; align-items: center; gap: 2px; }

.icon-btn {
  /* WCAG 2.2 target size is 24x24 minimum; 28x26 cleared it but only just,
     and it read as cramped next to 15px icons. 30x28 with the icon at 15 is
     comfortable without turning the toolbar into a touch UI. */
  width: 30px;
  height: 28px;
  border-radius: var(--r-sm);
  display: grid;
  place-items: center;
  color: var(--label-2);
  font-size: var(--t-md);
  transition: background 0.12s, color 0.12s;
}
.icon-btn:hover { background: var(--bg-hover); color: var(--label); }
.icon-btn:active { background: var(--bg-active); }

.pill {
  padding: 4px 11px;
  border-radius: var(--r-sm);
  font-size: var(--t-sm);
  color: var(--label-2);
  transition: background 0.12s, color 0.12s;
}
.pill:hover { background: var(--bg-hover); color: var(--label); }

.seg {
  display: flex;
  background: var(--bg-hover);
  border-radius: var(--r-md);
  padding: 2px;
  gap: 2px;
}
.seg button {
  padding: 3px 12px;
  border-radius: var(--r-sm);
  font-size: var(--t-sm);
  color: var(--label-2);
  transition: background 0.12s, color 0.12s;
}
.seg button.on {
  background: var(--bg);
  color: var(--label);
  box-shadow: 0 0 0 var(--hair) var(--sep), 0 1px 2px rgba(0, 0, 0, 0.1);
  font-weight: 400;
}

.new-btn {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 5px 11px 5px 9px;
  border-radius: var(--r-sm);
  background: var(--accent);
  color: var(--accent-fg);
  font-size: var(--t-sm);
  font-weight: 700;
  transition: filter 0.12s, transform 0.1s var(--ease);
}
.new-btn:hover { filter: brightness(1.07); }
.new-btn:active { transform: scale(0.97); }

.search-hint {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 9px 4px 8px;
  border-radius: var(--r-sm);
  background: var(--bg-hover);
  color: var(--label-2);
  font-size: var(--t-sm);
  min-width: 150px;
}
.search-hint:hover { background: var(--bg-active); }
.search-hint kbd {
  margin-left: auto;
  font-family: inherit;
  font-size: var(--t-xs);
  padding: 1px 4px;
  border-radius: var(--r-sm);
  border: var(--hair) solid var(--sep-strong);
  color: var(--label-3);
}

/* --- body --------------------------------------------------------------- */

.body { flex: 1; display: flex; min-height: 0; }

.sidebar {
  width: var(--sidebar-w);
  flex: none;
  border-right: var(--hair) solid var(--sep);
  background: var(--bg-side);
  padding: 10px 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.sidebar.hidden { display: none; }

/* --- narrow screens: the sidebar becomes a drawer ----------------------- */
/* The Electron window never gets this narrow — this is for the hosted client
   and the userscript running on a phone. There a fixed 208px rail crushes the
   calendar (measured: .main was 112–206px at 320–414px wide, and the toolbar
   clipped 228–322px off its right edge). So below 640px the sidebar slides in
   over the calendar from a ☰ button, and the toolbar wraps instead of clipping. */
.tb-burger { display: none; }   /* the drawer toggle — shown only on narrow screens */
.nav-scrim { display: none; }   /* NOT .scrim — that's the modal/sheet backdrop below */

@media (max-width: 640px) {
  .toolbar {
    height: auto;
    min-height: var(--toolbar-h);
    flex-wrap: wrap;
    padding-top: 5px;
    padding-bottom: 5px;
    row-gap: 5px;
  }
  .tb-burger { display: grid; }
  .tb-title { min-width: 0; }               /* was 130px — let it shrink to share the row */
  .search-hint { min-width: 0; }            /* was 150px */
  .search-hint .sh-t,
  .search-hint kbd { display: none; }       /* the search box collapses to its icon */

  .body { position: relative; }             /* positioning context for the drawer + scrim */
  .sidebar {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    /* Below the app's overlay band (menu 45/46, modals 50+), above the calendar
       (≤3): otherwise the account menu — opened FROM this drawer — renders behind
       it and account switch is unreachable on a phone. */
    z-index: 30;
    width: min(80vw, 280px);                /* a touch wider than the 208px desktop rail */
    transform: translateX(-100%);
    transition: transform 0.22s var(--ease);
    box-shadow: var(--shadow);
  }
  #app.nav-open .sidebar { transform: translateX(0); }
  #app.nav-open .nav-scrim {
    display: block;
    position: absolute;
    inset: 0;
    z-index: 25;                            /* under the drawer, over the calendar */
    background: rgba(0, 0, 0, 0.4);
  }
}

/* --- account switcher --------------------------------------------------- */

.acct {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 8px 4px;
  padding: 5px 6px;
  border-radius: var(--r-md);
  width: calc(100% - 16px);
  text-align: left;
  transition: background 0.12s;
}
.acct:hover { background: var(--bg-hover); }

.acct-av {
  width: 24px;
  height: 24px;
  border-radius: var(--r-lg);
  flex: none;
  display: grid;
  place-items: center;
  color: #fff;
  font-size: var(--t-sm);
  font-weight: 700;
  box-shadow: inset 0 0 0 var(--hair) rgba(0, 0, 0, 0.15);
}
.acct-av.sm { width: 20px; height: 20px; border-radius: var(--r-md); font-size: var(--t-xs); }

.acct-box { display: flex; flex-direction: column; min-width: 0; flex: 1; line-height: 15px; }
.acct-nm { font-size: var(--t-sm); font-weight: 400; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct-em { font-size: var(--t-xs); color: var(--label-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.acct-cv { color: var(--label-3); flex: none; display: grid; place-items: center; }

.menu {
  position: fixed;
  background: var(--bg-elev);
  border-radius: var(--r-md);
  box-shadow: var(--shadow);
  padding: 5px;
  z-index: 46;
  max-width: 320px;
  opacity: 0;
  transform: scale(0.96) translateY(-3px);
  transform-origin: top left;
  transition: opacity 0.12s ease, transform 0.17s var(--ease);
}
.menu.in { opacity: 1; transform: scale(1) translateY(0); }

.menu-i {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-radius: var(--r-sm);
  width: 100%;
  text-align: left;
  position: relative;
  transition: background 0.1s;
}
.menu-i:hover { background: var(--bg-hover); }
.menu-i.on { background: var(--bg-active); }
.menu-ck { color: var(--accent); flex: none; display: grid; place-items: center; }
.menu-ic { width: 20px; display: grid; place-items: center; color: var(--label-2); flex: none; }
.menu-sep { height: var(--hair); background: var(--sep); margin: 5px 6px; }

.menu-x {
  opacity: 0;
  color: var(--label-3);
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: var(--r-sm);
  flex: none;
  transition: opacity 0.12s, background 0.12s, color 0.12s;
}
.menu-i:hover .menu-x { opacity: 1; }
.menu-x:hover { background: var(--red); color: #fff; }

.side-h {
  font-size: var(--t-xs);
  font-weight: 700;
  color: var(--label-3);
  padding: 10px 14px 3px;
  letter-spacing: 0.03em;
}

.side-item {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 8px;
  padding: 4px 6px;
  border-radius: var(--r-sm);
  font-size: var(--t-sm);
  color: var(--label);
  width: calc(100% - 16px);
  text-align: left;
  transition: background 0.12s;
}
.side-item:hover { background: var(--bg-hover); }
.side-item .dot {
  width: 9px; height: 9px; border-radius: var(--r-sm); flex: none;
  box-shadow: inset 0 0 0 var(--hair) rgba(0, 0, 0, 0.15);
}
.side-item .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.side-item.off { color: var(--label-3); }
.side-item.off .dot { opacity: 0.25; }

.side-foot { margin-top: auto; padding: var(--s2) 0 2px; }

/* A run of empty days, said once. */
.ag-gap {
  display: block;
  width: 100%;
  padding: 5px var(--s4);
  font-size: var(--t-xs);
  color: var(--label-3);
  border-bottom: var(--hair) solid var(--sep);
  transition: background 0.12s, color 0.12s;
}
.ag-gap:hover { background: var(--bg-hover); color: var(--label-2); }
.mini-btn {
  padding: 3px 8px;
  font-size: var(--t-xs);
  border-radius: var(--r-sm);
  border: var(--hair) solid var(--sep);
  color: var(--label-2);
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.mini-btn:hover { background: var(--bg-hover); color: var(--label); border-color: var(--sep-strong); }

.side-status { padding: 6px 14px 0; font-size: var(--t-xs); color: var(--label-3); font-variant-numeric: tabular-nums; }

/* How old what you're looking at is. Cheap to show, and the difference between
   a calendar you can act on and one you have to second-guess. */
.side-sync { padding: 1px 14px 2px; font-size: var(--t-xs); color: var(--label-3); }
.side-sync.bad { color: var(--red); }
.side-sync:empty { display: none; }

.main { flex: 1; min-width: 0; position: relative; overflow: hidden; }

/* --- agenda ------------------------------------------------------------- */

.agenda { height: 100%; overflow-y: auto; }

.ag-month {
  position: sticky;
  top: 0;
  z-index: 3;
  padding: 5px var(--s4);
  font-size: var(--t-xs);
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--label-2);
  background: var(--bg-elev);
  border-bottom: var(--hair) solid var(--sep);
  font-variant-numeric: tabular-nums;
}

.ag-day {
  display: grid;
  grid-template-columns: 46px 1fr;
  gap: 12px;
  padding: 6px var(--s4);
  border-bottom: var(--hair) solid var(--sep);
  align-items: start;
}
.ag-day.today { background: var(--accent-wash); }
.ag-day.empty .ag-date { opacity: 0.45; }
.ag-day.month-start { border-top: 0; }

.ag-date { text-align: right; font-variant-numeric: tabular-nums; padding-top: 1px; }
.ag-date .d { font-size: var(--t-lg); font-weight: 700; line-height: 20px; letter-spacing: -0.02em; }
.ag-date .w { font-size: var(--t-xs); line-height: 12px; color: var(--label-2); }
.ag-date.sat .d, .ag-date.sat .w { color: var(--blue); }
.ag-date.sun .d, .ag-date.sun .w { color: var(--red); }
.ag-day.today .ag-date .d { color: var(--accent); }

.ag-events { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

.ev {
  display: grid;
  width: 100%;
  /* dot · time · title · author. The time is right-aligned into its column so
     every colon lines up and the titles start on one edge — the old layout
     left-aligned 終日 / 祝 / 10:30 in a fixed box, giving three different
     right edges and a ragged gutter down the whole list. */
  grid-template-columns: 7px 44px 1fr 18px;
  gap: 8px;
  align-items: start;
  padding: 2px 6px 2px 0;
  border-radius: var(--r-sm);
  min-width: 0;
  transition: background 0.1s;
}

/* The author's initial, at the end of the row. Reserved even when empty so the
   titles don't shift by 18px depending on who wrote what. */
.ev-by {
  width: 17px;
  height: 17px;
  border-radius: var(--r-md);
  margin-top: 2px;
  display: grid;
  place-items: center;
  color: #fff;
  font-size: var(--t-xs);
  font-weight: 700;
  box-shadow: inset 0 0 0 var(--hair) rgba(0, 0, 0, 0.15);
}
.ev-by.empty { background: none; box-shadow: none; }
.ev:hover { background: var(--bg-hover); }
.ev .dot {
  width: 7px;
  height: 7px;
  border-radius: var(--r-pill);
  margin-top: 7px;
  background: var(--label-3);
  box-shadow: inset 0 0 0 var(--hair) rgba(0, 0, 0, 0.2);
}
.ev .t {
  font-size: var(--t-sm); line-height: 21px; color: var(--label-2);
  font-variant-numeric: tabular-nums; white-space: nowrap;
  text-align: right;
}
.ev .ti { font-size: var(--t-md); line-height: 21px; min-width: 0; overflow-wrap: anywhere; }
.ev .meta { font-size: var(--t-sm); color: var(--label-2); }
.ev.allday .t { color: var(--accent); }
/* Red is reserved for "not a working day". 七夕 is a memorialday too, but you
   still go to school — colouring it like 海の日 makes the one thing a calendar
   most needs to say unreadable. */
.ev.holiday .t, .ev.holiday .ti { color: var(--red); }
.ev.holiday.workday .t, .ev.holiday.workday .ti { color: var(--label-2); }

/* The dash is the reading state; the same spot becomes the writing one on
   hover, so an empty day advertises what it's for without adding chrome to
   all 30 of them. */
.ag-add {
  text-align: left;
  font-size: var(--t-sm);
  line-height: 21px;
  color: var(--label-3);
  padding: 0 4px;
  border-radius: var(--r-sm);
  align-self: flex-start;
  transition: background 0.12s, color 0.12s;
}
.ag-add .ag-plus { display: none; }
.ag-add:hover { color: var(--accent); background: var(--bg-hover); }
.ag-add:hover .ag-dash { display: none; }
.ag-add:hover .ag-plus { display: inline; }

/* --- month grid --------------------------------------------------------- */

.month { height: 100%; display: flex; flex-direction: column; }

.m-head {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  flex: none;
  border-bottom: var(--hair) solid var(--sep);
}
.m-head div {
  text-align: center;
  padding: 5px 0;
  font-size: var(--t-xs);
  font-weight: 700;
  color: var(--label-2);
}
.m-head div.sat { color: var(--blue); }
.m-head div.sun { color: var(--red); }

/* Six week rows, not 42 loose cells: a span has to be able to reach across
   days, and it can only do that if the week is the thing it lives in. */
/* --weeks is set by renderMonth: 5 or 6, whatever the month needs. Hardcoding 6
   drew a spare week of next month in most months AND shrank every cell by ~17%,
   which pushed events into 「+N件」 that would otherwise have fit. */
.m-grid {
  flex: 1;
  display: grid;
  grid-template-rows: repeat(var(--weeks, 6), 1fr);
  min-height: 0;
}

.m-week {
  position: relative;
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  min-height: 0;
  --lanes: 0;
  --lane-h: 16px;
}

/* The bar layer sits over the cells, below the date numbers. Same 7 columns,
   so a bar's grid-column IS its span in days. */
.m-bars {
  position: absolute;
  top: 22px;
  left: 0;
  right: 0;
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  grid-auto-rows: var(--lane-h);
  gap: 1px 0;
  pointer-events: none;
  z-index: 1;
}
.m-bar {
  pointer-events: auto;
  display: flex;
  align-items: center;
  height: 15px;
  margin: 0 2px;
  padding: 0 5px;
  border-radius: var(--r-sm);
  font-size: var(--t-xs);
  line-height: 15px;
  overflow: hidden;
  white-space: nowrap;
  transition: filter 0.12s;
}
.m-bar:hover { filter: brightness(1.08); }
/* A bar that runs off the edge of the week keeps going — the rounded cap is
   what says "it ends here", so don't draw one where it doesn't. */
.m-bar.open-s { margin-left: 0; border-top-left-radius: 0; border-bottom-left-radius: 0; }
.m-bar.open-e { margin-right: 0; border-top-right-radius: 0; border-bottom-right-radius: 0; }

/* Single-day chips start below whatever lanes this week needs. */
.m-week .m-evs { margin-top: calc(var(--lanes) * (var(--lane-h) + 1px)); }

.m-cell {
  border-right: var(--hair) solid var(--sep);
  border-bottom: var(--hair) solid var(--sep);
  padding: 3px 4px;
  min-width: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.m-week .m-cell:last-child { border-right: 0; }
.m-cell.outside { background: var(--bg-side); }
.m-cell.outside .m-num { opacity: 0.4; }
.m-cell.today { background: var(--accent-wash); }
/* Free space in a cell is clickable (it creates there); say so quietly. */
.m-cell:hover { box-shadow: inset 0 0 0 1px var(--sep-strong); }

.m-num {
  font-size: var(--t-sm);
  font-weight: 700;
  text-align: center;
  padding: 1px 0 2px;
  font-variant-numeric: tabular-nums;
  flex: none;
}
.m-cell.sat .m-num { color: var(--blue); }
.m-cell.sun .m-num { color: var(--red); }
.m-cell.today .m-num {
  color: var(--accent-fg);
  background: var(--accent);
  border-radius: var(--r-md);
  width: 18px;
  height: 18px;
  line-height: 18px;
  margin: 0 auto 2px;
}

/* flex:1 so this box reports the space actually available, which is what
   measureCells() divides by. Sized to content it would report the overflow. */
.m-evs { display: flex; flex-direction: column; gap: 1px; overflow: hidden; flex: 1 1 0; min-height: 0; }

.m-ev {
  display: flex;
  width: 100%;
  align-items: baseline;
  gap: 4px;
  font-size: var(--t-xs);
  line-height: 15px;
  padding: 1px 4px;
  border-radius: var(--r-sm);
  white-space: nowrap;
  overflow: hidden;
  flex: none;
}
.m-ev .m-t { font-variant-numeric: tabular-nums; opacity: 0.75; font-size: var(--t-xs); flex: none; }
.m-ev .m-ti { overflow: hidden; text-overflow: ellipsis; }
/* colour set by fill(): black or white, whichever reads on this label */
.m-ev.chip { font-weight: 400; }
.m-ev.dotted { background: none; }
.m-ev.dotted .m-dot { width: 6px; height: 6px; border-radius: var(--r-sm); flex: none; align-self: center; }

/* TimeTree silently drops events past the cell's height. We count them. */
.m-more {
  font-size: var(--t-xs);
  color: var(--label-2);
  padding: 0 4px;
  flex: none;
  text-align: left;
}
.m-more:hover { color: var(--accent); text-decoration: underline; }

/* --- week (time grid) --------------------------------------------------- */

.week { height: 100%; display: flex; flex-direction: column; --gut: 48px; }

.w-head {
  display: grid;
  grid-template-columns: var(--gut) repeat(7, 1fr);
  flex: none;
  border-bottom: var(--hair) solid var(--sep);
  /* The body below loses this much to its scrollbar; give it up here too or the
     seven columns drift apart on the way to 日曜. Measured per platform in
     app.js — 0 where scrollbars are an overlay. */
  padding-right: var(--sbw, 0px);
}
.w-hd {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  padding: 5px 0 6px;
  border-left: var(--hair) solid var(--sep);
  transition: background 0.12s;
}
.w-hd:hover { background: var(--bg-hover); }
.w-hw { font-size: var(--t-xs); color: var(--label-2); }
.w-hn { font-size: var(--t-xl); font-weight: 700; line-height: 21px; font-variant-numeric: tabular-nums; letter-spacing: -0.02em; }
.w-hd.sat .w-hw, .w-hd.sat .w-hn { color: var(--blue); }
.w-hd.sun .w-hw, .w-hd.sun .w-hn { color: var(--red); }
.w-hd.today .w-hn {
  background: var(--accent);
  color: var(--accent-fg);
  border-radius: var(--r-lg);
  min-width: 22px;
  height: 22px;
  line-height: 22px;
  text-align: center;
}

.w-gut {
  font-size: var(--t-xs);
  color: var(--label-3);
  display: grid;
  place-items: center;
  text-align: center;
}

.w-allday {
  display: grid;
  grid-template-columns: var(--gut) repeat(7, 1fr);
  flex: none;
  max-height: 84px;
  overflow-y: auto;
  /* Reserve the gutter whether or not it is scrolling, so this row does not
     jump a column sideways the moment a fourth all-day event shows up. */
  scrollbar-gutter: stable;
  border-bottom: var(--hair) solid var(--sep-strong);
  background: var(--bg-side);
}
.w-ad-cell {
  border-left: var(--hair) solid var(--sep);
  padding: 2px;
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.w-ad-cell.today { background: var(--accent-wash); }
.w-ad {
  width: 100%;
  display: block;
  font-size: var(--t-xs);
  line-height: 15px;
  /* colour set by fill() */
  border-radius: var(--r-sm);
  padding: 0 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-weight: 400;
}

.w-scroll { flex: 1; overflow-y: auto; min-height: 0; }
.w-grid { display: grid; grid-template-columns: var(--gut) repeat(7, 1fr); position: relative; }

.w-gut-col { position: relative; }
.w-hour {
  height: var(--hour-h);
  font-size: var(--t-xs);
  color: var(--label-3);
  text-align: right;
  padding-right: 6px;
  /* sit on the line, not under it */
  transform: translateY(-6px);
  font-variant-numeric: tabular-nums;
}

.w-col { position: relative; border-left: var(--hair) solid var(--sep); min-width: 0; }
.w-col.today { background: var(--accent-wash); }
.w-slot { height: var(--hour-h); border-top: var(--hair) solid var(--sep); }
.w-slot:first-child { border-top: 0; }

.w-ev {
  position: absolute;
  border-radius: var(--r-sm);
  padding: 1px 4px;
  /* colour set by fill() */
  overflow: hidden;
  font-size: var(--t-xs);
  line-height: 14px;
  box-shadow: 0 0 0 var(--hair) rgba(0, 0, 0, 0.16);
  transition: filter 0.12s, transform 0.12s var(--ease);
}
.w-ev:hover { filter: brightness(1.1); transform: scale(1.015); z-index: 2; }
.w-ev-t { font-variant-numeric: tabular-nums; opacity: 0.85; font-size: var(--t-xs); }
.w-ev-n { font-weight: 400; overflow: hidden; text-overflow: ellipsis; }

/* --- command palette ---------------------------------------------------- */

.scrim {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.28);
  display: grid;
  place-items: start center;
  padding-top: 12vh;
  z-index: 50;
  animation: fade 0.12s ease;
}
@keyframes fade { from { opacity: 0; } }

.palette {
  width: min(620px, 92vw);
  max-height: 62vh;
  background: var(--bg-elev);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: pop 0.16s cubic-bezier(0.32, 0.72, 0, 1);
}
@keyframes pop { from { transform: translateY(-6px) scale(0.985); opacity: 0; } }

.palette input {
  border: 0;
  outline: 0;
  background: transparent;
  padding: 14px 16px;
  font-size: var(--t-lg);
  border-bottom: var(--hair) solid var(--sep);
  user-select: text;
}

.p-list { overflow-y: auto; padding: 5px; }
.p-empty { padding: 22px; text-align: center; color: var(--label-3); font-size: var(--t-sm); }

.p-sec {
  font-size: var(--t-xs);
  font-weight: 700;
  color: var(--label-3);
  padding: 7px 11px 3px;
  letter-spacing: 0.03em;
}

.p-item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 11px;
  border-radius: var(--r-md);
  width: 100%;
  text-align: left;
  font-size: var(--t-md);
}
.p-item.sel { background: var(--accent); color: var(--accent-fg); }
.p-item.sel .p-sub, .p-item.sel .p-when { color: var(--accent-fg); opacity: 0.8; }
.p-item .p-ic { display: grid; place-items: center; width: 16px; flex: none; color: var(--label-2); }
.p-item.sel .p-ic { color: var(--accent-fg); }
.p-item .p-main { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.p-item .p-sub { color: var(--label-2); font-size: var(--t-sm); }
.p-item .p-when {
  color: var(--label-2); font-size: var(--t-sm); flex: none;
  font-variant-numeric: tabular-nums;
}
/* A dot, not a rail. See .ev .dot. */
.p-item .p-dot { width: 7px; height: 7px; border-radius: var(--r-sm); flex: none; margin: 0 4px; box-shadow: inset 0 0 0 var(--hair) rgba(0,0,0,0.2); }

/* --- event detail ------------------------------------------------------- */

.d-scrim { position: fixed; inset: 0; z-index: 45; }

.d-card {
  position: fixed;
  background: var(--bg-elev);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
  padding: 12px 14px 13px;
  display: flex;
  flex-direction: column;
  gap: 7px;
  /* Never taller than the window: openDetail() clamps the card to both edges,
     which it can only honour if the card is clampable in the first place. The
     note is the only part that grows without bound, and it already scrolls. */
  max-height: calc(100vh - 16px);
  /* Grows out of the row you clicked — see transformOrigin in openDetail(). */
  opacity: 0;
  transform: scale(0.94);
  transition: opacity 0.14s ease, transform 0.2s var(--ease);
}
.d-card.in { opacity: 1; transform: scale(1); }

/* The colour lives on a dot beside the title, matching the agenda row this
   card grew out of. It used to be a 3px stripe down the left edge — the shape
   two separate write-ups name as the most reliable "generated UI" tell, and
   which here also indented the whole card by 9px for one bar of colour. */
.d-head { display: grid; grid-template-columns: 9px 1fr; gap: 8px; align-items: start; }
.d-head::before {
  content: '';
  width: 9px;
  height: 9px;
  margin-top: 6px;
  border-radius: var(--r-pill);
  background: var(--d-accent, var(--accent));
  box-shadow: inset 0 0 0 var(--hair) rgba(0, 0, 0, 0.2);
}
.d-title { font-size: var(--t-lg); font-weight: 700; line-height: 21px; overflow-wrap: anywhere; }
.d-by {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-top: 3px;
  font-size: var(--t-xs);
  color: var(--label-2);
}
.d-by .acct-av { width: 16px; height: 16px; border-radius: var(--r-md); font-size: var(--t-xs); }

.d-row { display: grid; grid-template-columns: 16px 1fr; gap: 9px; align-items: start; font-size: var(--t-sm); }
.d-row.act {
  width: 100%;
  color: var(--accent);
  border-radius: var(--r-sm);
  margin: -2px -4px;
  padding: 2px 4px;
  transition: background 0.12s;
}
.d-row.act:hover { background: var(--bg-hover); }
/* The icon column: fixed box so every row's label starts on the same x,
   whatever the glyph. Emoji could never do this — they all had different
   widths. */
.d-ic { display: grid; place-items: center; height: 19px; color: var(--label-3); }
.d-k { color: var(--label-2); font-size: var(--t-sm); }
.d-v { overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }

.d-cl { display: flex; flex-direction: column; gap: 2px; max-height: 150px; overflow-y: auto; }
.d-cl-i { display: flex; gap: 6px; font-size: var(--t-sm); line-height: 18px; align-items: baseline; }
.d-cl-b { color: var(--label-3); flex: none; display: grid; place-items: center; height: 18px; }
.d-cl-i.on { color: var(--label-2); text-decoration: line-through; }

.d-note {
  margin-top: 2px;
  padding: 7px 9px;
  background: var(--bg-hover);
  border-radius: var(--r-md);
  font-size: var(--t-sm);
  line-height: 19px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  max-height: 180px;
  overflow-y: auto;
  user-select: text;
}

/* --- comments ----------------------------------------------------------- */

.d-cmt {
  display: flex;
  flex-direction: column;
  gap: 7px;
  margin-top: 3px;
  padding-top: 9px;
  border-top: var(--hair) solid var(--sep);
  /* The thread is the part that grows without bound, so it's the part that
     gives: everything else in the card keeps its height and this shrinks. */
  min-height: 0;
}
.d-feed {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 190px;
  /* `flex: 1 1 auto` + `min-height: 0` is what actually lets this shrink below
     its content on a short window. Without the min-height a flex item refuses
     to go under min-content, and the card would push its own compose box —
     the one thing here you have to be able to reach — off the screen. */
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  user-select: text;
}
.d-fmsg {
  display: flex;
  gap: 6px;
  align-items: baseline;
  font-size: var(--t-xs);
  color: var(--label-3);
  padding: 2px 0;
}
.d-fi { display: grid; grid-template-columns: 22px 1fr auto; gap: 7px; align-items: start; }
/* System rows aren't from a person, so they don't get a face — the avatar
   column collapses and the line runs full width, quieter than what was said. */
.d-fi.sys {
  display: flex;
  gap: 6px;
  align-items: baseline;
  font-size: var(--t-xs);
  color: var(--label-3);
}
.d-fi .acct-av { width: 22px; height: 22px; border-radius: var(--r-pill); font-size: var(--t-xs); }
.d-fb { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.d-fw { display: flex; gap: 6px; align-items: baseline; }
.d-fn { font-size: var(--t-xs); font-weight: 700; color: var(--label-2); }
.d-ft {
  font-size: var(--t-xs);
  color: var(--label-3);
  font-variant-numeric: tabular-nums;
  flex: none;
}
.d-fx {
  font-size: var(--t-sm);
  line-height: 19px;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* 編集/削除 on your own comments. Hidden until you reach for the row — but
   `:focus-within` is not optional here: without it these stay invisible while
   holding focus, and Tab would appear to send focus nowhere. */
.d-fa { display: flex; gap: 1px; opacity: 0; transition: opacity 0.12s; }
.d-fi:hover .d-fa,
.d-fi:focus-within .d-fa { opacity: 1; }
.d-fab {
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  border-radius: var(--r-sm);
  color: var(--label-3);
}
.d-fab:hover { background: var(--bg-hover); color: var(--label); }

/* The edit box takes over the comment's own line, so the row doesn't jump. */
.d-fe { display: flex; flex-direction: column; gap: 5px; margin-top: 1px; }
.d-fe-acts { display: flex; gap: 5px; justify-content: flex-end; }
.d-fe-acts .btn { padding: 3px 9px; font-size: var(--t-xs); }

/* Inline confirm, rather than a second modal stacked on this one. */
.d-fd {
  grid-column: 1 / -1;
  display: flex;
  gap: 5px;
  align-items: center;
  margin-top: 5px;
  padding: 6px 7px;
  border-radius: var(--r-md);
  background: var(--bg-hover);
}
.d-fd-q { flex: 1; font-size: var(--t-xs); color: var(--label-2); }
.d-fd .btn { padding: 3px 9px; font-size: var(--t-xs); }

.d-cbox {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 6px;
  align-items: end;
}
.d-cin {
  width: 100%;
  padding: 6px 9px;
  border: var(--hair) solid var(--sep);
  border-radius: var(--r-md);
  background: var(--bg);
  color: var(--label);
  font: inherit;
  font-size: var(--t-sm);
  line-height: 18px;
  resize: none;
  overflow-y: auto;
  transition: border-color 0.12s;
}
.d-cin:focus { outline: none; border-color: var(--accent); }
.d-cin:disabled { opacity: 0.6; }
.d-csend {
  display: grid;
  place-items: center;
  width: 31px;
  height: 31px;
  border-radius: var(--r-md);
  color: #fff;
  background: var(--accent);
  transition: opacity 0.12s;
}
.d-csend:disabled { opacity: 0.35; }
.d-csend:not(:disabled):hover { opacity: 0.85; }

.lnk {
  color: var(--accent);
  font-size: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.d-acts {
  display: flex;
  gap: 6px;
  justify-content: flex-end;
  margin-top: 3px;
  padding-top: 9px;
  border-top: var(--hair) solid var(--sep);
}

/* --- buttons ------------------------------------------------------------ */

.btn {
  padding: 5px 13px;
  border-radius: var(--r-sm);
  font-size: var(--t-sm);
  font-weight: 400;
  color: var(--label);
  background: var(--bg-hover);
  transition: background 0.12s, color 0.12s, filter 0.12s, opacity 0.12s;
}
.btn:hover { background: var(--bg-active); }
.btn:active { transform: scale(0.98); }
.btn.primary { background: var(--accent); color: var(--accent-fg); }
.btn.primary:hover { filter: brightness(1.06); background: var(--accent); }
.btn.danger { color: var(--red); }
.btn.danger:hover { background: var(--red); color: #fff; }
.btn:disabled { opacity: 0.4; pointer-events: none; }
.btn kbd {
  font-family: inherit;
  font-size: var(--t-xs);
  margin-left: 6px;
  opacity: 0.7;
  font-variant-numeric: tabular-nums;
}

/* --- create / edit form ------------------------------------------------- */

.f-scrim { padding-top: 9vh; }

.form {
  width: min(480px, 92vw);
  max-height: 82vh;
  background: var(--bg-elev);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* Same curve and band as the palette and the detail card — one motion
     vocabulary, a hair slower because the surface is bigger. */
  animation: sheet 0.2s var(--ease);
}
@keyframes sheet { from { transform: translateY(-8px) scale(0.985); opacity: 0; } }
/* Leaving mirrors arriving. Every `scrim > card` modal (form, settings, map
   picker) enters with @keyframes sheet; animateModalOut() adds `.out`, and the
   card slides back up as the dim lifts — the node removed when the sheet's exit
   ends. Targeting the scrim's direct child covers each modal's card without
   naming them one by one. (The confirm dialog keeps the instant close: it
   resolves a choice and hands straight off to the next action.) */
.scrim.out { animation: fade-out 0.16s ease forwards; }
.scrim.out > * { animation: sheet-out 0.16s var(--ease) forwards; }
@keyframes fade-out { to { opacity: 0; } }
@keyframes sheet-out { to { transform: translateY(-6px) scale(0.985); opacity: 0; } }

.f-head { padding: 13px 16px 0; }
.f-h-t { font-size: var(--t-md); font-weight: 700; letter-spacing: -0.01em; }

.f-body {
  padding: 11px 16px 14px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.f-warn {
  background: var(--accent-soft);
  color: var(--label);
  border-radius: var(--r-md);
  padding: 7px 10px;
  font-size: var(--t-sm);
  line-height: 17px;
}

.f-title {
  border: 0;
  outline: 0;
  background: transparent;
  font-size: var(--t-xl);
  font-weight: 700;
  letter-spacing: -0.01em;
  padding: 2px 0 8px;
  border-bottom: var(--hair) solid var(--sep);
  user-select: text;
  transition: border-color 0.12s;
}
.f-title:focus { border-color: var(--accent); }
.f-title::placeholder { color: var(--label-3); font-weight: 400; }

.f-row { display: flex; align-items: center; gap: 9px; min-height: 26px; }
.f-row.top { align-items: flex-start; }
.f-k {
  width: 62px;
  flex: none;
  font-size: var(--t-sm);
  color: var(--label-2);
  padding-top: 1px;
}
.f-row.top .f-k { padding-top: 6px; }
.f-fact { font-size: var(--t-sm); }

/* The colour IS the label's meaning, so every one is on screen at once —
   TimeTree makes you open a dropdown to find out what you picked. */
.f-labels { display: flex; gap: 5px; flex-wrap: wrap; }
.f-lb {
  width: 18px;
  height: 18px;
  border-radius: var(--r-sm);
  flex: none;
  box-shadow: inset 0 0 0 var(--hair) rgba(0, 0, 0, 0.18);
  transition: transform 0.14s var(--ease), box-shadow 0.14s;
}
.f-lb:hover { transform: scale(1.14); }
.f-lb.on {
  box-shadow: inset 0 0 0 var(--hair) rgba(0, 0, 0, 0.18),
              0 0 0 2px var(--bg-elev), 0 0 0 3.5px var(--label);
}
.f-lb-n { font-size: var(--t-sm); color: var(--label-2); margin-left: 2px; }

.sw {
  width: 38px;
  height: 22px;
  border-radius: var(--r-lg);
  background: var(--sep-strong);
  position: relative;
  flex: none;
  transition: background 0.2s var(--ease);
}
.sw.on { background: var(--accent); }
.sw-k {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 18px;
  height: 18px;
  border-radius: var(--r-md);
  background: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
  transition: transform 0.2s var(--ease);
}
.sw.on .sw-k { transform: translateX(16px); }

.f-date, .f-time, .f-text, .f-note, .f-sel {
  background: var(--bg-hover);
  border: var(--hair) solid transparent;
  border-radius: var(--r-sm);
  padding: 4px 8px;
  font-size: var(--t-sm);
  color: var(--label);
  outline: 0;
  user-select: text;
  transition: background 0.12s, border-color 0.12s;
}
.f-date:focus, .f-time:focus, .f-text:focus, .f-note:focus, .f-sel:focus {
  border-color: var(--accent);
  background: var(--bg);
}
.f-date { font-variant-numeric: tabular-nums; }
.f-time { font-variant-numeric: tabular-nums; width: 88px; }
.f-text { flex: 1; min-width: 0; }
.f-note { flex: 1; min-width: 0; resize: vertical; line-height: 18px; font-family: inherit; }
.f-sel { flex: 1; min-width: 0; cursor: default; }
.f-date::-webkit-calendar-picker-indicator,
.f-time::-webkit-calendar-picker-indicator { opacity: 0.5; }

/* All-day means the clock is not part of the answer — so remove the control
   rather than leaving a disabled one to be reasoned about. */
.form.allday .f-time { display: none; }

/* Same rule for repeats: "which weekdays" is only a question for 毎週, and an
   end date is only a question if it repeats at all. */
.f-byday, .f-until { display: none; }
.form.repeats .f-until { display: flex; }
.form.weekly .f-byday { display: flex; }

/* Reminders and members are both "a set you add to", so they share a shape:
   the current picks read left to right, the way to add sits after them. */
.f-alerts { display: flex; gap: 4px; flex-wrap: wrap; }
.f-alerts:empty { display: none; }
.f-chip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 3px 2px 8px;
  border-radius: var(--r-lg);
  background: var(--accent-soft);
  color: var(--label);
  font-size: var(--t-sm);
  white-space: nowrap;
}
.f-chip-x {
  color: var(--label-2);
  width: 16px;
  height: 16px;
  border-radius: var(--r-md);
  display: grid;
  place-items: center;
  transition: background 0.12s, color 0.12s;
}
.f-chip-x:hover { background: var(--red); color: #fff; }
.f-add { flex: none; width: auto; min-width: 96px; }

.f-cl { display: flex; flex-direction: column; gap: 3px; flex: 1; min-width: 0; }
.f-cl-i { display: flex; align-items: center; gap: 5px; }
.f-cl-c {
  width: 17px;
  height: 17px;
  border-radius: var(--r-sm);
  flex: none;
  border: 1.5px solid var(--sep-strong);
  font-size: var(--t-xs);
  line-height: 1;
  color: transparent;
  display: grid;
  place-items: center;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.f-cl-c:hover { border-color: var(--accent); }
.f-cl-c.on { background: var(--accent); border-color: var(--accent); color: var(--accent-fg); }
.f-cl-t {
  flex: 1;
  min-width: 0;
  background: none;
  border: 0;
  border-bottom: var(--hair) solid transparent;
  outline: 0;
  padding: 2px 2px;
  font-size: var(--t-sm);
  user-select: text;
  transition: border-color 0.12s;
}
.f-cl-t:focus { border-bottom-color: var(--accent); }
.f-cl-i:has(.f-cl-c.on) .f-cl-t { color: var(--label-2); text-decoration: line-through; }
.f-cl-x {
  opacity: 0;
  color: var(--label-3);
  width: 18px;
  height: 18px;
  border-radius: var(--r-md);
  flex: none;
  display: grid;
  place-items: center;
  transition: opacity 0.12s, background 0.12s, color 0.12s;
}
.f-cl-i:hover .f-cl-x { opacity: 1; }
.f-cl-x:hover { background: var(--red); color: #fff; }
.f-cl-add {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: var(--t-sm);
  color: var(--label-2);
  text-align: left;
  padding: 3px 2px;
  border-radius: var(--r-sm);
  align-self: flex-start;
  transition: color 0.12s;
}
.f-cl-add:hover { color: var(--accent); }

.f-members { display: flex; gap: 4px; flex-wrap: wrap; }
.f-mem {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px 2px 2px;
  border-radius: var(--r-lg);
  background: var(--bg-hover);
  font-size: var(--t-sm);
  border: 1px solid transparent;
  transition: background 0.12s, border-color 0.12s, opacity 0.12s;
  opacity: 0.55;
}
.f-mem:hover { background: var(--bg-active); opacity: 0.85; }
.f-mem.on { opacity: 1; border-color: var(--accent); background: var(--accent-soft); }
.f-mem .acct-av { box-shadow: none; }
.f-mem-n { max-width: 96px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.f-days { display: flex; gap: 3px; }
.f-day {
  width: 26px;
  height: 24px;
  border-radius: var(--r-sm);
  font-size: var(--t-sm);
  font-weight: 400;
  color: var(--label-2);
  background: var(--bg-hover);
  transition: background 0.12s, color 0.12s;
}
.f-day.sat { color: var(--blue); }
.f-day.sun { color: var(--red); }
.f-day:hover { background: var(--bg-active); }
.f-day.on { background: var(--accent); color: var(--accent-fg); }
.f-day.on.sat, .f-day.on.sun { color: var(--accent-fg); }
.f-until .mini-btn { flex: none; }

.f-span {
  font-size: var(--t-xs);
  color: var(--label-2);
  padding-left: 71px;
  font-variant-numeric: tabular-nums;
  min-height: 0;
}
.f-span:empty { display: none; }

.f-foot {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 10px 14px;
  border-top: var(--hair) solid var(--sep);
  background: var(--bg-side);
  flex: none;
}
.f-err { font-size: var(--t-sm); color: var(--red); }

/* --- map ---------------------------------------------------------------- */

.f-pin {
  display: flex;
  align-items: center;
  gap: var(--s1);
  flex: none;
  padding: 4px var(--s2);
  border-radius: var(--r-sm);
  background: var(--bg-hover);
  color: var(--label-2);
  font-size: var(--t-xs);
  transition: background 0.12s, color 0.12s;
}
.f-pin:hover { background: var(--bg-active); color: var(--label); }
.f-pin.on { background: var(--accent-soft); color: var(--accent); }

.mp-scrim { padding-top: 6vh; }

.mp-card {
  width: min(640px, 94vw);
  background: var(--bg-elev);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: sheet 0.2s var(--ease);
}

.mp-bar { padding: var(--s2) var(--s4) var(--s1); display: flex; }
.mp-bar .f-text { flex: 1; }

.mp-results { max-height: 168px; overflow-y: auto; padding: 0 var(--s3); }
.mp-results:empty { display: none; }
.mp-r {
  display: flex;
  flex-direction: column;
  gap: 1px;
  width: 100%;
  padding: var(--s1) var(--s2);
  border-radius: var(--r-sm);
  transition: background 0.12s;
}
.mp-r:hover { background: var(--bg-hover); }
.mp-r-n { font-size: var(--t-sm); }
.mp-r-a {
  font-size: var(--t-xs);
  color: var(--label-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.mp-box {
  position: relative;
  height: 340px;
  margin: var(--s2) var(--s4);
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--bg-hover);
  cursor: grab;
  touch-action: none;
}
.mp-box.grabbing { cursor: grabbing; }
.mp-tiles { position: absolute; top: 0; left: 0; display: grid; }
.mp-t { display: block; }

/* The pin doesn't move; the map does. So there's no "did I click exactly on
   it" — whatever is under the centre is what you're choosing. */
.mp-pin {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 2;
  transform: translate(-50%, -100%);
  color: var(--red);
  pointer-events: none;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
}
.mp-pin .i { fill: var(--bg-elev); }

.mp-coord {
  font-size: var(--t-xs);
  color: var(--label-3);
  font-variant-numeric: tabular-nums;
}

/* --- settings ----------------------------------------------------------- */

.st-scrim { padding-top: 8vh; }

.settings {
  width: min(520px, 92vw);
  max-height: 82vh;
  background: var(--bg-elev);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: sheet 0.2s var(--ease);
}

.st-body {
  padding: var(--s2) var(--s4) var(--s4);
  overflow-y: auto;
}

.st-sec {
  font-size: var(--t-xs);
  font-weight: 700;
  color: var(--label-3);
  letter-spacing: 0.03em;
  padding: var(--s4) 0 var(--s1);
}
.st-sec:first-child { padding-top: var(--s2); }

.st-row {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding: var(--s2) 0;
  border-top: var(--hair) solid var(--sep);
}
.st-box { flex: 1; min-width: 0; }
.st-l { font-size: var(--t-md); }
.st-s { font-size: var(--t-xs); color: var(--label-2); line-height: 1.5; margin-top: 1px; }
.st-row .f-sel { flex: none; min-width: 160px; }

/* Export is a list of destinations, not four abbreviations in a row. "JSON"
   with no explanation is a developer's shelf; "生データ" is a sentence. */
.st-exports { display: flex; flex-direction: column; gap: 2px; margin-top: var(--s1); }
.st-exp {
  display: flex;
  align-items: baseline;
  gap: var(--s2);
  padding: var(--s2);
  border-radius: var(--r-sm);
  width: 100%;
  color: var(--label-2);
  transition: background 0.12s;
}
.st-exp:hover { background: var(--bg-hover); }
.st-exp .i { align-self: center; }
.st-exp-t { font-size: var(--t-md); color: var(--label); min-width: 84px; }
.st-exp-s { font-size: var(--t-xs); }

.st-note { font-size: var(--t-xs); color: var(--label-3); padding: var(--s2) var(--s2) 0; line-height: 1.6; }
.st-about {
  font-size: var(--t-sm);
  line-height: 1.6;
  padding-top: var(--s1);
  display: flex;
  flex-direction: column;
  gap: var(--s1);
}
/* Prose, not a label: this is the one place in the app with something to read
   rather than scan, so it gets the leading that CJK reading actually wants,
   and proportional kana is turned off because palt is for tight labels. */
.st-about .st-s { line-height: 1.75; font-feature-settings: normal; }

/* --- confirm ------------------------------------------------------------ */

.cf-scrim { place-items: center; padding-top: 0; z-index: 55; }

.confirm {
  width: min(340px, 88vw);
  background: var(--bg-elev);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
  padding: 17px 18px 13px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  animation: sheet 0.18s var(--ease);
}
.cf-t { font-size: var(--t-md); font-weight: 700; }
.cf-b { font-size: var(--t-sm); line-height: 19px; color: var(--label-2); overflow-wrap: anywhere; }
.cf-f { display: flex; justify-content: flex-end; gap: 6px; margin-top: 8px; }

/* Scope choices are the content of the dialog, not a footer of it: each is a
   full sentence about what will change, stacked in widening order. */
.cf-opts { display: flex; flex-direction: column; gap: 3px; margin-top: 9px; }
.cf-opt {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 8px 11px;
  border-radius: var(--r-md);
  text-align: left;
  transition: background 0.12s;
}
.cf-opt:hover, .cf-opt:focus-visible { background: var(--bg-hover); outline: 0; }
.cf-opt:focus-visible { box-shadow: inset 0 0 0 1.5px var(--accent); }
.cf-opt-t { font-size: var(--t-md); font-weight: 400; }
.cf-opt-s { font-size: var(--t-xs); color: var(--label-2); }
.cf-opt.danger .cf-opt-t { color: var(--red); }

/* --- misc --------------------------------------------------------------- */

.center {
  height: 100%;
  display: grid;
  place-items: center;
  text-align: center;
  gap: 14px;
}
.center .stack { display: flex; flex-direction: column; align-items: center; gap: 12px; max-width: 380px; }
.center h2 { font-size: var(--t-xl); font-weight: 700; }
.center p { color: var(--label-2); font-size: var(--t-md); line-height: 20px; }

.cta {
  background: var(--accent);
  color: var(--accent-fg);
  padding: 8px 18px;
  border-radius: var(--r-md);
  font-size: var(--t-md);
  font-weight: 700;
}
.cta:hover { filter: brightness(1.06); }
.cta:active { transform: scale(0.98); }

.spinner {
  width: 22px; height: 22px; border-radius: 50%;
  border: 2px solid var(--sep);
  border-top-color: var(--accent);
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

.toast {
  position: fixed;
  bottom: 18px;
  left: 50%;
  transform: translateX(-50%) translateY(8px);
  background: var(--bg-elev);
  border: var(--hair) solid var(--sep);
  color: var(--label);
  padding: 8px 15px;
  border-radius: var(--r-md);
  font-size: var(--t-sm);
  box-shadow: var(--shadow);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s, transform 0.2s cubic-bezier(0.32, 0.72, 0, 1);
  z-index: 60;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-thumb {
  background: var(--sep-strong);
  border-radius: var(--r-sm);
  border: 3px solid transparent;
  background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--label-3); background-clip: content-box; }
::-webkit-scrollbar-track { background: transparent; }

/* --- motion ------------------------------------------------------------- */

/* Curve and durations follow iOS: a decelerating cubic that starts fast and
 * settles, in the 180–260ms band. Long enough to read as movement, short
 * enough that a held arrow key still feels responsive rather than queued. */
:root {
  --ease: cubic-bezier(0.32, 0.72, 0, 1);
  --dur: 240ms;
}

/* Only the content pane travels. The toolbar and sidebar are the fixed frame
 * you're looking *through*, so they must not animate with it. Naming .main
 * pulls it out of the root snapshot and lets it run its own keyframes. */
.main { view-transition-name: main; }
.tb-title { view-transition-name: tbtitle; }

::view-transition-old(root),
::view-transition-new(root) { animation: none; }

::view-transition-group(main) { animation-duration: var(--dur); }

/* Direction encodes travel along the timeline: next month enters from the
 * right, previous from the left. Getting this backwards is worse than no
 * animation at all — it fights the user's mental model of where they are. */
html[data-nav="next"]::view-transition-old(main) { animation: pane-out-left var(--dur) var(--ease) both; }
html[data-nav="next"]::view-transition-new(main) { animation: pane-in-right var(--dur) var(--ease) both; }
html[data-nav="prev"]::view-transition-old(main) { animation: pane-out-right var(--dur) var(--ease) both; }
html[data-nav="prev"]::view-transition-new(main) { animation: pane-in-left var(--dur) var(--ease) both; }

/* Switching agenda↔month isn't travel — it's a change of lens. So: no slide,
 * just a short cross-dissolve with a hair of scale. */
html[data-nav="fade"]::view-transition-old(main) { animation: lens-out 130ms ease both; }
html[data-nav="fade"]::view-transition-new(main) { animation: lens-in 200ms var(--ease) both; }

/* The month label swaps with the pane, so let it cross-dissolve in place. */
::view-transition-old(tbtitle),
::view-transition-new(tbtitle) { animation-duration: 180ms; }

/* Theme change dissolves the entire window — including the frame, which is
 * the one case where the toolbar and sidebar *should* animate. */
html[data-nav="theme"]::view-transition-old(root),
html[data-nav="theme"]::view-transition-old(main) { animation: x-out 280ms ease both; }
html[data-nav="theme"]::view-transition-new(root),
html[data-nav="theme"]::view-transition-new(main) { animation: x-in 280ms ease both; }

@keyframes x-out { to { opacity: 0; } }
@keyframes x-in { from { opacity: 0; } }

@keyframes pane-out-left  { to   { transform: translateX(-4%); opacity: 0; } }
@keyframes pane-in-right  { from { transform: translateX(4%);  opacity: 0; } }
@keyframes pane-out-right { to   { transform: translateX(4%);  opacity: 0; } }
@keyframes pane-in-left   { from { transform: translateX(-4%); opacity: 0; } }
@keyframes lens-out { to   { opacity: 0; transform: scale(0.994); } }
@keyframes lens-in  { from { opacity: 0; transform: scale(1.006); } }

/* The segmented thumb slides between options instead of blinking across. */
.seg button.on { view-transition-name: segthumb; }
::view-transition-group(segthumb) { animation-duration: 220ms; animation-timing-function: var(--ease); }

@media (prefers-reduced-motion: reduce) {
  *, ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}
