/* --- Buttons -------------------------------------------------------------- */
/* Shared by the editor, the project hero and the boot card. Small enough to
   live with the sheet rather than earn a file. */

.btn {
  padding: 6px 14px;
  border-radius: var(--r);
  border: 1px solid var(--hair);
  background: var(--fill);
  font-size: 12px;
  font-weight: 550;
  color: var(--ink-2);
  cursor: pointer;
  white-space: nowrap;
  box-shadow: inset 0 1px 0 var(--glass-hi);
  transition: background .16s ease, color .16s ease, border-color .16s ease;
}

.btn:hover { color: var(--ink); border-color: var(--line); }
.btn:disabled { opacity: .5; cursor: default; }

.btn.primary {
  color: var(--on-accent);
  background: var(--accent);
  border-color: transparent;
}

.btn.primary:hover { color: var(--on-accent); background: var(--accent-ink); }

/* Destructive is quiet until you reach for it — a permanently red button in a
   form trains people to ignore it. */
.btn.danger { color: var(--danger); }
.btn.danger:hover { color: var(--danger); background: var(--danger-bg); border-color: transparent; }

.icon-btn {
  display: grid;
  place-content: center;
  width: 22px;
  height: 22px;
  flex: none;
  border-radius: var(--r);
  color: var(--muted);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}

.icon-btn:hover { color: var(--ink); background: var(--hover); }

/* --- The editor ----------------------------------------------------------- */

.sheet {
  width: min(520px, calc(100vw - 28px));
  padding: 0;
  border: 0;
  border-radius: var(--r-lg);
  background: var(--glass);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  box-shadow: var(--glass-shadow), 0 0 0 1px var(--glass-edge);
  color: var(--ink);
  overflow: visible;
}

.sheet::backdrop {
  background: rgba(20, 30, 45, .34);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
}

/* The dialog sits above a blurred page, so it gets the specular edge that
   every other floating surface has. */
.sheet::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(170deg, var(--glass-hi) 0%, var(--glass-edge) 38%, var(--glass-edge) 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
}

.sheet-head {
  padding: 15px 20px 13px;
  border-bottom: 1px solid var(--hair);
}

.sheet-head h2 {
  font-size: 14px;
  font-weight: 650;
  letter-spacing: -.005em;
  color: var(--ink);
}

.sheet-body {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 13px 14px;
  padding: 17px 20px 19px;
  max-height: min(62vh, 560px);
  overflow-y: auto;
}

/* --- the split form ------------------------------------------------------- */

/* Two panes: what the task is on the left, when it happens on the right. Only
   the forms that ask for it — see layout() in views/sheet.js — which is why
   this keys off a class on the body rather than off the dialog.

   `:has()` is doing the one thing a class on `.sheet` would have done, without
   a second place to remember to set it: a form with a left pane needs a wider
   dialog, and the dialog is the ancestor. */
.sheet:has(.is-split) { width: min(840px, calc(100vw - 28px)); }

.sheet-body.is-split {
  /* The note side gets the extra width. It is the only field on either side
     with something to read in it; the other pane holds dates and selects, which
     have a natural width and stop benefiting from more. */
  grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
  gap: 0;
  padding: 0;
  /* A floor, so the note is a place to write rather than a box that happens to
     be as tall as the six controls beside it. */
  min-height: min(52vh, 400px);
}

.f-main {
  display: flex;
  flex-direction: column;
  gap: 13px;
  min-width: 0;
  padding: 17px 18px 19px 20px;
}

/* The right pane keeps the two-column grid the form has always had, so `wide`
   goes on meaning what it meant — just within the narrower half. Rows sit at
   their own height at the top rather than stretching to share the pane. */
.f-aside {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-content: start;
  gap: 13px 14px;
  min-width: 0;
  padding: 17px 20px 19px 18px;
  border-left: 1px solid var(--hair);
}

/* Takes the height the pane is not using. `min-height: 0` is the part that is
   easy to leave out: without it a flex child refuses to shrink below its
   content and the pane grows instead of the field scrolling. */
.f-row.grow {
  flex: 1;
  min-height: 0;
}

.f-row.grow .f-md,
.f-row.grow .f-md-view,
.f-row.grow .f-area {
  flex: 1;
  min-height: 0;
}

.f-row.grow .f-md { display: flex; flex-direction: column; }
.f-row.grow .f-md-view { overflow-y: auto; }

/* Free resizing is what a lone textarea in a stack of fields wants. Here the
   field is already as tall as the pane, and dragging it taller would push the
   footer off the dialog. */
.f-row.grow .f-area { resize: none; }

.sheet-foot {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 20px 14px;
  border-top: 1px solid var(--hair);
}

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

.f-row {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
}

.f-row.wide { grid-column: 1 / -1; }

.f-label {
  display: flex;
  align-items: baseline;
  gap: 7px;
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--faint);
}

/* On the one field that has to be filled in. Everything else in these forms is
   optional, and marking the exception is quieter than marking the rule. */
.f-req {
  font-weight: 500;
  letter-spacing: .06em;
  color: var(--accent-ink);
  opacity: .8;
}

/* Set when a save was stopped by this field being empty. Cleared by typing,
   because the point is to answer "why did nothing happen", not to scold. */
.f-input.is-missing {
  border-color: var(--danger);
  box-shadow: 0 0 0 3px var(--danger-bg);
}

/* An explicit height, because these do not agree on one by themselves: a date
   input carries a picker indicator, a time input carries segment spinners, and
   a select carries its own chrome, so identical padding still gives four boxes
   of three different heights. Fixing the height and centring the text instead
   of padding to it makes every field in the grid the same box. */
.f-input {
  width: 100%;
  height: 34px;
  padding: 0 10px;
  border: 1px solid var(--hair);
  border-radius: var(--r);
  background: var(--fill);
  font-size: 13px;
  font-family: inherit;
  line-height: normal;
  color: var(--ink);
}

/* Safari lays a date or time value out as its own inline box, which ignores the
   centring above and sits the text high in the field. */
.f-input::-webkit-date-and-time-value {
  text-align: left;
  margin: 0;
}

.f-input::-webkit-calendar-picker-indicator { opacity: .45; }
.f-input:hover::-webkit-calendar-picker-indicator { opacity: .8; }

.f-input::placeholder { color: var(--faint); }
.f-input:hover { border-color: var(--line); }

.f-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-bg);
}

/* The one field that is not a single line, so it opts back out of the fixed
   height and pads to its text the normal way. */
.f-area {
  height: auto;
  min-height: 62px;
  padding: 8px 10px;
  resize: vertical;
  line-height: 1.5;
}

.f-select { appearance: none; cursor: pointer; padding-right: 26px; }

.f-hint {
  font-size: 11px;
  color: var(--faint);
  line-height: 1.4;
}

.f-check {
  display: flex;
  align-items: center;
  gap: 8px;
  padding-top: 18px;
  font-size: 13px;
  color: var(--ink-2);
  cursor: pointer;
}

.f-check input[type="checkbox"] {
  appearance: none;
  width: 15px;
  height: 15px;
  flex: none;
  display: grid;
  place-content: center;
  border: 1.5px solid var(--line);
  border-radius: 5px;
  background: var(--fill);
  cursor: pointer;
}

.f-check input[type="checkbox"]::after {
  content: "";
  width: 7px;
  height: 4px;
  border-left: 1.5px solid #fff;
  border-bottom: 1.5px solid #fff;
  transform: rotate(-45deg) translate(.5px, -1px);
  opacity: 0;
}

.f-check input[type="checkbox"]:checked {
  background: var(--ok);
  border-color: var(--ok);
}

.f-check input[type="checkbox"]:checked::after { opacity: 1; }

/* Reuses .cp-dot from the project page so the picker in the editor and the one
   on the page are visibly the same control. */
.f-colors {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 4px 0 2px;
}

/* The swatches. Each carries its own data-color, so none of them names a hex.
   They live here rather than with the project page because the editor is now
   the only place a color is chosen. */
.cp-dot {
  width: 16px;
  height: 16px;
  flex: none;
  border-radius: 50%;
  background: var(--tint);
  cursor: pointer;
  box-shadow: inset 0 0 0 1px var(--glass-edge);
  transition: transform .14s var(--ease);
}

.cp-dot:hover { transform: scale(1.16); }

/* An outline rather than a box-shadow ring: it draws outside the box without
   touching layout, and the offset gives the gap for free. */
.cp-dot.on {
  outline: 1.5px solid var(--tint);
  outline-offset: 2.5px;
}

/* The eighth swatch is a real `<input type="color">`, so the picker, the
   eyedropper and the platform's own recent colors all come for free — which is
   what makes this a color picker rather than seven colors. Set apart by a gap,
   because it is a different kind of choice: the seven already have a dark-mode
   tint worked out and this one is yours to get right.

   A color input paints its value through pseudo-elements rather than through
   `background`, and they have to be flattened or the swatch renders as a small
   bevelled well with a stripe in it. */
/* A note: the textarea and the rendered view, one of which is showing.

   They have to look like the same control, or clicking into it reads as the
   field being replaced rather than opened. Same box, same padding, same
   background — the only difference is that one of them is formatted. */
.f-md { display: block; }

.f-md-view {
  min-height: 58px;
  padding: 9px 11px;
  border-radius: 10px;
  border: 1px solid var(--hair);
  background: var(--fill-2);
  font-size: 12.5px;
  color: var(--ink-2);
  cursor: text;
}

.f-md-view:hover { border-color: var(--line); }

.f-md-view:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-bg);
}

/* An author `display:` beats the UA's `[hidden] { display: none }` at any
   specificity, so without these both halves of the field are on screen at once. */
.f-md-view[hidden],
.f-md .f-area[hidden] { display: none; }

/* A date or time and the button that empties it.
   Beside the field rather than laid over it. Overlaying is tidier and was the
   first attempt, but the native picker glyph sits at the right-hand end of a
   date input and browsers disagree about how wide it is — so an offset that
   clears it in Chrome lands on top of it in Safari. A flex sibling cannot
   collide with anything. */
.f-clearable {
  display: flex;
  align-items: center;
  gap: 6px;
}

.f-clearable .f-input { flex: 1; min-width: 0; }

.f-clear {
  display: grid;
  place-content: center;
  flex: none;
  width: 24px;
  height: 24px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid var(--hair);
  background: var(--fill-2);
  color: var(--muted);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
}

.f-clear:hover { background: var(--hover); color: var(--ink); border-color: var(--line); }

/* An author `display:` beats the UA's `[hidden] { display: none }` whatever the
   specificity, so without this the attribute does nothing and every empty field
   carries a button offering to clear the nothing in it. */
.f-clear[hidden] { display: none; }

.cp-any {
  appearance: none;
  -webkit-appearance: none;
  width: 16px;
  height: 16px;
  padding: 0;
  border: 0;
  margin-left: 8px;
  background: none;
}

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

.cp-any::-webkit-color-swatch,
.cp-any::-moz-color-swatch {
  border: 0;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1px var(--glass-edge);
}

/* Its own ring rather than the inherited one: --tint is what the other dots
   outline themselves in, and behind a hex there is no --tint to read. This rule
   has to sit after .cp-dot.on and match its specificity to win. */
.cp-any.on {
  outline: 1.5px solid var(--ink-2);
  outline-offset: 2.5px;
}

/* --- The boot card -------------------------------------------------------- */

.boot {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: grid;
  place-content: center;
  padding: 24px;
  background: var(--ground);
}

.boot[hidden] { display: none; }

.boot-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: min(380px, 100%);
  padding: 30px 28px 28px;
  text-align: center;
  border-radius: var(--r-lg);
  background: var(--glass);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  box-shadow: var(--glass-shadow), inset 0 1px 0 var(--glass-hi);
}

.boot-mark {
  width: 30px;
  height: 30px;
  margin-bottom: 4px;
  border-radius: 9px;
  background:
    linear-gradient(135deg, var(--c-blue) 0 50%, transparent 50%),
    linear-gradient(225deg, var(--c-violet) 0 50%, transparent 50%),
    linear-gradient(45deg,  var(--c-teal) 0 50%, transparent 50%),
    var(--c-ochre);
  box-shadow: 0 1px 3px rgba(20, 30, 45, .28), inset 0 0 0 1px var(--glass-edge);
  animation: boot-pulse 1.6s var(--ease) infinite;
}

/* Only while waiting: a mark still breathing next to an error message reads as
   "still trying", which would be a lie. */
.boot.is-error .boot-mark { animation: none; opacity: .5; }

@keyframes boot-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: .55; transform: scale(.93); }
}

.boot-msg {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
}

.boot-hint {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.b-why {
  font-family: var(--font-mono);
  font-size: 11.5px;
  color: var(--danger);
}

.b-hint {
  font-size: 12px;
  line-height: 1.55;
  color: var(--muted);
}

.boot .btn { margin-top: 6px; }

/* --- The toast ------------------------------------------------------------ */

/* Wide enough, and rounded rather than pill-shaped, because some failures carry
   the command that fixes them and that does not fit on one line. */
.toast {
  position: fixed;
  left: 50%;
  bottom: 22px;
  z-index: 60;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  max-width: min(560px, calc(100vw - 32px));
  padding: 10px 11px 11px 15px;
  transform: translateX(-50%);
  line-height: 1.45;
  border-radius: var(--r-lg);
  background: var(--glass);
  -webkit-backdrop-filter: var(--blur);
  backdrop-filter: var(--blur);
  box-shadow: var(--glass-shadow), inset 0 0 0 1px var(--danger);
  font-size: 12.5px;
  color: var(--ink);
  animation: toast-in .22s var(--ease);
}

.toast[hidden] { display: none; }

@keyframes toast-in {
  from { opacity: 0; transform: translate(-50%, 10px); }
  to { opacity: 1; transform: translate(-50%, 0); }
}

.toast-x {
  display: grid;
  place-content: center;
  width: 20px;
  height: 20px;
  flex: none;
  border-radius: 50%;
  color: var(--muted);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
}

.toast-x:hover { color: var(--ink); background: var(--hover); }

@media (max-width: 720px) {
  .sheet-body { grid-template-columns: minmax(0, 1fr); }

  /* The panes stack, so the divider between them becomes a rule across the
     form and the note stops claiming the height — on a phone the six controls
     underneath are what you scrolled down to reach. */
  .sheet-body.is-split { min-height: 0; }
  .f-aside { border-left: 0; border-top: 1px solid var(--hair); }
  .f-row.grow { flex: none; }
  .f-row.grow .f-area { min-height: 120px; resize: vertical; }
}
