/* ABOUTME: Component layer for the Divine Connect partner app.
   ABOUTME: Every color here is a brand.css token; this file holds no literals. */

/* ---------------------------------------------------------------- base --- */

p {
  margin: 0 0 var(--space-4);
  color: var(--ink-muted);
  line-height: 1.5;
}

a {
  color: var(--ink);
  text-decoration: underline;
  text-underline-offset: 2px;
}

code {
  padding: 0 var(--space-1);
  border-radius: var(--space-1);
  background: var(--surface-inset);
  font-size: var(--text-xs);
}

/* --------------------------------------------------------------- icons --- */

/* Sized in `em` so an icon always matches the text it sits beside. */
.icon {
  width: 1.25em;
  height: 1.25em;
  flex: 0 0 auto;
  fill: currentColor;
}

/* 48px is the brand's hard ceiling for icon artwork. Nothing renders larger. */
.icon--lg {
  width: 48px;
  height: 48px;
}

/* -------------------------------------------------------------- header --- */

/* `#chrome` is the placeholder the page ships with; chrome.js replaces its
   contents. Reserving the header height keeps the first paint from shifting. */
#chrome {
  display: block;
  min-height: var(--header-height);
}

.app-header {
  position: sticky;
  top: 0;
  z-index: 10;
  display: grid;
  min-height: var(--header-height);
  background: var(--surface-chrome);
  color: var(--ink-on-chrome);
}

.app-header__row {
  display: flex;
  min-height: var(--header-height);
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-2) clamp(var(--space-4), 4vw, var(--space-7));
}

.brand-mark {
  display: inline-flex;
  align-items: flex-end;
  gap: var(--space-2);
  flex: 0 0 auto;
  color: var(--ink-on-chrome);
  font-family: var(--divine-font-display);
  font-optical-sizing: auto;
  font-size: var(--text-lg);
  font-weight: 800;
  letter-spacing: -0.01em;
  text-decoration: none;
}

/* Width comes from the logotype's own aspect ratio. Pinning both axes
   letterboxed the artwork inside its frame and shrank it by two thirds. */
.brand-mark__logo {
  width: auto;
  /* Sized in em, which resolves against `.brand-mark`'s own
     `font-size: var(--text-lg)` — the same token the "Connect" wordmark
     uses — so logo and wordmark scale together. That keeps the baseline
     when a reader raises their default font size, and when `--text-lg` is
     retuned for one of its other consumers. Measured in Chromium: these
     compute to 21.98px / 5.999px at a 16px root, reproducing the 22px / 6px
     they replace, and the logo-to-wordmark size ratio then holds at
     1.221–1.222 across 16/20/24/32px roots, where fixed px fell from 1.22 to
     0.61. Not verified in Safari or Firefox. */
  height: 1.2222em;
  /* `align-items: flex-end` lines up the two boxes' bottom edges, not their
     visual baselines, so the artwork lands too low and needs nudging up.
     Most of that gap belongs to the text, not the SVG: the logotype is
     tight-cropped (its ink reaches within ~0.4 of 114 viewBox units of the
     bottom edge, well under a rendered pixel), so only the tail flourish —
     roughly a quarter of the offset — comes from the artwork. The rest is
     the "Connect" wordmark's descent plus half-leading. The dependency is
     therefore `--text-lg` and `line-height`, not the SVG. */
  margin-bottom: 0.3333em;
  color: currentColor;
  fill: currentColor;
}

.brand-mark__word {
  font-size: var(--text-lg);
  font-weight: 700;
}

.nav {
  display: flex;
  flex: 1 1 auto;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

.nav a {
  text-decoration: none;
}

.nav__link {
  display: inline-flex;
  min-height: var(--control-height);
  align-items: center;
  gap: var(--space-2);
  padding: 0 var(--space-4);
  border: var(--border-chunky) solid transparent;
  border-radius: var(--radius-pill);
  background: none;
  color: var(--ink-on-chrome);
  cursor: pointer;
  font-family: var(--divine-font-sans);
  font-size: var(--text-sm);
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
}

.nav__link:hover {
  background: var(--chrome-hover);
}

/* The current page is outlined, not filled. The solid Green fill belongs to
   the one primary call to action, and two solid Green pills side by side
   would leave neither of them saying anything. */
.nav__link.is-active {
  border-color: var(--action);
  color: var(--action);
  font-weight: 700;
}

.nav__link--primary {
  background: var(--action);
  color: var(--action-ink);
  font-weight: 700;
}

/* "New video" is also the current page on the composer. It keeps the primary
   fill and takes the marker as a Dark Green ring inside it. */
.nav__link--primary.is-active {
  border-color: var(--action-ink);
  color: var(--action-ink);
}

.nav__link--primary:hover {
  background: var(--action-hover);
}

/* The sign-out control is the only item pushed away from the nav group. */
.nav__link--signout {
  margin-left: auto;
}

.policy-banner {
  margin: 0;
  padding: var(--space-2) clamp(var(--space-4), 4vw, var(--space-7));
  border-top: var(--border-chunky) solid var(--danger-accent);
  background: var(--danger-fill);
  color: var(--danger-ink);
  font-size: var(--text-xs);
  line-height: 1.4;
  text-align: center;
}

.policy-banner .icon {
  vertical-align: -0.25em;
  margin-right: var(--space-1);
}

.policy-banner strong {
  font-weight: 800;
}

/* -------------------------------------------------------------- layout --- */

.app-main {
  width: min(1180px, 100%);
  margin: 0 auto;
  padding: var(--space-6) clamp(var(--space-4), 4vw, var(--space-7)) var(--space-7);
}

/* The signed-out pages centre one panel and close with the footer logotype.
   Making the body the column keeps the footer at the bottom of the viewport
   rather than a screen-height below it. */
.page-centered {
  display: flex;
  min-height: 100vh;
  flex-direction: column;
}

.page-centered > .centered {
  flex: 1 1 auto;
  min-height: 0;
}

.centered {
  display: grid;
  min-height: 100vh;
  place-content: center;
  justify-items: center;
  gap: var(--space-4);
  padding: var(--space-5);
  text-align: center;
}

.landing {
  width: min(920px, 100%);
  margin: 0 auto;
}

.landing-image {
  display: block;
  width: min(720px, 100%);
  aspect-ratio: 16 / 9;
  justify-self: center;
  border: var(--border-chunky) solid var(--divine-border);
  border-radius: var(--radius-card);
  object-fit: cover;
}

.landing-copy {
  display: grid;
  justify-items: center;
  gap: var(--space-2);
}

.app-footer {
  display: grid;
  justify-items: center;
  gap: var(--space-3);
  padding: var(--space-7) var(--space-4);
  color: var(--ink-muted);
  font-size: var(--text-sm);
  text-align: center;
}

.app-footer__logo {
  width: 96px;
  height: auto;
  color: var(--ink);
  fill: currentColor;
}

/* ------------------------------------------------------------- buttons --- */

/* Depth is a solid offset, never a blur: the button lifts off the page by a
   hard 2px edge and presses back down on :active. */
.btn-primary,
.btn-secondary,
.btn-danger {
  display: inline-flex;
  min-height: var(--control-height);
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: 0 var(--space-4);
  border: var(--border-chunky) solid transparent;
  border-radius: var(--radius-control);
  background: none;
  box-shadow: var(--shadow-solid);
  cursor: pointer;
  font-family: var(--divine-font-sans);
  font-size: var(--text-sm);
  font-weight: 700;
  line-height: 1;
  text-decoration: none;
}

.btn-primary {
  background: var(--action);
  color: var(--action-ink);
}

.btn-primary:hover {
  background: var(--action-hover);
}

.btn-secondary {
  border-color: var(--divine-border-strong);
  background: var(--surface-raised);
  color: var(--ink);
}

.btn-secondary:hover {
  background: var(--surface-sunken);
}

.btn-danger {
  border-color: var(--danger-accent);
  background: var(--danger-fill);
  color: var(--danger-ink);
}

.btn-danger:hover {
  background: var(--danger-accent);
  color: var(--divine-green-dark);
}

.btn-primary:active,
.btn-secondary:active,
.btn-danger:active {
  transform: translateY(2px);
  box-shadow: none;
}

/* The focus halo is carried on box-shadow, which would otherwise replace the
   solid drop edge and make a focused button read as permanently pressed.
   Declared after :active so the ring survives the press; the press still
   registers through the transform. */
.btn-primary:focus-visible,
.btn-secondary:focus-visible,
.btn-danger:focus-visible {
  box-shadow: var(--focus-halo), var(--shadow-solid);
}

/* Disabled controls stay fully opaque. Fading them made Publish read as a
   rendering bug rather than a state, so the state is a flat neutral fill. */
.btn-primary:disabled,
.btn-secondary:disabled,
.btn-danger:disabled {
  border-color: transparent;
  background: var(--surface-inset);
  box-shadow: none;
  color: var(--ink-muted);
  cursor: not-allowed;
  transform: none;
}

/* ---------------------------------------------------------------- form --- */

.composer-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(300px, 0.75fr);
  gap: var(--space-5);
  align-items: start;
}

.form-stack {
  display: grid;
  gap: var(--space-4);
}

/* `--header-height` covers the nav row only; the policy banner always adds to
   it. chrome.js measures the real header and publishes `--chrome-height`, so
   the panel clears the whole thing instead of sliding under the banner. */
.preview-panel {
  position: sticky;
  top: calc(var(--chrome-height, var(--header-height)) + var(--space-4));
  display: grid;
  align-content: start;
  gap: var(--space-4);
}

.field {
  display: grid;
  gap: var(--space-2);
}

.field span,
.field label {
  font-size: var(--text-sm);
  font-weight: 700;
}

input,
textarea,
select {
  width: 100%;
  min-height: var(--control-height);
  padding: var(--space-2) var(--space-3);
  border: var(--border-chunky) solid var(--divine-border);
  border-radius: var(--radius-control);
  background: var(--surface-raised);
  color: var(--ink);
}

input:focus,
textarea:focus,
select:focus {
  border-color: var(--action);
}

input::placeholder,
textarea::placeholder {
  color: var(--ink-muted);
}

textarea {
  min-height: 112px;
  padding: var(--space-3);
  line-height: 1.5;
  resize: vertical;
}

input[type="checkbox"],
input[type="radio"] {
  width: auto;
  min-height: 0;
  accent-color: var(--action);
}

/* A section of the composer, grouped so a long form reads as a few blocks. */
.form-section {
  display: grid;
  gap: var(--space-4);
  padding: var(--space-5);
  border: var(--border-hairline) solid var(--divine-border);
  border-radius: var(--radius-card);
  background: var(--surface-raised);
}

/* Sentence case, always. The brand forbids all-caps headlines and labels. */
.form-section__title {
  margin: 0;
  font-family: var(--divine-font-display);
  font-optical-sizing: auto;
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: 0;
}

.form-section--collapsible > summary {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
  list-style: none;
}

.form-section--collapsible > summary::-webkit-details-marker {
  display: none;
}

/* A CSS triangle rather than sprite artwork, so the marker can rotate
   without a second DOM node. */
.form-section--collapsible > summary::after {
  content: "";
  width: 0;
  height: 0;
  margin-left: auto;
  border-right: 6px solid transparent;
  border-left: 6px solid transparent;
  border-top: 7px solid currentColor;
  transition: transform 120ms ease;
}

.form-section--collapsible[open] > summary::after {
  transform: rotate(180deg);
}

.drop-zone {
  display: grid;
  min-height: 200px;
  place-content: center;
  justify-items: center;
  gap: var(--space-2);
  padding: var(--space-5);
  border: var(--border-chunky) dashed var(--action);
  border-radius: var(--radius-card);
  background: var(--surface-raised);
  cursor: pointer;
  text-align: center;
}

.drop-zone .icon--lg {
  color: var(--action);
}

.drop-zone strong {
  font-family: var(--divine-font-display);
  font-optical-sizing: auto;
  font-size: var(--text-lg);
  font-weight: 800;
}

.drop-zone.dragging {
  border-style: solid;
  background: var(--divine-green-light);
}

.progress {
  width: 100%;
  height: 10px;
  overflow: hidden;
  border-radius: var(--radius-pill);
  background: var(--surface-inset);
}

/* composer.js drives this element's inline width; never constrain it here. */
.progress > span {
  display: block;
  width: 0%;
  height: 100%;
  background: var(--action);
}

/* -------------------------------------------------------------- status --- */

.status,
.muted {
  color: var(--ink-muted);
}

.error {
  color: var(--danger-ink);
}

.success {
  color: var(--success-ink);
}

/* composer.js replaces this element's class list wholesale, so the element
   must read correctly with only `.status` or only `.error` applied. */
#status {
  margin: 0;
  min-height: 1.4em;
  font-size: var(--text-sm);
  font-weight: 600;
}

.form-status {
  margin: 0;
  min-height: 1.4em;
  font-size: var(--text-sm);
  color: var(--ink-muted);
}

.form-status.success,
.form-status.error {
  display: inline-block;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-control);
  font-weight: 700;
}

.form-status.success {
  background: var(--success-fill);
  color: var(--success-ink);
}

.form-status.error {
  background: var(--danger-fill);
  color: var(--danger-ink);
}

/* --------------------------------------------------- cards and records --- */

.card,
.preview-card,
.video-row,
.invite-row {
  display: grid;
  gap: var(--space-3);
  padding: var(--space-4);
  border: var(--border-hairline) solid var(--divine-border);
  border-radius: var(--radius-card);
  background: var(--surface-raised);
  box-shadow: var(--shadow-solid-sm);
}

.card {
  margin-bottom: var(--space-5);
  padding: var(--space-5);
}

.preview-video {
  width: 100%;
  max-height: 62vh;
  aspect-ratio: 9 / 16;
  border-radius: var(--radius-control);
  background: var(--divine-green-dark);
  object-fit: contain;
}

.video-list,
.invite-list {
  display: grid;
  gap: var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.video-list > li.muted,
.video-list > li.error,
.invite-list > li.muted,
.invite-list > li.error {
  padding: var(--space-4) 0;
}

.video-row,
.invite-row,
.partner-row {
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
}

.video-main {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr);
  gap: var(--space-4);
  align-items: center;
  min-width: 0;
}

.video-thumb {
  width: 96px;
  height: 96px;
  border-radius: var(--radius-control);
  background: var(--surface-inset);
  object-fit: cover;
}

.video-thumb--empty {
  display: block;
}

.video-title,
.invite-title {
  font-weight: 800;
}

.video-meta,
.invite-from {
  color: var(--ink-muted);
  font-size: var(--text-sm);
}

.video-actions,
.invite-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* An invitation nobody has answered yet — the one place yellow appears. */
.collab-status:not(:empty) {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--pending-fill);
  color: var(--pending-ink);
  font-size: var(--text-xs);
  font-weight: 700;
}

.video-sentinel {
  min-height: 1px;
  padding: var(--space-2) 0;
  list-style: none;
  text-align: center;
}

.invite-main,
.partner-main {
  display: grid;
  grid-template-columns: 48px minmax(0, 1fr);
  gap: var(--space-3);
  align-items: center;
  min-width: 0;
}

.invite-avatar,
.partner-avatar {
  display: flex;
  width: 48px;
  height: 48px;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border-radius: 50%;
  background: var(--surface-inset);
}

.invite-avatar img,
.partner-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.edit-notice {
  display: grid;
  gap: var(--space-1);
  padding: var(--space-4);
  border: var(--border-hairline) solid var(--divine-border);
  border-left: var(--border-chunky) solid var(--action);
  border-radius: var(--radius-card);
  background: var(--surface-sunken);
}

.edit-notice strong {
  display: block;
  font-family: var(--divine-font-display);
  font-optical-sizing: auto;
  font-weight: 800;
}

.edit-notice p {
  margin: 0;
  font-size: var(--text-sm);
  line-height: 1.4;
}

/* Nothing here yet, said with the same confidence as a full page. */
.empty-state {
  display: grid;
  justify-items: center;
  gap: var(--space-3);
  padding: var(--space-7) var(--space-4);
  text-align: center;
}

.empty-state .icon--lg {
  color: var(--ink-muted);
}

.empty-state h2 {
  margin: 0;
}

.empty-state p {
  max-width: 46ch;
  margin: 0;
}

/* ---------------------------------------------------- contact picker --- */

.picker {
  position: relative;
  display: grid;
  gap: var(--space-2);
}

.picker-results {
  position: absolute;
  top: calc(var(--control-height) + var(--space-1));
  right: 0;
  left: 0;
  z-index: 20;
  max-height: 260px;
  overflow: auto;
  margin: 0;
  padding: var(--space-1);
  border: var(--border-chunky) solid var(--divine-border);
  border-radius: var(--radius-control);
  background: var(--surface-raised);
  box-shadow: var(--shadow-solid);
  list-style: none;
}

.picker-results li {
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-control);
  cursor: pointer;
}

.picker-results li:hover {
  background: var(--divine-green-light);
}

.picker-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.picker-help {
  color: var(--ink-muted);
  font-size: var(--text-xs);
}

.chip {
  display: inline-flex;
  min-height: 32px;
  align-items: center;
  gap: var(--space-1);
  padding: 0 var(--space-1) 0 var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--divine-green-light);
  color: var(--ink);
  font-size: var(--text-sm);
  font-weight: 700;
}

.chip-remove {
  display: inline-flex;
  width: 24px;
  height: 24px;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--surface-raised);
  color: var(--ink);
  cursor: pointer;
  font-size: var(--text-xs);
  line-height: 1;
}

/* ------------------------------------------------------------- policy --- */

.policy-agreement {
  display: grid;
  max-width: 56ch;
  gap: var(--space-3);
  padding: var(--space-4);
  border: var(--border-chunky) solid var(--danger-accent);
  border-radius: var(--radius-card);
  background: var(--danger-fill);
  color: var(--danger-ink);
  text-align: left;
}

.policy-agreement p {
  margin: 0;
  color: var(--danger-ink);
  font-size: var(--text-sm);
}

.policy-agreement label {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  font-weight: 700;
}

.policy-agreement input[type="checkbox"] {
  flex: 0 0 auto;
  margin: var(--space-1) 0 0;
}

/* -------------------------------------------------------------- small --- */

@media (max-width: 760px) {
  /* Stacked, the nav wraps onto three rows and the policy banner onto four
     lines — around 300px of a 812px phone screen. Pinning that permanently
     would leave the page a letterbox, so the header scrolls away here. It
     still publishes its measured height as `--chrome-height`; nothing sticky
     consumes that below this breakpoint, and desktop is untouched. */
  .app-header {
    position: static;
  }

  .app-header__row {
    align-items: stretch;
    flex-direction: column;
    padding-top: var(--space-3);
    padding-bottom: var(--space-3);
  }

  .nav__link--signout {
    margin-left: 0;
  }

  .composer-grid {
    grid-template-columns: 1fr;
  }

  /* Sticky is a two-column affordance; stacked, it would trap the panel. */
  .preview-panel {
    position: static;
  }

  .video-row,
  .invite-row,
  .partner-row {
    align-items: stretch;
    grid-template-columns: 1fr;
  }

  .video-main {
    grid-template-columns: 72px minmax(0, 1fr);
  }
}

/* Stack the publish/schedule controls as full-width grid items so the buttons
   fill the panel column exactly as the standalone Publish button did before it
   was grouped (.preview-panel stretches its direct grid children; these nested
   grids pass that stretch down to #publish/#schedule/#undoSchedule). The gap
   also gives the date picker room to breathe from the Schedule button. */
.publish-actions,
.schedule-field,
.schedule-confirm {
  display: grid;
  gap: var(--space-3);
}

/* An explicit `display` overrides the UA `[hidden] { display: none }` rule, so
   the JS-toggled groups must re-hide themselves when the attribute is set. */
.publish-actions[hidden],
.schedule-confirm[hidden] {
  display: none;
}

.schedule-badge { font-weight: 600; }
.schedule-badge--pending { color: var(--pending-ink); }
.schedule-badge--failed { color: var(--danger-ink); }
.schedule-badge--published { color: var(--success-ink); }

/* Success-panel action rows lay their buttons out horizontally (wrapping on
   narrow panels) rather than inheriting the full-width stretch of the
   publish/schedule button groups. */
.confirm-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
}

/* Separate the My Videos queue sections from what follows, matching the card
   rhythm, so "Recently published" and "Scheduled" read as distinct blocks
   rather than sitting flush against the next heading. */
#recent-published-section,
#scheduled-section {
  margin-bottom: var(--space-5);
}
