/* ============================================================
   ENGENE Voting Calendar — submission form
   Extends style.css. Same tokens, same slab language as the cards:
   numbered blocks, member-hue focus choices, KST inputs that echo
   the reader's local time.
   ============================================================ */

.masthead-link {
  font-family: var(--font-display);
  font-variation-settings: 'wdth' 92, 'wght' 700;
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-text-muted);
  text-decoration: none;
  padding: 0.4375rem var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  transition: color var(--transition-interactive), border-color var(--transition-interactive);
}
.masthead-link:hover {
  color: var(--color-text);
  border-color: var(--color-text-faint);
}

/* ---------- Intro CTA (calendar page) ---------- */

.intro-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: var(--space-6) var(--space-12);
  margin-top: var(--space-4);
}
.intro-split .intro-lede {
  margin-top: 0;
}
.intro-cta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-3);
  max-width: 30ch;
}
.cta-submit {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-6);
  font-family: var(--font-display);
  font-variation-settings: 'wdth' 95, 'wght' 800;
  font-size: var(--text-base);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  text-decoration: none;
  border-radius: var(--radius-md);
}
.cta-submit svg {
  transition: transform var(--transition-interactive);
}
.cta-submit:hover svg {
  transform: translateX(3px);
}
.cta-note {
  font-size: var(--text-xs);
  color: var(--color-text-faint);
  margin: 0;
  line-height: 1.5;
}

@media (max-width: 860px) {
  .intro-split {
    grid-template-columns: 1fr;
    align-items: start;
  }
  .intro-cta {
    max-width: none;
  }
  .cta-submit {
    width: 100%;
    justify-content: center;
  }
}

.intro--form {
  padding-bottom: var(--space-8);
}

.form-wrap {
  padding-bottom: var(--space-24);
}

.submit-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

/* ---------- Paste a link ---------- */

.paster {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  padding: var(--space-5);
  position: relative;
  overflow: hidden;
}
.paster::before {
  content: '';
  position: absolute;
  inset: 0 auto 0 0;
  width: 4px;
  background: var(--color-primary);
}
.paster-head {
  margin-bottom: var(--space-4);
}
.paster-title {
  font-family: var(--font-display);
  font-variation-settings: 'wdth' 95, 'wght' 800;
  font-size: var(--text-base);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin: 0 0 var(--space-1);
}
.paster-note {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  margin: 0;
  max-width: 56ch;
}
.paster-row {
  display: flex;
  gap: var(--space-3);
  align-items: stretch;
}
.paster-row .field-input {
  flex: 1 1 auto;
}
.paster-row .btn {
  flex: 0 0 auto;
}
.paster-status {
  font-size: var(--text-xs);
  margin: var(--space-3) 0 0;
  min-height: 1em;
  color: var(--color-text-muted);
}
.paster-status[data-tone='ok'] {
  color: var(--color-live);
}
.paster-status[data-tone='warn'] {
  color: var(--color-prep);
}
.paster-status[data-tone='bad'] {
  color: var(--color-primary);
}

/* ---------- Blocks ---------- */

.form-block {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  padding: var(--space-6) var(--space-5) var(--space-5);
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}
.form-legend {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-family: var(--font-display);
  font-variation-settings: 'wdth' 95, 'wght' 800;
  font-size: var(--text-base);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 0 var(--space-2);
  margin-left: calc(var(--space-2) * -1);
}
.legend-num {
  font-variation-settings: 'wdth' 80, 'wght' 900;
  font-size: 0.75rem;
  color: var(--color-primary);
  font-variant-numeric: tabular-nums;
}
.block-note {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  margin: calc(var(--space-3) * -1) 0 0;
  max-width: 72ch;
}
.block-note b {
  color: var(--color-text);
  font-weight: 700;
}
/* KST is the one thing a submitter gets wrong, and getting it wrong shifts every
   deadline on the calendar by hours, so it is flagged in the brand red rather
   than merely bolded. */
.block-note b.kst-flag {
  color: var(--color-primary);
}

/* The one thing on this page a submitter must read before typing. As a grey box
   with a thin red edge it lost every staring contest with the form below it, so
   it is now tinted, labelled and outlined in the brand red — loud enough to stop
   a scroll, still calm enough not to read as an error. */
/* Mixed against the page surface rather than using `--color-primary-highlight`:
   that token is a solid pink built for small chips, and across a full-width box
   it turned the top of the form pink. 5% of the brand red over the surface keeps
   a warm tint that says "notice" and nothing more, and because the mix is
   against a variable it follows dark mode on its own. A left rule carries the
   accent instead, matching the footer quote on the calendar. */
.form-notice {
  position: relative;
  margin-top: var(--space-6);
  padding: var(--space-4) var(--space-5);
  border: 1px solid color-mix(in srgb, var(--color-primary) 16%, transparent);
  border-left: 3px solid color-mix(in srgb, var(--color-primary) 55%, transparent);
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--color-primary) 5%, var(--color-surface));
}
/* No max-width: the paragraph runs the full width of the box, the same way the
   footer statement runs the full width of the shell. */
.form-notice p {
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--color-text);
}

/* ---------- Fields ---------- */

/* Two fixed columns rather than auto-fit: auto-fit packed as many 240px tracks
   as the card could hold, so on a wide desktop a two-field row (app + contact)
   sat in the first two of four narrow tracks, leaving half the card empty and
   squeezing every hint under those fields into three ragged lines that
   re-wrapped at each breakpoint. Fixed tracks make the fields half the card at
   every desktop width, so the hints wrap the same way at all of them. */
.field-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-5);
}
@media (max-width: 700px) {
  .field-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}
.field--wide {
  grid-column: 1 / -1;
}
.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
}
.field-label {
  font-family: var(--font-display);
  font-variation-settings: 'wdth' 92, 'wght' 700;
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--color-text-muted);
}
.field-label b {
  color: var(--color-primary);
  font-weight: inherit;
}
.field-label .opt {
  font-variation-settings: 'wdth' 92, 'wght' 400;
  letter-spacing: 0.06em;
  text-transform: lowercase;
  color: var(--color-text-faint);
}

.field-input {
  width: 100%;
  padding: var(--space-3);
  font-family: var(--font-body);
  font-size: var(--text-sm);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-2);
  transition: border-color var(--transition-interactive), background var(--transition-interactive);
}
.field-input::placeholder {
  color: var(--color-text-faint);
}
.field-input:hover {
  border-color: var(--color-text-faint);
}
.field-input:focus-visible {
  outline: none;
  border-color: var(--color-primary);
  background: var(--color-surface);
}
.field-input[aria-invalid='true'] {
  border-color: var(--color-primary);
  background: color-mix(in srgb, var(--color-primary) 6%, var(--color-surface-2));
}
.field-textarea {
  resize: vertical;
  min-height: 5.5rem;
  line-height: 1.55;
}
input[type='datetime-local'].field-input {
  font-variant-numeric: tabular-nums;
  /* iOS derives an intrinsic width for this control from its locale date
     format and will not shrink below it inside a grid track, so it can reach
     past the edge of the form. These let the track win. */
  min-width: 0;
  max-width: 100%;
}

.field-hint {
  font-size: var(--text-xs);
  color: var(--color-text-faint);
  margin: 0;
  max-width: 70ch;
  line-height: 1.5;
}
.field-hint--above {
  margin-top: calc(var(--space-1) * -1);
}
.field-count {
  font-size: 0.6875rem;
  color: var(--color-text-faint);
  margin: 0;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.field-count[data-tone='near'] {
  color: var(--color-prep);
}
.field-echo {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin: 0;
  min-height: 1.2em;
  font-variant-numeric: tabular-nums;
}
.field-echo b {
  color: var(--color-text);
  font-weight: 500;
}
.field-error,
.form-error {
  font-size: var(--text-sm);
  color: var(--color-primary);
  margin: 0;
}
.form-error {
  border: 1px solid color-mix(in srgb, var(--color-primary) 45%, transparent);
  background: var(--color-primary-highlight);
  border-radius: var(--radius-md);
  padding: var(--space-4);
}

/* ---------- Focus choices ---------- */

.choice-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.choice {
  position: relative;
}
.choice input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}
.choice span {
  display: inline-flex;
  align-items: center;
  padding: 0.5rem 0.625rem 0.4375rem;
  font-family: var(--font-display);
  font-variation-settings: 'wdth' 92, 'wght' 800;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  line-height: 1;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--focus-hue) 16%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--focus-hue) 42%, transparent);
  color: color-mix(in srgb, var(--focus-hue) var(--focus-ink-weight), var(--focus-ink-base));
  transition: background var(--transition-interactive), box-shadow var(--transition-interactive);
}
.choice:hover span {
  background: color-mix(in srgb, var(--focus-hue) 26%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--focus-hue) 62%, transparent);
}
.choice input:checked + span {
  background: var(--focus-hue);
  box-shadow: inset 0 0 0 1px var(--focus-hue);
  color: #0a0a0c;
}
.choice input:focus-visible + span {
  outline: 2px solid var(--color-text);
  outline-offset: 2px;
}

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

.preview {
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-5) var(--space-5);
  background: var(--color-surface-2);
}
.preview-label {
  font-family: var(--font-display);
  font-variation-settings: 'wdth' 92, 'wght' 700;
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--color-text-faint);
  margin: 0 0 var(--space-3);
}
.preview-card {
  border: 1px solid var(--color-border);
  border-left: 4px solid var(--focus-hue, var(--color-primary));
  border-radius: var(--radius-md);
  background: var(--color-surface);
  padding: var(--space-4);
}
.preview-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}
.preview-name {
  font-family: var(--font-display);
  font-variation-settings: 'wdth' 100, 'wght' 700;
  font-size: var(--text-lg);
  margin: 0 0 var(--space-2);
  line-height: 1.15;
}
.preview-times {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}
.preview-times b {
  display: block;
  font-size: var(--text-sm);
  color: var(--color-text);
  font-weight: 500;
}
.preview-app {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

/* Under the app field: the icon we matched, plus whether we matched at all.
   Uses the same .app-icon as the calendar, so what you see here is literally
   what the card will show. */
#app-echo {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
}
#app-echo[data-known='true'] {
  color: var(--color-text-muted);
}
#app-echo[data-known='false'] {
  color: var(--color-text-faint);
}

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

.form-actions {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
}
.btn-submit {
  padding: var(--space-3) var(--space-6);
  font-family: var(--font-display);
  font-variation-settings: 'wdth' 95, 'wght' 800;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  cursor: pointer;
  transition: background var(--transition-interactive);
}
.btn-submit[disabled] {
  opacity: 0.6;
  cursor: progress;
}
.form-actions-note {
  font-size: var(--text-xs);
  color: var(--color-text-faint);
  margin: 0;
  max-width: 44ch;
}

/* ---------- Done ---------- */

.done {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  padding: var(--space-12) var(--space-6);
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
}
.done-mark {
  width: 56px;
  height: 56px;
  border-radius: var(--radius-full);
  display: grid;
  place-items: center;
  background: var(--color-live-bg);
  color: var(--color-live);
}
.done-title {
  font-family: var(--font-display);
  font-variation-settings: 'wdth' 100, 'wght' 800;
  font-size: var(--text-xl);
  text-transform: uppercase;
  letter-spacing: 0.02em;
  margin: 0;
}
.done-copy {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  margin: 0;
  max-width: 58ch;
}
.done-actions {
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
  justify-content: center;
  margin-top: var(--space-2);
}
/* The submitter is told the entry is in as soon as the request leaves the
   browser; this line carries the slower truth from the sheet. */
.done-status {
  display: inline-flex;
  /* The warning wraps to three lines, so keep the dot on the first one. */
  align-items: flex-start;
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  /* Not a full pill: the longer warning wraps, and a three-line capsule looks
     like a mistake. */
  border-radius: var(--radius-lg);
  font-size: var(--text-xs);
  line-height: 1.45;
  color: var(--color-text-muted);
  background: var(--color-surface-2);
  text-align: left;
  max-width: 46ch;
}
.done-status::before {
  content: '';
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
  margin-top: 0.42em;
  border-radius: 50%;
  background: currentColor;
}
/* Work is still happening behind this screen, so this state is the one the
   submitter has to actually read: tinted, left-aligned, a size up from the quiet
   confirmation that replaces it, and a red pulsing dot — the brand red, the same
   signal a recording light gives. */
.done-status[data-state='sending'] {
  color: var(--color-text);
  border-color: color-mix(in srgb, var(--color-primary) 32%, transparent);
  background: var(--color-primary-highlight);
  font-size: var(--text-sm);
  text-align: left;
  max-width: 44ch;
}
.done-status[data-state='sending'] strong {
  font-weight: 700;
}
.done-status[data-state='sending']::before {
  background: var(--color-primary);
  animation: done-status-pulse 1.1s ease-in-out infinite;
}
.done-status[data-state='ok'] {
  color: var(--color-text);
}
.done-status[data-state='ok']::before {
  background: var(--color-live);
}
.done-status[data-state='warn'] {
  color: var(--color-text);
  border-color: var(--color-primary);
  background: color-mix(in srgb, var(--color-primary) 8%, var(--color-surface-2));
}
.done-status[data-state='warn']::before {
  background: var(--color-primary);
}
/* A dot that only fades reads as decoration; scaling it as well reads as a
   heartbeat, which is the point — something is running. */
@keyframes done-status-pulse {
  0%,
  100% {
    opacity: 0.3;
    transform: scale(0.82);
  }
  50% {
    opacity: 1;
    transform: scale(1.15);
  }
}
@media (prefers-reduced-motion: reduce) {
  .done-status[data-state='sending']::before {
    animation: none;
  }
}

/* The focus chips are a group, not an input, so the invalid state has to be
   drawn on the row itself. */
#focus-row[aria-invalid='true'] {
  border-radius: var(--radius-md);
  box-shadow: 0 0 0 2px var(--color-primary);
}

.done-actions .btn {
  cursor: pointer;
}

@media (max-width: 640px) {
  .paster-row {
    flex-direction: column;
  }
  .form-block {
    padding: var(--space-5) var(--space-4) var(--space-4);
  }
  .form-actions {
    flex-direction: column;
    align-items: stretch;
  }
  .btn-submit {
    width: 100%;
  }
}

/* ---------- App-or-platform picker (app-picker.js) ---------- */

/* Replaces the native <datalist>, whose popup rendered in the top-left corner
   of an Android screen with the keyboard over it. The panel is a child of
   <body> so neither the admin drawer's overflow nor its transform can clip it,
   which is also why it is styled from here rather than inside .field. */

.app-pick {
  position: relative;
  display: block;
}
.app-pick .field-input {
  padding-right: 2.25rem; /* room for the chevron */
}
.app-pick-toggle {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: 2.25rem;
  display: grid;
  place-items: center;
  border: 0;
  background: none;
  color: var(--color-text-faint);
  cursor: pointer;
  border-radius: var(--radius-md);
  -webkit-tap-highlight-color: transparent;
  transition: color var(--transition-interactive), transform var(--transition-interactive);
}
.app-pick-toggle:hover {
  color: var(--color-text);
}
.app-pick.is-open .app-pick-toggle {
  color: var(--color-text);
  transform: rotate(180deg);
}

.app-pick-panel {
  position: fixed;
  z-index: 90; /* above the admin drawer (50) and its veil (40) */
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  padding: 0.25rem;
}
.app-pick-veil {
  position: fixed;
  inset: 0;
  z-index: 89;
  background: rgba(8, 8, 11, 0.28);
}

.app-pick-opt {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  /* 44px minimum: these rows are tapped with a thumb, and the old native list
     offered ~28px rows that were easy to miss. */
  min-height: 2.75rem;
  padding: 0 var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  color: var(--color-text);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}
.app-pick-opt .app-icon {
  flex: 0 0 auto;
}
.app-pick-opt.is-active {
  background: var(--color-surface-2);
}
.app-pick-opt--free {
  color: var(--color-text-muted);
}
.app-pick-opt--free em {
  display: block;
  font-style: normal;
  font-size: var(--text-xs);
  color: var(--color-text-faint);
}
.app-pick-empty {
  padding: var(--space-3);
  font-size: var(--text-xs);
  color: var(--color-text-faint);
}

/* Group heading inside the picker. Deliberately NOT sticky: with only two groups
   a sticky heading meant both labels piled up at the top of the sheet while the
   rows they belonged to were scrolled away, which read worse than no heading at
   all. Not clickable and not an option — pointer-events off so a thumb dragging
   the sheet never lands on it. */
.app-pick-group {
  padding: var(--space-2) var(--space-3) 0.3rem;
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text-faint);
  pointer-events: none;
}
/* Every heading after the first gets air above it and a hairline, so the two
   groups read as two lists rather than one list with labels in it. */
.app-pick-group ~ .app-pick-group {
  margin-top: var(--space-2);
  border-top: 1px solid var(--color-border);
  padding-top: var(--space-3);
}

/* Bottom sheet on touch layouts: always within thumb reach, and pinned to the
   top of the keyboard by app-picker.js rather than to the field. */
.app-pick-panel.is-sheet {
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  border-left: 0;
  border-right: 0;
  border-bottom: 0;
  padding: var(--space-2) var(--space-2) calc(var(--space-3) + env(safe-area-inset-bottom));
  box-shadow: 0 -14px 34px rgb(0 0 0 / 0.22);
}
.app-pick-panel.is-sheet .app-pick-opt {
  min-height: 3rem;
  font-size: var(--text-base);
}

/* Under the app field in the admin drawer — same treatment as #app-echo on the
   public form, which is scoped to its own id. */
#d-app-echo {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-2);
}
#d-app-echo[data-known='false'] {
  color: var(--color-text-faint);
}

/* ---------- iOS zoom guard ----------
   Mobile Safari zooms the entire page whenever a focused form control renders
   its text below 16px, and it does not zoom back out afterwards. That is what
   made this form look like it no longer fitted the screen and drifted
   sideways once a field was tapped: --text-sm resolves to about 14.2px at
   phone widths. 16px is the exact threshold, so pinning to it on small screens
   stops the zoom without touching the desktop scale. */
@media (max-width: 640px) {
  .field-input,
  input[type='text'],
  input[type='url'],
  input[type='email'],
  input[type='search'],
  input[type='number'],
  input[type='datetime-local'],
  input[type='date'],
  input[type='time'],
  select,
  textarea {
    font-size: 1rem;
  }
}

/* ---------- Honeypot ----------
   The correction form's bot trap (correct.html). Positioned off-screen rather
   than display:none or hidden, because the crude form-filling scripts this is
   meant to catch skip fields the browser reports as hidden. Nothing a person
   using a keyboard or a screen reader can reach: the wrapper is aria-hidden and
   the input is out of the tab order. */
.hp-field {
  position: absolute;
  left: -9999px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}
