/* calendar.css — this app's own components.
 *
 * Prefix: cal-. Never rst- (rastrillo's namespace, and a future release
 * may claim a name we took) and never estilo- (the suite's, and these
 * are not suite components until a second app copies one).
 *
 * Grammar: the attribute spelling, matching rastrillo v3 — the KIND is
 * a bare attribute, a VARIANT is a token in its value matched with ~=,
 * and a PART is its own flat attribute. Adopted per screen, never per
 * element.
 *
 * Every colour here is a --rst-* token or derived from one with
 * color-mix. Nothing is a literal hex: the theme's own contrast table
 * is measured, and a hex dropped in beside it is a value nobody has
 * measured against anything.
 */

/* ── The view switcher ─────────────────────────────────────────── */

/* One layout for all four views. Day, Week, Month and Agenda share this
 * main box, so switching views never moves the header, the toolbar or
 * the grid's edge (test/browser/views.mjs). 90rem, not rst-page's 64rem
 * reading column: seven day columns at 64rem are ~134px, too narrow for a
 * chip's time and title. rst-page's own inline padding is the gutter, the
 * same 1rem as the estilo bar's, so at 1440 the view's edge lines up with
 * the bar's brand. */
[cal-view] { max-width: 90rem; }  /* after tokens.css, so it beats rst-page's 64rem */

/* The bar is sticky, so an anchor or :target lands below it, not under
 * it (shared-chrome spec §5.3). */
html { scroll-padding-block-start: var(--estilo-bar-block-size); }

/* The toolbar (layout.html "calendar-toolbar"): the steps at the start,
 * the view switcher at the end, one row wherever it fits. Every label in
 * it is the same in every view, so nothing in it moves. */
[cal-bar] {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--rst-sp-3);
  justify-content: space-between;
  margin-block-end: var(--rst-sp-4);
}

[cal-bar-steps] {
  display: flex;
  gap: var(--rst-sp-2);
}

/* A view tab is as wide as its label in the current tab's weight (600,
 * against 550), whether or not it is current: a hidden bold copy of the
 * label (the cal-tab attribute) sizes the column. Without it the switcher
 * narrowed and widened by a fraction of a pixel with every view, and at
 * the end of the row that moved it. */
[cal-tab] { display: inline-grid; text-align: center; }
[cal-tab]::after {
  block-size: 0;
  content: attr(cal-tab);
  font-weight: 600;
  overflow: hidden;
  visibility: hidden;
}

[cal-bar-spacer] { flex: 1 1 auto; }

/* The day's sentence. It is the page's opening line, so it reads at
 * body size rather than as a caption — a summary set in small grey
 * type is a summary people learn to skip. */
[cal-sentence] {
  color: var(--rst-text);
  margin: 0 0 var(--rst-sp-4);
  max-width: 52rem;
}

/* The agenda's scope line: a caption, not an answer, so it is muted. */
[cal-sentence-muted] { color: var(--rst-text-muted); }

/* ── Month ─────────────────────────────────────────────────────── */

/* A real table: a month IS tabular data, so the row and column headers
 * come free for a screen reader and a scriptless page keeps working
 * semantics a div grid would have to rebuild with ARIA. */
[cal-month] {
  border-collapse: collapse;
  table-layout: fixed;
  width: 100%;
}

[cal-month] th {
  color: var(--rst-text-muted);
  font-size: var(--rst-fs-sm);
  font-weight: 600;
  padding: var(--rst-sp-2);
  text-align: start;
}

[cal-month] td {
  border: 1px solid var(--rst-line);
  height: 7.5rem;
  padding: 0;
  vertical-align: top;
}

[cal-day-cell] {
  display: flex;
  flex-direction: column;
  gap: 2px;
  height: 100%;
  padding: var(--rst-sp-1);
}

/* Outside the month: dimmed, but never hidden. A month grid whose
 * leading and trailing days are blank makes the first week look
 * broken. */
[cal-day-cell~="outside"] { opacity: 0.55; }

[cal-day-cell~="weekend"] { background: var(--rst-surface-2); }

[cal-day-num] {
  color: var(--rst-text-muted);
  display: block;
  font-size: var(--rst-fs-sm);
  padding: 2px;
  text-decoration: none;
}

[cal-day-num]:hover { text-decoration: underline; }

/* Today. Three signals, never colour alone: a filled marker, bold
 * weight, and the visually-hidden word "today" in the link's
 * accessible name. */
[cal-day-num~="today"] {
  background: var(--rst-accent);
  border-radius: var(--rst-radius-pill);
  color: var(--rst-on-accent);
  font-weight: 700;
  inline-size: 1.6rem;
  text-align: center;
}

[cal-more] {
  color: var(--rst-text-muted);
  font-size: var(--rst-fs-xs);
  padding-inline-start: 2px;
}

/* ── Chips ─────────────────────────────────────────────────────── */

/* One event, wherever it appears. The hue is set inline per calendar
 * as --cal-hue, and every colour below is mixed from it against a
 * theme token — so a chip stays legible in both schemes without the
 * calendar's colour being a measured value of its own. */
[cal-chip] {
  align-items: baseline;
  background: color-mix(in oklab, hsl(var(--cal-hue, 220) 60% 50%) 14%, var(--rst-surface));
  border-inline-start: 3px solid hsl(var(--cal-hue, 220) 55% 45%);
  border-radius: 0;
  color: var(--rst-text);
  display: flex;
  font-size: var(--rst-fs-sm);
  gap: var(--rst-sp-1);
  overflow: hidden;
  padding: 2px var(--rst-sp-1);
  text-decoration: none;
  white-space: nowrap;
}

[cal-chip]:hover { text-decoration: underline; }

[cal-chip-time] {
  color: var(--rst-text-muted);
  flex: none;
  font-variant-numeric: tabular-nums;
}

[cal-chip-title] {
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The past is dimmed AND written in the past tense — the tense is in
 * the markup, so the meaning survives greyscale and a stylesheet that
 * failed to load. */
[cal-chip~="past"] { opacity: 0.6; }

/* A cancelled event is struck through and says "Cancelled" in its own
 * text. Never the line alone. */
[cal-chip~="cancelled"] [cal-chip-title] { text-decoration: line-through; }

/* A note is a thing that is true of a day without occupying it, so it
 * is drawn as an outline rather than a filled block. */
[cal-chip~="note"] {
  background: transparent;
  border: 1px dashed color-mix(in oklab, hsl(var(--cal-hue, 220) 55% 45%) 60%, var(--rst-line));
  border-inline-start-width: 3px;
}

/* A commitment is yours rather than shared: a quieter fill than a
 * meeting, so a day of your own work does not read as a day of
 * meetings. */
[cal-chip~="commitment"] {
  background: color-mix(in oklab, hsl(var(--cal-hue, 220) 60% 50%) 7%, var(--rst-surface));
}

[cal-chip~="tentative"] {
  border-inline-start-style: dotted;
}

/* ── Day and week columns ──────────────────────────────────────── */

[cal-grid] {
  display: grid;
  gap: 0;
  grid-template-columns: 3.5rem 1fr;
  /* The grid scrolls inside the first screen, but never below 24rem: on
   * a short window (or a phone on its side) 100vh - 20rem is a sliver. */
  max-block-size: max(24rem, calc(100vh - 20rem));
  overflow: auto;
}

[cal-week-grid] {
  display: grid;
  gap: 0;
  grid-template-columns: 3.5rem repeat(7, minmax(6rem, 1fr));
}

[cal-week-scroll] {
  /* The grid scrolls inside the first screen, but never below 24rem: on
   * a short window (or a phone on its side) 100vh - 20rem is a sliver. */
  max-block-size: max(24rem, calc(100vh - 20rem));
  overflow: auto;
}

[cal-hours] {
  border-inline-end: 1px solid var(--rst-line);
  position: relative;
}

[cal-hour] {
  block-size: 3rem;
  color: var(--rst-text-faint);
  font-size: var(--rst-fs-xs);
  font-variant-numeric: tabular-nums;
  padding-inline-end: var(--rst-sp-2);
  text-align: end;
  /* The label sits at the line it names, not in the box below it. */
  transform: translateY(-0.5em);
}

/* The day column. 72rem is 24 hours at 3rem each, so an hour label and
 * the grid line beside it stay in step. */
[cal-column] {
  block-size: 72rem;
  border-inline-end: 1px solid var(--rst-line);
  position: relative;
}

[cal-column-head] {
  /* A fixed height, so the all-day row can stick exactly beneath it. */
  align-items: center;
  block-size: var(--cal-week-head-size);
  border-block-end: 1px solid var(--rst-line);
  box-sizing: border-box;
  display: flex;
  justify-content: center;
  padding: 0 var(--rst-sp-2);
  position: sticky;
  text-align: center;
  /* 0, not --estilo-bar-block-size: the heads stick inside
   * [cal-week-scroll], which is their own scroll box, so the sticky bar
   * above the page never overlaps them. The bar's offset here left a 52px
   * band of hour rows painted above the day names. */
  top: 0;
  z-index: 2;
  background: var(--rst-bg);
}

[cal-column-head~="today"] { font-weight: 700; }

[cal-week-grid] { --cal-week-head-size: 2.5rem; }

/* The hour rules, drawn as a repeating background so 24 of them cost
 * one declaration rather than 24 elements. */
[cal-column-rules] {
  background-image: repeating-linear-gradient(
    to bottom,
    var(--rst-line) 0,
    var(--rst-line) 1px,
    transparent 1px,
    transparent 3rem
  );
  inset: 0;
  position: absolute;
}

/* An empty half hour, as a link. This is what makes "click a time to
 * create an event" work with no JavaScript: 48 ordinary links per day,
 * each with a screen-reader label naming its time. */
[cal-slot] {
  display: block;
  inset-inline: 0;
  position: absolute;
}

[cal-slot]:hover {
  background: color-mix(in oklab, var(--rst-accent) 10%, transparent);
}

[cal-slot]:focus-visible {
  outline: 2px solid var(--rst-accent);
  outline-offset: -2px;
}

/* A placed event. top/height/left/width arrive as inline style from
 * the server's own packer — rastrillo's CSP allows inline styles, so
 * this needs no exception. */
[cal-placed] {
  position: absolute;
  z-index: 1;
}

[cal-placed] [cal-chip] {
  block-size: 100%;
  align-items: flex-start;
  flex-direction: column;
  gap: 0;
  white-space: normal;
}

/* Under an hour there is room for one line, so time and title share
 * it. Stacked, the title is the second line and the first thing the
 * chip's own overflow cuts off. */
[cal-placed~="short"] [cal-chip] {
  align-items: baseline;
  flex-direction: row;
  gap: var(--rst-sp-1);
  line-height: 1.15;
  padding-block: 0;
  white-space: nowrap;
}

/* The all-day strip above the timed grid. All-day events are not
 * packed into the column: giving a birthday a height would make it
 * look like a 24-hour meeting. */
[cal-allday] {
  border-block-end: 1px solid var(--rst-line);
  display: flex;
  flex-wrap: wrap;
  gap: var(--rst-sp-1);
  padding: var(--rst-sp-2);
}

/* In the week the all-day strip is one ROW of the grid, a cell per
 * day, so every day's cell is as tall as the busiest one and the
 * timed columns below all start on the same line. */
[cal-allday="week"] {
  align-content: flex-start;
  border-inline-end: 1px solid var(--rst-line);
  flex-direction: column;
  flex-wrap: nowrap;
  min-inline-size: 0;
  padding: var(--rst-sp-1);
}

/* The week's all-day row sticks under the day headings. The grid opens
 * scrolled to working hours, and a row that scrolled away with midnight
 * hid every all-day event from the first screen. */
[cal-allday="week"], [cal-allday-label] {
  background: var(--rst-bg);
  position: sticky;
  top: var(--cal-week-head-size);
  z-index: 2;
}

[cal-allday-label] {
  border-block-end: 1px solid var(--rst-line);
  border-inline-end: 1px solid var(--rst-line);
  color: var(--rst-text-faint);
  font-size: var(--rst-fs-xs);
  padding: var(--rst-sp-1) var(--rst-sp-2) 0 0;
  text-align: end;
}

/* ── The whisper ───────────────────────────────────────────────── */

/* An event authored in another zone. The primary time is always the
 * reader's own; this is a note beside it, never the headline. */
[cal-elsewhere] {
  color: var(--rst-text-faint);
  font-size: var(--rst-fs-xs);
}

/* ── Narrow screens ────────────────────────────────────────────── */

/* At 320px the month reflows to a stacked list of days. A seven-column
 * grid at that width is seven columns of nothing.
 *
 * Week keeps its two dimensions and scrolls sideways: a week view IS a
 * two-dimensional comparison, and flattening it to a list makes it the
 * agenda, which already exists. */
@media (max-width: 40rem) {
  [cal-month], [cal-month] thead { display: block; }
  [cal-month] thead { display: none; }
  [cal-month] tbody, [cal-month] tr { display: block; }
  [cal-month] td { display: block; height: auto; min-height: 2.5rem; }
  /* A day with nothing in it is not worth a row of its own on a
   * phone. */
  [cal-month] td:not(:has([cal-chip])):not(:has([cal-day-num~="today"])) { display: none; }
  [cal-day-cell] { flex-direction: row; align-items: baseline; gap: var(--rst-sp-2); }

  [cal-week-scroll] { overflow-x: auto; }
  [cal-week-grid] { min-inline-size: 48rem; }
}

/* ── Motion ────────────────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  [cal-slot], [cal-chip] { transition: none; }
}

/* ── Quick add ─────────────────────────────────────────────────── */

/* The header's action group. Quick add sits beside New event rather
 * than replacing it: one is the fast path, the other is the one that
 * shows every field. */
[cal-header-actions] {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--rst-sp-2);
}

/* The disclosure is a <details>, so it opens with no script at all.
 * Everything below is appearance; none of it is behaviour. */
[cal-quick] { position: relative; }

/* The closed state reads as a small text input, because that is what it
 * behaves like — click it and type. The marker is removed in both
 * spellings: Safari still wants the -webkit- pseudo-element and leaving
 * it in puts a disclosure triangle inside what looks like a field. */
[cal-quick] > summary {
  align-items: center;
  background: var(--rst-surface);
  border: 1px solid var(--rst-line);
  border-radius: var(--rst-radius-sm);
  color: var(--rst-text-muted);
  cursor: text;
  display: flex;
  font-size: var(--rst-fs-sm);
  gap: var(--rst-sp-1);
  list-style: none;
  min-width: 12rem;
  padding: var(--rst-sp-2) var(--rst-sp-3);
}

[cal-quick] > summary::-webkit-details-marker { display: none; }

[cal-quick] > summary:hover { border-color: var(--rst-accent); }

[cal-quick][open] > summary {
  border-color: var(--rst-accent);
  color: var(--rst-text);
}

/* The popover. Absolute so opening it does not push the calendar down
 * the page — the grid behind stays exactly where it was, which is what
 * makes this feel like a panel rather than a page change. */
[cal-quick-form] {
  background: var(--rst-surface);
  border: 1px solid var(--rst-line);
  /* A float, so the suite's float radius and shadow (estilo menu.css;
   * shared-chrome spec §11), not a hand-built 6px and shadow of its own. */
  border-radius: var(--estilo-float-radius);
  box-shadow: var(--rst-shadow-pop);
  /* Border-box, here and on the textarea below: both are width: 100%
   * on a phone, and content-box added the padding and border on top,
   * so the open panel ran ~15px past the right edge at 390px and the
   * page scrolled sideways. TestQuickAddOnANarrowScreen (make browser)
   * measures it. */
  box-sizing: border-box;
  /* Anchored to the END edge, so the panel opens back across the page
   * instead of off the side of it. The box sits at the far right of the
   * header, beside New event: anchored at the start edge a 30rem panel
   * hangs ~28px past a 1280px viewport, which every string assertion in
   * the Go suite happily passes. The browser gate measured it. */
  inset-inline-end: 0;
  margin-block-start: var(--rst-sp-1);
  padding: var(--rst-sp-3);
  position: absolute;
  top: 100%;
  width: min(30rem, 88vw);
  z-index: 30;
}

[cal-quick-text] {
  background: var(--rst-bg);
  border: 1px solid var(--rst-line);
  border-radius: var(--rst-radius-sm);
  box-sizing: border-box;
  color: var(--rst-text);
  display: block;
  font: inherit;
  padding: var(--rst-sp-2);
  resize: vertical;
  width: 100%;
}

/* iOS zooms the page into any text control under 16px, and tokens.css
 * brings controls up to 16px where the pointer is coarse. The box above
 * sets `font: inherit` with a selector that outweighs tokens.css's bare
 * `textarea`, and fell back to 14px. Same rule, same max(), restated
 * here at its weight. TestNoZoomOnTap (make browser) measures it. */
@media (pointer: coarse) {
  [cal-quick-text] {
    font-size: max(1rem, 1em);
  }
}

[cal-quick-pane] {
  background: var(--rst-surface-2);
  border-radius: var(--rst-radius-sm);
  font-size: var(--rst-fs-sm);
  margin-block: var(--rst-sp-2);
  padding: var(--rst-sp-2) var(--rst-sp-3);
}

[cal-quick-blank] { color: var(--rst-text-muted); margin: 0; }

[cal-quick-detail] { margin: 0; }

[cal-quick-row] {
  display: grid;
  gap: var(--rst-sp-2);
  grid-template-columns: 8rem 1fr;
  padding-block: var(--rst-sp-1);
}

[cal-quick-row] dt { color: var(--rst-text-muted); margin: 0; }
[cal-quick-row] dd { margin: 0; }

/* The caveat rides in the LABEL. Somebody who reads only the left
 * column still learns that nothing was emailed. */
[cal-quick-caveat] { color: var(--rst-text-faint); font-size: var(--rst-fs-xs); }

[cal-quick-people] { list-style: none; margin: 0; padding: 0; }

[cal-quick-notes] {
  color: var(--rst-text-muted);
  font-size: var(--rst-fs-xs);
  margin: var(--rst-sp-2) 0 0;
  padding-inline-start: var(--rst-sp-4);
}

[cal-quick-actions] {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--rst-sp-2);
}

[cal-quick-hint] {
  color: var(--rst-text-faint);
  font-size: var(--rst-fs-xs);
  flex: 1 1 12rem;
}

/* Narrow: the popover stops floating and becomes part of the page. A
 * 30rem panel pinned to a header on a 320px screen is off the side of
 * it. */
@media (max-width: 40rem) {
  [cal-header-actions] { width: 100%; }
  [cal-quick] { flex: 1 1 100%; }
  [cal-quick] > summary { min-width: 0; }
  [cal-quick-form] {
    box-shadow: none;
    position: static;
    width: 100%;
  }
  [cal-quick-row] { grid-template-columns: 1fr; gap: 0; }
}

/* The keyboard hint. Revealed by quickadd.js, never by the template:
 * with no script Enter is just a newline, and advertising a shortcut
 * the page cannot honour is worse than staying quiet. */
[cal-quick-key] {
  color: var(--rst-text-faint);
  font-size: var(--rst-fs-xs);
}

/* ── Booking pages, the host's side ─────────────────────────────── */

[cal-book-pane] {
  background: var(--rst-surface-2);
  border-radius: var(--rst-radius-sm);
  font-size: var(--rst-fs-sm);
  margin-block: var(--rst-sp-2) var(--rst-sp-4);
  padding: var(--rst-sp-2) var(--rst-sp-3);
}

[cal-book-blank] { color: var(--rst-text-muted); margin: 0; }

[cal-book-lines] { margin: 0; padding-inline-start: var(--rst-sp-4); }

/* Problems first, and unmistakably: the sentence cannot be saved while
 * one stands. */
[cal-book-problems] {
  color: var(--rst-tone-negative-fg);
  margin: 0 0 var(--rst-sp-2);
  padding-inline-start: var(--rst-sp-4);
}

[cal-book-page] { margin-block-end: var(--rst-sp-4); }

[cal-book-page-body] { padding: var(--rst-sp-3); }

[cal-book-url] { font-size: var(--rst-fs-sm); overflow-wrap: anywhere; }

[cal-book-page-actions] {
  align-items: flex-start;
  display: flex;
  flex-wrap: wrap;
  gap: var(--rst-sp-3);
  margin-block-start: var(--rst-sp-3);
}

/* The publish sentence sits above its own button and nowhere else. */
[cal-book-caveat] {
  color: var(--rst-text-muted);
  font-size: var(--rst-fs-xs);
  margin: 0 0 var(--rst-sp-2);
  max-width: 40rem;
}

[cal-booked-when] { margin: 0; }

/* ── Booking, the stranger's side ────────────────────────────────── */

[cal-book-zone-form] {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--rst-sp-2);
  margin-block-end: var(--rst-sp-4);
}

[cal-book-days] { list-style: none; margin: 0; padding: 0; }
[cal-book-days] li { padding-block: var(--rst-sp-1); }
[cal-book-days] small { color: var(--rst-text-muted); margin-inline-start: var(--rst-sp-2); }

/* Each slot is its own form: a button that posts an instant. */
[cal-book-slots] {
  display: flex;
  flex-wrap: wrap;
  gap: var(--rst-sp-2);
  margin-block: var(--rst-sp-3);
}

[cal-book-details] { padding: var(--rst-sp-3); }

[cal-book-manage-actions] {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: var(--rst-sp-2);
  margin-block: var(--rst-sp-4) var(--rst-sp-2);
}

/* The booker's closing row (rastrillo's form-foot, reached through our
 * own form's attribute: this file names no rst- selector). form-foot
 * draws its buttons at the lg size, and on a phone "Hold this time" and
 * "Pick another time" no longer fit side by side; without wrap the row
 * squeezes them into two- and three-line labels. Wrapping puts the
 * second button on a row of its own instead.
 * TestFormFootOnANarrowScreen (make browser) measures it. */
[cal-book-details] > div:has(> button[type="submit"]) {
  flex-wrap: wrap;
}

/* ── Layout that used to be style attributes ──────────────────────────
 * rastrillo's CSP (fb806a2) refuses style attributes by default. These
 * were constant, so they live here; what is still an attribute is a
 * per-event or per-calendar value (serving.go says which, and why the
 * policy keeps 'unsafe-inline' for them). */
[cal-day-chips] {
  display: flex;
  flex-direction: column;
  gap: var(--rst-sp-1);
  padding: var(--rst-sp-3);
}
[cal-box-body] { padding: var(--rst-sp-3); }
[cal-reminder] {
  display: flex;
  align-items: center;
  gap: var(--rst-sp-2);
}
[cal-reminder-add] {
  display: flex;
  align-items: flex-end;
  gap: var(--rst-sp-2);
  margin-top: var(--rst-sp-3);
}
[cal-feed-new] { margin-top: var(--rst-sp-4); }
/* A calendar's swatch on the Calendars list: its hue, the one value the
 * attribute still carries. */
[cal-dot] {
  background: hsl(var(--cal-hue, 220) 55% 45%);
  border-radius: var(--rst-radius-pill);
  inline-size: 12px;
  block-size: 12px;
}
