/*
 * Screen chrome for the generator: the options panel and the preview frame.
 *
 * None of this reaches paper. The whole shell is hidden in the print block at
 * the bottom of the file so that what comes out of the printer is the calendar
 * and nothing else.
 */

:root {
  --ink: #16161a;
  --ink-soft: #5c5c66;
  /* Contrast floor: this is the lightest ink used, and it sits on both the
     white panel and the sunken body, so it has to clear 4.5:1 on the darker
     of the two. */
  --ink-faint: #6e6e78;
  --rule: #e2e2e6;
  --surface: #ffffff;
  --surface-sunken: #f1f1f3;
  --surface-raised: #fafafb;
  --accent: #b3271f;
  --accent-strong: #931c15;
  --accent-ink: #ffffff;
  --focus: #2a6df4;
  /*
   * The notice reads in the accent's family rather than amber, so the one
   * warning on the page belongs to the same palette as the buttons. The icon
   * runs lighter than the text it sits beside: it is a marker, not the message.
   */
  --warning-surface: #fdf4f3;
  --warning-rule: #f0d7d4;
  --warning-icon: #c96760;
  --warning-ink: #8e2f28;
  --warning-text: #6f3a36;
  --radius: 6px;
}

* {
  box-sizing: border-box;
}

/*
 * A column the height of the window at minimum, with the shell taking the slack
 * between the header and the footer. That is what keeps the footer on the
 * bottom edge when the calendar is short, without positioning anything.
 */
body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  margin: 0;
  background: var(--surface-sunken);
  color: var(--ink);
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

.app-header {
  flex: none;
  background: var(--surface);
  border-bottom: 1px solid var(--rule);
  padding: 16px 28px;
}

/* Matches the shell's own measure, so the title and the repo link sit on the
   same left and right edges as the content below them. */
.app-header-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 12px 20px;
  max-width: 1600px;
  margin: 0 auto;
}

.app-identity {
  flex: 1 1 auto;
  min-width: 0;
}

.repo-link {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: 8px;
  padding: 8px 14px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface-raised);
  color: var(--ink);
  font-size: 13.5px;
  font-weight: 600;
  text-decoration: none;
  transition: background-color 90ms ease, border-color 90ms ease;
}

.repo-link:hover {
  background: #ececed;
  border-color: #d5d5da;
}

.repo-link:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.repo-icon {
  width: 17px;
  height: 17px;
  flex: none;
  fill: currentColor;
}

.app-title {
  margin: 0;
  font-size: 19px;
  font-weight: 650;
  letter-spacing: -0.015em;
}

.app-tagline {
  margin: 3px 0 0;
  color: var(--ink-soft);
  font-size: 13.5px;
}

.app-shell {
  display: flex;
  gap: 28px;
  align-items: flex-start;
  padding: 28px;
  max-width: 1600px;
  margin: 0 auto;
  width: 100%;

  /* Takes the space between the header and the footer, which is what holds the
     footer at the bottom of a short page without positioning it. */
  flex: 1 1 auto;
}

/* Preview --------------------------------------------------------------- */

.preview {
  flex: 1 1 auto;
  min-width: 0;
}

.preview-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 14px;
  margin-bottom: 16px;
  color: var(--ink-soft);
  font-size: 13.5px;
}

.preview-count {
  color: var(--ink);
  font-weight: 650;
}

.preview-hint {
  margin-left: auto;
  color: var(--ink-faint);
  font-size: 12.5px;
}

.preview-frame {
  overflow-x: auto;
  padding-bottom: 8px;
}

/* Controls -------------------------------------------------------------- */

/*
 * The sidebar holds two cards: what the calendar is, and what to do with it.
 * Splitting them makes clear that the buttons and the print settings are not
 * more options.
 */
.sidebar {
  position: sticky;
  top: 28px;
  flex: 0 0 auto;
  width: 372px;
  display: flex;
  flex-direction: column;
  gap: 14px;

  /* A sticky column taller than the window would pin its top and put the Print
     button permanently out of reach, so it scrolls within itself instead. On a
     tall window there is nothing to scroll and no scrollbar appears. */
  max-height: calc(100vh - 56px);
  overflow-y: auto;
}

.card {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 10px;
}

.controls {
  padding: 0 18px 16px;

  /* Lets the fields below react to the card's own width rather than the
     window's, which is what actually decides whether a label fits. */
  container-type: inline-size;
  container-name: panel;
}

.output {
  padding: 16px 18px 18px;
}

/* Tabs ------------------------------------------------------------------ */

/*
 * Folder tabs: the strip is recessed and runs to the card's edges, and the
 * selected tab is the same white as the panel with its bottom edge removed, so
 * it joins the content below instead of floating above it as a row of buttons.
 */
.tab-list {
  display: flex;
  gap: 4px;
  margin: 0 -18px;
  padding: 9px 14px 0;
  background: var(--surface-sunken);
  border-bottom: 1px solid var(--rule);
  border-radius: 9px 9px 0 0;
}

.tab {
  appearance: none;
  flex: 1 1 0;
  min-width: 0;
  margin-bottom: -1px;
  padding: 8px 6px;
  border: 1px solid transparent;
  border-bottom: 0;
  border-radius: 7px 7px 0 0;
  background: none;
  color: var(--ink-soft);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 90ms ease, color 90ms ease;
}

.tab:hover:not([aria-selected='true']) {
  background: rgb(255 255 255 / 55%);
  color: var(--ink);
}

.tab[aria-selected='true'] {
  background: var(--surface);
  border-color: var(--rule);
  color: var(--accent);
  padding-bottom: 9px;
}

.tab:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: -3px;
}

/*
 * Rows are separated by space rather than rules. The gap between rows is set
 * wider than the gap inside a field, so a label, its control and its note read
 * as one block without needing a line drawn under them.
 */
.tab-panel {
  display: flex;
  flex-direction: column;
  gap: 19px;
  padding-top: 15px;
}

/*
 * Label above its controls, and narrow options paired two to a row. Stacking
 * the label gives each button group the full width of its column, and pairing
 * keeps the whole panel within a screen instead of running off the bottom.
 */
.control-row {
  display: flex;
  gap: 14px;
}

/* One field fills the row; two share it evenly. */
.control-field {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 5px;
  min-width: 0;
}

/*
 * The longest pairing here is "Months to Print" beside a three-digit page
 * count, which fills most of a half-width column. A tight gap and a shrinkable
 * label keep that on one line rather than overflowing the column on a system
 * whose default sans runs a little wider.
 */
.control-label-row {
  display: flex;
  align-items: baseline;
  gap: 6px;
  min-width: 0;
}

.control-label {
  min-width: 0;
  color: var(--ink-soft);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: 0.02em;
}

/*
 * Markers beside a field label: a warning for a setting that needs something
 * enabled in the print dialog, and an info mark carrying the field's
 * description so it does not take a line of its own under every control.
 */
.control-label-marker {
  position: relative;
  display: inline-flex;
  align-self: center;
  cursor: help;
}

.control-label-marker.is-warning {
  color: var(--warning-icon);
}

.control-label-marker.is-info {
  color: var(--ink-faint);
}

.control-label-marker.is-info:hover,
.control-label-marker.is-info:focus-visible {
  color: var(--ink-soft);
}

.control-label-marker:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 50%;
}

.control-label-marker-icon {
  width: 14px;
  height: 14px;
  display: block;
}

/*
 * The bubble opens downward, because the sidebar is a scroll container and a
 * tooltip above the first row of a card would be clipped by its own top edge.
 * It is anchored to an edge of the marker rather than centered on it for the
 * same reason: centered, a bubble beside a left-hand label hangs off the side of
 * the card and gets cut off. Fields in the right-hand column anchor the other
 * way so the bubble opens inward from there.
 */
.control-label-marker::after {
  content: attr(data-tooltip);
  position: absolute;
  top: calc(100% + 7px);
  left: 0;
  z-index: 5;
  width: max-content;
  max-width: 170px;
  padding: 6px 9px;
  border-radius: 5px;
  background: var(--ink);
  color: #fff;
  font-size: 11.5px;
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: 0;
  text-align: left;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 100ms ease;
}

.control-row.is-split > .control-field:nth-child(2) .control-label-marker::after {
  left: auto;
  right: 0;
}

.control-label-marker:hover::after,
.control-label-marker:focus-visible::after {
  opacity: 1;
  visibility: visible;
}

/*
 * A field that has nothing to act on stays where it is and dims. Removing it
 * would shift every row below it the moment an unrelated toggle is flipped.
 */
.control-field.is-disabled {
  opacity: 0.4;
}

.control-field.is-disabled .option-button,
.control-field.is-disabled .control-color {
  cursor: not-allowed;
}


/* The row of controls belonging to one field: usually a single group, but Year
   and Grid Color pair a group with a free-entry box beside it. */
.control-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}

.option-group {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}

/*
 * Segmented control: one bordered pill split into equal parts, so a set of
 * mutually exclusive choices reads as a single switch and squares off against
 * the select boxes and inputs sharing the panel.
 */
.option-group.is-segmented {
  flex: 1 1 0;
  flex-wrap: nowrap;
  min-width: 0;
  gap: 0;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface-raised);
  overflow: hidden;
}

.option-button {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface-raised);
  color: var(--ink-soft);
  font: inherit;
  font-size: 13px;
  font-weight: 550;
  padding: 6px 11px;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 90ms ease, color 90ms ease, border-color 90ms ease;
}

.is-segmented .option-button {
  flex: 1 1 0;
  min-width: 0;
  border: 0;
  border-left: 1px solid var(--rule);
  border-radius: 0;
  padding: 6px 8px;
}

.is-segmented .option-button:first-child {
  border-left: 0;
}

.option-icon {
  width: 15px;
  height: 15px;
  flex: none;
}

/*
 * Below this the two paired columns are too narrow for a word like "Landscape"
 * beside its neighbor, so the icon carries the meaning on its own. The label
 * stays in the accessibility tree and in the tooltip rather than being removed.
 */
@container panel (max-width: 430px) {
  .control-row.is-split .option-button.has-icon {
    padding: 6px 9px;
  }

  .control-row.is-split .option-button.has-icon .option-button-text {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

.option-button:hover:not(:disabled) {
  background: #ececed;
  color: var(--ink);
}

.option-button[aria-pressed='true'] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
}

/*
 * Hovering the selected option has to stay selected-looking. Without this the
 * generic hover rule above wins on source order and the active segment flips
 * from accent to gray under the pointer, which reads as deselecting it.
 */
.option-button[aria-pressed='true']:hover:not(:disabled) {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
  color: var(--accent-ink);
}

/* The segmented pill clips its children, so the ring has to sit inside. */
.is-segmented .option-button:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: -2px;
}

.option-button:focus-visible,
.control-select:focus-visible,
.control-number:focus-visible,
.control-color:focus-visible,
.action-button:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.control-select,
.control-number {
  appearance: none;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface-raised);
  color: var(--ink);
  font: inherit;
  font-size: 13px;
  padding: 6px 10px;
}

.control-select {
  width: 100%;
  min-width: 0;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-soft) 50%),
    linear-gradient(135deg, var(--ink-soft) 50%, transparent 50%);
  background-position: right 12px center, right 7px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 26px;
}

/*
 * Sits next to a segmented group on the Year row, so it gets a plainer surface
 * to read as a free-entry box rather than one more segment.
 */
.control-number {
  width: 74px;
  background: var(--surface);
}

/*
 * An input joined to a read-only addon, so a value and what it produces read as
 * one control: the month count and the number of pages it comes to.
 */
.input-group {
  display: flex;
  flex: 1 1 0;
  min-width: 0;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
}

.input-group .control-number {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
}

.input-group-addon {
  display: flex;
  flex: none;
  align-items: center;
  padding: 0 9px;
  border-left: 1px solid var(--rule);
  background: var(--surface-sunken);
  color: var(--ink-faint);
  font-size: 12px;
  white-space: nowrap;
}

.input-group:focus-within {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.input-group .control-number:focus-visible {
  outline: 0;
}

/* Square, centered against the segmented group it sits beside. */
.control-color {
  width: 32px;
  height: 32px;
  flex: none;
  align-self: center;
  padding: 3px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
  cursor: pointer;
}

.control-color::-webkit-color-swatch-wrapper {
  padding: 0;
}

/* The swatch keeps its own hairline so a pale custom color still has an edge. */
.control-color::-webkit-color-swatch {
  border: 1px solid rgb(0 0 0 / 14%);
  border-radius: 2px;
}

.control-color::-moz-color-swatch {
  border: 1px solid rgb(0 0 0 / 14%);
  border-radius: 2px;
}

/* Tucked up against its control, so it groups with it rather than floating
   midway between two rows. */
.control-note {
  margin: -2px 0 0;
  color: var(--ink-faint);
  font-size: 12px;
  line-height: 1.35;
}

.control-row[hidden] {
  display: none;
}

/* Actions --------------------------------------------------------------- */

.actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.action-button {
  appearance: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  font: inherit;
  font-size: 16px;
  font-weight: 600;
  padding: 13px 16px;
  cursor: pointer;
  transition: background-color 90ms ease;
}

.action-icon {
  width: 19px;
  height: 19px;
  flex: none;
}

.action-icon path,
.action-icon rect {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.action-button.is-primary {
  background: var(--accent);
  color: var(--accent-ink);
}

.action-button.is-primary:hover {
  background: var(--accent-strong);
}

.action-button.is-secondary {
  background: var(--surface-raised);
  border-color: var(--rule);
  color: var(--ink);
}

.action-button.is-secondary:hover {
  background: #ececed;
}

.actions-note {
  margin: 2px 0 0;
  color: var(--ink-faint);
  font-size: 12px;
  line-height: 1.45;
}

/* Instructions ---------------------------------------------------------- */

.instructions {
  margin-top: 16px;
  padding: 12px 13px 13px;
  background: var(--surface-sunken);
  border-radius: var(--radius);
}

.instructions-title {
  margin: 0 0 6px;
  font-size: 12px;
  font-weight: 650;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.instructions-lead {
  margin: 0 0 10px;
  color: var(--ink);
  font-size: 12.5px;
  line-height: 1.5;
}

/*
 * A settings table rather than a sentence: the left column names the control in
 * the print dialog and the right column is what to set it to, so it can be read
 * straight down while the dialog is open.
 */
.instructions-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 0;
  font-size: 12.5px;
  line-height: 1.4;
}

.instructions-row {
  display: flex;
  gap: 12px;
}

.instructions-row dt {
  flex: 0 0 auto;
  width: 84px;
  color: var(--ink-faint);
}

.instructions-row dd {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  color: var(--ink);
  font-weight: 600;
}

kbd {
  display: inline-block;
  padding: 1px 5px;
  border: 1px solid var(--rule);
  border-bottom-width: 2px;
  border-radius: 4px;
  background: var(--surface);
  color: var(--ink);
  font-family: inherit;
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.4;
}

/* Notice ---------------------------------------------------------------- */

.notice {
  margin-top: 10px;
  padding: 10px 12px;
  border: 1px solid var(--warning-rule);
  border-radius: var(--radius);
  background: var(--warning-surface);
  color: var(--warning-text);
  font-size: 12.5px;
  line-height: 1.5;
}

.notice[hidden] {
  display: none;
}

.notice-message {
  margin: 0;
}

.notice-icon {
  width: 14px;
  height: 14px;
  display: inline-block;
  vertical-align: -2px;
  margin-right: 6px;
  color: var(--warning-icon);
}

.notice strong {
  color: var(--warning-ink);
  font-weight: 650;
}

/* About ----------------------------------------------------------------- */

.app-footer {
  flex: none;
  border-top: 1px solid var(--rule);
  background: var(--surface);
  padding: 26px 28px 34px;
}

.app-footer-inner {
  max-width: 1600px;
  margin: 0 auto;
}

.about-title {
  margin: 0 0 9px;
  color: var(--ink-soft);
  font-size: 12px;
  font-weight: 650;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/*
 * Three paragraphs, three columns. They wrap to one column when the window is
 * too narrow to keep a readable measure in each.
 */
.about-columns {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 32px;
}

/*
 * --ink-faint is the lightest ink in the palette and was set to clear 4.5:1 on
 * both surfaces, so this is as dim as body text can go and still be readable.
 */
.about-columns p {
  flex: 1 1 260px;
  min-width: 0;
  margin: 0;
  color: var(--ink-faint);
  font-size: 11.5px;
  line-height: 1.5;
}

.about-link {
  color: var(--accent);
  text-underline-offset: 2px;
}

.about code {
  font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
  font-size: 11px;
  color: var(--ink-soft);
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

@media screen and (max-width: 1060px) {
  .app-shell {
    flex-wrap: wrap;
  }

  .sidebar {
    width: 100%;
  }

  .sidebar {
    position: static;
    order: -1;
    max-height: none;
    overflow: visible;
  }
}

@media screen and (max-width: 560px) {
  .app-shell,
  .app-header {
    padding: 16px;
  }
}

/*
 * Print: everything but the sheets disappears, and the page background goes
 * white so no shell color bleeds into the output.
 */
@media print {
  /* The screen shell is a flex column sized to the window; on paper it is just
     a block, and the sheets supply their own geometry. */
  body {
    display: block;
    min-height: 0;
    margin: 0;
    padding: 0;
    background: #fff;
  }

  /*
   * Nothing but the calendar reaches the paper, and that has to hold for things
   * this page did not put there. Browser extensions inject their own overlays
   * at body level (a breakpoint badge, a grid ruler, a colour picker), and a
   * fixed one lands on every printed sheet. Allowing only the shell through
   * covers those as well as our own header and footer.
   */
  body > *:not(.app-shell) {
    display: none !important;
  }

  /* The whole sidebar goes, not just the options card: the actions and print
     settings live in a second card beside it and would otherwise print. */
  .sidebar,
  .preview-bar {
    display: none;
  }

  .app-shell {
    display: block;
    padding: 0;
    margin: 0;
    max-width: none;
  }

  .preview,
  .preview-frame {
    overflow: visible;
    padding: 0;
    margin: 0;
  }

  /* The sheets themselves are reset for print in css/calendar.css, which is
     the sheet the Download HTML export carries. */
}
