/* Bakalari web app.

   The look is borrowed from the subject: a school day is a timeline, so the day
   view is a rail with the clock down the left, real breaks drawn as gaps and a
   live line at the current time. Times, rooms and hour numbers are data, so they
   are set in mono; everything else is prose.

   Themes are swapped by [data-theme] on <html>, never by a media query alone,
   so the switcher can override the system. */

:root {
  --bg: #f1f3f0;
  --surface: #ffffff;
  --surface-2: #e7eae6;
  --line: #d6dbd5;
  --line-soft: #e4e8e2;
  --text: #14181a;
  --muted: #667069;
  --faint: #8d968f;
  --accent: #00794a;
  --accent-ink: #ffffff;
  --accent-wash: #dff0e6;
  --warn: #9a6100;
  --warn-wash: #fbeed6;
  --danger: #b8331f;
  /* What a task is, at a glance, in every view that shows one. Independent of
     the accent colour - these mean something, the accent is only taste. */
  --todo: #b45309;
  --todo-bg: #fbeddb;
  --done: #15803d;
  --done-bg: #dcf1e3;
  --note: #1d4ed8;
  --note-bg: #dee7fc;
  --shadow: 0 1px 2px rgb(20 24 26 / .06), 0 8px 24px -16px rgb(20 24 26 / .3);
  --radius: 12px;
  color-scheme: light;
}

[data-theme="dark"] {
  --bg: #0d1012;
  --surface: #161a1d;
  --surface-2: #1e2428;
  --line: #293035;
  --line-soft: #21282c;
  --text: #e4e9e6;
  --muted: #8c9a94;
  --faint: #6b7873;
  --accent: #37dd8c;
  --accent-ink: #062516;
  --accent-wash: #12291f;
  --warn: #e0a552;
  --warn-wash: #2a2115;
  --danger: #f0705f;
  --todo: #efb055;
  --todo-bg: #2c2317;
  --done: #4ade80;
  --done-bg: #15291d;
  --note: #7da8ff;
  --note-bg: #172337;
  --shadow: 0 1px 2px rgb(0 0 0 / .4), 0 12px 32px -20px rgb(0 0 0 / .9);
  color-scheme: dark;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
}

/* A short view and a tall one must not move the page sideways, so the
   scrollbar's space is always reserved whether or not one is showing. */
html { scrollbar-gutter: stable; }

body {
  font: 400 15px/1.5 "IBM Plex Sans", system-ui, sans-serif;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior-y: none;
}

h1, h2, h3, .brand strong, .day-name {
  font-family: "Space Grotesk", "IBM Plex Sans", system-ui, sans-serif;
  font-weight: 700;
  letter-spacing: -.015em;
  margin: 0;
}

.mono, .time, .hour, .meta, .count, .side-range {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-variant-numeric: tabular-nums;
}

[hidden] { display: none !important; }

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

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

.btn {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: .875rem;
  padding: .45rem .8rem;
  border-radius: 9px;
  cursor: pointer;
  transition: background .12s, border-color .12s, color .12s;
}
.btn:hover { background: var(--surface-2); }
.btn:active { transform: translateY(1px); }
.btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  font-weight: 600;
}
.btn.primary:hover { filter: brightness(1.08); }
.btn.ghost { border-color: transparent; background: transparent; color: var(--muted); }
.btn.ghost:hover { background: var(--surface-2); color: var(--text); }
.btn.danger:hover { color: var(--danger); }
.btn.block { width: 100%; justify-content: center; padding: .65rem; }
.btn.sm { padding: .3rem .6rem; font-size: .8125rem; }
.btn.xs { padding: .15rem .35rem; font-size: .75rem; }
.btn.icon { padding: .4rem; width: 2rem; height: 2rem; justify-content: center; }
.btn.icon.xs { width: 1.5rem; height: 1.5rem; padding: 0; }
.ico { font-size: 1rem; line-height: 1; }

.week-nav { display: inline-flex; gap: .25rem; align-items: center; }

/* ------------------------------------------------------------------- fields */

.field { display: block; margin-bottom: .9rem; }
.field > span {
  display: block;
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--muted);
  margin-bottom: .35rem;
}
.field input, .field textarea, .field select {
  width: 100%;
  font: inherit;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: .6rem .7rem;
  resize: vertical;
}
.field select {
  appearance: none;
  padding-right: 2rem;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 1rem) 55%, calc(100% - .7rem) 55%;
  background-size: .3rem .3rem, .3rem .3rem;
  background-repeat: no-repeat;
  cursor: pointer;
}

.field input:focus, .field textarea:focus, .field select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-wash);
}
.error { color: var(--danger); font-size: .875rem; margin: 0 0 .6rem; min-height: 1.2em; }
.warn-text { color: var(--warn); font-size: .875rem; margin: 0 0 .8rem; }

/* ------------------------------------------------------------------ sign in */

.login {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 1.25rem;
}
.login[hidden] { display: none; }
.login-card {
  width: min(23rem, 100%);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 1.6rem;
  box-shadow: var(--shadow);
}
.login-brand { display: flex; gap: .8rem; align-items: center; margin-bottom: 1.5rem; }
.login-brand h1 { font-size: 1.25rem; }
.login-brand p { margin: .1rem 0 0; font-size: .8125rem; color: var(--muted); }
.login-foot {
  display: flex;
  gap: .25rem;
  justify-content: center;
  margin-top: 1rem;
  padding-top: .9rem;
  border-top: 1px solid var(--line-soft);
}

.mark {
  display: grid;
  place-items: center;
  width: 2.1rem;
  height: 2.1rem;
  flex: none;
  border-radius: 8px;
  background: var(--accent);
  color: var(--accent-ink);
  font-family: "Space Grotesk", sans-serif;
  font-weight: 700;
  font-size: 1.1rem;
}

/* ----------------------------------------------------------------- the shell */

.shell { min-height: 100dvh; }
.shell[hidden] { display: none; }
.main { display: flex; flex-direction: column; min-height: 100dvh; }

.sidebar {
  display: none;
  border-right: 1px solid var(--line);
  background: var(--surface);
  padding: 1rem .75rem;
  flex-direction: column;
  gap: 1.25rem;
}
/* The row keeps the mark's height whatever is in it, so collapsing the sidebar
   does not shunt everything below it up or down. */
.brand {
  display: flex;
  gap: .7rem;
  align-items: center;
  padding: .25rem .35rem;
  /* Tall enough for the two lines of the open sidebar and for the lone toggle
     of the rail, so the row is one height and never both. */
  min-height: 2.75rem;
}
/* The toggle is one of the sidebar's own buttons and looks like one: no border,
   no fill, the same muted icon that goes quiet until you point at it. */
.brand #menu-toggle {
  margin-left: auto;
  flex: none;
  width: 2.1rem;
  height: 2.1rem;
  border: 0;
  background: transparent;
  color: var(--muted);
  border-radius: 9px;
}
.brand #menu-toggle:hover { background: var(--surface-2); color: var(--text); }
.brand-text { display: grid; line-height: 1.25; min-width: 0; }
.brand strong { font-size: 1.0625rem; }
.brand span {
  font-size: .75rem;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.nav { display: grid; gap: 2px; }
/* One height for every button down this side, in both states: the labels come
   and go, the boxes they sit in do not. */
.nav-item, .side-foot .btn { min-height: 2.45rem; box-sizing: border-box; }
.nav-item {
  display: flex;
  align-items: center;
  gap: .65rem;
  width: 100%;
  border: 0;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: .9375rem;
  text-align: left;
  padding: .5rem .6rem;
  border-radius: 9px;
  cursor: pointer;
}
.nav-item:hover { background: var(--surface-2); color: var(--text); }
.nav-item[aria-current="true"] {
  background: var(--accent-wash);
  color: var(--accent);
  font-weight: 600;
}
.nav-item .ico { width: 1.1rem; text-align: center; }
.nav-item .count {
  margin-left: auto;
  font-size: .75rem;
  background: var(--surface-2);
  color: var(--muted);
  padding: .05rem .4rem;
  border-radius: 999px;
}
.nav-item .count.hot { background: var(--accent); color: var(--accent-ink); }

.side-week { border-top: 1px solid var(--line-soft); padding-top: .9rem; }
.side-week-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: .75rem;
  color: var(--muted);
  padding: 0 .35rem .5rem;
}
.side-days { display: grid; gap: 2px; }

/* A day button carries a density bar: which hours of the day are actually
   taught. It says "Thursday is full, Tuesday is one lesson" without a number. */
.day-btn {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: .15rem .5rem;
  align-items: center;
  border: 0;
  background: transparent;
  color: var(--text);
  font: inherit;
  text-align: left;
  padding: .4rem .6rem;
  border-radius: 9px;
  cursor: pointer;
}
.day-btn:hover { background: var(--surface-2); }
.day-btn[aria-current="true"] { background: var(--accent-wash); }
.day-btn[aria-current="true"] .day-btn-name { color: var(--accent); font-weight: 600; }
.day-btn.is-today .day-btn-date { color: var(--accent); }
.day-btn-name { font-size: .9375rem; }
.day-btn-date { font-size: .75rem; color: var(--muted); font-family: "IBM Plex Mono", monospace; }
.density { grid-column: 1 / -1; display: flex; gap: 2px; height: 3px; }
.density i { flex: 1; border-radius: 1px; background: var(--line); }
.density i.on { background: var(--accent); opacity: .55; }
.density i.note { background: var(--tone, var(--todo)); opacity: .9; }
.day-btn[aria-current="true"] .density i.on { opacity: 1; }

.side-foot {
  margin-top: auto;
  display: grid;
  gap: 2px;
  border-top: 1px solid var(--line-soft);
  padding-top: .6rem;
}
.side-foot .btn {
  justify-content: flex-start;
  font-size: .875rem;
  /* Same icon column as the nav above it. These carry a border and the nav
     items do not, so it comes back out of the padding. */
  padding: .5rem calc(.6rem - 1px);
}

/* ----------------------------------------------------------------- top bar */

.head-stack { position: sticky; top: 0; z-index: 20; }

.topbar {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: .75rem 1rem;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--line);
}
.head-title { min-width: 0; }
.head-title h1 { font-size: 1.25rem; line-height: 1.2; }
.head-title p { margin: .1rem 0 0; font-size: .8125rem; color: var(--muted); }
.head-actions { margin-left: auto; display: flex; align-items: center; gap: .25rem; }
.status { font-size: .8125rem; color: var(--warn); }

.day-strip {
  display: flex;
  gap: .4rem;
  overflow-x: auto;
  scrollbar-width: none;
  padding: .6rem 1rem;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.day-strip::-webkit-scrollbar { display: none; }
.chip {
  flex: none;
  display: grid;
  justify-items: center;
  gap: .1rem;
  min-width: 3.4rem;
  padding: .4rem .5rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  cursor: pointer;
}
.chip small { font-size: .6875rem; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }
.chip b { font-family: "IBM Plex Mono", monospace; font-size: .9375rem; font-weight: 500; }
.chip[aria-current="true"] { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.chip[aria-current="true"] small { color: var(--accent-ink); opacity: .8; }
.chip.is-today:not([aria-current="true"]) { border-color: var(--accent); }
.chip .dot { width: 4px; height: 4px; border-radius: 50%; background: var(--todo); }

.content {
  flex: 1;
  width: 100%;
  max-width: 56rem;
  --wide: 74rem;
  margin: 0 auto;
  padding: 1.25rem 1rem calc(5.5rem + env(safe-area-inset-bottom));
  display: grid;
  gap: 1.25rem;
  align-content: start;
}

/* ---------------------------------------------------------------- tab bar */

.tabbar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 30;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  backdrop-filter: blur(12px);
  border-top: 1px solid var(--line);
  padding-bottom: env(safe-area-inset-bottom);
}
.tab {
  display: grid;
  justify-items: center;
  gap: .1rem;
  border: 0;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: .6875rem;
  padding: .5rem .25rem .55rem;
  cursor: pointer;
  position: relative;
}
.tab .ico { font-size: 1.15rem; }
.tab[aria-current="true"] { color: var(--accent); }
.tab .count {
  position: absolute;
  top: .3rem;
  left: 50%;
  margin-left: .35rem;
  min-width: 1rem;
  font-size: .625rem;
  line-height: 1rem;
  text-align: center;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-ink);
}

/* ------------------------------------------------------------------ cards */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.card-head {
  display: flex;
  align-items: center;
  gap: .6rem;
  padding: .75rem .9rem;
  border-bottom: 1px solid var(--line-soft);
}
.card-head h2 { font-size: .8125rem; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); font-weight: 600; }
.card-head .btn { margin-left: auto; }
.card-body { padding: .9rem; display: grid; gap: .7rem; }

.card.overdue { border-color: color-mix(in srgb, var(--danger) 55%, var(--line)); }
.card.overdue .card-head h2 { color: var(--danger); }

.card.attention { border-color: var(--warn); background: var(--warn-wash); }
.card.attention .card-head { border-bottom-color: color-mix(in srgb, var(--warn) 30%, transparent); }
.card.attention .card-head h2 { color: var(--warn); }

.empty {
  text-align: center;
  color: var(--muted);
  padding: 2.5rem 1rem;
  border: 1px dashed var(--line);
  border-radius: var(--radius);
}
.empty strong { display: block; font-family: "Space Grotesk", sans-serif; color: var(--text); margin-bottom: .25rem; }

.section-title {
  font-size: .8125rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--muted);
  font-weight: 600;
  display: flex;
  align-items: baseline;
  gap: .5rem;
}

/* --------------------------------------------------------------- the rail */

.rail { list-style: none; margin: 0; padding: 0; }

.slot {
  display: grid;
  grid-template-columns: 3.1rem 1.6rem 1fr auto;
  align-items: stretch;
  column-gap: .5rem;
}
.slot .time {
  font-size: .8125rem;
  color: var(--muted);
  padding-top: .95rem;
  text-align: right;
}
.slot .spine { position: relative; display: grid; justify-items: center; }
.slot .spine::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--line);
}
.slot:first-child .spine::before { top: .9rem; }
.slot:last-child .spine::before { bottom: calc(100% - .9rem); }
.slot .hour {
  position: relative;
  margin-top: .45rem;
  width: 1.6rem;
  height: 1.6rem;
  display: grid;
  place-items: center;
  border-radius: 7px;
  background: var(--surface-2);
  color: var(--muted);
  font-size: .8125rem;
  font-weight: 500;
}
.slot .lesson {
  min-width: 0;
  padding: .7rem .8rem;
  margin: .25rem 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
}
.slot .lesson h3 { font-size: .9375rem; font-weight: 600; font-family: inherit; letter-spacing: 0; }
.slot .meta { font-size: .75rem; color: var(--faint); margin-top: .15rem; }
.slot .meta b { color: var(--muted); font-weight: 500; }
.slot .add {
  align-self: center;
  opacity: 0;
  transition: opacity .12s;
}
.slot:hover .add, .slot:focus-within .add { opacity: 1; }

.slot.has-notes .hour { background: var(--tone); color: var(--surface); }
.slot.has-notes .lesson { border-color: color-mix(in srgb, var(--tone) 45%, var(--line)); }
.slot.cancelled .lesson { background: var(--bg); }
.slot.cancelled h3 { text-decoration: line-through; color: var(--muted); font-weight: 500; }
.slot.cancelled .hour { background: transparent; border: 1px dashed var(--line); }

.change { font-size: .75rem; color: var(--warn); margin-top: .35rem; }
.theme-line { font-size: .8125rem; color: var(--muted); margin-top: .35rem; }

/* A real gap in the day - lunch, a free period - drawn as one. */
.rail .gap {
  display: grid;
  grid-template-columns: 3.1rem 1.6rem 1fr;
  column-gap: .5rem;
  align-items: center;
  height: 2.2rem;
}
.rail .gap .spine { position: relative; height: 100%; display: grid; justify-items: center; }
.rail .gap .spine::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  background: repeating-linear-gradient(var(--line) 0 3px, transparent 3px 7px);
}
.rail .gap span { font-size: .75rem; color: var(--faint); }

/* Where the clock is right now, only ever drawn on today. */
.rail .now {
  display: grid;
  grid-template-columns: 3.1rem 1.6rem 1fr;
  column-gap: .5rem;
  align-items: center;
  height: 1.4rem;
}
.rail .now .time { color: var(--accent); font-weight: 500; padding: 0; font-size: .75rem; text-align: right; }
.rail .now .spine { position: relative; height: 100%; display: grid; justify-items: center; }
.rail .now .spine::before { content: ""; position: absolute; top: 0; bottom: 0; width: 2px; background: var(--line); }
.rail .now .spine::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 9px;
  height: 9px;
  margin-top: -4.5px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-wash);
}
.rail .now .line { height: 2px; background: linear-gradient(to right, var(--accent), transparent); }

/* ------------------------------------------------------------------ notes */

/* Three states, three colours, the same three everywhere a task shows up:
   homework still to do, homework done, and a plain note. */
.tone-todo { --tone: var(--todo); --tone-bg: var(--todo-bg); }
.tone-done { --tone: var(--done); --tone-bg: var(--done-bg); }
.tone-note { --tone: var(--note); --tone-bg: var(--note-bg); }

.note-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: .55rem;
  align-items: start;
  font-size: .875rem;
  border-left: 3px solid var(--tone);
  padding: .1rem 0 .1rem .55rem;
}
.note-row + .note-row { border-top: 1px solid var(--line-soft); padding-top: .55rem; }
.note-row .box {
  appearance: none;
  width: 1.05rem;
  height: 1.05rem;
  margin: .12rem 0 0;
  border: 1.5px solid var(--tone);
  border-radius: 5px;
  background: var(--bg);
  cursor: pointer;
  display: grid;
  place-items: center;
  flex: none;
}
.note-row .box:checked { background: var(--done); border-color: var(--done); }
.box-note {
  width: 1.05rem;
  height: 1.05rem;
  margin: .12rem 0 0;
  display: grid;
  place-items: center;
  flex: none;
}
.box-note::after {
  content: "";
  width: .5rem;
  height: .5rem;
  border-radius: 50%;
  background: var(--note);
}

.note-row .box:checked::after {
  content: "";
  width: .3rem;
  height: .55rem;
  margin-top: -2px;
  border: solid var(--surface);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.note-row .body { min-width: 0; }
.note-row.done .text { text-decoration: line-through; color: var(--faint); }
.note-row .tags { font-size: .75rem; color: var(--faint); margin-top: .15rem; display: flex; flex-wrap: wrap; gap: .35rem; }
.note-row .edit { opacity: .45; }
.note-row:hover .edit { opacity: 1; }

.pill {
  font-size: .6875rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  padding: .05rem .35rem;
  border-radius: 5px;
  background: var(--surface-2);
  color: var(--muted);
}
.pill.hw { background: var(--surface-2); color: var(--muted); }
.pill.late { background: color-mix(in srgb, var(--danger) 18%, transparent); color: var(--danger); }
.pill.soon { background: var(--warn-wash); color: var(--warn); }

/* --------------------------------------------------------------- messages */

.msg { border: 0; background: transparent; padding: 0; }
.msg + .msg { border-top: 1px solid var(--line-soft); }
.msg summary {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: .6rem;
  align-items: baseline;
  padding: .8rem .9rem;
  cursor: pointer;
  list-style: none;
}
.msg summary::-webkit-details-marker { display: none; }
.msg .unread { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); align-self: center; }
.msg .read-dot { width: 7px; height: 7px; }
.msg h3 { font-size: .9375rem; font-weight: 600; font-family: inherit; letter-spacing: 0; }
.msg .from { font-size: .75rem; color: var(--muted); margin-top: .1rem; }
.msg .when { font-size: .75rem; color: var(--faint); font-family: "IBM Plex Mono", monospace; }
.msg .text {
  padding: 0 .9rem 1rem 2.1rem;
  font-size: .875rem;
  white-space: pre-wrap;
  color: var(--text);
  line-height: 1.65;
}
/* --bg is darker than the card in dark mode, so tinting towards surface-2
   instead reads as "opened" in both themes rather than as a hole. */
.msg[open] { background: color-mix(in srgb, var(--surface-2) 55%, var(--surface)); }
.msg[open] summary { border-left: 3px solid var(--accent); padding-left: calc(.9rem - 3px); }

/* ---------------------------------------------------------------- lunch */

.lunch { display: flex; gap: .8rem; align-items: flex-start; }
.lunch .ico { font-size: 1.4rem; }
.lunch .label { font-weight: 600; font-size: .9375rem; }
.lunch .menu { font-size: .875rem; color: var(--muted); margin-top: .15rem; }

/* -------------------------------------------------------------- calendar */

/* A week laid out along the clock: one row per day, time running left to
   right. Rows share the height going spare, so the grid fills whatever room
   the window gives it; hours never squash below --slot-min and scroll instead.
   Which hours are drawn is a setting, hence --slots. */
.cal-wrap {
  overflow: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  min-height: 0;
}
.cal {
  --gutter: 5.5rem;
  --slot-min: 6rem;
  display: grid;
  grid-template-rows: auto repeat(var(--rows, 7), minmax(2.75rem, 1fr));
  /* --seen is how many days should fill the frame. Ask for more rows than that
     and the grid grows past its scroll box, so the rest is one scroll down. */
  --seen: 7;
  min-height: max(100%, calc(var(--rows, 7) / var(--seen) * 100%));
  min-width: calc(var(--gutter) + var(--slots) * var(--slot-min));
}

.cal-head, .cal-row { display: grid; grid-template-columns: var(--gutter) 1fr; }
.cal-head {
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
.cal-row + .cal-row { border-top: 1px solid var(--line-soft); }
.cal-row.is-today { background: color-mix(in srgb, var(--accent-wash) 55%, transparent); }

.cal-hours, .cal-track {
  position: relative;
  display: grid;
  grid-template-columns: repeat(var(--slots), minmax(var(--slot-min), 1fr));
}
.cal-hours .cal-slot { padding: .45rem 0 .4rem .4rem; }
.cal-hours span {
  font-family: "IBM Plex Mono", monospace;
  font-size: .6875rem;
  color: var(--faint);
}
.cal-slot + .cal-slot { border-left: 1px solid var(--line-soft); }
.cal-track .cal-slot { min-width: 0; }

/* The day names stay put while the clock scrolls under them. */
.cal-corner, .cal-daylabel {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--surface);
  border-right: 1px solid var(--line);
}
.cal-head .cal-corner { z-index: 4; }
.cal-daylabel {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: .05rem;
  padding: .3rem .6rem;
  min-width: 0;
}
.cal-row.is-today .cal-daylabel { background: var(--accent-wash); }
.cal-daylabel small {
  font-size: .75rem;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cal-daylabel b {
  font-family: "IBM Plex Mono", monospace;
  font-size: .6875rem;
  font-weight: 400;
  color: var(--muted);
  white-space: nowrap;
}
.cal-row.is-today .cal-daylabel small,
.cal-row.is-today .cal-daylabel b { color: var(--accent); }
.cal-daylabel .on-narrow { display: none; }

.cal-track { cursor: copy; }
.cal-block {
  position: absolute;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: .05rem;
  overflow: hidden;
  text-align: left;
  font: inherit;
  padding: .2rem .4rem .2rem .55rem;
  margin: 1px 0;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--surface-2);
  color: var(--text);
  cursor: pointer;
}
.cal-block b, .cal-block span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cal-block b { font-size: .75rem; font-weight: 600; line-height: 1.25; }
.cal-block span {
  font-family: "IBM Plex Mono", monospace;
  font-size: .625rem;
  color: var(--muted);
  line-height: 1.2;
}
.cal-block:hover { filter: brightness(.97); z-index: 2; }
.cal-block .bar {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  display: flex;
  flex-direction: column;
  border-radius: 6px 0 0 6px;
  overflow: hidden;
}
.cal-block .bar i { flex: 1; background: var(--tone); }
.cal-block .bar .seg-plain { background: var(--muted); }
.cal-block .bar .seg-event { background: var(--accent); }

.cal-block.event { background: var(--accent-wash); }
.cal-block.event b, .cal-block.event span { color: var(--accent); }
.cal-block.has-notes { background: var(--tone-bg); }
.cal-block.has-notes b { color: var(--tone); }
.cal-block.cancelled b { text-decoration: line-through; color: var(--muted); }
.cal-block .repeats {
  position: absolute;
  top: .1rem;
  right: .25rem;
  font-size: .625rem;
  font-style: normal;
  opacity: .7;
}

/* What the drag is blocking out, while it is still being dragged. */
.cal-ghost {
  position: absolute;
  top: 2px;
  bottom: 2px;
  min-width: 2px;
  display: grid;
  place-items: center;
  border: 1px solid var(--accent);
  border-radius: 6px;
  background: var(--accent-wash);
  pointer-events: none;
  z-index: 3;
}
.cal-ghost span {
  font-family: "IBM Plex Mono", monospace;
  font-size: .625rem;
  color: var(--accent);
  white-space: nowrap;
}

.cal-track { touch-action: pan-y; user-select: none; }

.cal-now {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--danger);
  z-index: 1;
  pointer-events: none;
}
.cal-now::before {
  content: "";
  position: absolute;
  top: -3px;
  left: -3px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--danger);
}


/* The rail below the grid, in place of the native scrollbar: a finger cannot
   drag one of those, and it fades out exactly when it is wanted. Sticky so it
   sitting directly under the grid, where the eye already is. */
.cal-wrap { scrollbar-width: none; }
.cal-wrap::-webkit-scrollbar { display: none; }

.cal-pan {
  position: relative;
  height: 1.15rem;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  touch-action: none;
  cursor: pointer;
}
.cal-pan-thumb {
  position: absolute;
  top: 2px;
  bottom: 2px;
  min-width: 2.5rem;
  border-radius: 999px;
  background: var(--muted);
  touch-action: none;
}
.cal-pan-thumb.dragging { background: var(--accent); }

/* ---------------------------------------------------------------- admin */

.user-add {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;
  gap: .5rem;
  margin-bottom: 1rem;
}
.user-add input {
  min-width: 0;
  padding: .55rem .7rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--surface);
  color: var(--text);
  font: inherit;
  font-size: .9rem;
}
.user-add input:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }

.user-row {
  display: flex;
  align-items: center;
  gap: .75rem;
  padding: .7rem 1rem;
}
.user-row + .user-row { border-top: 1px solid var(--line-soft); }
.user-row .body { flex: 1; min-width: 0; }
.user-row .name { font-size: .95rem; word-break: break-all; }
.user-row .tags {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .4rem;
  margin-top: .2rem;
  font-size: .78rem;
  color: var(--muted);
}
.user-row .btn.armed { background: var(--danger); border-color: var(--danger); color: #fff; }

@media (max-width: 30rem) {
  .user-add { grid-template-columns: minmax(0, 1fr); }
}

/* --------------------------------------------------------- detail sheet */

.detail-body { padding: 1.25rem 1.25rem .25rem; }
.detail-head h2 { font-size: 1.15rem; }
.detail-head .when { margin: .2rem 0 0; font-size: .875rem; color: var(--muted); }
.detail-facts { margin: 1rem 0 0; display: grid; gap: .5rem; }
.detail-row { display: grid; grid-template-columns: 5.5rem 1fr; gap: .6rem; font-size: .875rem; }
.detail-row dt {
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--muted);
  padding-top: .1rem;
}
.detail-row dd { margin: 0; }
.detail-notes { margin-top: 1.1rem; display: grid; gap: .55rem; }
.detail-notes h3 {
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--muted);
  font-family: inherit;
  font-weight: 600;
}
.muted-line { margin: 0; font-size: .875rem; color: var(--faint); }
#detail .sheet-foot { padding: 1rem 1.25rem 1.25rem; margin-top: 0; }

/* Canteen login */
.strava { display: grid; gap: .5rem; }
.strava-status { margin: 0; font-size: .8125rem; color: var(--muted); }
.strava-status.on { color: var(--done); font-weight: 500; }
.strava-field { display: grid; gap: .2rem; }
.strava-field small { font-size: .6875rem; color: var(--muted); }
.strava-field input {
  width: 100%;
  font: inherit;
  font-size: .875rem;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: .45rem .6rem;
}
.strava-field input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-wash);
}
.strava-actions { display: flex; gap: .4rem; margin-top: .1rem; }
.strava .hint { margin: 0; }

.two-up { display: grid; grid-template-columns: 1fr 1fr; gap: .6rem; }
.two-up .field { margin-bottom: 0; }
.hint { display: block; margin-top: .3rem; font-size: .75rem; color: var(--faint); }

/* ----------------------------------------------------------------- sheet */

.sheet {
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 0;
  width: min(30rem, calc(100vw - 2rem));
  max-height: calc(100dvh - 2rem);
  overflow-y: auto;
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--shadow);
}
.sheet::backdrop { background: rgb(0 0 0 / .45); backdrop-filter: blur(2px); }
.sheet form { padding: 1.25rem; }
.sheet h2 { font-size: 1.0625rem; margin-bottom: 1rem; }

/* Stays put while the rest of the sheet scrolls, so the way out is always in
   the same corner - which is the whole point of it. */
.sheet-head {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: flex-start;
  gap: .75rem;
  margin: -1.25rem -1.25rem 0;
  padding: 1.25rem 1.25rem .9rem;
  background: var(--surface);
}
.sheet-head h2 { flex: 1; margin: 0; }
.sheet-head .btn { flex: none; margin-top: -.3rem; }
.sheet-foot { display: flex; gap: .5rem; align-items: center; margin-top: .25rem; }
.sheet-foot .spacer { flex: 1; }

.check-row {
  display: flex;
  align-items: center;
  gap: .55rem;
  margin-bottom: .9rem;
  cursor: pointer;
}
.check-row input { width: 1.05rem; height: 1.05rem; accent-color: var(--accent); }
.check-row span { font-size: .9rem; }

.kinds { display: flex; gap: .4rem; margin-bottom: .9rem; }
.kind-pick {
  flex: 1;
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--muted);
  font: inherit;
  font-size: .875rem;
  padding: .45rem;
  border-radius: 9px;
  cursor: pointer;
}
.kind-pick[aria-pressed="true"] {
  background: var(--tone-bg);
  border-color: var(--tone);
  color: var(--tone);
  font-weight: 600;
}

/* Colour picker: the presets cover the usual answer, the last swatch is a
   native colour input wearing the same clothes. */
.swatches { display: flex; flex-wrap: wrap; gap: .5rem; }
.swatch {
  position: relative;
  width: 1.9rem;
  height: 1.9rem;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / .12);
  cursor: pointer;
  overflow: hidden;
}
.swatch[aria-pressed="true"] {
  border-color: var(--text);
  outline: 2px solid var(--bg);
  outline-offset: -4px;
}
.custom-row { display: flex; align-items: center; gap: .5rem; margin-top: .6rem; }
.custom-row input[type="color"] {
  appearance: none;
  flex: none;
  width: 1.9rem;
  height: 1.9rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  box-shadow: inset 0 0 0 1px rgb(0 0 0 / .15);
  cursor: pointer;
}
.custom-row input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; }
.custom-row input[type="color"]::-webkit-color-swatch { border: 0; border-radius: 50%; }
.custom-row input[type="color"]::-moz-color-swatch { border: 0; border-radius: 50%; }
.custom-row .hex {
  width: 7rem;
  font: inherit;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
  font-size: .8125rem;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 9px;
  padding: .4rem .55rem;
}
.custom-row .hex:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-wash);
}

/* ----------------------------------------------------------------- layout */

.only-mobile { display: inline-flex; }

/* Nothing hovers on a phone, so the per-lesson add button cannot hide there. */
@media (hover: none) {
  .slot .add { opacity: .5; }
}

/* Below the desktop breakpoint the clock is wider than the screen, and a drag
   on the grid draws an event rather than panning - so the bar at the bottom is
   the way across. Styling it at all opts out of the overlay scrollbar that
   fades away, which is the point: it has to be there before it is needed. */
@media (max-width: 59.99rem) {
  /* A phone has room for the school week and not much else, so the weekend
     waits below the fold rather than squeezing the days that matter. */
  .cal { --seen: 5; }

  /* "Po" says as much as "Pondělí" next to the date, and gives the column back
     to the clock, which is the part with something to show. */
  .cal { --gutter: 3.6rem; }
  .cal-daylabel { padding: .3rem .45rem; }
  .cal-daylabel .on-wide { display: none; }
  .cal-daylabel .on-narrow { display: inline; }

  /* A school week is five chips and they should use the width, rather than
     leaving a gap on the right. They still scroll if a week ever needs more. */
  .day-strip .chip { flex: 1 1 auto; }

}

@media (max-width: 30rem) {
  .cal { --slot-min: 5rem; }
  .slot, .rail .gap, .rail .now { grid-template-columns: 2.6rem 1.4rem 1fr auto; column-gap: .4rem; }
  .topbar { padding: .7rem .75rem; }
  .content { padding-left: .75rem; padding-right: .75rem; }
  .day-strip { padding-left: .75rem; padding-right: .75rem; }
}

/* The calendar wants the room, so it takes the whole frame and scrolls inside
   itself rather than growing the page. */
.content.wide {
  max-width: none;
  min-height: 0;
  align-content: stretch;
  /* The rail belongs to the grid above it, not to the page, and the room under
     it only has to clear the tab bar - the usual bottom padding is for content
     that scrolls under the tabs, which this never does. Both gaps come off the
     same value so the rail sits evenly between the grid and the tabs. */
  --rail-gap: .55rem;
  gap: var(--rail-gap);
  padding-bottom: calc(3.7rem + var(--rail-gap) + env(safe-area-inset-bottom));
  /* the grid, then the pan rail under it */
  grid-template-rows: minmax(0, 1fr) auto;
}

.desk-only { display: none; }

@media (min-width: 60rem) {
  .shell:not([hidden]) { display: grid; grid-template-columns: 16.5rem 1fr; }
  /* Minimised, not closed: the sidebar keeps its icons and loses only the
     words, so every destination is still one click away. */
  .shell.mini:not([hidden]) { grid-template-columns: 3.75rem 1fr; }
  /* Same padding as the open sidebar, vertical and horizontal both: the icons
     keep the exact line and column they were already on, and only the width of
     the panel around them changes. */
  .shell.mini .sidebar { padding: 1rem .75rem; }
  /* The rail is too narrow for the mark and the toggle side by side, so the
     toggle takes the mark's place: same size, same spot, nothing moves. */
  .shell.mini .brand { padding: .25rem 0; }
  .shell.mini .brand .mark { display: none; }
  /* In the rail it takes a nav item's shape, so its icon lands in the same
     column as everything below it. */
  .shell.mini .brand #menu-toggle {
    margin-left: 0;
    width: 100%;
    justify-content: flex-start;
    padding-left: .6rem;
  }
  .shell.mini .brand-text,
  .shell.mini .side-week,
  .shell.mini .nav-item .label,
  .shell.mini .side-foot .btn > span:not(.ico) { display: none; }
  .shell.mini .nav { width: 100%; }
  .shell.mini .nav-item,
  .shell.mini .side-foot .btn { position: relative; }
  .shell.mini .nav-item .count {
    position: absolute;
    top: .15rem;
    right: .3rem;
    margin: 0;
    min-width: 1rem;
    padding: 0 .2rem;
    font-size: .625rem;
    line-height: 1rem;
    text-align: center;
    background: var(--accent);
    color: var(--accent-ink);
  }
  .shell.mini .side-foot { width: 100%; }
  /* The week rail is gone with the labels, so the day chips step in for it -
     but only where they were wanted; :not([hidden]) keeps this !important from
     overriding the one on [hidden]. */
  .shell.mini .day-strip:not([hidden]) { display: flex !important; }
  .sidebar { display: flex; position: sticky; top: 0; height: 100dvh; }
  .tabbar, .day-strip, .only-mobile { display: none !important; }
  .desk-only { display: inline-flex; }
  /* On a desktop the week always fits: the hours divide whatever width there
     is rather than forcing a scrollbar. Only a phone scrolls the clock. */
  .cal { --slot-min: 0px; --gutter: 4.25rem; }
  .content.wide { padding: 1rem 1rem 1.25rem; }
  .content { padding: 1.5rem 1.75rem 3rem; }
  .topbar { padding: 1rem 1.75rem; }
  .head-title h1 { font-size: 1.4rem; }
}

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