/* ============ Qalendar — the agenda side of Quriou Mail ============
   The palette is Quriou Mail's, deliberately: two apps, one account, one
   look. Contrast values here are the ones the mail app was corrected to,
   so both pass AA on the same measurements. */

:root[data-theme="light"] {
  --bg: #f6f4ef;
  --bg-panel: #ffffff;
  --bg-hover: #eceae3;
  --text: #1c2b2c;
  --text-dim: #5f6f70;        /* 5.9:1 on --bg */
  --border: #dfe3e0;
  --border-strong: #7b8888;   /* 3.34:1 on --bg — #8b9a9a measured 2.66 and failed */
  --accent: #6b551f;          /* matt gold, dark enough to read as text: 6.49:1 */
  --accent-strong: #6b551f;   /* white on this: 7.13:1 */
  --accent-soft: #f0e9d8;
  --shadow: 0 2px 12px rgba(30, 45, 45, 0.08);
}
:root[data-theme="dark"] {
  /* The identity: dark teal ground, matt gold on it. Gold is a light colour,
     so it works as the accent HERE, where the mail app's teal cannot -- and
     the two apps end up as inverses of each other rather than copies. Every
     value below was measured, not chosen by eye. */
  --bg: #101d1f;
  --bg-panel: #16292c;
  --bg-hover: #1d3438;
  --text: #e8eceb;            /* 14.48:1 on --bg */
  --text-dim: #93a3a3;        /* 6.58:1 on --bg */
  --border: #223a3d;
  --border-strong: #5c7173;   /* 3.34:1 on --bg */
  --accent: #c9a34e;          /* matt, not brassy: 7.25:1 on --bg, 6.36:1 on panel */
  --accent-strong: #7d6329;   /* white on this: 5.69:1 */
  --accent-soft: #2a2415;
  --shadow: 0 2px 12px rgba(0, 0, 0, 0.45);
}
:root { color-scheme: light dark; }
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"]  { color-scheme: dark; }

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

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  background: var(--bg);
  color: var(--text);
  height: 100dvh;
  overflow: hidden;
  -webkit-tap-highlight-color: transparent;
}

.hidden { display: none !important; }
.muted { color: var(--text-dim); font-size: 13px; padding: 14px 2px; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }

button, input, select, textarea { font-family: inherit; }
input, select, textarea {
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: 10px;
  padding: 10px 11px; font-size: 16px;   /* 16px keeps iOS from zooming on focus */
  min-height: 44px; width: 100%;
}
textarea { min-height: 76px; resize: vertical; }

.btn {
  border: 1px solid var(--border-strong); border-radius: 10px;
  background: transparent; color: var(--text);
  padding: 10px 16px; font-size: 14px; cursor: pointer;
  min-height: 44px; min-width: 44px;
}
.btn-primary { background: var(--accent-strong); border-color: var(--accent-strong); color: #fff; font-weight: 600; }
.btn-ghost { color: var(--text-dim); }
.btn-ghost:hover { color: var(--text); border-color: var(--accent); }

.icon-btn {
  border: none; background: transparent; color: var(--text-dim);
  min-width: 44px; min-height: 44px; border-radius: 10px;
  font-size: 19px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  text-decoration: none;
}
.icon-btn:hover { background: var(--bg-hover); color: var(--text); }

/* ============ signed-out gate ============ */
.gate { height: 100dvh; display: grid; place-items: center; padding: 24px; }
.gate-card {
  max-width: 340px; text-align: center;
  background: var(--bg-panel); border: 1px solid var(--border);
  border-radius: 16px; padding: 30px 24px; box-shadow: var(--shadow);
}
.gate-mark {
  width: 62px; height: 62px; margin: 0 auto 16px;
  background: var(--accent-strong); color: #fff; border-radius: 16px;
  font-size: 32px; font-weight: 700; line-height: 62px;
}
.gate-card h1 { font-size: 22px; margin-bottom: 6px; }
.gate-sub { color: var(--text-dim); font-size: 13px; margin-bottom: 18px; }
.gate-msg { font-size: 13px; margin-bottom: 16px; }
.gate-card .btn { display: inline-block; text-decoration: none; }
.gate-note { color: var(--text-dim); font-size: 12px; margin-top: 14px; }

/* ============ shell ============ */
.app { display: flex; flex-direction: column; height: 100dvh; }

.topbar {
  display: flex; align-items: center; gap: 4px;
  padding: 6px max(10px, env(safe-area-inset-right)) 6px max(10px, env(safe-area-inset-left));
  background: var(--bg-panel); border-bottom: 1px solid var(--border);
  flex: none;
}
.month-title {
  font-size: 16px; font-weight: 700; flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: center;
}
.pill {
  border: 1px solid var(--border-strong); background: transparent; color: var(--text-dim);
  border-radius: 20px; padding: 0 12px; min-height: 44px; min-width: 44px;
  font-size: 13px; cursor: pointer;
}
.pill:hover { color: var(--text); border-color: var(--accent); }
.seg { display: flex; border: 1px solid var(--border); border-radius: 10px; padding: 3px; gap: 2px; }
.seg button {
  border: none; background: transparent; color: var(--text-dim);
  padding: 0 10px; min-height: 44px; min-width: 44px;
  border-radius: 8px; font-size: 13px; cursor: pointer;
}
.seg button.is-on { background: var(--accent-strong); color: #fff; font-weight: 600; }

.body {
  flex: 1; overflow-y: auto;
  padding: 0 max(12px, env(safe-area-inset-right)) calc(90px + env(safe-area-inset-bottom)) max(12px, env(safe-area-inset-left));
}

/* ============ month grid ============ */
.dow, .grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.dow {
  padding: 10px 0 6px; position: sticky; top: 0; background: var(--bg); z-index: 2;
}
.dow span { text-align: center; font-size: 11px; color: var(--text-dim); text-transform: uppercase; letter-spacing: .06em; }

.cell {
  aspect-ratio: 1 / 1; min-height: 44px;
  border: 1px solid transparent; border-radius: 10px;
  background: var(--bg-panel); color: var(--text);
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  cursor: pointer; font-size: 14px; padding: 2px;
}
.cell.empty { background: transparent; cursor: default; }
.cell:hover { background: var(--bg-hover); }
.cell.is-today { border-color: var(--accent); font-weight: 700; }
.cell.is-sel { background: var(--accent-soft); border-color: var(--accent); }
.cell .n { line-height: 1; }
.cell .dots { display: flex; gap: 3px; height: 6px; }
.cell .dots i { width: 5px; height: 5px; border-radius: 50%; display: block; }

/* ============ day panel + agenda rows ============ */
.day-panel { margin-top: 16px; }
.day-head {
  font-size: 13px; font-weight: 700; color: var(--text-dim);
  text-transform: uppercase; letter-spacing: .05em;
  margin: 16px 0 8px; display: flex; align-items: center; gap: 8px;
}
.chip {
  background: var(--accent-strong); color: #fff; border-radius: 10px;
  padding: 2px 8px; font-size: 10px; letter-spacing: 0; text-transform: none;
}
.ag-sep { font-size: 12px; color: var(--text-dim); margin: 26px 0 4px; text-transform: uppercase; letter-spacing: .05em; }
.ag-day.is-past { opacity: .62; }

.item {
  display: flex; align-items: center; gap: 10px;
  background: var(--bg-panel); border: 1px solid var(--border);
  border-radius: 10px; padding: 10px 10px 10px 0;
  margin-bottom: 6px; overflow: hidden; min-height: 56px;
}
.item .bar { width: 4px; align-self: stretch; flex: none; border-radius: 0 3px 3px 0; }
.it-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }
.it-title { font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.it-sub { font-size: 12px; color: var(--text-dim); }
.bell { font-size: 13px; flex: none; opacity: .9; }
.bell.fired { opacity: .4; }
.add-here { width: 100%; margin-top: 8px; }

/* ============ new-event button ============ */
.fab {
  position: fixed; z-index: 20;
  right: calc(16px + env(safe-area-inset-right));
  bottom: calc(16px + env(safe-area-inset-bottom));
  width: 56px; height: 56px; border-radius: 50%; border: none;
  background: var(--accent-strong); color: #fff;
  font-size: 28px; line-height: 1; cursor: pointer;
  box-shadow: 0 4px 16px rgba(0, 0, 0, .35);
}
.fab:active { transform: scale(.94); }

/* ============ editor sheet ============ */
.sheet-backdrop {
  position: fixed; inset: 0; z-index: 40;
  background: rgba(0, 0, 0, .45);
  display: flex; align-items: flex-end; justify-content: center;
}
.sheet {
  background: var(--bg-panel); width: 100%; max-width: 520px;
  border-radius: 16px 16px 0 0; border: 1px solid var(--border);
  max-height: 92dvh; display: flex; flex-direction: column;
  animation: up .2s ease;
}
@keyframes up { from { transform: translateY(18%); opacity: .4; } to { transform: none; opacity: 1; } }
.sheet-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 10px 8px 10px 16px; border-bottom: 1px solid var(--border);
  font-weight: 700; flex: none;
}
.sheet-body {
  padding: 14px max(16px, env(safe-area-inset-right)) calc(16px + env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
  overflow-y: auto;
  display: flex; flex-direction: column; gap: 12px;
}
.fld { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.fld > span { font-size: 12px; color: var(--text-dim); text-transform: uppercase; letter-spacing: .05em; }
.fld-row { display: flex; gap: 10px; }
.fld-row .fld { flex: 1; }
.hint { font-size: 12px; color: var(--text-dim); margin-top: -6px; }
.hint.warn { color: var(--accent); }
.colors { display: flex; flex-wrap: wrap; gap: 6px; }
.swatch {
  width: 44px; height: 44px; border-radius: 50%; border: 2px solid transparent;
  padding: 10px; background-clip: content-box; background-origin: content-box;
  cursor: pointer;
}
.swatch.sel { border-color: var(--text); }
.sheet-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 4px; }
.status { font-size: 12px; color: var(--text-dim); }

/* ============ toast ============ */
.toast {
  position: fixed; z-index: 60; left: 50%; transform: translateX(-50%);
  bottom: calc(84px + env(safe-area-inset-bottom));
  background: var(--text); color: var(--bg);
  padding: 10px 18px; border-radius: 10px; font-size: 13px; font-weight: 600;
  box-shadow: 0 6px 24px rgba(0, 0, 0, .35); max-width: 88vw; text-align: center;
}

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

/* roomier where there is width: the month keeps its square cells and the day
   panel moves alongside it instead of underneath */
@media (min-width: 860px) {
  .body { max-width: 1100px; margin: 0 auto; width: 100%; }
  #month-view { display: grid; grid-template-columns: 1fr 340px; gap: 24px; align-items: start; }
  #month-view .dow { grid-column: 1; }
  #month-view .grid { grid-column: 1; }
  .day-panel { grid-column: 2; grid-row: 1 / span 2; margin-top: 10px; }
}

/* ---- the month name is a heading, not a top-bar item (v3) ----
   Sharing one row with six controls left it a few pixels wide at 412px.
   As a sticky heading it gets the width it needs and the bar stays one row. */
.month-title {
  position: sticky; top: 0; z-index: 3;
  background: var(--bg);
  font-size: 18px; font-weight: 700; text-align: left;
  padding: 12px 2px 6px; margin: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.dow { top: 42px; }
@media (min-width: 860px) {
  #month-view .month-title { grid-column: 1 / -1; }
}

/* ---- linked calendars ---- */
.cal-list { list-style: none; display: flex; flex-direction: column; gap: 6px; margin: 4px 0 10px; }
.cal-item {
  display: flex; align-items: center; gap: 10px; min-height: 56px;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: 10px; padding: 8px 8px 8px 0; overflow: hidden;
}
.cal-item .bar { width: 4px; align-self: stretch; flex: none; border-radius: 0 3px 3px 0; }
.cal-item .it-sub { word-break: break-word; }
#cal-url { font-size: 16px; }

/* ---- the top bar after the linked-calendars button was added (v5) ----
   Seven controls did not fit 412px: the document measured 431 wide and the
   right-hand icons sat outside the viewport. The theme toggle leaves the
   phone bar — Qalendar follows the theme Quriou Mail is set to, and both
   apps share the setting — and the gutters tighten. Everything stays on
   desktop, where there is room. */
@media (max-width: 560px) {
  .topbar { gap: 3px; padding: 6px max(8px, env(safe-area-inset-right)) 6px max(8px, env(safe-area-inset-left)); }
  #theme { display: none; }
  .seg { padding: 2px; }
  .seg button { padding: 0 8px; font-size: 13px; }
  .pill { padding: 0 10px; font-size: 13px; }
}
/* long calendar addresses must wrap, not widen the sheet */
.cal-item .it-title { white-space: normal; overflow-wrap: anywhere; }
.sheet, .sheet-body, .cal-list, .cal-item { max-width: 100%; }
/* ============ Around you ============
   Discovery, kept visually distinct from your own calendar: these are
   suggestions from outside, each carrying the source it came from. */
.around { margin-top: 26px; padding-top: 18px; border-top: 1px solid var(--border); }
.around-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
.around-head h2 { font-size: 15px; font-weight: 700; }

/* The headline action, sized like one. It used to be a 40px emoji wedged
   between a text field and a Find button, so the single most useful control
   in the panel was also the least visible. */
.ar-scan {
  display: flex; align-items: center; gap: 12px; width: 100%;
  min-height: 60px; padding: 10px 14px; margin-bottom: 10px;
  border: 1px solid var(--accent-strong); border-radius: 16px;
  background: var(--bg-panel); color: var(--text);
  font-family: inherit; text-align: left; cursor: pointer;
  transition: transform .14s var(--ease-out), box-shadow .18s, border-color .18s;
}
.ar-scan:hover { border-color: var(--accent); box-shadow: 0 4px 18px rgba(0,0,0,.14); }
.ar-scan:active { transform: scale(.99); }
.ar-scan-pin {
  flex: none; display: grid; place-items: center; position: relative;
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--accent-strong); color: #fff;
}
/* the ring is a pseudo-element so the pulse animates transform and opacity
   only -- animating box-shadow on the button itself repaints the whole row */
.ar-scan-pin::after {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  border: 2px solid var(--accent-strong); opacity: 0; transform: scale(1);
}
.ar-scan.is-scanning .ar-scan-pin::after { animation: ar-ping 1.4s var(--ease-out) infinite; }
@keyframes ar-ping {
  0%   { opacity: .7; transform: scale(1); }
  100% { opacity: 0;  transform: scale(1.9); }
}
.ar-scan-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ar-scan-text b { font-size: 15px; font-weight: 650; }
.ar-scan-text small { font-size: 12px; color: var(--text-dim); overflow-wrap: anywhere; }

.around-where { display: flex; gap: 8px; margin-bottom: 10px; }
.around-where input { flex: 1; min-width: 0; }
.around-where .btn { flex: none; }
.around-radius { margin-bottom: 10px; }
.around-radius .seg { display: inline-flex; }
.around-cats { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.ar-chip {
  border: 1px solid var(--border-strong); background: transparent; color: var(--text-dim);
  border-radius: 20px; padding: 0 14px; min-height: 44px; min-width: 44px;
  font-size: 13px; cursor: pointer; font-family: inherit;
}
.ar-chip.is-on { background: var(--accent-strong); border-color: var(--accent-strong); color: #fff; font-weight: 600; }
.ar-chip-all { border-style: dashed; }
.around-list { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }
.ar-card {
  display: flex; align-items: flex-start; gap: 10px;
  background: var(--bg-panel); border: 1px solid var(--border);
  border-radius: 10px; padding: 10px 10px 10px 12px;
}
.ar-when {
  flex: none; width: 62px; display: flex; flex-direction: column; gap: 2px;
  font-size: 12px; color: var(--text-dim); text-align: left;
}
.ar-when b { font-size: 13px; color: var(--text); }
.ar-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.ar-title { font-size: 14px; font-weight: 600; overflow-wrap: anywhere; }
.ar-sub { font-size: 12px; color: var(--text-dim); overflow-wrap: anywhere; }
.ar-detail { font-size: 12px; color: var(--accent); }
.ar-src { font-size: 11px; color: var(--text-dim); text-decoration: none; }
.ar-src:hover { color: var(--accent); text-decoration: underline; }
.ar-add { flex: none; padding: 0 12px; font-size: 13px; }

/* Go / Call / Book / Info. Small, but still a 44px tap target: the padding
   is vertical margin the eye does not see, not a smaller hit area. */
.ar-acts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 5px; }
.ar-act {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 0 12px; border-radius: 10px;
  border: 1px solid var(--border-strong); background: transparent;
  color: var(--text); font-size: 13px; font-weight: 600;
  text-decoration: none; cursor: pointer;
}
.ar-act:hover { border-color: var(--accent); color: var(--accent); }
.ar-act-strong { background: var(--accent-strong); border-color: var(--accent-strong); color: #fff; }
.ar-act-strong:hover { color: #fff; opacity: .92; }
.ar-dist { font-variant-numeric: tabular-nums; }

/* ============ day / week / year views ============ */
.view-select {
  flex: 0 0 auto; width: auto; min-width: 104px; max-width: 130px;
  min-height: 44px; padding: 0 8px; font-size: 13px;
  border-radius: 10px;
}
.dv-nav { display: flex; gap: 8px; margin: 12px 0; }
.dv-nav .btn { flex: 1; }
.wk-day { margin-bottom: 4px; }
.wk-day.is-today .day-head { color: var(--accent); }
.wk-day .day-head { justify-content: flex-start; }
.wk-day .day-head .icon-btn { margin-left: auto; }
.wk-empty { padding: 2px 2px 10px; }

.year-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
@media (min-width: 620px) { .year-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 900px) { .year-grid { grid-template-columns: repeat(4, 1fr); } }
.ym {
  display: flex; flex-direction: column; gap: 8px; align-items: stretch;
  background: var(--bg-panel); border: 1px solid var(--border);
  border-radius: 10px; padding: 10px; cursor: pointer;
  color: var(--text); font-family: inherit; min-height: 44px;
}
.ym:hover { border-color: var(--accent); }
.ym-name { font-size: 13px; font-weight: 600; text-align: left; }
.ym-name b { color: var(--accent); font-weight: 700; }
.ym-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.yc { width: 100%; aspect-ratio: 1; border-radius: 2px; background: var(--bg-hover); display: block; }
.yc.empty { background: transparent; }
.yc.has { background: var(--accent); }
.yc.today { outline: 1.5px solid var(--text); outline-offset: 1px; }

/* ============ reminder chips ============ */
.leads { display: flex; flex-wrap: wrap; gap: 6px; }
.lead-chip {
  border: 1px solid var(--border-strong); background: transparent; color: var(--text-dim);
  border-radius: 20px; padding: 0 12px; min-height: 44px; min-width: 44px;
  font-size: 13px; cursor: pointer; font-family: inherit;
}
.lead-chip.is-on { background: var(--accent-strong); border-color: var(--accent-strong); color: #fff; font-weight: 600; }
#ar-here { flex: none; }

/* ============================================================
   Motion
   ------------------------------------------------------------
   Only transform and opacity are animated, so every one of these
   runs on the compositor and none of them can cause a layout pass.
   Stagger comes from --i, set inline where the markup is built.

   All of it is switched off under prefers-reduced-motion by the rule
   further up this file, which is why nothing here uses !important.
   ============================================================ */

/* the shared curve: a short overshoot, not a bounce */
:root {
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-spring: cubic-bezier(0.34, 1.28, 0.5, 1);
}

@keyframes q-rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes q-pop { from { opacity: 0; transform: scale(0.86); } to { opacity: 1; transform: none; } }
@keyframes q-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes q-sheet { from { transform: translateY(14%); } to { transform: none; } }
@keyframes q-shimmer { from { background-position: -180% 0; } to { background-position: 180% 0; } }

/* --- the view itself arrives --- */
#day-view, #week-view, #month-view, #year-view, #agenda-view {
  animation: q-fade 0.22s var(--ease-out) both;
}

/* --- month grid: cells cascade in, ~9ms apart, capped so a long month
       does not turn into a slow wave --- */
.grid .cell {
  animation: q-pop 0.30s var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 9ms);
  transition: background 0.16s, border-color 0.16s, transform 0.12s var(--ease-spring);
}
.cell:active { transform: scale(0.93); }
.cell.is-today { animation: q-pop 0.30s var(--ease-out) both; }
.cell .dots i { animation: q-pop 0.26s var(--ease-spring) both; animation-delay: calc(var(--i, 0) * 9ms + 120ms); }

/* --- rows arrive in sequence --- */
.item, .ar-card, .cal-item, .wk-day {
  animation: q-rise 0.30s var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 26ms);
}
.item, .ar-card { transition: transform 0.14s var(--ease-out), border-color 0.16s, box-shadow 0.16s; }
.item:hover, .ar-card:hover { transform: translateX(3px); border-color: var(--border-strong); }
.item:active, .ar-card:active { transform: scale(0.99); }

/* --- year: months cascade, their day cells fade after --- */
.ym {
  animation: q-rise 0.34s var(--ease-out) both;
  animation-delay: calc(var(--i, 0) * 34ms);
  transition: transform 0.16s var(--ease-spring), border-color 0.16s, box-shadow 0.16s;
}
.ym:hover { transform: translateY(-2px); box-shadow: var(--shadow); }
.ym:active { transform: scale(0.98); }
.yc.has { animation: q-fade 0.4s ease both; animation-delay: calc(var(--i, 0) * 34ms + 160ms); }

/* --- the editor and the linked-calendar sheet --- */
.sheet-backdrop { animation: q-fade 0.18s ease both; }
.sheet { animation: q-sheet 0.32s var(--ease-spring) both; }
.sheet-body > * { animation: q-rise 0.30s var(--ease-out) both; }
.sheet-body > *:nth-child(1) { animation-delay: 0.04s; }
.sheet-body > *:nth-child(2) { animation-delay: 0.07s; }
.sheet-body > *:nth-child(3) { animation-delay: 0.10s; }
.sheet-body > *:nth-child(4) { animation-delay: 0.13s; }
.sheet-body > *:nth-child(5) { animation-delay: 0.16s; }
.sheet-body > *:nth-child(6) { animation-delay: 0.19s; }
.sheet-body > *:nth-child(7) { animation-delay: 0.22s; }

/* --- controls answer the touch --- */
.btn, .icon-btn, .pill, .ar-chip, .lead-chip, .swatch, .seg button, .view-select {
  transition: background 0.16s, color 0.16s, border-color 0.16s, transform 0.12s var(--ease-spring);
}
.btn:active, .icon-btn:active, .pill:active, .seg button:active { transform: scale(0.94); }
.ar-chip:active, .lead-chip:active { transform: scale(0.92); }
.swatch { transition: transform 0.16s var(--ease-spring), border-color 0.16s; }
.swatch:active { transform: scale(0.88); }
.swatch.sel { transform: scale(1.08); }

/* --- the compose button, which should feel like the main thing --- */
.fab {
  animation: q-pop 0.4s var(--ease-spring) both;
  transition: transform 0.16s var(--ease-spring), box-shadow 0.2s;
}
.fab:hover { transform: translateY(-2px) scale(1.04); box-shadow: 0 8px 26px rgba(0, 0, 0, .42); }
.fab:active { transform: scale(0.92); }

/* --- the month name changes as you move through time --- */
.month-title { transition: opacity 0.16s ease; }
.month-title.is-changing { opacity: 0; }

/* --- toast --- */
.toast { animation: q-rise 0.26s var(--ease-spring) both; }

/* --- while a search is running, a shimmer instead of a spinner --- */
.ar-skeleton {
  height: 74px; border-radius: 10px; margin-bottom: 8px;
  background: linear-gradient(100deg,
    var(--bg-panel) 20%,
    color-mix(in srgb, var(--bg-hover) 80%, var(--bg-panel)) 40%,
    var(--bg-panel) 60%);
  background-size: 220% 100%;
  animation: q-shimmer 1.25s linear infinite;
}

/* Motion off: the app must still be usable, so opacity and transform are
   reset rather than left mid-animation. */
@media (prefers-reduced-motion: reduce) {
  .grid .cell, .item, .ar-card, .cal-item, .wk-day, .ym, .yc.has,
  .sheet, .sheet-body > *, .fab, .toast, .cell .dots i,
  #day-view, #week-view, #month-view, #year-view, #agenda-view {
    animation: none;
    opacity: 1;
    transform: none;
  }
  .ar-skeleton { animation: none; }
}

/* ============================================================
   Motion, corrected after measuring it (v9)
   ------------------------------------------------------------
   Two things the first pass got wrong, both found by reading the
   rendered stylesheet back rather than trusting the comment above it:

   1. The view picker was set to 13px, which re-introduced the iOS
      zoom-on-focus this app had already been cleared of.
   2. The "today" pulse animated box-shadow, which is a paint
      property — so the claim that only transform and opacity are
      animated was false. It is now a scaled pseudo-element.

   What is still not compositor-only, stated plainly rather than
   hidden: the loading shimmer animates background-position, and the
   press states transition background and border-colour. Both are
   discrete or small-area and neither runs continuously on a list.
   ============================================================ */

.view-select { font-size: 16px; min-width: 96px; max-width: 118px; padding: 0 6px; }

/* the today ring, as transform and opacity only */
.cell.is-today { animation: q-pop 0.30s var(--ease-out) both; position: relative; }
.cell.is-today::after {
  content: ''; position: absolute; inset: -1px;
  border-radius: inherit; pointer-events: none;
  border: 2px solid var(--accent);
  opacity: 0;
  animation: q-ring 2.4s var(--ease-out) 0.5s 2;
}
@keyframes q-ring {
  0%   { opacity: 0.55; transform: scale(1); }
  70%  { opacity: 0;    transform: scale(1.22); }
  100% { opacity: 0;    transform: scale(1.22); }
}
@media (prefers-reduced-motion: reduce) {
  .cell.is-today::after { animation: none; opacity: 0; }
}

/* ---- the compose button gets out of the way ----
   Fixed to the corner, it sat over whatever happened to scroll under it — in
   testing, over a category chip, which made that chip untappable at that
   scroll position. It now drops away when you scroll down and comes back when
   you stop or scroll up, which is both the fix and the nicest bit of motion
   in the app. */
.fab {
  transition: transform 0.24s var(--ease-spring), opacity 0.2s ease, box-shadow 0.2s;
}
.fab.is-away {
  transform: translateY(96px) scale(0.9);
  opacity: 0;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .fab.is-away { transform: none; opacity: 0; }
}

/* a nearby place is not a dated event, and the card says so */
.ar-nearby { color: var(--text-dim); font-weight: 600; font-size: 11px; text-transform: uppercase; letter-spacing: .04em; }

/* ---- the location request sheet (v13) ---- */
.loc-points, .loc-steps { margin: 8px 0 4px 18px; display: flex; flex-direction: column; gap: 7px; }
.loc-points { list-style: disc; }
.loc-steps { list-style: decimal; }
.loc-points li, .loc-steps li { font-size: 13px; color: var(--text-dim); line-height: 1.45; }
.loc-steps li b, .loc-points li b { color: var(--text); }
/* a blocked pin should not look like a working one */
.ar-scan.is-blocked { border-color: var(--border-strong); }
.ar-scan.is-blocked .ar-scan-pin { background: var(--border-strong); color: var(--text-dim); }
/* a network estimate is not a GPS fix and should not look like one */
.ar-scan.is-coarse .ar-scan-pin { background: var(--border-strong); color: var(--text); }

/* ---- "an update is ready" (v16) ----
   Same shape as Quriou Mail's, because it is the same promise: nothing
   reloads underneath you until you say so. */
.update-bar {
  position: fixed; z-index: 70;
  left: 50%; transform: translateX(-50%);
  bottom: calc(16px + env(safe-area-inset-bottom, 0px));
  display: flex; align-items: center; gap: 10px;
  max-width: calc(100vw - 24px);
  padding: 8px 8px 8px 16px; border-radius: 16px;
  background: color-mix(in srgb, var(--bg-panel) 92%, transparent);
  border: 1px solid var(--border-strong);
  box-shadow: 0 8px 28px rgba(0, 0, 0, 0.3);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  /* NOT the generic rise keyframe: that one ends at 'transform: none', which
     wipes the translateX(-50%) doing the centring and leaves the bar half a
     width to the right. The centring travels through every keyframe. */
  animation: ub-rise 0.3s var(--ease-out) both;
}
@keyframes ub-rise {
  from { opacity: 0; transform: translateX(-50%) translateY(10px); }
  to   { opacity: 1; transform: translateX(-50%); }
}
.update-bar .ub-text { font-size: 13px; color: var(--text); }
.update-bar .btn { min-height: 44px; padding: 0 14px; font-size: 13px; }
.update-bar .icon-btn { min-width: 44px; min-height: 44px; }

/* ---- manual update control (v18) ---- */
#btn-update.is-spinning { animation: q-spin 0.9s linear infinite; }
@keyframes q-spin { to { transform: rotate(360deg); } }
/* what the browser actually said, kept readable but visibly secondary */
.loc-raw { margin-top: 10px; font-size: 12px; color: var(--text-dim); overflow-wrap: anywhere; }
.loc-raw code { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 11px; }

/* ---- the brand mark ----
   The sibling of Quriou Mail's: same ring, same tail, gold instead of teal,
   so the two apps read as one family on a home screen. */
.logo-mark { width: 30px; height: 30px; flex: none; display: block; margin-right: 2px; }
/* the sibling app's mark, so the pair reads as a pair from inside either one */
.app-link { display: inline-flex; align-items: center; justify-content: center; }
.app-link img { display: block; }

/* ============================================================
   One control system (v25)
   ------------------------------------------------------------
   Measured at 375px, the panel was carrying five corner radii (8, 9, 10, 14,
   20) and four label sizes (12.5, 13, 13.33, 14) across controls sitting on
   the same row, and the Go button was 42px wide — under the 44 minimum every
   other control meets. Each value was defensible on its own; together they
   read as a layout that does not match itself.

   Now: one radius for controls, pills stay pills because a pill is a
   different thing, one label size, and nothing interactive under 44px in
   either direction.
   ============================================================ */
:root { --ctl-radius: 10px; --ctl-label: 13px; }

.btn, .ar-act, .ar-add, .seg button, .icon-btn, .pill, .view-select {
  border-radius: var(--ctl-radius);
}
/* icon buttons are excluded from the label size on purpose: they carry glyphs
   (arrows, a refresh mark, a close cross), and 13px shrank those to something
   you have to look for. Measured before this line existed: 19px. */
.btn, .ar-act, .ar-add, .seg button, .pill, .view-select {
  font-size: var(--ctl-label);
}
/* width counts as much as height: a 42px-wide target fails the same rule a
   42px-tall one does */
.ar-act, .ar-add, .btn, .icon-btn, .seg button {
  min-width: 44px; min-height: 44px;
}
.ar-act { padding: 0 12px; }
.seg { border-radius: calc(var(--ctl-radius) + 3px); }
/* chips are pills on purpose — that is what distinguishes a filter from a
   button — but they take the same label size as everything else */
.ar-chip { font-size: var(--ctl-label); }

/* The scan block is the one control allowed to be bigger, because it is the
   headline action. It was 62px tall against 44 everywhere else, which read as
   a different design rather than a louder one. 56 keeps it dominant while
   staying on the same rhythm. */
.ar-scan { min-height: 56px; border-radius: calc(var(--ctl-radius) + 2px); }
.ar-scan-pin { width: 36px; height: 36px; }
.ar-scan-text b { font-size: 15px; }
.ar-scan-text small { font-size: 12px; }

/* ---- the maker's mark ----
   The sibling of Quriou Mail's, and just as quiet: a signature belongs at the
   edge of a thing. Full weight where the app introduces itself, a smaller one
   closing the page. */
.by-line {
  font-size: 12px; color: var(--text-dim);
  letter-spacing: .08em; text-transform: uppercase; margin-top: 2px;
}
.by-line b { font-weight: 700; color: var(--text); letter-spacing: .12em; }
.by-line-sm { font-size: 10px; letter-spacing: .1em; }
.by-line-sm b { font-weight: 600; color: var(--text-dim); }
.app-sig { justify-content: center; margin: 26px 0 8px; opacity: .85; }

/* ---- the maker's mark: QENTRA's own lockup ----
   Two files, not one recoloured: the brand ships a dark-ink version for light
   grounds and a reverse for dark ones, and a logo is the last thing that
   should be approximated with a filter. Both are in the page and the theme
   decides which is shown, so there is no flash of the wrong one while a
   script works out the theme. */
.by-line { display: flex; align-items: center; gap: 7px; }
.by-line > span { font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--text-dim); }
.qentra-logo { height: 17px; width: auto; display: block; }
.by-line-sm .qentra-logo { height: 13px; }
.by-line-sm > span { font-size: 10px; }
/* light is the default; dark wins where the theme says so */
.q-on-dark { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .q-on-light { display: none; }
  :root:not([data-theme="light"]) .q-on-dark { display: block; }
}
:root[data-theme="dark"] .q-on-light { display: none; }
:root[data-theme="dark"] .q-on-dark { display: block; }
:root[data-theme="light"] .q-on-light { display: block; }
:root[data-theme="light"] .q-on-dark { display: none; }

/* ============================================================
   TOUCH TARGETS — the same 48dp floor Qmail got in v139
   ============================================================
   Qalendar runs on the same phone as Qmail: a Pixel 8, Android 14, a 412x915
   viewport. Android's guidance is 48dp; this file had settled on 44px, which
   is Apple's number. Measured live at 412px before this block: every real
   control — .btn, .icon-btn, .ar-chip, .ar-act, the gate buttons, the radius
   and category chips — sat at exactly 44.

   Guarded on `pointer: coarse` so the desktop layout is untouched, exactly as
   in Qmail. Selectors are listed at the specificity that actually wins, because
   in Qmail a plain floor was silently beaten by more specific rules written
   earlier and ten controls stayed under it. */
@media (pointer: coarse) {
  .btn, .btn-ghost, .btn-primary, .icon-btn,
  .ar-chip, .ar-chip-all, .ar-act, .ar-act-strong, .ar-add,
  .seg button, .gate-card .btn, .around-where .btn,
  #ar-radius button, #ar-cats button, .sheet-item, .dock-btn {
    min-height: 48px;
  }
  .icon-btn, .ar-chip, .ar-chip-all,
  #ar-radius button, #ar-cats button {
    min-width: 48px;
  }
  .update-bar .btn { min-height: 48px !important; }
  .update-bar .icon-btn { min-width: 48px !important; min-height: 48px !important; }
  /* the scan button is the primary action of the Around panel — it should not
     be the same height as a filter chip */
  .ar-scan { min-height: 60px; }
  /* 10px is below Android guidance for a control LABEL. .chip is excluded on
     purpose: it is the passive "today" badge, not a tap target, and inflating
     it to 13.5px changed a design decision rather than fixing a defect. */
  .ar-chip, .ar-chip-all { font-size: 13px; }
  /* Material spacing between adjacent targets */
  .ar-acts { gap: 8px; }
}

/* ---------- rating a place ---------- */
.rv-stars { display: flex; gap: 4px; }
.rv-star {
  background: none; border: none; cursor: pointer; padding: 4px;
  font-size: 30px; line-height: 1; color: var(--border-strong);
  min-width: 48px; min-height: 48px;
}
.rv-star.on { color: var(--accent); }
.rv-star:hover { color: var(--accent); }
.ar-act.has-review { border-color: var(--accent); color: var(--accent); }
.sheet-list { display: flex; flex-direction: column; gap: 6px; }

/* ---------- Settings ---------- */
.sheet-tall { max-height: 88vh; }
.sheet-tall .sheet-body { overflow-y: auto; -webkit-overflow-scrolling: touch; }
.set-block { padding: 14px 0; border-bottom: 1px solid var(--border); }
.set-block:last-child { border-bottom: none; }
.set-block h3 {
  margin: 0 0 8px; font-size: 12px; letter-spacing: .07em;
  text-transform: uppercase; color: var(--text-dim); font-weight: 700;
}
.set-block h4 { margin: 14px 0 6px; font-size: 13px; }
.set-block .seg { margin-bottom: 6px; flex-wrap: wrap; }
.set-kv { display: grid; grid-template-columns: auto 1fr; gap: 4px 14px; margin: 0 0 12px; font-size: 14px; }
.set-kv dt { color: var(--text-dim); }
.set-kv dd { margin: 0; font-variant-numeric: tabular-nums; }

.set-tags { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0; }
.set-tag {
  font-size: 12px; padding: 4px 9px; border-radius: 20px;
  background: var(--bg); border: 1px solid var(--border); color: var(--text-dim);
}
.set-tag b { color: var(--text); font-variant-numeric: tabular-nums; }

.set-table { width: 100%; border-collapse: collapse; font-size: 13px; margin: 8px 0; }
.set-table th {
  text-align: left; font-weight: 600; color: var(--text-dim);
  font-size: 11px; text-transform: uppercase; letter-spacing: .05em;
  padding: 4px 6px; border-bottom: 1px solid var(--border-strong);
}
.set-table td { padding: 5px 6px; border-bottom: 1px solid var(--border); }
.set-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.set-table td.bad { color: var(--danger, #b3391f); font-weight: 600; }

.set-errs { list-style: none; padding: 0; margin: 6px 0 0; font-size: 13px; }
.set-errs li { padding: 6px 0; border-bottom: 1px solid var(--border); overflow-wrap: anywhere; }
.set-errs time { color: var(--text-dim); font-variant-numeric: tabular-nums; margin-right: 6px; }

.set-rev { display: flex; align-items: flex-start; gap: 8px; padding: 9px 0; border-bottom: 1px solid var(--border); }
.set-rev:last-child { border-bottom: none; }
.set-rev-main { flex: 1; min-width: 0; font-size: 13px; }
.set-rev-stars { color: var(--accent); margin-left: 6px; }
.set-rev-note { color: var(--text-dim); font-size: 13px; margin-top: 2px; overflow-wrap: anywhere; }
.set-activity p.hint { margin-top: 0; }

/* ============================================================
   VISUAL SYSTEM — v36, matching Qmail v147
   ============================================================
   Same findings, same fixes, so the two apps stay one family rather than two
   near-misses. Qalendar additionally had NO --danger and NO --bg-active at
   all, which meant deleting an event looked exactly like saving one.
   ============================================================ */
/* light tokens above are on :root[data-theme="light"] (0,2,0), so a plain
   :root here would lose to them — match the specificity. */
:root, :root[data-theme="light"] {
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px; --sp-5: 24px; --sp-6: 32px;
  --fs-xs: 11px; --fs-sm: 12px; --fs-md: 13px; --fs-lg: 15px; --fs-xl: 18px; --fs-2xl: 22px;
  --r-chip: 6px; --r-ctl: 10px; --r-card: 16px; --r-pill: 999px;

  /* VIS-01 — 1.18:1 was not a boundary, it was a suggestion */
  --border: #c8cdc9;
  /* VIS-04 */
  --text-dim-raised: #4b5a5b;
  /* VIS-05 — these simply did not exist. Measured against this app's own
     grounds, not copied from Qmail's warmer palette. */
  --danger: #a3301c;
  --danger-strong: #8a2716;
  --bg-active: #e3e0d7;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --border: #35514f;
    --text-dim-raised: #a9b8b8;
    --danger: #f09079;
    --danger-strong: #b03a1f;
    --bg-active: #24403f;
  }
}
:root[data-theme="dark"] {
  --border: #35514f;
  --text-dim-raised: #a9b8b8;
  --danger: #f09079;
  --danger-strong: #b03a1f;
  --bg-active: #24403f;
}

/* VIS-06 — accent-strong was identical to accent in light theme */
:root, :root[data-theme="light"] { --accent-strong: #574517; }

/* VIS-05 applied — destructive actions look destructive */
#f-delete, .btn.danger, .danger-btn {
  color: var(--danger); border-color: var(--danger);
}
#f-delete:hover, .btn.danger:hover, .danger-btn:hover {
  background: var(--danger-strong); border-color: var(--danger-strong); color: #fff;
}

/* VIS-01 applied — the sheets state their edge */
.sheet, .ar-card, .gate-card { border: 1px solid var(--border); }

/* VIS-03 — the same weight discipline: 600 for headings and today, 400 for
   the rest, so the thing that matters is the only heavy thing on screen */
.gate-card h1, .month-title, .sheet-head, .set-block h3, .ar-title, .ag-date {
  font-weight: 600;
}

/* a result you have already chosen before — the search remembers */
.ar-seen {
  background: var(--accent-soft); border-color: var(--accent);
  color: var(--accent); cursor: default; font-weight: 600;
}
.set-history { margin-top: 4px; }
