/* Calendar event dialog */
/* ===================== */
/*
  The one dialog the calendar opens for an event, in all three of its faces: reading one,
  changing one, and making one. Built on the .calendar-sheet shell rather than the plain
  modal one - a bottom sheet on a phone, a centred dialog on a wider screen.

  There used to be two dialogs here, a sheet to read an event and a form to make one, and
  the work of keeping them looking alike was never going to be finished. There is one
  now, and the faces are the same surface with different contents: the same shell, the
  same header, the same labels in the same places. Moving between them is a turbo frame
  navigation, so the pencil changes what is in the dialog and nothing about the dialog.

  Almost all of this is the writing face, because a form is the harder of the two: it is
  taller than the viewport on a phone and needs its body to scroll under a footer that
  stays put. The reading face is at the foot of the file.
*/

dialog.pg-modal.calendar-form-sheet {
  /* Two radii for the whole dialog, rather than one per control invented as it was
     added: --pg-form-radius for anything you click or type in, --pg-panel-radius for
     a box that holds other things. The shell keeps the sheet's own 1rem.

     Both are Bootstrap's own scale rather than values of this dialog's choosing.
     That is the point: the fields here are .form-control-sm, so they already wear
     --bs-border-radius-sm, and every other form control in the app wears the same
     pair. A softer radius would have made this one dialog the odd one out - the
     detail sheet is rounder, but it is a sheet of panels to read rather than a form
     to fill in.

     The footer's Cancel / Add Event are left alone: they are .pg-button--chrome,
     shared across the app, and forking them for one dialog would be the wrong
     trade. */
  --pg-form-radius: var(--bs-border-radius-sm);
  --pg-panel-radius: var(--bs-border-radius);

  /* Taller than the detail sheet: a form has fields to reach, and on a phone the
     bottom sheet is the whole working surface rather than a glance at one event.

     dvh, not vh - vh is a fraction of the layout viewport, which on a phone
     browser stays the same height whether its own address bar is showing or has
     collapsed on scroll. Sized off that, a sheet tall enough to reach 92% of the
     larger, chrome-collapsed figure could end up taller than what's actually on
     screen with the chrome still showing, and this sheet is pinned to the
     viewport's bottom (see .calendar-sheet's own margin) - so the overflow comes
     off the top, cropping the header rather than clipping something lower down
     that would at least still scroll into view. dvh tracks the chrome as it
     shows and hides instead. vh stays first for a browser too old to know dvh -
     it'll just never see the second declaration override it. */
  max-height: 92vh;
  max-height: 92dvh;
}

@media (width >=576px) {
  dialog.pg-modal.calendar-form-sheet {
    width: 32rem;
    max-height: 86%;
  }
}

/* The datepicker renders inside this box rather than in <body> (see
   datepicker_controller.js), so it needs to be the positioning context - otherwise
   the popup is placed against the page and lands somewhere else entirely. */
.calendar-form-sheet .calendar-sheet__content {
  position: relative;
}

/* application/_form_error_banner's stock Bootstrap .alert-danger - a saturated red
   panel with a heavy border - reads as an alarm in here, well past what "fix the
   title" deserves and out of step with how this dialog already speaks about danger
   elsewhere (see .calendar-sheet__tool--danger, .calendar-form__tool--danger): a
   tint and a border colour, not a filled block. Scoped to this dialog rather than
   changed on the shared partial itself, which other, plainer pages still use as
   Bootstrap drew it. */
.calendar-form-sheet .alert-danger {
  padding: 0.625rem 0.75rem;

  /* Bootstrap's own .alert carries a 1rem margin-bottom, which doesn't collapse
     into calendar-form__body's row-gap the way two margins would - the two just
     add up, leaving nearly double the gap under this one field that every other
     one gets. This is the one place in the dialog spacing isn't owned entirely by
     row-gap, so it's reset here rather than left to stack with it. */
  margin-bottom: 0;

  background-color: rgba(var(--bs-danger-rgb), 0.12);
  border-color: rgba(var(--bs-danger-rgb), 0.4);

  color: var(--bs-danger-text-emphasis);
}

.calendar-form-sheet .alert-danger .alert-heading {
  margin-bottom: 0.25rem;

  font-size: 0.8125rem;
  font-weight: 700;

  color: var(--bs-danger-text-emphasis);
}

.calendar-form-sheet .alert-danger ul {
  font-size: 0.8125rem;
}

/* The form fills the dialog: a scrolling body between a header and a footer that
   both stay put. The frame between them is a plain block, so the form itself has to
   carry the column layout.

   Written against dialog.pg-modal.calendar-form-sheet rather than the class alone:
   the rule this overrides is dialog.pg-modal .modal-content > turbo-frame, which
   carries an element more, and a shorter selector would lose on specificity however
   late it loaded. */
dialog.pg-modal.calendar-form-sheet .modal-content > turbo-frame {
  display: flex;
  flex-direction: column;

  min-height: 0;

  /* The form scrolls internally; the frame must not scroll as well, or the footer
     would scroll away with it. */
  overflow: visible;
}

/* Both faces fill the frame the same way: a header and a footer that stay put with a
   scrolling middle between them. The reading face has no footer, which is the only
   difference and needs no rule of its own. */
.calendar-form,
.calendar-view {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;

  min-height: 0;
}

dialog.pg-modal .modal-body.calendar-form__body,
dialog.pg-modal .modal-body.calendar-view__body {
  flex: 1 1 auto;

  display: flex;
  flex-direction: column;
  row-gap: 0.875rem;

  min-height: 0;

  padding: 0.25rem 0.25rem 0.75rem;

  /* Both axes said explicitly. Setting only overflow-y would leave overflow-x at
     visible, which CSS then computes to auto - so the body would grow a horizontal
     scrollbar the moment anything inside it (a focus ring, the search box's addon)
     reached the edge. */
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
}

/* Fields */
.calendar-form__field {
  display: flex;
  flex-direction: column;
  row-gap: 0.3125rem;

  /* The location picker's results drop out of this box */
  position: relative;
}

/* The whole "when" question on one line: the date, and beside it either the field
   that was added to it or the offers to add one. Aligned on the inputs rather than
   on the tops of the boxes, so the buttons sit level with the field they follow and
   not with its label.

   Wraps rather than crushes - a date field and two buttons do not fit across a
   narrow phone, and a wrapped second line reads better than three squeezed columns. */
.calendar-form__row {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-end;
  column-gap: 0.5rem;
  row-gap: 0.5rem;
}

/* Date, and whichever of Time or End date is beside it - every field in this row
   asks for the same 6rem, so a row of them reads as equal columns rather than one
   field crowding the rest into what's left.

   Equal *because they're all asking for the same thing*, which is also what keeps
   an extra date's row (see _event_date_row) lined up under this one: flex divides
   a row's leftover space between whatever happens to be in that row, so two rows
   only land on the same widths if they're made of the same things asking for the
   same sizes. In multi-date mode they are - event-dates#addDate hides the End Date
   and More Dates offers and shows the header's trash instead, leaving the first
   row as Date + Time + trash, which is exactly an extra row's own shape. Nothing
   here has to arrange that; it just has to not break it, which means not sizing
   any of these off its own content.

   > rather than a descendant selector: an extra date's customTime is a
   .calendar-form__field nested inside the field that holds it (again see
   _event_date_row), and reaching inside would apply this a second time down that
   wrapper's own column axis, where 6rem is a height rather than a width. */
.calendar-form__row > .calendar-form__field {
  flex: 1 1 6rem;

  min-width: 0;
}

/* End Date and More Dates don't need a field's 6rem - each is showing a short
   label, not a value that wants room to stay readable, and reserving a full field
   for both was what pushed More Dates onto a second line while the row still had
   space for it. Sized to their own content instead, so the row wraps on what they
   actually need.

   grow: 0 as well: neither ever holds a value to make room for, so any slack the
   row does have is better spent on Date and Time than on stretching these two
   past their label - the same reasoning .calendar-form__row > .calendar-form__tool
   already applies to the trash can beside an extra date. No min-width: 0, either:
   an adder's label doesn't wrap (white-space: nowrap keeps its icon and text on
   one line), so shrinking it past its own content would spill over the button's
   edge rather than wrap the row the way a field's text can shrink instead. */
.calendar-form__row > .calendar-form__adders > .calendar-form__adder {
  flex: 0 1 auto;
}

/* Add Time, while it's still an offer, doesn't need the full 6rem either - and on
   a phone that 6rem is the difference between More Dates fitting on this line and
   dropping to its own. It's a short label like the two adders above, so it's sized
   the same way.

   Only while More Dates (or End Date) is still on offer, though, which is the same
   thing as saying only while this is the only date row on the page. The moment a
   second date exists, event-dates#addDate hides both those offers - so this stops
   matching, Time goes back to asking for the same 6rem every other field asks for,
   and the rows line up again. That guard is the whole point: content-sizing this
   unconditionally is what pulled the first row out of line with the extra rows
   below it, because "Time", "Same as first date" and a real "16:03" are three different
   widths, and a row's leftover - and so every other field's share of it - moves
   with whichever one is showing. With no second row to line up with, none of that
   matters and the width is just free. */
.calendar-form__row:has(> .calendar-form__adders > .calendar-form__adder:not([hidden]))
  > .calendar-form__field:has(> .calendar-form__adder) {
  flex-basis: auto;
}

/* Matches the stat labels on the detail sheet, so a field's name and a fact's name
   are the same kind of thing in the same kind of box. */
.calendar-form__label {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  text-transform: uppercase;

  color: var(--bs-secondary-color);
}

.calendar-form__label-row {
  display: flex;
  /* Centred rather than on the baseline: this row carries icon buttons as well as
     text ones, and an icon has no baseline worth aligning a label to. */
  align-items: center;
  justify-content: space-between;
  column-gap: 0.5rem;
}

/* Taking the location back off. At the end of the label row rather than under the
   field, since it undoes the offer that put the field there.

   Deliberately not the label's own type. It sat inches from the label wearing the same
   uppercase, the same weight and the same grey, and read as a second heading rather than
   as something to press. It is an outlined chip instead, in sentence case: the border
   says button, and dropping the caps and the letter-spacing is what stops it looking
   like the word beside it. Quiet enough not to outweigh the label it follows, and the
   whole thing turns red on hover to say what pressing it does.

   Time and End date used to share this same label-row chip, but a row of dashed date
   fields each wearing its own "Remove" above it read as more controls than the row
   actually has - see .calendar-form__clearable below for where those two live now. */
.calendar-form__clear {
  padding: 0.0625rem 0.4375rem;

  background: none;
  border: var(--bs-border-width) solid var(--bs-border-color);
  border-radius: var(--pg-form-radius);

  color: var(--bs-secondary-color);

  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.3;
}

.calendar-form__clear:hover,
.calendar-form__clear:focus-visible {
  border-color: var(--bs-danger);

  color: var(--bs-danger);
}

/* Time, End date, and an extra date's own customTime - a field that clears itself
   rather than wearing a separate "Remove" above it. Sitting inside the field it
   undoes reads as one control (a clearable date/time, the way a search box's own X
   works) instead of two - a dashed input plus a chip a row above it - and it's what
   let End date drop its label-row entirely: with nothing left to justify a
   space-between row against, the label goes back to sitting on its own like Date's
   does above it. */
.calendar-form__clearable {
  position: relative;
}

/* Room for the button so typed or picked text never runs under it. */
.calendar-form__clearable > .form-control {
  padding-right: 1.875rem;
}

.calendar-form__input-clear {
  position: absolute;
  top: 50%;
  right: 0.3125rem;

  transform: translateY(-50%);

  display: flex;
  align-items: center;
  justify-content: center;

  width: 1.375rem;
  height: 1.375rem;
  padding: 0;

  background: none;
  border: none;
  border-radius: 999px;

  color: var(--bs-secondary-color);
  font-size: 0.75rem;
}

.calendar-form__input-clear:hover,
.calendar-form__input-clear:focus-visible {
  background-color: rgba(var(--bs-danger-rgb), 0.12);

  color: var(--bs-danger);
}

.calendar-form__results {
  top: 100%;

  max-height: 14rem;
  overflow-y: auto;
}

/* The body's own overflow-y:auto (above) clips to whatever height its in-flow
   content actually reaches - a position:absolute dropdown hanging below the
   location field doesn't add to that height itself, so on a short form (Add/Edit
   Template, with nothing below location but the footer) the body ends right at
   the field and the list gets cut flat there: no rounded bottom corner, no
   shadow, however far past that it would otherwise run. It already caps its own
   height at 14rem with its own scrollbar above, so letting it spill past the
   body costs nothing - it still can't grow past the dialog itself, which keeps
   its own corners (see dialog.pg-modal's overflow: hidden). Scoped to while a
   list is actually open, so the rest of the time the body keeps the clip that
   stops a focus ring or the search box's Google addon from opening a horizontal
   scrollbar. */
.calendar-form-sheet .modal-body.calendar-form__body:has(.calendar-form__results.pg-menu--open) {
  overflow: visible;
}

/* Each Google Places suggestion - a .pg-menu-item (see application/dropdown.css)
   with a pin ahead of the text, so the list reads as a set of places rather than as
   a bare dump of strings. */
.place-autocomplete-suggestion {
  display: flex;
  align-items: center;
  column-gap: 0.5rem;

  overflow: hidden;
}

.place-autocomplete-suggestion__icon {
  flex: 0 0 auto;

  color: var(--bs-secondary-color);
}

.place-autocomplete-suggestion__text {
  min-width: 0;

  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.calendar-form__switch {
  margin: 0;
}

/* The toolbar and the editor as one control. The wrapper owns the border, the
   rounding and the focus ring; the two inside it give theirs up, so the field reads
   as a single box with a strip of tools along its top rather than as two boxes
   sharing a label. */
.calendar-form__editor {
  background-color: var(--bs-body-bg);
  border: var(--bs-border-width) solid var(--bs-border-color);
  border-radius: var(--pg-panel-radius);

  /* Keeps the toolbar's own corners inside the wrapper's rounding */
  overflow: hidden;
}

/* The ring belongs to the whole control now, since the element actually focused is
   the editor inside it. */
.calendar-form__editor:focus-within {
  border-color: var(--bs-primary);
  box-shadow: 0 0 0 0.25rem rgba(var(--bs-primary-rgb), 0.25);
}

/* A strip along the top of the control rather than a floating row of buttons */
.calendar-form-sheet .calendar-notes-toolbar {
  display: block;

  padding: 0.375rem 0.375rem 0;

  background-color: var(--bs-tertiary-bg);
  border-bottom: var(--bs-border-width) solid var(--bs-border-color);
}

/* Stock is 10px, which was spacing the toolbar off the editor below it. The strip
   supplies that separation now, so the groups only need to clear its own padding. */
.calendar-form-sheet .calendar-notes-toolbar .trix-button-group {
  margin-bottom: 0.375rem;
}

/* Inside the wrapper the editor gives up its own frame: no border, no rounding, no
   focus ring of its own (the wrapper shows one for it), and no background, so the
   wrapper's shows through.

   Two rows to start with. Only a min-height, so the editor grows with whatever is
   typed into it rather than scrolling inside a fixed box - two 1.5 line-heights
   plus the vertical padding .form-control gives it. */
.calendar-form-sheet trix-editor.calendar-form__notes {
  min-height: calc((2 * 1.5em) + 0.75rem);

  background-color: transparent;
  border: none;
  border-radius: 0;
  box-shadow: none;
}

/* Footer */
/*
  Written against dialog.pg-modal .modal-footer.calendar-form__footer, not the class
  alone. The shared rule it is correcting is dialog.pg-modal .modal-footer, which carries
  two classes and an element to this one's single class - so a shorter selector loses
  outright, whatever the stylesheet order, and every declaration below was silently doing
  nothing. That is exactly what went wrong here: the buttons kept the shared 0.75rem 1rem,
  which inset them a whole rem further than the fields above them and, worse, left twice
  as much air under them as over them once the sheet's own bottom padding was added on.

  The vertical padding is therefore asymmetric on purpose. Above the buttons this is the
  only gap there is, measured from the border. Below them the sheet's content box adds its
  own 1.125rem, less the quarter-rem this bleeds back - so the footer contributes nothing
  at the bottom and the two gaps come out level.
*/
dialog.pg-modal .modal-footer.calendar-form__footer {
  flex: 0 0 auto;

  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  column-gap: 0.75rem;
  row-gap: 0.5rem;

  flex-wrap: wrap;

  margin: 0 -0.25rem -0.25rem;

  padding: 0.875rem 0.25rem 0;

  border-top: var(--bs-border-width) solid var(--bs-border-color);
}

.calendar-form__actions {
  display: flex;
  flex-direction: row;
  column-gap: 0.5rem;

  margin-left: auto;
}

.calendar-form__save {
  display: inline-flex;
  align-items: center;
  column-gap: 0.375rem;
}

/* On a phone the two answers take the footer's whole width and split it, rather than
   sitting as a pair on the right with dead space beside them. */
@media (width <=575.98px) {
  .calendar-form__actions {
    flex: 1 1 100%;
  }

  .calendar-form__actions > * {
    flex: 1 1 0;
    justify-content: center;
  }
}

/* Adding a time or an end date */
/* ---------------------------- */
/*
  The optional halves of "when". Only one of the two can be on at a time (see
  event_when_controller.js), so this is a pair of offers rather than a pair of fields.
*/

/* `hidden` has to be said explicitly here: the field it is put on is a flex
   container, and a `display` of its own would otherwise win over the browser's
   default rule for the attribute. */
.calendar-form__field[hidden] {
  display: none;
}

/* Not a flex container of its own any more - its three buttons need to be this
   row's direct children to size the same as the fields either side of them (see
   the "why" note on .calendar-form__row .calendar-form__field above), so this
   wrapper takes no part in layout and exists only to group them in the markup. */
.calendar-form__adders {
  display: contents;
}

/* Quiet and dashed, so they read as places where something could be added rather
   than as controls with an effect of their own - the same voice the detail sheet's
   notice uses for a part of an event that is not there.

   Matched to the height of a .form-control so they sit flush with the date field on
   the same row rather than a couple of pixels adrift of it. */
.calendar-form__adder {
  display: inline-flex;
  align-items: center;
  column-gap: 0.375rem;

  /* Matched to a .form-control-sm, which is what every field in this dialog now is:
     1.5 line-heights of its 0.875rem text, plus its 0.5rem of vertical padding and
     its borders. Written in rem rather than em because this button sets its own
     smaller font-size, so an em would measure against that instead of against the
     input it is lining up with. */
  min-height: calc(1.3125rem + 0.5rem + (var(--bs-border-width) * 2));

  /* A little tighter than most padding in this dialog - up to four of these share
     a row with the date field on a new event, and every rem here is one this row
     runs out of before wrapping. */
  padding: 0 0.5rem;

  background: none;
  border: var(--bs-border-width) dashed var(--bs-border-color);
  border-radius: var(--pg-form-radius);

  color: var(--bs-secondary-color);

  font-size: 0.75rem;
  font-weight: 700;
  white-space: nowrap;
}

.calendar-form__adder:hover {
  border-style: solid;
  border-color: var(--bs-emphasis-color);

  color: var(--bs-emphasis-color);
}

/* Multiple dates */
/* -------------- */
/*
  "Add More Dates" - see event_dates_controller.js. Each row it adds is a
  .calendar-form__row, the same one the "when" question sits in, so a date-and-time
  pair here lines up exactly the way the first one does.
*/

[data-event-dates-target="panel"] {
  display: flex;
  flex-direction: column;
  row-gap: 0.5rem;

  margin-top: 0.5rem;
}

[data-event-dates-target="rows"] {
  display: flex;
  flex-direction: column;
  row-gap: 0.5rem;
}

/* The trash can that removes a date - an extra row's own, and (once there's more
   than one date) the first row's - aligned on the fields beside it rather than the
   label row above them, and given their height explicitly (the same .form-control-sm
   height calendar-form__adder and the colour toggle use) rather than the smaller
   size every other .calendar-form__tool wears. Without this it drew visibly shorter
   than the date and time either side of it instead of matching them. A direct-child
   selector catches both: every .calendar-form__row - the first row and each row the
   template clones - has at most one .calendar-form__tool of its own, sitting right
   in the row rather than nested in a field. */
.calendar-form__row > .calendar-form__tool {
  align-self: flex-end;
  flex: 0 0 auto;

  width: calc(1.3125rem + 0.5rem + (var(--bs-border-width) * 2));
  height: calc(1.3125rem + 0.5rem + (var(--bs-border-width) * 2));
}

/* "Same as first date" - a row's time before it's been given one of its own. Styled
   as a quiet placeholder rather than a button that does something, in the same voice
   the detail sheet's dashed notices use for a part of an event that isn't there -
   tapping it doesn't perform an action so much as reveal a field that was always
   implicitly there, just answered for now by the first date's own time. */
.calendar-form__inherit-time {
  display: flex;
  align-items: center;
  column-gap: 0.375rem;

  width: 100%;
  min-height: calc(1.3125rem + 0.5rem + (var(--bs-border-width) * 2));

  padding: 0 0.625rem;

  background: none;
  border: var(--bs-border-width) dashed var(--bs-border-color);
  border-radius: var(--pg-form-radius);

  color: var(--bs-secondary-color);

  font-size: 0.8125rem;
  text-align: left;
}

.calendar-form__inherit-time:hover {
  border-style: solid;
  border-color: var(--bs-emphasis-color);

  color: var(--bs-emphasis-color);
}

/* Field tools */
/* ----------- */
/*
  The icon controls that sit on a field label row - currently the bin that takes a
  location off. Built to the detail sheet tool buttons, one size down: they belong to
  a field rather than to the whole event, and should not outweigh the label beside.
*/

.calendar-form__tool {
  flex: 0 0 auto;

  display: flex;
  align-items: center;
  justify-content: center;

  width: 1.625rem;
  height: 1.625rem;

  padding: 0;

  background-color: transparent;
  border: var(--bs-border-width) solid var(--bs-border-color);
  border-radius: var(--pg-form-radius);

  color: var(--bs-secondary-color);

  font-size: 0.75rem;
  line-height: 1;
}

.calendar-form__tool:hover {
  border-color: var(--bs-emphasis-color);

  color: var(--bs-emphasis-color);
}

.calendar-form__tool--danger:hover {
  border-color: var(--bs-danger);

  color: var(--bs-danger);
}


/* Notes toolbar */
/* ------------- */
/*
  The dialog's cut-down Trix toolbar - see gear/calendar/_notes_toolbar.html.erb.

  Trix's own stylesheet lays the button row out with justify-content: space-between,
  which works for the default toolbar because it has five groups and a spacer to
  distribute. This one has two, so space-between does the only thing it can and sends
  one to each end of the row. Grouped from the left instead, with a gap of their own.
*/

/* Centred on the strip, rather than Trix's space-between - which with only two groups
   left would send one to each end of the row. Trix's overflow-x: auto is deliberately
   left alone: nine buttons fit the dialog at its usual widths so no scrollbar
   appears, and on a very narrow phone they can still be reached rather than clipped
   by the body, which no longer scrolls sideways itself. */
.calendar-notes-toolbar .trix-button-row {
  justify-content: center;
}

/* Trix spaces its groups with 1.5vw, which is measured against the whole window and
   so has nothing to do with how much room this dialog actually has. */
.calendar-notes-toolbar .trix-button-group:not(:first-child) {
  margin-left: 0.625rem;
}


/* Colour tag */
/* ---------- */
/*
  The event's colour, beside the title: one disc showing what is set, which opens the
  palette when tapped. Built on .pg-dropdown / .pg-menu (see application/dropdown.css)
  so it behaves like every other menu in the app - outside click and Escape close it.
*/

.calendar-form__title-row {
  /* Shared by Colour and Templates below, so the two pills read as matching
     columns rather than each sizing to its own label's width - "TEMPLATES" is
     the wider of the two, so it sets the width both take. */
  --calendar-form-tool-width: 4.75rem;

  display: flex;
  flex-direction: row;

  /* Aligned on the controls, not the tops of the columns, so the disc sits level
     with the title field whatever the two labels above them do. */
  align-items: flex-end;
  column-gap: 0.5rem;
}

/* The title takes the room; the tag and the template tools beside it stay the size
   they need */
.calendar-form__title-row .calendar-form__field {
  flex: 1 1 auto;

  min-width: 0;
}

/* Narrower still now that the template tools sit on the same row as the colour tag -
   a title rarely wants more than this, and the row would otherwise crowd the tools
   against the edge on a narrow phone. Trimmed further to fund the wider, matching
   Colour and Templates columns below (see --calendar-form-tool-width). */
.calendar-form__field--title {
  flex: 1 1 5.5rem;
}

/* "Show Templates" - laid out as its own column, label over control, the same
   shape .calendar-form__colour takes beside it: this row's fields all carry an
   uppercase label above them, and a bare icon with none would sit low and
   unexplained beside two that do. */
/* stretch, not flex-end: "Templates" is wider than the icon under it, and
   right-aligning a narrow button under a wide label left the button looking like
   an orphaned tile rather than that label's own control. Stretching makes the
   button's width follow the label's instead - the container is already exactly as
   wide as its widest child (the label, nowrap), so the button just fills that. */
.calendar-form__title-tools {
  flex: 0 0 var(--calendar-form-tool-width);

  display: flex;
  flex-direction: column;
  align-items: stretch;
  row-gap: 0.3125rem;

  min-width: 0;
}

.calendar-form__title-tools .calendar-form__label {
  white-space: nowrap;
}

/* Sits beside the title field and the colour toggle, not on a field's own label row
   the way every other .calendar-form__tool does (the bin that clears a location,
   say) - so it takes the height a .form-control-sm actually makes, the same one the
   colour toggle already uses, rather than the smaller size built for a label row.
   Without this it drew noticeably shorter than everything else in the row and never
   quite lined up with it, even flush against its bottom edge.
   Widened with padding rather than left square, the same reason the colour toggle
   is a pill rather than a bare disc: a bare icon this height read as a stray tile
   under a label wider than it, not as a control belonging to it. */
.calendar-form__title-tools .calendar-form__tool {
  width: auto;
  height: calc(1.3125rem + 0.5rem + (var(--bs-border-width) * 2));
  padding: 0 0.625rem;

  background-color: var(--bs-body-bg);

  font-size: 0.875rem;
}

/* A fieldset carries browser defaults that would draw a box round the group, and a
   min-width that stops it shrinking inside a flex row. Laid out as its own column so
   the label sits over the disc the way every other label sits over its field - the
   legend is visually hidden and absolutely positioned, so it takes no part in this.
   Fixed to the same width as .calendar-form__title-tools (see
   --calendar-form-tool-width) so the two pills beside the title match rather than
   each sizing to its own, differently-worded label. */
.calendar-form__colour {
  flex: 0 0 var(--calendar-form-tool-width);

  display: flex;
  flex-direction: column;
  align-items: stretch;
  row-gap: 0.3125rem;

  min-width: 0;

  margin: 0;
  padding: 0;

  border: none;
}

.calendar-form__colour .calendar-form__label {
  white-space: nowrap;
}

/* The dropdown wrapper - .pg-dropdown supplies the positioning context the menu
   hangs from. */
.calendar-form__colour-control {
  display: block;
}

/* A control the size of a field rather than a bare dot. A 30px disc under a label
   reading COLOUR left the label three times the width of the thing it named, which
   made the pair read as a second full field balancing the title. Given the height of
   a .form-control-sm and a caret to say it opens, it carries its label at a sane
   proportion and matches the row it sits in. */
.calendar-form__colour-toggle {
  /* .calendar-form__colour stretches .calendar-form__colour-control to the fixed
     column width (see --calendar-form-tool-width), but that div's own default
     width doesn't reach this button - a button doesn't stretch to fill a plain
     block parent the way it stretches to fill a flex parent (compare the
     Templates button, which sits directly in its own stretching flex column). */
  width: 100%;

  display: flex;
  align-items: center;
  justify-content: center;
  column-gap: 0.3125rem;

  height: calc(1.3125rem + 0.5rem + (var(--bs-border-width) * 2));
  padding: 0 0.5rem;

  background-color: var(--bs-body-bg);
  border: var(--bs-border-width) solid var(--bs-border-color);
  border-radius: var(--pg-form-radius);
}

/* Says the control opens something, the way a select does */
.calendar-form__colour-caret {
  color: var(--bs-secondary-color);

  font-size: 0.625rem;
  line-height: 1;
}

.calendar-form__colour-toggle:hover,
.calendar-form__colour-toggle[aria-expanded="true"] {
  border-color: var(--bs-emphasis-color);
}

.calendar-form__colour-chip {
  display: block;

  width: 1rem;
  height: 1rem;

  border-radius: 50%;

  /* A dark rim, so a pale colour still reads as a disc rather than as a hole */
  border: 1px solid rgba(0, 0, 0, 0.35);
}

/* Nothing chosen - a hollow ring rather than a colour, so "no colour" doesn't look
   like a colour that happens to match the field behind it. */
.calendar-form__colour-chip--none {
  background-color: transparent;
  border: 1px dashed var(--bs-secondary-color);
}

/* Hung from the right so a palette wider than its trigger opens back across the
   dialog rather than off the edge of it. */
.calendar-form__colour-menu {
  right: 0;
  left: auto;

  min-width: 0;

  padding: 0.5rem;

  /* A box holding other things, so it takes the panel radius rather than .pg-menu's
     app-wide one - it sits directly under a control wearing the form radius. */
  border-radius: var(--pg-panel-radius);
}

/* Two rows of five, which is what ten swatches make without a gap on the end */
.calendar-form__swatches {
  display: grid;
  grid-template-columns: repeat(5, auto);
  column-gap: 0.4375rem;
  row-gap: 0.4375rem;
}

.calendar-form__swatch {
  /* The chip is the control; the input behind it is only the state */
  cursor: pointer;
}

/* Kept in the page rather than display:none, so the group stays reachable by keyboard
   and readable to a screen reader - the chip beside it shows what it is doing. */
.calendar-form__swatch-input {
  position: absolute;

  width: 1px;
  height: 1px;

  opacity: 0;

  pointer-events: none;
}

.calendar-form__swatch-chip {
  display: flex;
  align-items: center;
  justify-content: center;

  width: 1.5rem;
  height: 1.5rem;

  border-radius: 50%;

  border: 1px solid rgba(0, 0, 0, 0.35);

  font-size: 0.75rem;
  line-height: 1;
}

.calendar-form__swatch-chip--none {
  background-color: var(--bs-secondary-bg);
  border-color: var(--bs-border-color);
  color: var(--bs-secondary-color);
}

/* The ring sits outside the chip with a gap of the menu's own background, so it reads
   as a ring round the colour rather than as a change to the colour. */
.calendar-form__swatch-input:checked + .calendar-form__swatch-chip {
  box-shadow: 0 0 0 2px var(--bs-body-bg), 0 0 0 4px var(--bs-emphasis-color);
}

.calendar-form__swatch-input:focus-visible + .calendar-form__swatch-chip {
  box-shadow: 0 0 0 2px var(--bs-body-bg), 0 0 0 4px var(--bs-primary);
}

/* Date fields */
/* ----------- */
/*
  The two date fields are readonly: their value only ever comes from the picker, and a
  typeable date field on a phone summons the soft keyboard the moment it is tapped -
  covering the very calendar the tap was meant to open.

  Bootstrap greys a readonly control to say "you cannot change this", which is the wrong
  message here: it is changed by tapping it. Put back to an ordinary field, with a
  pointer cursor to say it does something.
*/

.calendar-form-sheet .form-control[readonly] {
  background-color: var(--bs-body-bg);
  opacity: 1;

  cursor: pointer;
}

/* Gear picker */
/* ----------- */
/*
  Choosing the event's gear, as one collapsed disclosure per category - see
  gear/calendar/_gear_picker.html.erb. Built to look like the packing lists' category
  groups, which are the same idea on a bigger surface.
*/

.calendar-gear {
  display: flex;
  flex-direction: column;
  row-gap: 0.375rem;
}

.calendar-gear-group {
  background-color: var(--bs-body-bg);
  border: var(--bs-border-width) solid var(--bs-border-color);
  border-radius: var(--pg-form-radius);

  /* Keeps the item list's corners inside the group's own rounding */
  overflow: hidden;
}

.calendar-gear-group__title {
  display: flex;
  align-items: center;
  column-gap: 0.5rem;

  padding: 0.4375rem 0.625rem;

  color: var(--bs-emphasis-color);

  font-size: 0.8125rem;
  font-weight: 700;

  cursor: pointer;

  /* The row carries its own caret, so the browser's default marker would be a second
     one saying the same thing. */
  list-style: none;
}

.calendar-gear-group__title::-webkit-details-marker {
  display: none;
}

.calendar-gear-group__caret {
  flex: 0 0 auto;

  color: var(--bs-secondary-color);

  font-size: 0.625rem;

  transition: transform 0.15s ease-in-out;
}

.calendar-gear-group[open] .calendar-gear-group__caret {
  transform: rotate(90deg);
}

.calendar-gear-group__name {
  min-width: 0;

  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Reads "10" until something is chosen and "2/10" after - see gear_picker_controller */
.calendar-gear-group__count {
  margin-left: auto;

  color: var(--bs-secondary-color);

  font-size: 0.6875rem;
  font-weight: 700;
}

/* A shut category with something in it still says so */
.calendar-gear-group--chosen {
  border-color: var(--pg-accent);
}

.calendar-gear-group--chosen .calendar-gear-group__count {
  color: var(--pg-accent);
}

/* An opened category, as a grid of pictures. auto-fill so it takes as many columns as
   the dialog's width allows rather than a count fixed for one screen size. */
.calendar-gear-group__items {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(4.25rem, 1fr));
  gap: 0.5rem;

  /* A category of forty would otherwise push everything below it out of reach; the
     dialog body scrolls, but not so far that the footer is a journey away. */
  max-height: 15rem;

  padding: 0.5rem;

  border-top: var(--bs-border-width) solid var(--bs-border-color);

  overflow-y: auto;
  overscroll-behavior: contain;
}

.calendar-gear-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  row-gap: 0.25rem;

  min-width: 0;

  cursor: pointer;
}

/* The picture is the control. Kept in the page rather than display:none so the grid
   stays reachable by keyboard and readable to a screen reader. */
.calendar-gear-tile__check {
  position: absolute;

  width: 1px;
  height: 1px;

  opacity: 0;

  pointer-events: none;
}

.calendar-gear-tile__frame {
  position: relative;

  display: block;

  width: 100%;
  aspect-ratio: 1;

  background-color: var(--bs-tertiary-bg);
  border: 2px solid transparent;
  border-radius: var(--pg-form-radius);

  overflow: hidden;
}

.calendar-gear-tile__frame img {
  width: 100%;
  height: 100%;

  object-fit: cover;

  display: block;
}

/* Chosen: an accent ring round the picture and a tick in the corner. The ring alone
   would be easy to miss on a busy photograph. */
.calendar-gear-tile__check:checked + .calendar-gear-tile__frame {
  border-color: var(--pg-accent);
}

.calendar-gear-tile__tick {
  position: absolute;
  top: 0.1875rem;
  right: 0.1875rem;

  display: none;
  align-items: center;
  justify-content: center;

  width: 1.125rem;
  height: 1.125rem;

  background-color: var(--pg-accent);
  border-radius: 50%;
  color: var(--pg-accent-contrast);

  font-size: 0.6875rem;
  line-height: 1;
}

.calendar-gear-tile__check:checked + .calendar-gear-tile__frame .calendar-gear-tile__tick {
  display: flex;
}

.calendar-gear-tile__check:focus-visible + .calendar-gear-tile__frame {
  border-color: var(--bs-primary);
  box-shadow: 0 0 0 2px rgba(var(--bs-primary-rgb), 0.35);
}

.calendar-gear-tile__name {
  max-width: 100%;

  color: var(--bs-secondary-color);

  font-size: 0.6875rem;
  font-weight: 700;

  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.calendar-gear-tile__check:checked ~ .calendar-gear-tile__name {
  color: var(--bs-emphasis-color);
}


/* Reading face */
/* ------------ */
/*
  The same dialog with nothing to fill in - see gear/calendar/_event_view.html.erb.

  The middle ground between the form and the detail sheet this replaced. From the form
  it takes the labels, the rows and the spacing, so a fact's name sits exactly where the
  matching field's name sits and the pencil doesn't rearrange the dialog under the
  reader. From the sheet it takes the filled boxes, because a fact should not look like
  something you can type into: there is no border, no focus ring and no pointer, and the
  fill is the panel grey the sheet used for the same job.
*/

/* Every answer on this face is set in one type, said once. A place name, a pair of
   temperatures, a count of items and a note are all the same kind of thing here - what
   the event says in reply to a label - and three of them arriving in three different
   weights made the face look assembled rather than designed.

   600 rather than the 700 the short values used to carry: a note is prose, sometimes
   several lines of it, and bold prose reads as shouting. */
.calendar-view__value,
.calendar-view__notes {
  background-color: var(--bs-secondary-bg);
  border-radius: var(--pg-form-radius);

  color: var(--bs-emphasis-color);

  font-size: 0.875rem;
  font-weight: 600;
}

/* A one-line answer, matched to the height of a .form-control-sm so the two faces line
   up row for row - filled rather than outlined, so it reads as an answer rather than an
   empty field. */
.calendar-view__value {
  display: flex;
  align-items: center;
  column-gap: 0.375rem;

  min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2));
  min-width: 0;

  padding: 0.25rem 0.5rem;

  overflow: hidden;
  /* text-overflow only takes effect on text that isn't allowed to wrap - without this,
     a long answer (a location name, on a narrow phone) wraps to a second line instead
     of ellipsising, growing taller than the fixed-height answer beside it. */
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* Location and weather share .calendar-form__row's default even split, which is right
   for two fields of the same kind (the "when" row's date and time) but wrong for these
   two: a place name routinely wants more than half the row while a temperature pair
   never does, so the width the row hands weather sits empty and the width it holds
   back from location is exactly what pushed its text into a second line before the
   fix above. Roughly two thirds to location, one third to weather - close to the
   split the empty space in the box already suggested. */
.calendar-form__row .calendar-view__location-field {
  /* A zero basis rather than a size in rem, so the two thirds is exact at any width
     instead of only approaching it once there is enough spare room to grow into -
     with a basis the field's own minimum was doing most of the sizing and the ratio
     barely anything. */
  flex: 2 1 0;
}

.calendar-form__row .calendar-view__weather-field {
  flex: 1 1 0;
}

/* An event too far ahead for the ~10 days the forecast reaches leaves the value box
   empty rather than showing temperatures for the wrong day (see
   calendar_forecast_controller.js) - the same reason the event card's own weather
   badge disappears via .calendar-event__weather:empty. Reached from the field rather
   than the box itself, since an empty box would still leave the WEATHER label above
   answering nothing; location then takes the row alone instead of leaving weather's
   third of it sitting blank beside it. */
.calendar-form__row .calendar-view__weather-field:has(.calendar-view__value:empty) {
  display: none;
}

/* Whether an event is kept off the pup's calendar. A quiet tag on the date line rather
   than a box of its own: it is a fact about who can see the event, not a part of what
   the event is. */
.calendar-view__hidden-tag {
  display: inline-flex;
  align-items: center;
  column-gap: 0.25rem;

  margin-left: 0.375rem;

  padding: 0.05rem 0.375rem;

  background-color: var(--bs-secondary-bg);
  border-radius: 999px;

  font-size: 0.6875rem;
  font-weight: 700;
  white-space: nowrap;
}

/* The event's gear is drawn by the picker's own rules - .calendar-gear-group and
   .calendar-gear-group__items, one shut disclosure holding a grid of pictures. Not a
   resemblance: it is the same markup, so opening the pencil shows the same pictures at
   the same size in the same order, with ticks on them.

   What differs is the box around them. The picker's own disclosure is a control and
   wears a control's frame; here the summary is just another answer box, and opening it
   drops the grid underneath in a second box of the same fill. */
.calendar-view__disclosure {
  min-width: 0;
}

/* The answer that can be opened. Everything about it is .calendar-view__value; the only
   additions are the chevron pushed to the far end and the marker the browser would
   otherwise put at the near one. */
.calendar-view__summary {
  justify-content: space-between;

  cursor: pointer;

  list-style: none;
}

.calendar-view__summary::-webkit-details-marker {
  display: none;
}

/* Opened, the summary is the top of one taller box rather than a lid on a second one,
   so it gives up the two corners it now shares with the grid below. */
.calendar-view__disclosure[open] .calendar-view__summary {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

/* A hairline rather than a ring. <summary> is one of the few controls Chrome gives the
   focus ring to on a plain tap, so anything with weight to it - and the primary-blue
   glow that was here had plenty - reads as an error box appearing under the reader's
   thumb for no reason they asked for.

   Kept rather than removed, though. Tabbing to this control has to show somewhere, and
   the caret and the grid only answer once it has been pressed. */
.calendar-view__summary:focus-visible {
  outline: var(--bs-border-width) solid var(--bs-emphasis-color);
  outline-offset: -1px;
}

.calendar-view__caret {
  flex: 0 0 auto;

  color: var(--bs-secondary-color);

  font-size: 0.6875rem;

  transition: transform 0.15s ease-in-out;
}

.calendar-view__disclosure[open] .calendar-view__caret {
  transform: rotate(180deg);
}

/* The pictures, once asked for. Joined to the summary rather than floating under it:
   they are the same answer, so an opened Gear reads as one box that got taller. The gap
   that used to sit between them made it two things stacked, which is what the summary
   row is least - it is that box's own first line.

   Capped and scrolling: an event carrying thirty items would otherwise push the rest of
   the face out of reach. */
.calendar-view__gear {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(4.25rem, 1fr));
  gap: 0.5rem;

  max-height: 15rem;

  padding: 0.5rem;

  background-color: var(--bs-secondary-bg);
  border-radius: 0 0 var(--pg-form-radius) var(--pg-form-radius);

  /* The two halves share a fill, so without this the summary row would dissolve into
     the pictures under it. */
  border-top: var(--bs-border-width) solid var(--bs-border-color);

  overflow-y: auto;
  overscroll-behavior: contain;
}

/* Nothing to tick, so nothing that looks tickable. The picture is still a link to the
   full-size image, which is the one thing a tile here is for. */
.calendar-view__gear .calendar-gear-tile {
  cursor: default;
}

.calendar-view__gear .calendar-gear-tile__frame {
  cursor: zoom-in;
}

/* Notes, as written. Filled like the location and weather boxes rather than outlined
   like the editor it was typed into: on this face it is another stated fact, and an
   outline would be the one box on the page still suggesting you could type in it.

   The rich text inside is whatever Trix produced, and arrives wrapped in .trix-content
   (see layouts/action_text/contents/_content), which is what styles its headings and
   lists. Only the spacing at the two ends is corrected here: browser defaults would open
   a gap under the label and below the last line that no other field has. */
.calendar-view__notes {
  padding: 0.4375rem 0.625rem;

  /* Looser than the one-line boxes, which have no second line to breathe against */
  line-height: 1.5;

  /* A long unbroken string - a pasted URL - wraps rather than widening the dialog */
  overflow-wrap: anywhere;
}

.calendar-view__notes .trix-content > :first-child {
  margin-top: 0;
}

.calendar-view__notes .trix-content > :last-child {
  margin-bottom: 0;
}

/* States what the event is missing. Dashed, so it reads as a space nothing has been put
   in rather than as another panel of content - and deliberately not a link: location and
   gear are added through the pencil, so there is one route to each. */
.calendar-view__notice {
  display: flex;
  align-items: center;
  column-gap: 0.5rem;

  margin: 0;

  padding: 0.75rem 0.875rem;

  border: var(--bs-border-width) dashed var(--bs-border-color);
  border-radius: var(--pg-panel-radius);

  background-color: var(--bs-secondary-bg);
  color: var(--bs-secondary-color);

  font-size: 0.8125rem;
  font-weight: 600;
}
