/* horaire.css — the CVM agenda. Layered on styles.css: it reuses the site's tokens
   (--card, --border, --accent, --muted…) so the page follows the same light/dark theme
   as everything else, and only adds what a timetable needs. */

.horaire main { max-width: 1180px; margin: 0 auto; padding: 0 18px 60px; }
.hz-wrap { max-width: 760px; margin: 0 auto; }

/* ==============================================================================
   THE APP SHELL — only while the agenda itself is open (`body.hz-app`, set by the
   script). The import and sign-in screens stay an ordinary scrolling page.

   A timetable is a thing you look AT, not a document you read down. Making the window
   the frame means the date header and the view switcher never leave, the grid is sized
   to the room actually available (see fitScale in horaire-app.js), and a normal week
   needs no scrolling at all. What scrolls is one region in the middle, and only when
   there is genuinely more than fits.

   `100dvh` rather than `100vh`: on a phone the URL bar collapses and expands, and `vh`
   is frozen to the larger of the two — which puts the dock under the browser chrome for
   the whole session.
   ============================================================================== */
body.hz-app { display: flex; flex-direction: column; height: 100dvh; overflow: hidden; }
body.hz-app .topbar { flex: none; }
/* Full width. A timetable is a grid whose columns get better the wider they are, and a
   1180px column on a 2560px monitor spends half the screen on nothing while the room
   numbers ellipsise. The reading-width cap belongs to the prose screens, not to this. */
body.hz-app main {
  flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
  max-width: none; width: 100%;
  /* The vertical padding is explicitly zero: `.horaire main` reserves 60px at the bottom
     for the prose screens' footer, and under the app shell that is 60px of dead ground
     below the dock.
     `max(...)` on the sides rather than a plain value: held sideways, a notched phone
     puts the sensor housing over one edge of the window, and the timetable's first column
     is exactly what lands under it. Zero on every device without a cutout. */
  padding-top: 0; padding-bottom: 0;
  padding-left: max(clamp(14px, 1.8vw, 30px), env(safe-area-inset-left, 0px));
  padding-right: max(clamp(14px, 1.8vw, 30px), env(safe-area-inset-right, 0px));
}
body.hz-app #screen-agenda { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; position: relative; }
body.hz-app .site-footer { display: none; }
/* The bottom padding clears the floating dock. `env(safe-area-inset-bottom)` is the home
   indicator on a notched phone, which the dock also has to sit above — the two are added
   rather than one chosen, because the dock's own offset is measured from the safe area.
   `overscroll-behavior` stops the rubber-band from chaining out to the page underneath,
   which on iOS is what makes a scrolling grid feel like it belongs to the window. */
.hz-scroll {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden;
  padding-bottom: calc(74px + env(safe-area-inset-bottom, 0px));
  overscroll-behavior-y: contain; -webkit-overflow-scrolling: touch;
}
/* Set by renderWeek when the week grid misses fitting by a few pixels: those pixels are
   measurement slop, and the only thing scrolling them does is hide the row of day names.
   See lockWeekIfNearlyFits in horaire-app.js — it is cleared on every draw. */
.hz-scroll.hz-locked { overflow-y: hidden; }
/* Outside the shell (print, or a browser without dvh) it is just a plain block. */
body:not(.hz-app) .hz-scroll { overflow: visible; padding-bottom: 0; }
.hz-modal-legal { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); font-size: 13px; }
.topbar-brand .sub { font-size: 12px; color: var(--muted); margin-left: 6px; letter-spacing: .02em; }

.hz-h1 { font-size: clamp(26px, 4vw, 38px); letter-spacing: -.02em; margin: 34px 0 10px; }
.hz-h2 { font-size: 22px; letter-spacing: -.01em; margin: 30px 0 8px; }
.hz-lead { color: var(--muted); line-height: 1.6; margin: 0 0 22px; }
.hz-lead.small { font-size: 14.5px; }
.hz-note { color: var(--muted2); font-size: 13px; line-height: 1.6; }
.hz-error { color: var(--error); font-size: 14px; min-height: 1.2em; margin: 10px 0 0; }

/* --- import ------------------------------------------------------------------ */
.hz-drop {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 40px 20px; border: 2px dashed var(--border); border-radius: 16px;
  background: var(--card); color: var(--muted); cursor: pointer; text-align: center;
  transition: border-color .15s, background .15s;
}
.hz-drop:hover, .hz-drop:focus-visible, .hz-drop.over { border-color: var(--accent); background: var(--hover); outline: none; }
.hz-drop-main { font-size: 16px; font-weight: 650; color: var(--text); }
.hz-drop-sub { font-size: 13px; }
.hz-how { margin-top: 18px; border: 1px solid var(--border); border-radius: 12px; padding: 12px 14px; background: var(--card); }
.hz-how summary { cursor: pointer; font-weight: 600; font-size: 14.5px; }
.hz-how[open] summary { margin-bottom: 10px; }
.hz-steps { margin: 0 0 10px; padding-left: 20px; color: var(--muted); font-size: 14px; line-height: 1.7; }
.hz-how textarea { width: 100%; margin: 8px 0; font-family: ui-monospace, Menlo, monospace; font-size: 12px; }

/* --- term dates --------------------------------------------------------------- */
.hz-row { display: flex; gap: 12px; align-items: flex-end; margin: 12px 0; }
/* `stack`, not `wrap`.
   `.wrap` is the SITE's page-container class (styles.css: max-width 760, margin auto,
   `padding: 30px 18px 72px`, and `16px 12px 64px` under 640px). Every `.hz-row.wrap` in
   the settings sheet was quietly picking all of that up: a 12-18px indent that lined the
   buttons up with nothing, and 64-72px of dead space under them that read as a layout
   fault — which is exactly what it was. A generic class name on a shared stylesheet is a
   global, and this one had already been claimed. */
.hz-row.stack { flex-wrap: wrap; }
.hz-row.end { justify-content: flex-end; }
.hz-field { display: flex; flex-direction: column; gap: 5px; flex: 1 1 auto; min-width: 0; font-size: 13px; color: var(--muted); }
.hz-field input, .hz-field select, .hz-field textarea { width: 100%; }

/* ==============================================================================
   THE AGENDA — laid out the way Studyo lays one out.

   Four ideas carry the look, and each of them is doing a job rather than decorating:

     • the date is the biggest thing on the page, because every other pixel is an
       answer to "on which day";
     • a class is a solid block of its own colour with its details written ON it —
       a course is recognised by colour long before it is read;
     • the day is a continuous timeline, so the gaps between classes are visible as
       gaps rather than implied by a list;
     • the view switcher floats at the bottom, where a thumb is and the eye is not.
   ============================================================================== */

/* --- header ------------------------------------------------------------------- */
.hz-head {
  display: flex; align-items: center; gap: 18px; flex-wrap: nowrap;
  padding: 14px 0 13px; position: sticky; top: 0; z-index: 6;
  background: var(--bg); border-bottom: 1px solid var(--border);
}

/* The date stepper: one pill holding the four controls that are always used together. */
.hz-headnav {
  display: flex; align-items: center; gap: 2px; flex: none;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--r-pill);
  padding: 4px 6px; box-shadow: 0 1px 3px #0000000f;
}
/* `overflow: hidden` is not cosmetic here — see the input rule below. It clips the
   invisible date field to this box, and clipping takes the hit area with it. */
.hz-navcal { position: relative; display: grid; place-items: center; width: 30px; height: 30px; color: var(--muted); border-radius: 50%; overflow: hidden; }
.hz-navcal:hover { color: var(--accent); background: var(--hover); }
/* The native picker, made invisible but left clickable — a date field IS the control
   here, so nothing has to be reimplemented to open a calendar.
 *
 * `appearance: none` is what makes the width stick. iOS Safari sizes a date input to its
 * own content and will not shrink it below that, so `width: 100%` was advisory: the field
 * rendered far wider than its 36px box and spilled to the RIGHT, invisibly, straight over
 * the "previous" arrow. Tapping ‹ opened the date picker instead of stepping back a day,
 * while › — further along, past "Aujourd'hui" — kept working, which is exactly how the
 * bug presented. Chrome sizes it correctly and never showed a thing. */
.hz-navcal input[type="date"] {
  position: absolute; inset: 0; width: 100%; height: 100%; min-width: 0; max-width: 100%;
  -webkit-appearance: none; appearance: none;
  opacity: 0; padding: 0; border: 0; background: none; cursor: pointer;
}
/* And the arrows sit ABOVE it regardless. The two rules above stop the input growing;
   this one means it would not matter if a future iOS grew it anyway — a real button that
   overlaps an absolutely positioned sibling wins the hit test when it is painted later,
   and a stacking context makes that explicit rather than incidental. */
.hz-headnav .hz-icon, .hz-headnav .hz-today { position: relative; z-index: 1; }
.hz-headnav .hz-icon {
  border: 0; background: none; width: 28px; height: 28px; border-radius: 50%;
  font-size: 20px; line-height: 1; color: var(--muted); cursor: pointer;
}
.hz-headnav .hz-icon:hover { color: var(--accent); background: var(--hover); }
.hz-today {
  border: 0; background: var(--card2); color: var(--accent); font: inherit;
  font-size: 14px; font-weight: 650; padding: 6px 16px; border-radius: var(--r-pill); cursor: pointer;
}
.hz-today:hover { background: var(--accent-soft); }

.hz-headdate { display: flex; align-items: center; gap: 14px; min-width: 0; flex: 1 1 auto; }
.hz-bignum { font-size: 46px; font-weight: 300; line-height: 1; letter-spacing: -.035em; color: var(--text); font-variant-numeric: tabular-nums; }
.hz-bignum.hidden { display: none; }
.hz-headtext { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1 1 auto; }
/* The date stays on one line and the "what's next" note under it truncates instead: a
   wrapped date pushes the whole header taller on every screen, and the note is a
   convenience that can afford an ellipsis. */
.hz-head .hz-range { font-size: 23px; font-weight: 650; letter-spacing: -.02em; line-height: 1.15; white-space: nowrap; }
.hz-head .hz-range::first-letter { text-transform: uppercase; }
/* The month sits beside the weekday at the same size but unweighted — the way a masthead
   date reads: WEDNESDAY first, May 2026 as its qualifier. */
.hz-head .hz-range .hz-rangesoft { font-weight: 400; color: var(--muted); }
.hz-headsub { font-size: 13.5px; color: var(--muted); display: flex; align-items: center; gap: 7px; min-width: 0; }
.hz-headsub > span:not(.hz-subdot) { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hz-headsub:empty { display: none; }
.hz-headsub.live { color: var(--ok); font-weight: 600; }
.hz-headsub .hz-subdot { width: 8px; height: 8px; border-radius: 50%; background: currentColor; flex: none; }

.hz-headright { display: flex; align-items: center; gap: 12px; margin-left: auto; flex: none; }
.hz-tools {
  display: flex; align-items: center; gap: 2px;
  background: var(--card); border: 1px solid var(--border); border-radius: var(--r-pill);
  padding: 4px 6px; box-shadow: 0 1px 3px #0000000f;
}
.hz-tool {
  border: 0; background: none; width: 30px; height: 30px; border-radius: 50%;
  color: var(--muted); font-size: 17px; cursor: pointer;
}
.hz-tool:hover { color: var(--accent); background: var(--hover); }
.hz-add {
  width: 34px; height: 34px; border-radius: 50%; border: 0; cursor: pointer; flex: none;
  background: linear-gradient(135deg, var(--accent), var(--accent2)); color: #fff;
  font-size: 22px; box-shadow: 0 2px 8px color-mix(in srgb, var(--accent) 35%, transparent);
}
.hz-add:hover { filter: brightness(1.08); }

.hz-who { display: flex; align-items: center; gap: 10px; }
.hz-whotext { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.3; min-width: 0; }
.hz-whotext strong { font-size: 14.5px; letter-spacing: -.01em; }
.hz-whotext span { font-size: 12px; color: var(--muted); max-width: 190px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hz-avatar {
  width: 38px; height: 38px; border-radius: 50%; flex: none; display: grid; place-items: center;
  background: linear-gradient(135deg, var(--accent), var(--accent2)); color: #fff;
  font-size: 14px; font-weight: 700; letter-spacing: .02em;
}

.hz-progress { display: inline-flex; align-items: center; gap: 8px; }
.hz-plabel { font-size: 11.5px; color: var(--muted); white-space: nowrap; }
.hz-ptrack { width: 84px; height: 5px; border-radius: var(--r-pill); background: var(--border); overflow: hidden; }
.hz-pfill { display: block; height: 100%; background: var(--accent); border-radius: var(--r-pill); }

/* --- day-wide items ------------------------------------------------------------
   Exams, cancellations, the cegep's own calendar. These go ABOVE the timetable, not
   inside it, because none of them is a period. */
.hz-daybanner {
  display: flex; gap: 26px; flex-wrap: wrap; align-items: flex-start;
  padding: 13px 0; border-bottom: 1px solid var(--border);
}
/* A button here so that a task in the banner can be opened, but styled as a plain strip:
   the site's button chrome would turn six of these into six competing boxes. */
.hz-dbitem {
  display: flex; align-items: flex-start; gap: 10px; font: inherit; font-size: 13px;
  padding: 0 0 0 11px; margin: 0; background: none; border: 0; border-radius: 0;
  border-left: 4px solid var(--k, var(--muted)); text-align: left; min-width: 0; max-width: 460px;
}
.hz-dbitem.act { cursor: pointer; }
.hz-dbitem.act:hover .hz-dbtext strong { color: var(--accent); }
.hz-dbtext { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.hz-dbtext strong { font-size: 14px; letter-spacing: -.01em; }
.hz-dbtext span { font-size: 12.5px; color: var(--muted); line-height: 1.45; }
.hz-dbring {
  width: 21px; height: 21px; border-radius: 50%; flex: none; margin-top: 1px;
  border: 2px solid var(--k, var(--muted)); color: var(--k, var(--muted));
  display: grid; place-items: center;
}
.hz-dbring.done { background: var(--k, var(--muted)); color: #fff; }
.hz-dbring svg { display: block; }

/* --- the day, as a timeline ----------------------------------------------------
   Time-proportional rather than a plain list: a two-hour hole between classes is a
   real feature of a cegep day, and a list of blocks hides it. */
.hz-daygrid { display: grid; grid-template-columns: 42px minmax(0, 1fr); gap: 0 6px; margin-top: 14px; }
.hz-rail { position: relative; }
/* Centred in the rail rather than shoved against its inner edge: the rail is a column of
   its own with a background of its own, and a label hugging one side of it reads as
   misaligned with the column it labels. */
.hz-railh {
  position: absolute; left: 0; right: 0; text-align: center; transform: translateY(-50%);
  font-size: 12px; color: var(--muted2); font-variant-numeric: tabular-nums;
}
/* Rows are positioned by time, not stacked: that is what keeps them lined up with the
   hour rail beside them, and it is what makes a two-hour hole look like two hours. */
.hz-daycol { position: relative; }
.hz-period, .hz-gap { position: absolute; left: 0; right: 0; }

.hz-period {
  display: flex; align-items: stretch; overflow: hidden;
  border: 1.5px solid var(--c); border-radius: 10px; background: var(--card);
}
/* The class block: solid colour, four facts written on it in the four corners it has
   room for — what, when, which section, and where. */
.hz-ptab {
  flex: 0 0 clamp(190px, 24%, 290px); background: var(--c); color: #fff; min-width: 0;
  padding: 9px 12px; display: flex; flex-direction: column; justify-content: space-between; gap: 4px;
}
/* `min-width: 0` on every one of these: a flex item defaults to min-content width, so
   without it "Mathématique NYA — calcul différentiel" widens the tab past its basis and
   squeezes the pane beside it down to nothing on a phone. */
.hz-ptop, .hz-pmid, .hz-pbot { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; min-width: 0; }
.hz-pname { font-size: 15px; letter-spacing: -.01em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hz-ptime { font-size: 11.5px; opacity: .92; white-space: nowrap; font-variant-numeric: tabular-nums; }
.hz-pcode, .hz-pgroup { font-size: 12px; opacity: .9; white-space: nowrap; }
.hz-pteacher { font-size: 12px; opacity: .92; display: flex; align-items: center; gap: 6px; min-width: 0; }
.hz-pteacher span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hz-proom { font-size: 12.5px; font-weight: 700; white-space: nowrap; }

.hz-ppane {
  flex: 1 1 auto; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 8px; padding: 10px 56px 10px 18px; min-width: 0;
}
.hz-pchips { display: flex; align-items: flex-start; gap: 30px; flex-wrap: wrap; justify-content: center; max-width: 100%; min-width: 0; }
.hz-pnum {
  position: absolute; right: 16px; top: 50%; transform: translateY(-50%);
  font-size: 30px; font-weight: 300; color: var(--c); pointer-events: none; line-height: 1;
}
/* One rule for every round glyph button on the page.
   `place-items: center` on a grid centres the glyph's BOX; the extra half-pixel nudge is
   for the glyph itself — "+" sits optically high in most UI faces because its crossbar is
   above the visual centre, and at 26px that reads as "off-centre" rather than as nothing. */
.hz-pplus, .hz-bplus, .hz-addfor, .hz-add, .hz-tool, .hz-mmore, .hz-navcal, .hz-headnav .hz-icon {
  display: grid; place-items: center; line-height: 1; padding: 0; text-align: center;
}
.hz-pplus, .hz-bplus, .hz-addfor, .hz-add { font-variant-numeric: normal; }
.hz-pplus {
  width: 26px; height: 26px; border-radius: 50%; border: 1px dashed var(--border);
  background: none; color: var(--muted2); font-size: 17px; cursor: pointer; flex: none;
}
.hz-pplus:hover { border-style: solid; border-color: var(--accent); color: var(--accent); }

/* The holes: lunch, a free period, the stretch before the first class. Rendered rather
   than skipped, so the shape of the day is honest. */
.hz-gap {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 6px 14px; border-radius: 8px; background: var(--card2);
  color: var(--muted2); font-size: 12.5px; overflow: hidden;
}
.hz-gap.edge { background: color-mix(in srgb, var(--card2) 55%, transparent); }
.hz-gaptime { font-variant-numeric: tabular-nums; }
.hz-gapname { font-size: 13px; font-weight: 650; color: var(--muted); letter-spacing: .04em; text-transform: uppercase; }
.hz-gap.short { padding-top: 0; padding-bottom: 0; font-size: 11px; }
.hz-gap.short .hz-gapname { font-size: 11px; }
.hz-gap.tiny > * { display: none; }

/* The now line, on today only. Deliberately thin and unlabelled: orientation, not data. */
.hz-now { position: absolute; left: 0; right: 0; height: 2px; background: var(--error); pointer-events: none; z-index: 3; }
.hz-now::before {
  content: ""; position: absolute; left: -5px; top: -4px; width: 10px; height: 10px;
  border-radius: 50%; background: var(--error);
}

/* A task, as it appears inside a period: a ring you tick with its name under it. */
.hz-chip {
  display: inline-flex; flex-direction: column; align-items: center; gap: 6px; max-width: 190px;
  border: 0; background: none; cursor: pointer; font: inherit; font-size: 12.5px;
  color: var(--text); padding: 4px 6px; border-radius: 10px; text-align: center;
}
.hz-chip:hover { background: var(--hover); }
.hz-chip.done .hz-chiptext { text-decoration: line-through; color: var(--muted2); }
.hz-ring {
  width: 26px; height: 26px; border-radius: 50%; flex: none; padding: 0;
  border: 2.5px solid var(--k); color: var(--k); background: var(--card);
  display: inline-grid; place-items: center; cursor: pointer;
}
.hz-ring.sm { border-width: 2px; }
.hz-ring.done { background: var(--k); color: #fff; }
.hz-ring svg { display: block; }
.hz-chiptext { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; line-height: 1.3; }

.hz-loose { margin-top: 22px; padding-top: 14px; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 8px; }
.hz-loose h4 { margin: 0; font-size: 12.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }
.hz-addloose {
  align-self: flex-start; border: 1px dashed var(--border); background: none; cursor: pointer;
  color: var(--muted); font: inherit; font-size: 13px; padding: 8px 13px; border-radius: 10px;
}
.hz-addloose:hover { border-style: solid; border-color: var(--accent); color: var(--accent); }
.hz-swapnote {
  margin: 12px 0 0; padding: 9px 12px; border-radius: 10px; font-size: 13px; line-height: 1.5;
  background: #5bb3e81a; border: 1px solid #5bb3e855; color: var(--text);
}

/* --- the week ------------------------------------------------------------------
   Same block, shrunk: the colour tab keeps the course identifiable at a glance and the
   white half stays free for the work attached to it. */
/* Five columns and an hour rail have a floor below which they stop being readable — the
   room number turns into "C." and the hour rail clips its own digits. Below that width
   the week scrolls sideways instead of compressing, which is the honest answer: a week IS
   wider than a phone. The day view is the one to use there, and the dock is right below. */
/* The rail is a sibling of the scroller, not its first column. See the comment in
   renderWeek: a rail INSIDE the scrolling box has to paint over whatever slides behind
   it, and that trick fails the moment a column comes to rest half-underneath. Outside it,
   the scrolling box's own edge does the clipping and there is nothing to paint. */
.hz-weekwrap { display: flex; gap: 0 8px; margin-top: 14px; }
.hz-railfixed { flex: none; width: 42px; }
.hz-weekscroll { flex: 1 1 auto; min-width: 0; overflow-x: auto; overflow-y: hidden; }
.hz-week {
  display: grid; grid-template-columns: repeat(var(--cols), minmax(128px, 1fr));
  gap: 0 8px; min-width: min-content;
}
/* Room under the last hour label, which is centred on the grid's bottom edge and so hangs
   half its height below it. */
#view-week { overflow: hidden; padding-bottom: 10px; }
.hz-corner { height: 46px; }   /* keeps the rail level with the row of day names */
.hz-dayhead {
  height: 46px; display: flex; align-items: center; gap: 7px;
  font-size: 13px; color: var(--muted); cursor: pointer; border-radius: 9px; padding: 0 6px; min-width: 0;
}
.hz-dayhead:hover { background: var(--hover); }
.hz-dnum { font-size: 19px; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
.hz-dayname { font-size: 13.5px; }
.hz-dayname::first-letter { text-transform: uppercase; }
.hz-dayhead.today .hz-dnum {
  background: var(--accent); color: #fff; border-radius: 50%;
  width: 30px; height: 30px; display: grid; place-items: center; font-size: 15.5px;
}
.hz-dayhead.today .hz-dayname { color: var(--accent); font-weight: 650; }
.hz-hours { position: relative; }
.hz-hour {
  position: absolute; left: 0; right: 0; text-align: center; transform: translateY(-50%);
  font-size: 12px; color: var(--muted2); font-variant-numeric: tabular-nums;
}
.hz-col {
  position: relative; border-radius: 10px; background: var(--card2);
  background-image: repeating-linear-gradient(to bottom, transparent, transparent calc(var(--h) - 1px), var(--border) calc(var(--h) - 1px), var(--border) var(--h));
}
/* Today's column is washed rather than outlined: an outline competes with the coloured
   blocks sitting inside it, a wash sits behind them. */
.hz-col.today { background-color: color-mix(in srgb, var(--accent) 7%, var(--card2)); }
.hz-offday { position: absolute; inset: 0; display: grid; place-items: center; font-size: 11.5px; color: var(--muted2); text-align: center; padding: 0 6px; }

.hz-block {
  position: absolute; left: 3px; right: 3px; overflow: hidden; cursor: pointer; display: flex;
  border: 1.5px solid var(--c); border-radius: 9px; background: var(--card);
}
.hz-block:hover { box-shadow: 0 4px 14px #0000001f; }
.hz-btab {
  /* The floor is what "gr.103" and "GYM-3" need at 10.5px, not a guess: below it the tab
     ellipsises the room, which is the one thing on it that has to be read. */
  flex: 0 0 clamp(54px, 34%, 66px); background: var(--c); color: #fff; padding: 4px 6px;
  display: flex; flex-direction: column; justify-content: space-between; gap: 1px;
  min-width: 0; overflow: hidden;
}
/* Line heights are pinned rather than inherited: three lines of default leading do not
   fit the shortest block the week can produce, and the overflow lands on the room number
   — the one thing on the tab a student is actually looking for. */
.hz-babbr, .hz-bgroup, .hz-broom { min-width: 0; line-height: 1.15; }
.hz-babbr { font-size: 12.5px; font-weight: 700; letter-spacing: .01em; }
.hz-bgroup, .hz-broom { font-size: 10.5px; opacity: .92; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hz-broom { font-weight: 650; }
.hz-bpane { flex: 1 1 auto; padding: 5px 7px; min-width: 0; display: flex; flex-direction: column; gap: 3px; overflow: hidden; }
.hz-btime { font-size: 10.5px; color: var(--muted2); font-variant-numeric: tabular-nums; flex: none; }

/* The work in a week cell, WITH ITS NAME. A ring on its own announces that something is
   due and then refuses to say what — which sends you to the day view to read one line of
   text that would have fitted here. */
.hz-bwork { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; gap: 2px; overflow: hidden; }
.hz-bchip {
  display: flex; align-items: center; gap: 6px; min-width: 0; width: 100%;
  border: 0; background: none; padding: 1px 0; margin: 0; cursor: pointer;
  font: inherit; font-size: 11.5px; color: var(--text); text-align: left; border-radius: 6px;
}
.hz-bchip:hover { background: var(--hover); }
.hz-bchiptext { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.hz-bchip.done .hz-bchiptext { text-decoration: line-through; color: var(--muted2); }
.hz-bmore { font-size: 10px; font-weight: 700; color: var(--muted2); margin-right: auto; }

/* Add and the period index share the bottom rail, anchored to the same baseline in the
   corner. Both used to float in the middle of the pane, which is what read as off-centre:
   nothing was aligned to anything. */
.hz-bfoot { flex: none; display: flex; align-items: center; justify-content: flex-end; gap: 6px; margin-top: auto; }
.hz-bplus {
  width: 20px; height: 20px; border-radius: 50%; border: 1px dashed var(--border);
  background: none; color: var(--muted2); font-size: 14px; cursor: pointer; flex: none;
}
.hz-bplus:hover { border-style: solid; border-color: var(--accent); color: var(--accent); }
.hz-bnum { font-size: 15px; font-weight: 400; color: var(--c); line-height: 1; flex: none; }
/* Degrading a short block, in the order the information can be spared.

   FIRST the add button, and the row it was sitting in: clicking anywhere on the block
   already opens the same dialog, so the button is a convenience and the period number can
   move into the corner it was sharing. That alone buys a whole line, which at a cégep's
   most common block length (80 minutes) is the difference between showing the time and
   the work or showing neither.

   ONLY THEN the time and the task names, leaving a ring that still says work exists.

   What never goes is the course and the room — the two things a week grid is read for. */
.hz-block.tight .hz-bplus { display: none; }
.hz-block.tight .hz-bfoot { position: absolute; right: 6px; bottom: 2px; margin: 0; }
.hz-block.tight .hz-btab, .hz-block.tight .hz-bpane { padding: 3px 6px; }
.hz-block.tight .hz-bpane { padding-right: 20px; }
.hz-block.tight .hz-bchip { font-size: 11px; }
.hz-block.tighter .hz-btime, .hz-block.tighter .hz-bgroup, .hz-block.tighter .hz-bchiptext { display: none; }
.hz-block.tighter .hz-babbr { font-size: 11.5px; }
.hz-block.tighter .hz-bwork { flex-direction: row; flex-wrap: wrap; gap: 3px; align-content: flex-start; }
.hz-duepill { font-size: 10px; font-weight: 700; color: var(--accent); background: var(--accent-soft); border-radius: var(--r-pill); padding: 1px 7px; }

/* --- the month ------------------------------------------------------------------ */
.hz-month {
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  grid-template-rows: auto repeat(6, minmax(58px, 1fr)); gap: 8px; margin-top: 14px;
}
.hz-mhead { font-size: 15px; color: var(--text); text-align: center; padding: 4px 0 8px; font-weight: 500; }
.hz-mhead::first-letter { text-transform: uppercase; }
.hz-mcell {
  min-height: 0; border: 1px solid var(--border); border-radius: 10px; background: var(--card);
  padding: 8px 9px 26px; cursor: pointer; position: relative;
  display: flex; flex-direction: column; gap: 7px; box-shadow: 0 1px 2px #0000000a;
}
.hz-mcell:hover { border-color: var(--accent); }
.hz-mcell.out { background: none; border-color: transparent; box-shadow: none; }
.hz-mcell.out .hz-mnum { opacity: .38; }
.hz-mcell.today { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.hz-mtop { display: flex; align-items: baseline; gap: 7px; min-width: 0; }
.hz-mnum { font-size: 14.5px; color: var(--text); font-variant-numeric: tabular-nums; flex: none; }
.hz-mcell.today .hz-mnum { color: var(--accent); font-weight: 700; }
.hz-mdots { display: flex; flex-wrap: wrap; gap: 5px; }
.hz-mring { border-width: 2px; cursor: default; }
/* The overflow control: the day is already clickable, but a cell that silently hides
   three tasks needs to say so. */
.hz-mmore {
  position: absolute; right: 7px; bottom: 5px; border: 0; background: none; cursor: pointer;
  color: var(--muted2); font-size: 15px; line-height: 1; padding: 2px 5px; border-radius: 7px;
}
.hz-mmore:hover { background: var(--hover); color: var(--accent); }

/* --- the year -------------------------------------------------------------------- */
.hz-year { display: grid; grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); gap: 26px 30px; margin-top: 16px; }
.hz-minihead { margin: 0 0 8px; padding-bottom: 8px; font-size: 20px; font-weight: 600; letter-spacing: -.02em; border-bottom: 1px solid var(--border); }
.hz-minihead::first-letter { text-transform: uppercase; }
.hz-minigrid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; }
.hz-minidow { font-size: 11px; color: var(--muted2); text-align: center; padding-bottom: 5px; text-transform: uppercase; }
.hz-minid {
  aspect-ratio: 1; display: grid; place-items: center; font-size: 12px; color: var(--muted);
  border-radius: 7px; cursor: pointer; font-variant-numeric: tabular-nums; position: relative;
}
.hz-minid.out { opacity: 0; cursor: default; pointer-events: none; }
.hz-minid:not(.out):hover { background: var(--hover); }
/* A day carrying work is marked under the number, so the badge slot stays free for the
   cegep's own calendar — the two things are not the same and must not collide. */
.hz-minid.has::after {
  content: ""; position: absolute; bottom: 3px; left: 50%; transform: translateX(-50%);
  width: 4px; height: 4px; border-radius: 50%; background: var(--accent);
}
.hz-minid.ev { color: #fff; font-weight: 650; }
.hz-minid.today { background: var(--accent); color: #fff; font-weight: 700; border-radius: 50%; }
.hz-minid.today.has::after { background: #fff; }

/* --- courses ---------------------------------------------------------------------- */
.hz-agenda { display: flex; flex-direction: column; gap: 10px; margin-top: 14px; }
.hz-course { border: 1px solid var(--border); border-left: 5px solid var(--c); border-radius: 12px; padding: 14px 16px; background: var(--card); }
.hz-chead { display: flex; align-items: center; gap: 12px; }
input.hz-cswatch { width: 28px; height: 26px; padding: 0; border-radius: 8px; border: 1px solid var(--border); background: none; flex: none; }
.hz-meets { margin: 10px 0 0; padding-left: 18px; color: var(--muted); font-size: 13px; line-height: 1.7; }
.hz-meets li::first-letter { text-transform: uppercase; }
.hz-ctasks { display: flex; flex-direction: column; gap: 7px; margin-top: 11px; }
.hz-addfor {
  flex: 0 0 auto; width: 28px; height: 28px; border-radius: 50%; cursor: pointer;
  border: 1px solid var(--border); background: var(--card); color: var(--muted); font-size: 18px;
}
.hz-addfor:hover { border-color: var(--accent); color: var(--accent); }

/* --- tasks ------------------------------------------------------------------------
   Studyo splits these into "ready to plan / to do / done". The part of that distinction
   that earns its keep here is simply outstanding vs cleared, which is what a student
   sorts by anyway — plus a search box, because by November the list is long. */
.hz-worktools { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 16px; }
/* `flex-direction` is explicit for the same reason `.hz-crow` needs it: the site styles
   every <label> as a column, which would stack the magnifier on top of the field. */
.hz-search {
  display: flex; flex-direction: row; align-items: center; gap: 8px; flex: 1 1 220px; max-width: 340px;
  padding: 0 12px; border: 1px solid var(--border); border-radius: var(--r-pill); background: var(--card);
  color: var(--muted2);
}
.hz-search input:not([type="checkbox"]) {
  flex: 1 1 auto; border: 0; background: none; padding: 9px 0; font-size: 14px; min-width: 0;
}
.hz-search input:focus { outline: none; box-shadow: none; background: none; }
.hz-search:focus-within { border-color: var(--accent); }
.hz-tabs { display: flex; gap: 2px; background: var(--card2); border-radius: var(--r-pill); padding: 4px; }
.hz-tab {
  border: 0; background: none; color: var(--muted); font: inherit; font-size: 13.5px; font-weight: 650;
  padding: 7px 16px; border-radius: var(--r-pill); cursor: pointer;
}
.hz-tab:hover { color: var(--text); }
.hz-tab.on { background: var(--card); color: var(--accent); box-shadow: 0 1px 3px #00000014; }

.hz-work { display: flex; align-items: flex-end; gap: 4px; height: 128px; padding: 8px 0 0; margin: 18px 0 4px; }
.hz-wcol { flex: 1 1 0; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; gap: 5px; height: 100%; cursor: pointer; }
.hz-wbar { width: 100%; border-radius: 5px 5px 0 0; background: var(--accent); min-height: 3px; }
.hz-wbar.empty { background: var(--border); }
.hz-wcol.exam .hz-wbar { background: var(--error); }
.hz-wcol.today .hz-wlabel { color: var(--accent); font-weight: 700; }
.hz-wlabel { font-size: 10px; color: var(--muted2); }

.hz-inbox { display: flex; flex-direction: column; gap: 7px; }
.hz-section { margin: 18px 0 3px; font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }
.hz-section.late { color: var(--error); }
.hz-donebox { margin-top: 18px; border-top: 1px solid var(--border); padding-top: 12px; }
.hz-donebox summary { cursor: pointer; font-size: 13px; color: var(--muted); margin-bottom: 8px; }

/* A task row, the way the Studyo task panel draws one: the course as a colour block on
   the left carrying its code and section, the work itself in the middle, and how urgent
   it is as a bar down the right edge. */
.hz-task {
  display: flex; align-items: stretch; gap: 0; overflow: hidden; cursor: pointer;
  border: 1px solid var(--border); border-radius: 10px; background: var(--card);
}
.hz-task:hover { border-color: var(--accent); }
.hz-task.done { opacity: .6; }
.hz-task.done .hz-tktitle { text-decoration: line-through; }
.hz-tkcourse {
  flex: 0 0 74px; background: var(--c, var(--muted2)); color: #fff; padding: 8px 9px;
  display: flex; flex-direction: column; justify-content: center; gap: 1px; min-width: 0;
}
.hz-tkcode { font-size: 12px; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hz-tkgroup { font-size: 10.5px; opacity: .9; }
.hz-tkring { flex: none; align-self: center; margin: 0 12px; }
.hz-tkmid { flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; justify-content: center; padding: 9px 12px 9px 0; min-width: 0; }
.hz-tktitle { font-size: 14.5px; font-weight: 650; letter-spacing: -.01em; }
.hz-tkmeta { font-size: 12.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The urgency bar. Colour alone never carries it — it always has the "!" or a count. */
.hz-tkflag {
  flex: 0 0 26px; display: grid; place-items: center; color: #fff; font-size: 15px; font-weight: 800;
  background: var(--muted2); line-height: 1;
}
.hz-tkflag svg { display: block; }
.hz-tkflag.soon { background: var(--warn); }
.hz-tkflag.late { background: var(--error); }
.hz-tkflag.calm { background: var(--card2); color: var(--muted2); font-size: 12px; font-weight: 600; }

/* --- school-calendar events ------------------------------------------------------
   Colour follows the cegep's own legend so the app and the published calendar can be
   read side by side: yellow for a holiday, orange for a pedagogical day, pink for a
   deadline, green for the epreuve uniforme, blue for a day permutation. Colour is never
   the only signal — every one of these carries its label. */
.hz-event { display: flex; align-items: center; gap: 9px; padding: 9px 13px; border-radius: 11px;
  border: 1px solid var(--border); background: var(--card2); font-size: 13.5px; }
.hz-edot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); flex: none; }
.hz-evpill, .hz-mev {
  font-size: 10.5px; font-weight: 700; border-radius: var(--r-pill); padding: 1px 7px;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  background: var(--card2); color: var(--muted);
}
.hz-mev { border-radius: 6px; font-size: 11px; font-weight: 600; padding: 1px 6px; }
.k-holiday  .hz-edot, .hz-evpill.k-holiday,  .hz-mev.k-holiday  { background: #f4d03f33; color: #b08d05; }
.k-pedday   .hz-edot, .hz-evpill.k-pedday,   .hz-mev.k-pedday   { background: #f0a35e33; color: #c9762c; }
.k-deadline .hz-edot, .hz-evpill.k-deadline, .hz-mev.k-deadline { background: #e07f9b33; color: #c95f7d; }
.k-euf      .hz-edot, .hz-evpill.k-euf,      .hz-mev.k-euf      { background: #48b7a633; color: #2f8f80; }
.k-swap     .hz-edot, .hz-evpill.k-swap,     .hz-mev.k-swap     { background: #5bb3e833; color: #2e8fc4; }
.k-suspended .hz-edot, .hz-evpill.k-suspended, .hz-mev.k-suspended { background: #5bb3e833; color: #2e8fc4; }
.k-start .hz-edot, .k-end .hz-edot, .hz-evpill.k-start, .hz-evpill.k-end,
.hz-mev.k-start, .hz-mev.k-end { background: var(--accent-soft); color: var(--accent); }
.hz-event .hz-edot { width: 9px; height: 9px; }
/* In the year view the badge IS the day, so it needs the solid version of each hue. */
.hz-minid.ev.k-holiday { background: #d9b322; } .hz-minid.ev.k-pedday { background: #e08a3c; }
.hz-minid.ev.k-deadline { background: #e07f9b; } .hz-minid.ev.k-euf { background: #48b7a6; }
.hz-minid.ev.k-swap, .hz-minid.ev.k-suspended { background: #5bb3e8; }
.hz-minid.ev.k-start, .hz-minid.ev.k-end { background: var(--accent); }
.hz-minid.ev.k-welcome, .hz-minid.ev.k-schedchange { background: var(--muted2); }

/* --- touch ------------------------------------------------------------------------
   `touch-action: manipulation` now covers the whole document from styles.css, so what is
   left here is the tap highlight: each of these already has its own pressed state, and
   the browser's default grey rectangle only ever lands on the wrong bounds. */
.hz-dock .hz-view, .hz-block, .hz-period, .hz-chip, .hz-bchip, .hz-ring, .hz-task,
.hz-mcell, .hz-mmore, .hz-minid, .hz-dayhead, .hz-tab, .hz-today, .hz-icon, .hz-tool,
.hz-add, .hz-pplus, .hz-bplus, .hz-addfor, .hz-addloose, .hz-dbitem, .hz-navcal,
.hz-sync, .hz-col, .hz-wcol {
  -webkit-tap-highlight-color: transparent;
}

/* --- shared odds and ends --------------------------------------------------------- */
.hz-view-panel { margin-top: 4px; }
.hz-empty { color: var(--muted2); font-size: 13.5px; margin: 6px 0; }
.hz-warn {
  margin: 12px 0 0; padding: 10px 12px; border-radius: 10px; font-size: 13.5px; line-height: 1.55;
  background: var(--accent-soft); border: 1px solid var(--accent-line); color: var(--text);
}
.hz-amid { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; }
.hz-amid strong { font-size: 14.5px; letter-spacing: -.01em; }
.hz-ameta { font-size: 12.5px; color: var(--muted); }
.hz-note.ok { color: var(--ok); }
.hz-note.warn { color: var(--warn); }

/* --- the LÉA panel in Settings -----------------------------------------------------
   A list, not a queue: it does not empty as it is used, and nothing here is waiting for
   the student. A row already in the agenda is marked and offers to take it back out. */
.hz-lea-box { border: 1px solid var(--border); border-radius: 12px; padding: 14px; margin: 14px 0; background: var(--card2); }
.hz-lea-box h4 { margin: 0 0 8px; font-size: 14.5px; }
/* The status line. Colour is the SECOND channel here, never the only one: the sentence
   says what is wrong and what to do about it, and the tint only makes it findable. */
.hz-leastat { font-size: 12.5px; line-height: 1.5; margin: 8px 0; color: var(--muted); }
.hz-leastat:empty { display: none; }
.hz-leastat.ok { color: var(--ok); }
.hz-leastat.warn { color: var(--warn); }
.hz-leastat.bad { color: var(--error); }
.hz-lea-box .hz-note a { color: var(--accent); }
.hz-lealist { display: flex; flex-direction: column; gap: 2px; max-height: 42vh; overflow: auto; }
.hz-learow {
  display: flex; flex-direction: row; align-items: center; gap: 10px;
  padding: 8px 9px; border-radius: 10px;
}
.hz-learow:hover { background: var(--hover); }
.hz-learow.on { background: color-mix(in srgb, var(--accent) 7%, transparent); }
.hz-leamid { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; }
.hz-leamid strong { font-size: 14px; letter-spacing: -.01em; }
.hz-leameta { font-size: 12.5px; color: var(--muted); line-height: 1.45; }
.hz-learow .ghost-btn { flex: none; }
/* The prompt's rows are <label>s wrapping their own checkbox, so the whole row is the hit
   target — on a phone that is the difference between ticking six and missing three. */
.hz-learow.pick { cursor: pointer; align-items: flex-start; }
.hz-learow.pick input { flex: none; width: 18px; height: 18px; margin: 2px 0 0; accent-color: var(--accent); }

/* --- dialogs ---------------------------------------------------------------------- */
.hz-overlay { position: fixed; inset: 0; background: var(--overlay-bg); backdrop-filter: blur(3px); display: grid; place-items: center; z-index: 40; padding: 18px; }
.hz-modal { width: min(520px, 100%); max-height: 88vh; overflow: auto; background: var(--card); border: 1px solid var(--border); border-radius: var(--r-lg); padding: 20px; box-shadow: var(--shadow); }
.hz-modal h3 { margin: 0 0 14px; font-size: 18px; }
.hz-colors { display: flex; flex-direction: column; gap: 7px; margin: 14px 0; max-height: 190px; overflow: auto; }
/* `flex-direction` is explicit because the site's own `label` styling sets column, and
   without it the colour swatch stacked on top of the course name instead of beside it. */
.hz-crow { display: flex; flex-direction: row; align-items: center; gap: 9px; font-size: 13.5px; text-align: left; }
.hz-crow input[type="color"] { width: 30px; height: 26px; padding: 0; border-radius: 7px; border: 1px solid var(--border); background: none; flex: none; }
.ghost-btn.danger, .hz-modal .danger { color: var(--error); border-color: var(--error); }
.ghost-btn.small, .primary.small { padding: 7px 12px; font-size: 13px; }
.hz-taskctx { margin: 0 0 6px; font-size: 12.5px; color: var(--accent); }
.hz-taskctx::first-letter { text-transform: uppercase; }

.hz-toast {
  position: fixed; left: 50%; bottom: calc(92px + env(safe-area-inset-bottom, 0px)); transform: translate(-50%, 14px);
  background: var(--toast-bg); border: 1px solid var(--border); color: var(--text);
  padding: 10px 16px; border-radius: 11px; font-size: 13.5px; opacity: 0; pointer-events: none;
  transition: opacity .18s, transform .18s; z-index: 60; box-shadow: var(--shadow);
}
.hz-toast.show { opacity: 1; transform: translate(-50%, 0); }

/* --- sync ------------------------------------------------------------------------- */
.hz-sync {
  display: inline-flex; align-items: center; gap: 6px; border: 0; background: none;
  color: var(--muted); border-radius: var(--r-pill); padding: 5px 11px; font: inherit;
  font-size: 12px; font-weight: 650; cursor: pointer;
}
.hz-sync:hover { background: var(--hover); }
.hz-sync.ok { color: var(--ok); }
.hz-sync.ok .hz-syncword { display: none; }   /* nothing to say when it worked */
.hz-sync.busy { color: var(--accent); }
.hz-sync.pending { color: var(--warn); }
.hz-sync.offline { color: var(--muted2); }
.hz-sync-box { border: 1px solid var(--border); border-radius: 12px; padding: 14px; margin: 14px 0; background: var(--card2); }
.hz-sync-box h4 { margin: 0 0 6px; font-size: 14.5px; }
.hz-sync-box .hz-row { margin-bottom: 8px; }
.hz-restore { margin-top: 18px; border: 1px solid var(--accent-line); background: var(--accent-soft); border-radius: 12px; padding: 14px 16px; }
.hz-restore h3 { margin: 0 0 6px; font-size: 16px; }
.hz-keystrip {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 12px;
  padding: 10px 13px; border-radius: 11px; font-size: 13.5px;
  background: var(--accent-soft); border: 1px solid var(--accent-line);
}
.hz-keystrip input:not([type="checkbox"]) { width: 190px; padding: 7px 11px; font-size: 14px; }

/* --- the floating dock -------------------------------------------------------------
   Bottom-centre, where a thumb is on a phone and where the eye is not on a laptop —
   the right trade for a control used constantly but never looked at. */
.hz-dock {
  position: sticky; bottom: 18px; z-index: 8; margin: 30px auto 0; width: fit-content; max-width: 100%;
  /* In the shell it floats over the scrolling region instead of riding at the end of it,
     which is why .hz-scroll reserves padding for it. */
  display: flex; gap: 3px; padding: 6px; border-radius: 18px;
  background: color-mix(in srgb, var(--card) 92%, transparent);
  border: 1px solid var(--border); box-shadow: 0 10px 32px #00000024; backdrop-filter: blur(12px);
  overflow-x: auto;
}
.hz-dock .hz-view {
  display: flex; align-items: center; gap: 8px; padding: 10px 15px; border-radius: 13px;
  border: 0; background: none; color: var(--muted); font: inherit; font-size: 13.5px; font-weight: 600;
  cursor: pointer; white-space: nowrap; position: relative;
}
.hz-dock .hz-view:hover { color: var(--text); background: var(--hover); }
.hz-dock .hz-view.on { background: var(--accent-soft); color: var(--accent); }
.hz-dockbadge {
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: var(--r-pill);
  background: var(--error); color: #fff; font-size: 10.5px; font-weight: 700;
  display: inline-grid; place-items: center;
}
body.hz-app .hz-dock {
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: calc(16px + env(safe-area-inset-bottom, 0px)); margin: 0;
}

/* --- the settings sheet's appearance row --------------------------------------------
   Hidden by default: above 760px the topbar is on screen with one of each control on it,
   and two theme toggles in one window is a bug, not a convenience. */
.hz-appearance {
  display: none; align-items: center; justify-content: space-between; gap: 12px;
  margin: 12px 0 4px; padding: 9px 12px; border: 1px solid var(--border);
  border-radius: 12px; background: var(--card2);
}
.hz-appbtns { display: flex; align-items: center; gap: 8px; }

/* ==============================================================================
   NARROW SCREENS — the phone is not a smaller desktop

   Everything above assumes a pointer and a window wider than the week it draws. A phone
   has neither, and the three things that break are always the same:

     • VERTICAL ROOM. The site topbar plus the agenda header were 219px of an 812px
       window before the grid drew a single pixel — a quarter of the screen spent on
       chrome. They are ~108px here, and the timetable gets the difference.
     • HORIZONTAL ROOM. A week is five columns and an hour rail. That is genuinely wider
       than 375px, so the week SCROLLS sideways beside a fixed hour rail
       rather than compressing five columns into unreadable slivers. The day view is the
       one that fits a phone, and it is what the app opens on there.
     • TARGETS. A 20px icon is a fine click target and a poor tap target. Every control
       that survives to this width is at least ~36px, and the dock's are 44px.
   ============================================================================== */
@media (max-width: 900px) {
  .hz-who .hz-whotext { display: none; }
}
@media (max-width: 1080px) {
  .hz-head { flex-wrap: wrap; }
  /* The date leads on a narrow screen and the controls fall in under it, rather than the
     stepper pushing the date off to the side of a row it no longer fits on. */
  .hz-headdate { order: -1; width: 100%; }
  .hz-headright { margin-left: auto; }
}

@media (max-width: 760px) {
  /* --- the shell -------------------------------------------------------------------
     The site topbar goes while the agenda is open. It is 53px of an 812px window spent
     on a brand mark and a Settings button the agenda header already carries. Its two
     controls that are NOT duplicates — theme and language — move into the settings
     sheet (.hz-appearance below), and the way back to the site is the footer nav that
     sheet already had. Outside the app (import, sign-in) the topbar stays: those are
     ordinary pages and need their masthead. */
  body.hz-app .topbar { display: none; }
  .horaire main { padding: 0 12px 60px; }
  /* Edge to edge. A 12px gutter down each side of a 393px phone is 6% of the screen
     spent on nothing, and on the one view that wants every pixel — a week that is already
     wider than the window — it is 6% fewer minutes of timetable.

     So the shell itself has no side padding at this width, and the padding is given back
     only to the things that would otherwise sit against the bezel: text, controls, and
     the panels that read as documents. The week grid is deliberately NOT in that list —
     it is a scrolling surface, and a scrolling surface should run under the edge. */
  body.hz-app main { padding: 0; }
  body.hz-app .hz-head,
  body.hz-app #day-banner,
  body.hz-app #key-strip,
  body.hz-app #warn-bar,
  body.hz-app #work-toolbar,
  body.hz-app #view-day,
  body.hz-app #view-month,
  body.hz-app #view-year,
  body.hz-app #view-work,
  body.hz-app #view-courses {
    padding-left: max(10px, env(safe-area-inset-left, 0px));
    padding-right: max(10px, env(safe-area-inset-right, 0px));
  }
  /* The week keeps only the notch clearance, so the grid itself reaches both edges. */
  body.hz-app #view-week {
    padding-left: env(safe-area-inset-left, 0px);
    padding-right: env(safe-area-inset-right, 0px);
  }
  /* Columns snap, so the grid comes to rest on a day rather than wherever the thumb let
     go. The scroller starts where the rail ends, so no scroll-padding is needed for it. */
  body.hz-app .hz-weekscroll { scroll-snap-type: x mandatory; }
  /* The day heading defines each column's x position, and it is the row the eye uses to
     tell which day it landed on, so it is the right thing to snap to. */
  body.hz-app .hz-dayhead { scroll-snap-align: start; }
  /* The dock tucks in a little closer too, now that nothing else is inset by 12. */
  body.hz-app .hz-dock { left: 8px; right: 8px; }
  /* Clear the dock, which sits lower here (see below) and runs edge to edge. */
  .hz-scroll { padding-bottom: calc(78px + env(safe-area-inset-bottom, 0px)); }

  /* `svh`, not `dvh`, for the shell's own height.
     `dvh` is the viewport as it is RIGHT NOW, which is the right answer for a page that
     scrolls: the bar collapses as you read and the page grows to match. This one does not
     scroll — the body is `overflow: hidden` — so Safari keeps its bottom toolbar out
     permanently, while `dvh` can still report the taller layout viewport it would have if
     the bar were away. The difference is exactly the height of that toolbar, and what was
     sitting in it was the dock. `svh` is the height that is always actually on screen. */
  body.hz-app { height: 100svh; }

  /* --- the header, in two rows -----------------------------------------------------
     Row one is the date and what is happening now; row two is the stepper and the tools.
     Both rows are one line each, which is the whole trick: at the desktop sizes the
     stepper and the tools together were wider than the window, so the tools wrapped to a
     third row of their own and the header ate 166px. Shrinking both pills to fit one row
     is worth more on this screen than any font size on it. */
  /* Nothing sits above this any more — the site topbar is hidden at this width — so the
     header has to reserve the top of the window itself. It was flush against y=0 with
     8px over a 32px numeral, which is what reads as cramped on a real phone.

     `max()` covers both cases with one value: in a browser tab the inset is 0, because
     the browser's own chrome is already occupying that band, and the 14px floor is what
     gives the date room to breathe; opened from the home screen there is no chrome, the
     inset is the status bar, and on a phone with a Dynamic Island the date would
     otherwise be drawn underneath it. */
  .hz-head { gap: 6px 10px; padding-top: max(14px, env(safe-area-inset-top, 0px)); padding-bottom: 10px; }
  .hz-headdate { gap: 12px; }
  .hz-bignum { font-size: 32px; }
  .hz-head .hz-range { font-size: 17px; }
  .hz-headsub { font-size: 12px; gap: 6px; }
  .hz-headnav { order: 1; padding: 2px 4px; gap: 0; }
  .hz-headright { order: 2; margin-left: auto; gap: 6px; }
  /* Both of these say something the phone has no room for and the settings sheet does:
     which week of the term it is, and whose agenda this is. */
  .hz-progress, .hz-who { display: none; }
  /* The stepper and the tools share one row, so the sync word goes — the cloud and its
     colour say the same thing, and the tooltip keeps the words. */
  .hz-syncword { display: none; }
  .hz-today { padding: 8px 13px; font-size: 13px; }
  .hz-tools { padding: 2px 4px; }
  /* Tap targets. 36px is the floor a thumb hits reliably; the dock gets the full 44. */
  .hz-navcal { width: 36px; height: 36px; }
  .hz-headnav .hz-icon { width: 36px; height: 36px; font-size: 22px; }
  .hz-tool { width: 36px; height: 36px; }
  .hz-add { width: 38px; height: 38px; }
  .hz-sync { padding: 8px 9px; }

  .hz-daybanner { gap: 8px 16px; padding: 10px 0; }
  .hz-dbtext strong { font-size: 13.5px; }

  /* --- the day ---------------------------------------------------------------------
     The rail was 30px, which clips "08:00" to ":00" — an hour axis that cannot print an
     hour. 38px at 10.5px tabular figures fits it with room to spare. */
  .hz-daygrid { grid-template-columns: 38px minmax(0, 1fr); gap: 0 4px; margin-top: 6px; }
  .hz-railh { font-size: 10.5px; }

  .hz-ptab { flex: 0 0 clamp(116px, 44%, 158px); padding: 7px 9px; }
  /* Two wrapped lines beat five characters and an ellipsis: at this width the title is
     the only thing that says which class this is — and it only gets those two lines if it
     stops sharing a row with the time. Side by side in a 154px tab, "Calcul différentiel"
     is allotted about 65px and comes out as "Cal… diff…", which names nothing. */
  .hz-ptop { flex-direction: column; align-items: flex-start; gap: 1px; }
  .hz-pname { font-size: 13px; white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; line-height: 1.25; }
  .hz-ptime, .hz-pcode, .hz-pgroup, .hz-pteacher { font-size: 11px; }
  /* Four lines of tab in a block that is 90 minutes tall does not always fit, so the
     order decides what is lost rather than the layout deciding for us. The room moves up
     under the time — where you have to be is the whole reason to look at a timetable
     mid-morning — and the course code, which nobody reads off a screen they are already
     standing in front of, is what falls off the bottom edge instead.
     `space-between` has to go with it: it was pinning the room to the bottom, which is
     precisely the edge that gets clipped. */
  .hz-ptab { justify-content: flex-start; gap: 2px; }
  .hz-ptop { order: 1; }
  .hz-pbot { order: 2; }
  .hz-pmid { order: 3; }
  /* The teacher shares that middle row with the room, and a 158px tab does not hold both
     — "✉ Marie-Claude Trem…" beside "A-3412" spends the row on the half that does not
     matter. The room is where you have to be; the teacher's name is in the Courses view
     and is a fact you already know. Hidden at every phone width, not only the narrow
     ones, because the row is the same size on all of them. */
  .hz-pteacher { display: none; }

  /* The work beside a class becomes a LIST rather than a centred cloud of chips.

     The cloud is right on a desktop, where the pane is 500px wide and three chips sit
     side by side under it. In a 190px pane it stacks anyway, and because the pane centres
     its content a stack taller than the block overflows out of BOTH ends — which is what
     put a task title above the top edge of its own class. A left-aligned list of one-line
     rows starts at the top, fills downward, and stops at the bottom edge; the script caps
     how many are drawn (see fitChips in horaire-app.js) so the rest are counted, not
     silently cropped. */
  .hz-ppane { padding: 6px 8px; align-items: stretch; justify-content: flex-start; gap: 3px; }
  .hz-pchips { flex-direction: column; flex-wrap: nowrap; gap: 1px; align-items: stretch; justify-content: flex-start; width: 100%; overflow: hidden; }
  .hz-chip { flex-direction: row; align-items: center; gap: 7px; max-width: none; width: 100%; min-height: 26px; text-align: left; padding: 1px 2px; font-size: 12px; }
  .hz-chip .hz-ring { width: 22px; height: 22px; border-width: 2px; }
  .hz-chiptext { display: block; -webkit-line-clamp: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .hz-pmore { font-size: 10.5px; font-weight: 700; color: var(--muted2); padding-left: 3px; }
  .hz-pplus { align-self: flex-end; margin-top: auto; width: 24px; height: 24px; font-size: 16px; }
  /* A short class sheds its add button, the way a short week block does: tapping the
     block opens the same dialog, so the button is a convenience, and the 26px it holds
     is a line of the work that is not. */
  .hz-period.tight .hz-pplus { display: none; }
  /* The period index is a 30px numeral sitting in the corner the task list now needs.
     Numbering the classes of a day is a luxury at this width. */
  .hz-pnum { display: none; }
  .hz-addloose { padding: 11px 15px; font-size: 13.5px; }

  /* --- the week --------------------------------------------------------------------
     Narrower columns, so three of the five are on screen at once rather than two and a
     half, and the script scrolls the grid to today when it opens. Below the 106px floor
     the room number ellipsises, which is the one thing on a week block that has to be
     read. */
  .hz-week { grid-template-columns: repeat(var(--cols), minmax(164px, 1fr)); }
  .hz-railfixed { width: 40px; }
  /* The row of day names moves up to sit close under the toolbar, rather than floating a
     third of the way down the screen with a band of nothing above it. */
  .hz-weekwrap { margin-top: 6px; }
  .hz-view-panel { margin-top: 0; }
  .hz-hour { font-size: 10.5px; }
  .hz-dnum { font-size: 16px; }
  /* The weekday name comes BACK at this width, where the desktop layout drops it. There
     it is redundant — five columns under a header that says "Week of 31 August" are
     obviously Monday to Friday. Here three of the five are on screen at a time and the
     other two are off the edge, so a bare "2 3 4" genuinely does not say which days
     these are. */
  .hz-dayname { display: inline; font-size: 12px; }
  .hz-dayhead { height: 46px; padding: 0 4px; gap: 4px; overflow: hidden; }
  .hz-duepill, .hz-evpill { flex: none; }
  /* --- the week block is the DESKTOP block, given room to be itself ------------------
     It is not restyled here at all, and that is deliberate. Two earlier attempts tried to
     make the desktop block survive a 106px column — first by forcing it into its smallest
     degradation tier (which left a colour swatch and a ring, saying nothing), then by
     stacking it (which turned a mostly-coloured block into a mostly-grey card with a thin
     colour hat, and moved the room and the time somewhere they read worse). Both were
     solving the wrong problem. The block is fine; the column was too narrow for it.

     So the ONLY thing that changes at this width is the column: wide enough that the tab
     is a full-height band of the course's colour with the room legible on it, and the
     pane beside it holds the time and the work by name — exactly as on a laptop. The
     week then scrolls sideways beside a fixed hour rail, opening on today, which is the
     honest trade: a phone shows about two days of a week at a time, and both of them
     properly, instead of five badly. */
  .hz-week { gap: 0 6px; }
  .hz-weekwrap { gap: 0 6px; }
  /* The tab takes the desktop's MAXIMUM width as a fixed basis rather than 34% of the
     block. 34% of a 158px block is 54px — the clamp's floor — which is where "Gymnase 2"
     was being cut to "Gymnas…". Pinning it at 66 gives the room the same 54px of legible
     space it has on a laptop, and because the block around it is narrower the colour
     takes a LARGER share of it, not a smaller one. */
  .hz-btab { flex: 0 0 66px; }
  /* A class held in two rooms is "C-4501, D-1120" — 74px at 10.5px, which no tab that
     fits a phone will hold on one line. Where the block is tall enough to spare a line,
     the room takes a second one instead of ellipsising away the half that says where the
     rest of the class happens. Short blocks keep the single line: they have no line to
     give, and that is what `tight` already means. */
  .hz-block:not(.tight) .hz-broom {
    white-space: normal; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  }

  /* --- the month -------------------------------------------------------------------
     Weekday headings become initials in the script (`weekday: "narrow"`): seven spelled
     out names do not fit 351px, and "Dimanche" was being cut off mid-word. The cell
     loses the 26px of bottom padding it was reserving for an overflow control that is
     now only drawn when there IS overflow. */
  .hz-month { gap: 4px; grid-template-rows: auto repeat(6, minmax(54px, 1fr)); }
  .hz-mhead { font-size: 11.5px; padding: 2px 0 6px; color: var(--muted); }
  .hz-mcell { min-height: 0; padding: 4px 5px; border-radius: 9px; gap: 4px; }
  .hz-mnum { font-size: 13px; }
  /* 15px, not 19: two rings and the gap between them have to fit a 37px cell interior,
     and at 19 they missed by a pixel and stacked one per row — four of which is taller
     than the cell, so the fourth was sliced in half by its own bottom edge. */
  .hz-mdots { gap: 4px; }
  .hz-mring { width: 15px; height: 15px; }
  .hz-mev { font-size: 9.5px; padding: 0 4px; }
  /* An empty overflow control is a "⋯" in each of 42 cells saying nothing. It is hidden
     rather than dropped because the cell itself is a <div>: this button is the only way
     to reach a day from a keyboard, so it stays focusable and comes back the moment it
     is focused. */
  .hz-mmore.empty {
    position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap;
  }
  .hz-mmore.empty:focus-visible {
    position: absolute; width: auto; height: auto; clip-path: none;
    right: 6px; bottom: 4px; padding: 2px 5px;
  }

  /* --- the year --------------------------------------------------------------------
     One month per row. Seven columns of a mini-month at 351px is a 47px cell, which is
     both readable and a real tap target — this view is better on a phone than it is on a
     laptop, where the same grid is a third the size. */
  .hz-year { grid-template-columns: 1fr; gap: 22px; }
  .hz-minihead { font-size: 18px; }
  .hz-minid { font-size: 13px; border-radius: 9px; }

  /* --- tasks ------------------------------------------------------------------------ */
  .hz-worktools { gap: 8px; margin-top: 12px; }
  .hz-search { flex: 1 1 100%; max-width: none; }
  .hz-tabs { flex: 1 1 auto; }
  .hz-tab { flex: 1 1 0; padding: 9px 10px; }
  .hz-work { height: 96px; margin: 14px 0 4px; }
  .hz-tkcourse { flex: 0 0 58px; padding: 6px 7px; }
  .hz-tkring { margin: 0 9px; }
  .hz-tkmid { padding: 8px 10px 8px 0; }
  .hz-tktitle { font-size: 14px; }
  .hz-tkmeta { font-size: 12px; }

  /* --- courses ---------------------------------------------------------------------- */
  .hz-course { padding: 12px 13px; }
  .hz-chead { flex-wrap: wrap; gap: 9px; }

  /* --- the dock ---------------------------------------------------------------------
     Edge to edge, six equal thumb-sized targets, above the home indicator. The labels go
     — six words do not fit — so the accessible name moved to an aria-label on each
     button (see horaire.html), and the task count becomes a badge pinned to the icon
     rather than an item riding beside a word that is no longer there. */
  body.hz-app .hz-dock {
    left: 12px; right: 12px; transform: none; width: auto; max-width: none;
    bottom: calc(10px + env(safe-area-inset-bottom, 0px));
  }
  .hz-dock { gap: 1px; padding: 5px; border-radius: 17px; overflow: visible; }
  .hz-dock .hz-view { flex: 1 1 0; justify-content: center; padding: 11px 0; min-height: 44px; }
  .hz-dock .hz-view span:not(.hz-dockbadge) { display: none; }
  .hz-dockbadge {
    position: absolute; top: 3px; left: 50%; margin-left: 3px;
    min-width: 16px; height: 16px; font-size: 10px;
  }

  /* --- dialogs, as bottom sheets -----------------------------------------------------
     A centred card is a desktop idiom: on a phone it floats in the middle of the screen
     with the keyboard covering its lower half and its buttons out of thumb reach. A
     sheet is anchored to the bottom edge, so it grows upward from where the thumb is and
     the keyboard pushes it rather than covering it. */
  .hz-overlay { padding: 0; display: grid; place-items: end stretch; }
  .hz-modal {
    width: 100%; max-width: none; max-height: 92dvh; overscroll-behavior: contain;
    border-radius: 20px 20px 0 0; border-bottom: 0;
    padding: 10px 16px calc(18px + env(safe-area-inset-bottom, 0px));
    animation: hz-sheet-in .22s cubic-bezier(.22, .8, .3, 1);
  }
  /* The grab handle. It does not drag — it says which edge the sheet is attached to,
     which is what stops it reading as a card that happens to be near the bottom. */
  .hz-modal::before {
    content: ""; display: block; width: 38px; height: 4px; margin: 0 auto 12px;
    border-radius: var(--r-pill); background: var(--border);
  }
  .hz-modal h3 { margin: 0 0 12px; }
  /* 16px is not a style choice. Below it, iOS Safari zooms the whole page in when a field
     takes focus and does not zoom back out — every text entry in the app would leave the
     student on a page a third too wide with no way back but a pinch. */
  .hz-modal input:not([type="checkbox"]), .hz-modal select, .hz-modal textarea,
  .hz-search input:not([type="checkbox"]), .hz-keystrip input:not([type="checkbox"]),
  .hz-wrap input:not([type="checkbox"]), .hz-wrap textarea { font-size: 16px; }
  .hz-modal .hz-row { flex-wrap: wrap; gap: 10px; }
  /* One button per line, all the same width.
     Left to size themselves these came out 286, 103, 335, 218 and 167px wide, stacked in
     a ragged column with each label centred inside its own box — so no two labels started
     or ended in the same place and the whole panel read as badly aligned. A phone has one
     column of room; using it is both tidier and a bigger tap target. */
  .hz-modal .hz-row.stack > button { flex: 1 1 100%; }
  /* Equal halves, so "Devoir" is not clipped to "Devo" beside a wider neighbour. */
  .hz-modal .hz-row > .hz-field { flex: 1 1 0; }
  .hz-modal .primary, .hz-modal .ghost-btn { min-height: 44px; }
  .hz-modal .hz-row.end { gap: 8px; }
  /* The colour list had its own scrollbar inside a sheet that already scrolls — two
     nested scroll regions on a touch screen, where the inner one swallows the gesture
     meant for the outer. The sheet scrolls; the list is just a list. */
  .hz-colors { max-height: none; overflow: visible; }
  .hz-crow input[type="color"] { width: 38px; height: 34px; }
  .hz-keystrip input:not([type="checkbox"]) { width: 100%; }

  /* Theme and language, which the hidden topbar was carrying. */
  .hz-appearance { display: flex; }
}

@keyframes hz-sheet-in {
  from { transform: translateY(16px); }
  to { transform: none; }
}

/* --- roomier phones (iPhone 16 and most current handsets) ----------------------------
   The block above is floored at 375px, which is an iPhone 13 mini. An iPhone 16 is 393,
   a 16 Pro Max is 440 — between 18 and 65 unused pixels on the widest line the header
   draws. The date takes them back: at 375 the sizes are what fits, not what reads best,
   and there is no reason for a 440px phone to inherit a 375px phone's compromise. */
@media (min-width: 390px) and (max-width: 760px) {
  .hz-head { padding-top: max(16px, env(safe-area-inset-top, 0px)); }
  .hz-bignum { font-size: 35px; }
  .hz-head .hz-range { font-size: 18.5px; }
  .hz-headsub { font-size: 12.5px; }
  .hz-today { padding: 8px 15px; font-size: 13.5px; }
}

/* --- genuinely small phones (320–389px) ----------------------------------------------
   The ceiling is 389 and not 400 on purpose. An iPhone 16 is 393px wide and a 16 Pro is
   402: a 400px ceiling swept both of them into the small-phone rules, so the newest
   phones were being handed an iPhone-SE compromise — a 28px date on a screen with room
   for 35. The two blocks must not overlap, or the later one silently wins. */
@media (max-width: 389px) {
  .hz-bignum { font-size: 28px; }
  .hz-head .hz-range { font-size: 16px; }
  .hz-today { padding: 8px 10px; font-size: 12.5px; }
  .hz-week { grid-template-columns: repeat(var(--cols), minmax(156px, 1fr)); }
  .hz-railfixed { width: 38px; }
  /* A 44px month cell cannot show "Congé férié" as anything but "Co…". The day view's
     banner still names it, and the year view still colours the day. */
  .hz-mev { display: none; }
}

/* --- a phone held sideways ------------------------------------------------------------
   ~375px of height once the browser chrome is out. Every horizontal band has to earn its
   pixels twice over — and the week, the one view that WANTS the width, is finally the
   right one to be in. */
@media (max-height: 500px) and (max-width: 940px) {
  body.hz-app .topbar { display: none; }
  .hz-head { padding: 4px 0; gap: 4px 10px; }
  .hz-bignum { display: none; }
  .hz-headsub { display: none; }
  .hz-daybanner { padding: 6px 0; }
  body.hz-app .hz-dock { bottom: calc(6px + env(safe-area-inset-bottom, 0px)); }
  .hz-dock .hz-view { padding: 8px 0; min-height: 38px; }
  .hz-scroll { padding-bottom: calc(62px + env(safe-area-inset-bottom, 0px)); }
  .hz-modal { max-height: 96dvh; }
}

/* Motion is a hint about which edge a sheet came from, never information. Off when asked. */
@media (prefers-reduced-motion: reduce) {
  .hz-modal { animation: none; }
}

/* Printing a week is the one thing a paper agenda still does better, so let it. */
@media print {
  .topbar, .hz-head, .hz-dock, .hz-toast, .hz-overlay, .site-footer, .hz-warn,
  .hz-keystrip, .hz-worktools { display: none !important; }
  .horaire main { padding: 0; max-width: none; }
  .hz-block, .hz-period { break-inside: avoid; }
  .hz-ptab, .hz-btab, .hz-tkcourse { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  .hz-col { background: none; }
}

/* --- Mio -----------------------------------------------------------------------------
   A mail client, laid out the way one has been laid out for twenty years because that
   layout is correct: folders at the left edge, a scannable list, a reading pane. The
   school's own version is a three-deep frameset and the whole point of this one is that
   reading your mail should not feel like operating machinery.

   Two panes on a laptop, one on a phone — where the list becomes the screen and opening a
   message replaces it, because 380px of list plus 380px of message is neither. */
#view-mio { display: flex; gap: 0 18px; align-items: flex-start; }

.hz-miorail { flex: none; width: 148px; display: flex; flex-direction: column; gap: 2px; padding-top: 4px; }
.hz-miotab {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 8px 12px; border: 0; border-radius: 999px; cursor: pointer;
  background: none; color: var(--muted); font: inherit; font-size: 13.5px; text-align: left;
}
.hz-miotab:hover { background: var(--hover); color: var(--text); }
.hz-miotab.on { background: var(--accent-soft); color: var(--text); font-weight: 600; }
.hz-miotabname { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.hz-miotabn { flex: none; font-size: 11.5px; font-weight: 700; color: var(--accent); }

.hz-miomain { flex: 1 1 auto; min-width: 0; }
/* Provenance. Quiet, permanent, and never in the way — the point is only that nobody has
   to wonder how their school mail got here. */
.hz-miosrc {
  margin: 0 0 8px; font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); opacity: .75;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
}

/* --- which path is answering -------------------------------------------------
   Two sources can produce the same homework and the same mailbox. The colour is the
   distinction that matters: `local` means it never left this machine, `relay` means our
   server signed in to do it. Never let these two read as interchangeable. */
.hz-srcpill {
  display: inline-block; padding: 2px 9px; border-radius: 999px;
  font-size: 10.5px; letter-spacing: .04em; text-transform: none; font-weight: 600;
  border: 1px solid var(--border); background: var(--card2); color: var(--muted);
  white-space: nowrap;
}
.hz-srcpill.local { color: var(--ok); border-color: color-mix(in srgb, var(--ok) 40%, var(--border)); }
.hz-srcpill.relay { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); }
.hz-srcpill.off { opacity: .7; }
.hz-src { margin: 6px 0 10px; }
.hz-src:empty { display: none; }
.hz-miosearch { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
.hz-miosearch input {
  flex: 1 1 auto; min-width: 0; padding: 9px 14px; font-size: 16px;
  border-radius: 999px; border: 1px solid var(--border); background: var(--card2); color: var(--text);
}
.hz-miosearch input:focus { outline: none; border-color: var(--accent); background: var(--card); }
.hz-mioicon {
  flex: none; display: grid; place-items: center; width: 34px; height: 34px;
  border-radius: 50%; border: 1px solid var(--border); background: none;
  color: var(--muted); cursor: pointer; font-size: 16px; line-height: 1;
}
.hz-mioicon:hover { color: var(--text); border-color: var(--accent); }
.hz-miopad { padding: 18px 4px; }

.hz-miosplit { display: flex; gap: 0 18px; align-items: flex-start; }
.hz-miolist { flex: 0 0 340px; display: flex; flex-direction: column; gap: 1px; max-height: 68vh; overflow-y: auto; }
.hz-miopane { flex: 1 1 auto; min-width: 0; max-height: 68vh; overflow-y: auto; padding-left: 18px; border-left: 1px solid var(--border); }

.hz-miorow {
  display: flex; align-items: flex-start; gap: 10px; width: 100%; text-align: left;
  padding: 9px 10px; border: 0; border-left: 3px solid transparent; border-radius: 10px;
  background: none; color: inherit; font: inherit; cursor: pointer;
}
.hz-miorow:hover { background: var(--hover); }
.hz-miorow.on { background: var(--accent-soft); }
.hz-miorow.unread { border-left-color: var(--accent); }
.hz-miorow.unread .hz-mioname, .hz-miorow.unread .hz-miosubj { font-weight: 680; color: var(--text); }

/* The avatar's hue is hashed from the sender's name, so one teacher is one colour
   everywhere without anything being stored. Decoration that aids recognition. */
.hz-mioav {
  flex: none; display: grid; place-items: center; width: 34px; height: 34px;
  border-radius: 50%; font-size: 12.5px; font-weight: 700; letter-spacing: .02em;
  color: hsl(var(--h) 62% 28%); background: hsl(var(--h) 68% 82%);
}
:root[data-theme="dark"] .hz-mioav, html:not([data-theme="light"]) .hz-mioav { color: hsl(var(--h) 70% 86%); background: hsl(var(--h) 42% 30%); }

.hz-miomid { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 auto; }
.hz-miotop { display: flex; align-items: baseline; gap: 10px; }
.hz-mioname { font-size: 13.5px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hz-miodate { margin-left: auto; flex: none; font-size: 11.5px; color: var(--muted); white-space: nowrap; }
.hz-miosubj { font-size: 13px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hz-miaprev { font-size: 12.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hz-mioclip { flex: none; font-size: 12px; opacity: .75; }
.hz-miodot { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); margin-top: 12px; }

/* --- the reading pane --- */
.hz-mioreadbar { display: none; margin-bottom: 10px; }
.hz-miotitle { margin: 0 0 14px; font-size: 20px; line-height: 1.25; letter-spacing: -.01em; }
.hz-miowho { display: flex; align-items: flex-start; gap: 10px; padding-bottom: 14px; border-bottom: 1px solid var(--border); }
.hz-miowho .hz-mioname { font-size: 14px; font-weight: 620; }
.hz-miometa { font-size: 12.5px; color: var(--muted); line-height: 1.45; overflow-wrap: anywhere; }
.hz-miobody { font-size: 14.5px; line-height: 1.65; padding: 16px 0 4px; overflow-wrap: anywhere; }
.hz-miobody p { margin: 0 0 12px; }
.hz-miobody a { color: var(--accent); }
.hz-miobody ul, .hz-miobody ol { margin: 0 0 12px; padding-left: 22px; }
/* A message can carry a table wider than the pane; it scrolls itself, never the page. */
.hz-miobody table { display: block; overflow-x: auto; max-width: 100%; border-collapse: collapse; }
.hz-miobody td, .hz-miobody th { border: 1px solid var(--border); padding: 5px 9px; }

.hz-miofiles { display: flex; flex-wrap: wrap; gap: 8px; margin: 6px 0 18px; }
.hz-miofile {
  display: flex; align-items: center; gap: 9px; max-width: 260px;
  padding: 8px 13px 8px 10px; border: 1px solid var(--border); border-radius: 11px;
  background: var(--card2); color: inherit; font: inherit; cursor: pointer; text-align: left;
}
.hz-miofile:hover { border-color: var(--accent); }
.hz-miofile[disabled] { opacity: .55; cursor: default; }
.hz-miofileico { flex: none; font-size: 15px; }
.hz-miofilename { font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.hz-mioreplybtn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 9px 20px; border-radius: 999px; border: 1px solid var(--border);
  background: var(--card2); color: var(--text); font: inherit; font-size: 14px; cursor: pointer;
}
.hz-mioreplybtn:hover { border-color: var(--accent); background: var(--accent-soft); }

.hz-mionew {
  margin: 0 0 10px; padding: 9px 16px; border-radius: 999px; border: 0; cursor: pointer;
  background: var(--accent); color: #fff; font: inherit; font-size: 13.5px; font-weight: 600;
  text-align: center;
}
.hz-mionew:hover { filter: brightness(1.08); }
/* Destructive, so it never sits where Reply's muscle memory lands. */
.hz-miotrash {
  margin-left: auto; padding: 9px 16px; border-radius: 999px;
  border: 1px solid var(--border); background: none; color: var(--muted);
  font: inherit; font-size: 13.5px; cursor: pointer;
}
.hz-miotrash:hover { color: var(--error); border-color: var(--error); }
.hz-miotowho { display: block; font-size: 13.5px; margin-bottom: 2px; }
.hz-miocompose { margin: 4px 0 20px; padding: 14px; border: 1px solid var(--border); border-radius: 14px; background: var(--card2); }
.hz-mioreply {
  width: 100%; min-height: 150px; margin: 8px 0 10px; padding: 11px 12px;
  font: inherit; font-size: 16px; line-height: 1.55; resize: vertical;
  border-radius: 10px; border: 1px solid var(--border); background: var(--card); color: var(--text);
}
.hz-mioreply:focus { outline: none; border-color: var(--accent); }
.hz-mioacts { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 4px; }
.hz-miosend {
  padding: 9px 22px; border-radius: 999px; border: 0; cursor: pointer;
  background: var(--accent); color: #fff; font: inherit; font-size: 14px; font-weight: 600;
}
.hz-miosend[disabled] { opacity: .6; cursor: default; }

/* --- one pane at a time on a phone -------------------------------------------------- */
@media (max-width: 860px) {
  #view-mio { flex-direction: column; gap: 0; }
  /* Folders become a horizontal strip that scrolls, rather than a rail eating a third
     of a 375px screen. */
  .hz-miorail {
    flex-direction: row; width: auto; gap: 6px; overflow-x: auto; padding: 0 0 10px;
    scrollbar-width: none;
  }
  .hz-miorail::-webkit-scrollbar { display: none; }
  .hz-miotab { width: auto; flex: none; padding: 7px 14px; border: 1px solid var(--border); }
  .hz-mionew { flex: none; margin: 0; order: -1; }
  .hz-miomain { width: 100%; }
  .hz-miosplit { display: block; }
  .hz-miolist { flex: none; max-height: none; overflow: visible; }
  .hz-miopane { max-height: none; overflow: visible; padding-left: 0; border-left: 0; }
  /* Reading replaces the list instead of splitting the screen in two unusable halves. */
  #view-mio.reading .hz-miolist, #view-mio.reading .hz-miosearch { display: none; }
  #view-mio:not(.reading) .hz-miopane { display: none; }
  .hz-mioreadbar { display: flex; }
  .hz-miotitle { font-size: 18px; }
}

/* --- Omnivox direct sync (the phone's path) ------------------------------------------
   The consent block is styled to be READ, not skimmed past: it is the one place in this
   product where a password leaves the device, and the copy that says so should not look
   like fine print. */
.hz-ovx-box { border: 1px solid var(--border); border-radius: 12px; padding: 14px; margin: 14px 0; background: var(--card2); }
.hz-ovx-box h4 { margin: 0 0 6px; font-size: 14.5px; }
.hz-ovx-box .hz-field { margin: 10px 0; }
.hz-ovx-box input[type="text"], .hz-ovx-box input[type="password"] { font-size: 16px; }
.hz-ovx-consent {
  margin: 12px 0; padding: 12px 14px; border-radius: 10px;
  border: 1px solid var(--accent-line); background: var(--accent-soft);
}
.hz-ovx-consent strong { display: block; font-size: 13.5px; margin-bottom: 6px; }
.hz-ovx-consent p { margin: 0 0 8px; font-size: 12.5px; line-height: 1.55; color: var(--muted); }
.hz-ovx-verify {
  margin: 12px 0; padding: 13px 14px; border-radius: 11px;
  border: 1px solid var(--warn); background: color-mix(in srgb, var(--warn) 10%, transparent);
}
.hz-ovx-verify strong { display: block; font-size: 13.5px; margin-bottom: 4px; }
.hz-ovx-verify input { font-size: 16px; letter-spacing: .18em; }
.hz-ovx-agree { display: flex; flex-direction: row; align-items: center; gap: 9px; font-size: 13px; color: var(--text); }
.hz-ovx-agree input { width: 18px; height: 18px; flex: none; accent-color: var(--accent); }
