/* Circle — layout and typography, shared by both themes.
 *
 * Both theme sheets are always linked (index.html never swaps <link> tags,
 * only the data-theme attribute on <html>), so this file owns geometry only:
 * grids, spacing, sizing, type scale. Every place a colour would go it reads
 * a custom property instead, and retro.css / clean.css each define the whole
 * set. If a property read here is missing, a theme forgot it — this file is
 * not allowed to pick a colour on its own, and tests/css.test.js enforces
 * that by refusing any hex or rgb() in here.
 *
 * The shape is the Apple Human Interface Guidelines one: a sticky top bar, a
 * sidebar on regular widths that becomes a bottom tab bar on compact ones,
 * 44px touch targets everywhere, and safe-area insets so an installed iPhone
 * app does not tuck its tab bar under the home indicator.
 */

/* ============================== Foundations ============================== */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

html, body {
  margin: 0;
  padding: 0;
  min-height: 100%;
}

body {
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.45;
  color: var(--text);
  background-color: var(--bg);
  overflow-x: hidden;
}

h1, h2, h3, h4, p, ul, ol, dl, dd, figure, fieldset {
  margin: 0;
}

ul, ol {
  list-style: none;
  padding: 0;
}

fieldset {
  border: 0;
  padding: 0;
  min-inline-size: 0;
}

a { color: var(--link); }

img { max-width: 100%; }

button, input, select, textarea {
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
  color: inherit;
}

/* Every control is a touch target before it is anything else, and a bare
   <select> in the demo bar is as much a control as one inside a .field-row.
   The checkbox and radio exceptions are two rules further down: those are
   glyphs, and their <label> is what the finger actually lands on. */
input, select, textarea {
  min-height: 44px;
  transition:
    background-color var(--duration-fast) var(--ease-standard),
    color var(--duration-fast) var(--ease-standard),
    border-color var(--duration-fast) var(--ease-standard),
    box-shadow var(--duration-fast) var(--ease-standard);
}

textarea { resize: vertical; }

h1 { font-size: 1.6rem; line-height: 1.2; }
h2 { font-size: 1.15rem; line-height: 1.25; }
h3 { font-size: 1rem; line-height: 1.3; }

/* The shape of the ring lives here so it survives a theme switch; each theme
   supplies only --focus. Every interactive control in the markup is one of
   these tags, which is what makes one rule enough. */
:is(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

/* The app toggles whole sections with the `hidden` attribute rather than a
   class, so this has to out-rank every display value either theme sets. */
[hidden] { display: none !important; }

/* The standard visually-hidden pattern: still in the accessibility tree and
   still focusable, just not painted. The :is() line is the exception that
   matters — a visually-hidden *control* (the profile photo file input) is a
   real touch target reached through its <label class="button">, and a 1px
   box fails a 44px audit that is right to ask. clip-path removes it from
   hit-testing and from view either way, so the larger box costs nothing. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.sr-only:is(input, select, textarea, button) {
  width: 44px;
  height: 44px;
  margin: 0;
  pointer-events: none;
}

/* ============================== The shell ================================ */

#app {
  --topbar-h: 3.5rem;
  --tabbar-h: 3.75rem;
  --gutter: 1.25rem;
  /* /site-nav/'s header bar (willperlak.com's site-wide nav, not sticky)
     sits above this element as the first child of <body>. It is not part of
     this grid, but it does take real height in the document, so the shell's
     own "full viewport" math has to give it back or every short page would
     scroll by that much. nav.css publishes the bar's height; the fallback 0
     is right when it isn't loaded (run locally, the bar is absent). */
  --site-nav-h: var(--wp-site-nav-h, 0px);
  min-height: calc(100dvh - var(--site-nav-h));
  display: grid;
  grid-template-columns: 15rem minmax(0, 1fr);
  grid-template-rows: auto auto auto minmax(0, 1fr);
  grid-template-areas:
    "topbar topbar"
    "banner banner"
    "demo   demo"
    "nav    main";
}

#topbar {
  grid-area: topbar;
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
  min-height: var(--topbar-h);
  padding: 0.35rem max(var(--gutter), env(safe-area-inset-right)) 0.35rem max(var(--gutter), env(safe-area-inset-left));
  padding-top: calc(0.35rem + env(safe-area-inset-top));
}

#brand {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: 1.4rem;
  font-weight: bold;
  letter-spacing: 0.02em;
  text-decoration: none;
}

#topbar-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.topbar-btn {
  white-space: nowrap;
}

.topbar-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  text-decoration: none;
}

/* The unread count on the bell. Small, but never smaller than a circle that
   fits two digits. */
.badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.25rem;
  height: 1.25rem;
  padding: 0 0.3rem;
  font-size: 0.7rem;
  font-weight: bold;
  line-height: 1;
}

#topbar-actions .badge {
  position: absolute;
  top: 0.15rem;
  right: 0.05rem;
}

#offline-banner {
  grid-area: banner;
  padding: 0.4rem var(--gutter);
  font-size: 0.85rem;
  text-align: center;
}

#demo-bar {
  grid-area: demo;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.4rem 0.75rem;
  padding: 0.4rem max(var(--gutter), env(safe-area-inset-right)) 0.4rem max(var(--gutter), env(safe-area-inset-left));
  font-size: 0.85rem;
}

#demo-user {
  min-width: 0;
  max-width: 14rem;
}

#demo-note {
  min-width: 0;
  flex: 1 1 12rem;
  font-size: 0.8rem;
}

/* ================================= Nav =================================== */

#nav {
  grid-area: nav;
  position: sticky;
  top: var(--topbar-h);
  /* Tall enough that its trailing edge reads as a sidebar rule rather than
     as a floating card that stops halfway down an empty screen. It stretches
     to its grid row, capped at one viewport under the topbar: on a long
     page that is the whole screen once the site-nav bar has scrolled away,
     and on a short one the row (and so #app's min-height, which gives the
     bar its 44px back) is the limit, so the page never scrolls just for the
     sidebar. */
  align-self: stretch;
  min-height: calc(100dvh - var(--topbar-h) - var(--site-nav-h));
  max-height: calc(100dvh - var(--topbar-h));
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  padding: 0.75rem 0.6rem;
  padding-left: max(0.6rem, env(safe-area-inset-left));
}

.nav-link {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  min-height: 44px;
  padding: 0.5rem 0.75rem;
  text-decoration: none;
  font-size: 0.95rem;
  transition:
    background-color var(--duration-fast) var(--ease-standard),
    color var(--duration-fast) var(--ease-standard);
}

.nav-icon {
  flex: none;
  width: 1.5rem;
  height: 1.5rem;
}

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

.icon {
  flex: none;
  width: 1.25rem;
  height: 1.25rem;
  vertical-align: -0.2em;
}

/* Login is a single centred column: no tabs, no "new plan" button, nothing
   to navigate to yet. */
#app[data-screen="login"] #nav,
#app[data-screen="login"] #btn-new-event,
#app[data-screen="login"] #nav-feed {
  display: none;
}

#app[data-screen="login"] {
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "topbar"
    "banner"
    "demo"
    "main";
}

/* ================================= Main ================================== */

#main {
  grid-area: main;
  min-width: 0;
  padding: var(--gutter) max(var(--gutter), env(safe-area-inset-right)) 4rem max(var(--gutter), env(safe-area-inset-left));
}

.view {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  max-width: 68rem;
  min-width: 0;
}

/* 5.2: a view becoming visible (app.js's show() drops [hidden] on route
   change) fades and rises into place once. */
.view:not([hidden]) {
  animation: wp-enter var(--duration) var(--ease-out);
}

.view-narrow {
  max-width: 30rem;
  margin-inline: auto;
  width: 100%;
}

.view-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
}

/* The big bold screen title of every iOS app. */
.view-title {
  font-size: 2.1rem;
  font-weight: bold;
  line-height: 1.1;
  letter-spacing: -0.01em;
  min-width: 0;
  overflow-wrap: anywhere;
}

.muted { font-size: 0.9rem; }

.empty {
  padding: 1.5rem 1rem;
  text-align: center;
  font-size: 0.95rem;
}

.back-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  min-width: 44px;
  padding-right: 0.3rem;
  text-decoration: none;
  font-size: 0.95rem;
}

.tbd-hint {
  display: block;
  font-size: 0.78rem;
}

/* ================================ Panels ================================= */

.panel {
  padding: 1rem 1.1rem;
  border-radius: var(--radius);
  min-width: 0;
}

.panel > * + * { margin-top: 0.75rem; }

.profile-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.35rem;
}

.photo-row {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
}

.photo-actions {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-width: 0;
}

/* ================================ Forms ================================== */

.field-row {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-width: 0;
}

.field-row > label,
.field-label {
  font-size: 0.82rem;
  font-weight: bold;
  letter-spacing: 0.01em;
}

.field-row input,
.field-row select,
.field-row textarea,
.inline-form input,
.inline-form select {
  width: 100%;
  min-width: 0;
  min-height: 44px;
  padding: 0.55rem 0.7rem;
  border-radius: var(--radius-sm);
}

/* A responsive 2-3 column block for fields that belong together (date/start/
   end, first/last name). auto-fit rather than a fixed count so it collapses
   on its own instead of at a breakpoint someone has to remember. */
.field-grid {
  display: grid;
  /* 10rem: a date field with its picker button needs the room, or the
     value is clipped under the button. */
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: 0.75rem;
  min-width: 0;
}

/* Value and "who can see it" on one line, wrapping to two on a phone. */
.vis-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(7rem, auto);
  align-items: end;
  gap: 0.4rem 0.6rem;
}

.vis-row > label:first-child {
  grid-column: 1;
  grid-row: 1;
}

.vis-row input,
.vis-row textarea {
  grid-column: 1;
  grid-row: 2;
}

/* "Visible to", above its dropdown, so the dropdown is not a mystery. */
.vis-label {
  grid-column: 2;
  grid-row: 1;
  font-size: 0.8rem;
}

.vis-select {
  grid-column: 2;
  width: 100%;
  min-height: 44px;
  font-size: 0.85rem;
}

/* A checkbox is a glyph, not a field: without this it takes the full field
   width, puts its 13px box at the far left and shoves its own label a panel
   away (run 3 shipped exactly that, 420px wide). */
.checkbox-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 44px;
  cursor: pointer;
}

.checkbox-row input {
  width: auto;
  min-width: 0;
  max-width: none;
  min-height: 0;
  flex: none;
  margin: 0;
}

.inline-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  align-items: end;
  gap: 0.6rem;
}

.inline-form .errors,
.inline-form > button {
  grid-column: 1 / -1;
  justify-self: start;
}

.errors {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.6rem 0.8rem;
  border-radius: var(--radius-sm);
  font-size: 0.9rem;
}

.form-actions,
.actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}

button,
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  min-height: 44px;
  min-width: 44px;
  padding: 0.5rem 1rem;
  border-radius: var(--radius-sm);
  font-size: 0.95rem;
  text-decoration: none;
  cursor: pointer;
  /* 5.2: structure only (tokens, no colour) — the colour a property crosses
     to still comes from retro.css/clean.css. */
  transition:
    background-color var(--duration-fast) var(--ease-standard),
    color var(--duration-fast) var(--ease-standard),
    border-color var(--duration-fast) var(--ease-standard),
    box-shadow var(--duration-fast) var(--ease-standard),
    transform var(--duration-fast) var(--ease-standard);
}

/* The press scale is clean.css's, not here (review N6): retro's :active is
   its own border-style: inset (unchanged from main), and this file's own
   "retro must look exactly as on main" bar is easiest kept by scoping the
   one purely decorative motion addition to the theme that asked for it. */

.primary,
.danger {
  font-weight: bold;
}

/* A button that reads as a link: no box, no fill, just the label. */
.link-btn {
  min-height: 44px;
  padding: 0.25rem 0;
  text-decoration: underline;
  cursor: pointer;
}

input[type="checkbox"],
input[type="radio"] {
  width: auto;
  min-height: 0;
  accent-color: var(--accent);
}

/* The native clock button eats about 20px, which inside a seven-across day
   card is the difference between "09:00 AM" and "09:00 A". */
.sched-interval input[type="time"]::-webkit-calendar-picker-indicator {
  display: none;
}

/* =========================== Segmented control =========================== */

.segmented {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  padding: 0.2rem;
  border-radius: var(--radius-sm);
}

.seg-btn {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 44px;
  padding: 0.4rem 0.7rem;
  border-radius: var(--radius-sm);
  font-size: 0.9rem;
  text-align: center;
  white-space: nowrap;
  cursor: pointer;
}

/* The radios are the state; their labels are the control. */
.seg-radio {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  margin: 0;
}

.kind-toggle {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  padding: 0.2rem;
  border-radius: var(--radius-sm);
}

.rsvp-group {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.rsvp-btn {
  font-weight: bold;
}

/* ================================ Cards ================================== */

.card-list {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.event-card {
  display: block;
  padding: 0.8rem 0.9rem;
  border-radius: var(--radius);
  min-width: 0;
}

.event-card-link {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-height: 44px;
  text-decoration: none;
  min-width: 0;
}

.event-card-title {
  font-size: 1.05rem;
  font-weight: bold;
  overflow-wrap: anywhere;
}

.event-card-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.15rem 0.75rem;
  font-size: 0.88rem;
}

.event-card-when,
.event-card-where {
  min-width: 0;
  overflow-wrap: anywhere;
}

.event-card-badges,
.badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
}

.event-card-counts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  font-size: 0.85rem;
}

.badge-kind,
.badge-status,
.badge-conflict,
.badge-full {
  display: inline-flex;
  align-items: center;
  padding: 0.12rem 0.5rem;
  border-radius: var(--radius-sm);
  font-size: 0.75rem;
  font-weight: bold;
  letter-spacing: 0.02em;
  white-space: nowrap;
}

/* ================================ Facts ================================== */

.facts {
  display: grid;
  grid-template-columns: minmax(5rem, max-content) minmax(0, 1fr);
  gap: 0.35rem 0.9rem;
}

.fact { display: contents; }

.facts dt {
  font-size: 0.82rem;
  font-weight: bold;
}

.facts dd {
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* ============================ People and rows ============================ */

.person-list {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.person-row {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.5rem 0.65rem;
  border-radius: var(--radius-sm);
  min-width: 0;
}

.person-row-link {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 44px;
  text-decoration: none;
  /* It is a <button>, and a button centres its text unless told otherwise. */
  text-align: left;
}

.person-row-main {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-width: 0;
}

.person-row-name {
  font-weight: bold;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.person-row-sub {
  font-size: 0.82rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.person-row-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  flex: none;
}

.admin-remove {
  font-size: 0.85rem;
}

.person-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.person-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.2rem 0.6rem 0.2rem 0.2rem;
  border-radius: 999px;
  font-size: 0.85rem;
  max-width: 100%;
}

.person-chip-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ================================ Avatars ================================ */

.avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 50%;
  overflow: hidden;
  font-weight: bold;
  font-size: 0.85rem;
  line-height: 1;
  text-transform: uppercase;
  user-select: none;
}

.avatar-small {
  width: 1.75rem;
  height: 1.75rem;
  font-size: 0.65rem;
}

.avatar-large {
  width: 6rem;
  height: 6rem;
  font-size: 2rem;
}

.avatar-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: inherit;
}

.avatar-initials { line-height: 1; }

/* ============================== Checklists =============================== */

.checklist {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  max-height: 18rem;
  overflow-y: auto;
}

.check-item,
.check-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  min-height: 44px;
  padding: 0.25rem 0.4rem;
  border-radius: var(--radius-sm);
  min-width: 0;
}

.check-item input,
.check-row input {
  width: auto;
  min-height: 0;
  flex: none;
  margin: 0;
}

.check-label,
.check-row label {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
  /* The label is the thumb's target, not the 16px box beside it. */
  display: flex;
  align-items: center;
  min-height: 44px;
}

/* =============================== Comments ================================ */

.comment-list {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.comment {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.55rem 0.7rem;
  border-radius: var(--radius-sm);
  min-width: 0;
}

.comment-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.4rem;
  font-size: 0.82rem;
}

.comment-author { font-weight: bold; }

.comment-time { font-size: 0.78rem; }

.comment-body { overflow-wrap: anywhere; }

.comment-delete {
  margin-left: auto;
  font-size: 0.8rem;
}

#comment-body {
  width: 100%;
  min-height: 3.5rem;
  padding: 0.55rem 0.7rem;
  border-radius: var(--radius-sm);
}

/* ============================== Date options ============================= */

.option-list {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.option-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.6rem;
  padding: 0.45rem 0.6rem;
  border-radius: var(--radius-sm);
  min-width: 0;
}

.option-main {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-width: 0;
}

.option-meta { font-size: 0.82rem; }

.option-vote,
.option-remove {
  flex: none;
  font-size: 0.85rem;
  padding: 0.35rem 0.7rem;
}

/* ================================= Feed ================================== */

.feed-list {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.feed-item {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.55rem 0.7rem;
  border-radius: var(--radius-sm);
  min-width: 0;
}

.feed-link {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 44px;
  text-align: left;
  align-items: flex-start;
  justify-content: center;
  text-decoration: none;
}

.feed-text { overflow-wrap: anywhere; }

.feed-time { font-size: 0.78rem; }

.feed-item-unread { font-weight: bold; }

/* ================================ Calendar =============================== */

.calendar-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
}

.calendar-nav {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex: 1 1 16rem;
  min-width: 0;
}

.cal-title {
  flex: 1 1 auto;
  min-width: 0;
  text-align: center;
  font-size: 1.05rem;
}

#cal-prev,
#cal-next,
#ft-cal-prev,
#ft-cal-next {
  flex: none;
  min-width: 2.5rem;
  padding: 0.3rem 0.6rem;
  font-size: 1.2rem;
  line-height: 1;
}

#calendar-pane,
#ft-calendar-pane {
  min-width: 0;
  border-radius: var(--radius);
  padding: 0.85rem;
}

/* repeat(7, minmax(0, 1fr)), never repeat(7, 1fr): `1fr` is minmax(auto, 1fr)
   and that `auto` floor lets a cell refuse to shrink below its own content,
   which is how a seven-column month grid ran off the right of the page. */
.cal-weekdays {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 0.2rem;
  margin-bottom: 0.3rem;
}

.cal-weekday {
  text-align: center;
  font-size: 0.7rem;
  font-weight: bold;
  padding: 0.2rem 0;
  min-width: 0;
}

.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 0.2rem;
}

/* Column, not row: the cell holds the date and the availability count as two
   separate spans, and side by side with no separator they read as one number
   — the 1st showing 2 of 2 free rendered as "12/2". */
.cal-day {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.1rem;
  aspect-ratio: 1;
  min-width: 0;
  min-height: 44px;
  padding: 0.1rem;
  border-radius: var(--radius-sm);
  font-size: 0.8rem;
  line-height: 1.1;
  cursor: pointer;
}

.cal-day-number {
  font-size: 0.9rem;
  font-weight: bold;
}

.cal-day-count {
  font-size: 0.65rem;
  font-variant-numeric: tabular-nums;
}

.cal-day[data-bucket="none"] .cal-day-count { visibility: hidden; }

.cal-day-marks {
  display: flex;
  justify-content: center;
  gap: 0.15rem;
  min-height: 0.4rem;
}

.cal-mark {
  display: block;
  width: 0.36rem;
  height: 0.36rem;
  border-radius: 50%;
}

.cal-busy {
  font-size: 0.6rem;
  font-weight: bold;
  letter-spacing: 0.04em;
}

.legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.9rem;
  margin-top: 0.75rem;
  font-size: 0.8rem;
}

.legend-item {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

/* The swatch carries the colour; the label stays body text. Painting both the
   same value is what dragged five of Scheduler's eight legend labels under
   4.5:1 — a fill that reads as a 12px block does not read as 12px type. */
.legend-item::before {
  content: "";
  display: inline-block;
  width: 0.75rem;
  height: 0.75rem;
  flex: none;
  border-radius: var(--radius-sm);
  background: var(--swatch, currentColor);
  border: 1px solid var(--border);
}

#calendar-day-events { margin-top: 0.5rem; }

/* =============================== Find a time ============================= */

#find-time-body.three-col {
  display: grid;
  grid-template-columns: 17rem minmax(0, 1fr) 19rem;
  align-items: start;
  gap: 1rem;
}

#ft-controls,
#ft-side {
  min-width: 0;
}

#ft-side {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

#ft-people-checklist { margin-top: 0.4rem; }

#import-scheduler summary {
  min-height: 44px;
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-weight: bold;
  cursor: pointer;
  list-style: none;
}
#import-scheduler summary::-webkit-details-marker { display: none; }
/* A disclosure it is; look like one. */
#import-scheduler summary::before {
  content: "\25B8";
  display: inline-block;
  width: 1em;
  transition: transform var(--duration-fast) var(--ease-standard);
}
#import-scheduler[open] summary::before { transform: rotate(90deg); }

#import-scheduler > * + * { margin-top: 0.6rem; }

/* ========================== Schedule editor grid ========================= */

.sched-grid {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.sched-week {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 0.4rem;
}

.sched-day {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  min-width: 0;
  min-height: 5.5rem;
  padding: 0.4rem;
  border-radius: var(--radius-sm);
}

.sched-day-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.3rem;
  min-width: 0;
}

.sched-day-name,
.sched-day-label {
  font-size: 0.75rem;
  font-weight: bold;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sched-day-toggle {
  width: auto;
  min-height: 0;
  flex: none;
  margin: 0;
  cursor: pointer;
}

.sched-day-empty {
  font-size: 0.72rem;
}

.sched-intervals {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}

/* Label beside its input, one pair per row, Remove across the bottom. A card
   seven-across is about 140px and Safari's time input is wider than
   Chrome's, so anything that puts both times on one line only looks right on
   the machine it was written on. */
.sched-interval {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 0.2rem 0.3rem;
  padding: 0.25rem 0.3rem;
  border-radius: var(--radius-sm);
  font-size: 0.75rem;
}

.sched-time-label,
.sched-interval-time {
  font-size: 0.68rem;
}

.sched-interval input[type="time"] {
  width: 100%;
  min-width: 0;
  max-width: none;
  min-height: 2rem;
  padding: 0.15rem 0.25rem;
  font-size: 0.78rem;
}

.sched-interval .sched-remove-window {
  grid-column: 1 / -1;
}

.sched-add-window,
.sched-copy,
.sched-remove-window {
  width: 100%;
  min-width: 0;
  min-height: 2rem;
  padding: 0.25rem 0.4rem;
  font-size: 0.72rem;
  border-radius: var(--radius-sm);
}

/* ================================= Toast ================================= */

.toast {
  position: fixed;
  left: 50%;
  /* The standalone `translate` property, not `transform`, so the entry
     animation below (which animates `transform`) does not clobber the
     horizontal centering. */
  translate: -50% 0;
  bottom: calc(1.25rem + env(safe-area-inset-bottom));
  z-index: 100;
  max-width: min(92vw, 30rem);
  padding: 0.7rem 1.1rem;
  border-radius: var(--radius);
  text-align: center;
  font-weight: bold;
}

/* 5.2: in, translateY(12px) and fade, on show. */
.toast:not([hidden]) {
  animation: wp-toast-in var(--duration) var(--ease-out);
}

/* ============================= Compact widths ============================
 * Under 700px the sidebar becomes the iOS bottom tab bar: icons over small
 * labels, fixed to the bottom, padded by the home-indicator inset, with
 * #main reserving the same height so nothing hides behind it.
 */

/* The panels answer to the width of the main column, not the window: a
   935px desktop window with the sidebar open leaves ~700px for them, and a
   viewport breakpoint never saw that. (A phone held sideways is short and
   wide; its own landscape rules below override these.) */
#main { container-type: inline-size; }
@container (max-width: 960px) {
  #find-time-body.three-col {
    grid-template-columns: minmax(0, 1fr) 15rem;
  }
  #ft-controls { grid-column: 1 / -1; }
}
@container (max-width: 640px) {
  #find-time-body.three-col {
    grid-template-columns: minmax(0, 1fr);
  }
  .sched-week {
    grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  }
}

@media (max-width: 900px) {
  .sched-week {
    grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
  }
}

@media (max-width: 700px) {
  #app {
    --gutter: 1rem;
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "topbar"
      "banner"
      "demo"
      "main";
  }

  #nav {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 40;
    min-height: 0;
    max-height: none;
    overflow: visible;
    flex-direction: row;
    gap: 0;
    padding: 0.2rem max(0.2rem, env(safe-area-inset-right)) 0 max(0.2rem, env(safe-area-inset-left));
    padding-bottom: env(safe-area-inset-bottom);
  }

  .nav-link {
    flex: 1 1 0;
    flex-direction: column;
    justify-content: center;
    gap: 0.1rem;
    min-width: 0;
    min-height: 44px;
    padding: 0.3rem 0.15rem;
  }

  .nav-text {
    font-size: 0.62rem;
    line-height: 1.1;
    max-width: 100%;
  }

  #main {
    padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  }

  .toast {
    bottom: calc(var(--tabbar-h) + 0.75rem + env(safe-area-inset-bottom));
  }

  .view-title { font-size: 1.75rem; }

  .vis-row { grid-template-columns: minmax(0, 1fr); }
  .vis-row > * { grid-column: 1; grid-row: auto; }

  /* Five tabs in a fixed grid, so the strip never reflows between taps. */
  #events-filters.segmented {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
  }
  #events-filters .seg-btn {
    min-width: 0;
    padding-left: 0.15rem;
    padding-right: 0.15rem;
    font-size: 0.82rem;
  }

  /* One field per row: pairing them at this width puts related fields
     on different rows and unrelated ones side by side. */
  .field-grid { grid-template-columns: minmax(0, 1fr); }

  /* The page scrolls; a nested scroller inside it is a trap for a thumb. */
  .checklist { max-height: none; overflow: visible; }

  /* Save stays in reach at the bottom of a long form, above the tab bar. */
  #event-form .form-actions,
  #profile-form .form-actions,
  #availability-form .form-actions {
    position: sticky;
    bottom: calc(var(--tabbar-h, 3.5rem) + env(safe-area-inset-bottom));
    z-index: 5;
    padding: 0.5rem 0;
    background-color: var(--surface);
  }

  .facts { grid-template-columns: minmax(0, 1fr); gap: 0.1rem; }
  .facts dd { margin-bottom: 0.45rem; }

  .panel { padding: 0.85rem 0.9rem; }
}

/* ============================ Phone landscape ============================
 * 390px of height with a keyboard up is nothing. On a wide-but-short screen
 * the tabs move to a narrow left rail of icons; on a narrow-and-short one
 * they stay at the bottom but lose their labels.
 */

@media (orientation: landscape) and (max-height: 500px) and (min-width: 701px) {
  /* A phone on its side has a notch down one edge: the rail's column has
     to be the rail PLUS that inset, or the inset eats the rail and the
     icons are pushed under the edge of the screen. */
  #app {
    --topbar-h: 3rem;
    grid-template-columns: calc(5rem + env(safe-area-inset-left, 0px)) minmax(0, 1fr);
  }
  #nav {
    padding: 0.4rem 0.25rem;
    padding-left: calc(0.25rem + env(safe-area-inset-left, 0px));
  }
  #main { padding-right: max(1rem, env(safe-area-inset-right, 0px)); }
  #topbar { padding-left: max(1rem, env(safe-area-inset-left, 0px)); padding-right: max(1rem, env(safe-area-inset-right, 0px)); }
  .nav-link {
    flex-direction: column;
    justify-content: center;
    gap: 0.1rem;
    padding: 0.35rem 0.1rem;
    text-align: center;
  }
  .nav-text {
    font-size: 0.6rem;
    line-height: 1.05;
    white-space: normal;
    overflow: visible;
  }
  .nav-icon { flex: none; }
  #topbar { min-height: 3rem; }
  .view-title { font-size: 1.6rem; }
}

/* Short and wide: keep the panels beside each other, and keep the month
   grid short enough that more than one week is on screen. */
@media (orientation: landscape) and (max-height: 500px) {
  #find-time-body.three-col {
    grid-template-columns: 14rem minmax(0, 1fr);
  }
  #ft-side { grid-column: 1 / -1; }
}
/* Wide enough for all three panels side by side, as on a desktop. */
@media (orientation: landscape) and (max-height: 500px) and (min-width: 800px) {
  #find-time-body.three-col {
    grid-template-columns: 11rem minmax(0, 1fr) 13rem;
  }
  #ft-side, #ft-controls { grid-column: auto; }
  .cal-day {
    aspect-ratio: auto;
    min-height: 2.4rem;
  }
  .view-title { font-size: 1.4rem; margin: 0.2rem 0; }
}

@media (orientation: landscape) and (max-height: 500px) and (max-width: 700px) {
  #app { --tabbar-h: 3rem; }
  #nav { padding-top: 0; }
  .nav-link { min-height: 44px; padding: 0.15rem; }
  .nav-text { display: none; }
  .nav-icon { width: 1.35rem; height: 1.35rem; }
}

/* Reduced motion is one rule for the whole page now, in css/kit/motion.css. */

/* ===================== Touch targets on small screens ==================== */
/* The schedule editor packs seven cards across on a desktop, where 32px
   inputs are fine under a mouse. On a phone, in either orientation, every
   time field and every day toggle has to take a thumb. */
@media (max-width: 900px), (max-height: 500px) {
  .sched-interval input[type="time"] {
    min-height: 44px;
    font-size: 1rem;
  }
  .sched-day-label {
    display: flex;
    align-items: center;
    min-height: 44px;
  }
}

/* The busy swatch matches the hatched day it stands for, not a plain dot. */
.legend-item[data-mark="busy"]::before {
  background-color: transparent;
  background-image: repeating-linear-gradient(45deg, var(--swatch, currentColor) 0 2px, transparent 2px 4px);
  border: 1px solid var(--swatch, currentColor);
}

/* Native date and time controls: never narrower than their value, and the
   picker button has room of its own instead of sitting over the text. */
input[type="date"],
input[type="time"] {
  min-width: 0;
  padding-right: 0.4rem;
}

/* ============================ Invite codes ============================= */

.invite-row { align-items: center; }
.invite-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 1.05rem;
  letter-spacing: 0.06em;
}
.invite-row[data-used="true"] .invite-code { text-decoration: line-through; }
.invite-actions { display: flex; flex-wrap: wrap; gap: 0.35rem; }

/* The narrow column suits a login form; a profile with a seven-day editor
   and an event form with a date row do not. Their sections take the room. */
#view-profile.view-narrow { max-width: 64rem; }
#view-event-form.view-narrow,
#view-settings.view-narrow { max-width: 40rem; }

/* The schedule editor answers to its own width, wherever it is placed:
   seven cards across when there is room for seven readable cards, a
   wrapping grid of ~130px cards when there is not. */
.sched-grid { container-type: inline-size; }
@container (max-width: 880px) {
  .sched-week { grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); }
}
.invite-copy,
.invite-share,
.invite-revoke { font-size: 0.85rem; padding: 0.35rem 0.7rem; }

/* Accept / Ignore on a friend request, beside the entry; on a phone the
   pair drops under the text rather than squeezing it. */
.feed-item { flex-wrap: wrap; }
.feed-actions { display: flex; gap: 0.35rem; flex: none; margin-left: auto; }
.feed-accept,
.feed-ignore { font-size: 0.85rem; padding: 0.35rem 0.7rem; }
