/* ============================================================================
   ORPPIO — the uploader (.ou-*)

   One upload control for the whole product. The agency back office is bo-*, the
   client portal is kp-*, the master console is bo-* again and the public pages
   have their own languages; an upload area is the one component all four needed
   and none of them should own. So it lives here, spends only kernol tokens, and
   is loaded everywhere (`stylesheet_link_tag :app` links every sheet in this
   directory).

   It is deliberately quiet. The surfaces around it differ a great deal, and a
   dropzone that shouted would look wrong on three of the four; what it borrows
   from each is the token layer they all already spend.

   Two tone modifiers exist for the places where the surrounding chrome is
   unmistakably one thing:

     .ou--calm    the client portal — softer border, more air, larger hit areas
     .ou--compact a composer footer or a settings row, where the zone is one line

   Markup: app/views/shared/_file_uploader.html.erb
   Behaviour: app/javascript/controllers/file_uploader_controller.js
   ============================================================================ */

.ou { display: block; }

.ou-label {
  display: block;
  margin-bottom: var(--kernol-space-1);
  font-size: var(--kernol-text-12);
  font-weight: 500;
  color: var(--kernol-text-secondary);
}

/* ── The drop zone ───────────────────────────────────────────────────────────
   A real <label> wrapping a real <input type="file">, so a click opens the
   picker and a screen reader announces a file input, with or without
   JavaScript. Drag and drop is added on top and is never the only way in. */

.ou-zone {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--kernol-space-2);
  width: 100%;
  padding: var(--kernol-space-6) var(--kernol-space-5);
  text-align: center;
  cursor: pointer;
  border: 1.5px dashed var(--kernol-border-strong);
  border-radius: var(--kernol-radius-md);
  background: var(--kernol-grey-25);
  color: var(--kernol-text-secondary);
  transition: border-color .15s ease, background-color .15s ease, color .15s ease;
}

.ou-zone:hover { border-color: var(--kernol-accent); background: var(--kernol-surface); }

/* The visible focus state belongs to the zone, not to the off-screen input the
   browser actually focuses — :focus-within is what carries it across. */
.ou-zone:focus-within {
  outline: none;
  border-color: var(--kernol-accent);
  background: var(--kernol-surface);
  box-shadow: var(--kernol-focus-ring);
}

.ou-zone.is-dragging {
  border-color: var(--kernol-accent);
  border-style: solid;
  background: var(--kernol-selected);
  color: var(--kernol-info-ink);
}

.ou-zone__icon { color: var(--kernol-grey-500); }
.ou-zone.is-dragging .ou-zone__icon,
.ou-zone:hover .ou-zone__icon { color: var(--kernol-accent); }

.ou-zone__lead {
  font-size: var(--kernol-text-13);
  font-weight: 500;
  color: var(--kernol-text-body);
}

.ou-zone__link { color: var(--kernol-accent); text-decoration: underline; text-underline-offset: 2px; }

/* A phone has no drag and drop, so it is not offered one. What is left reads as
   "Choose files", which is the only gesture that device has. */
@media (hover: none) {
  .ou-zone__drag { display: none; }
  .ou-zone__link::first-letter { text-transform: uppercase; }
}

.ou-zone__hint {
  font-size: var(--kernol-text-11);
  color: var(--kernol-text-muted);
}

/* The input is off-screen rather than display:none, so it stays focusable and
   stays a form control a screen reader can name. */
.ou-input {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── The list of chosen files ─────────────────────────────────────────────── */

.ou-list {
  list-style: none;
  margin: var(--kernol-space-2) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--kernol-space-2);
}

.ou-list:empty { margin: 0; }

.ou-file {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr) auto auto;
  align-items: center;
  gap: var(--kernol-space-3);
  padding: var(--kernol-space-2) var(--kernol-space-3);
  border: 1px solid var(--kernol-border-subtle);
  border-radius: var(--kernol-radius-sm);
  background: var(--kernol-surface);
}

.ou-file[data-state="error"] {
  border-color: var(--kernol-red-200);
  background: var(--kernol-danger-soft);
}

/* Small on purpose: a form with ten photos on it is a list of files, not a
   gallery, and a page of full-size previews is a page nobody can scan. */
.ou-file__thumb {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px; height: 36px;
  border-radius: var(--kernol-radius-xs);
  background: var(--kernol-grey-100);
  color: var(--kernol-text-muted);
  font-size: var(--kernol-text-10);
  font-weight: 600;
  letter-spacing: .02em;
  overflow: hidden;
  flex: none;
}

.ou-file__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

.ou-file__body { min-width: 0; display: block; }

.ou-file__name {
  display: block;
  font-size: var(--kernol-text-13);
  color: var(--kernol-text-body);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ou-file__meta {
  display: block;
  margin-top: 1px;
  font-size: var(--kernol-text-11);
  color: var(--kernol-text-muted);
  font-variant-numeric: tabular-nums;
}

.ou-file[data-state="error"] .ou-file__meta { color: var(--kernol-danger-ink); }
.ou-file[data-state="uploaded"] .ou-file__meta { color: var(--kernol-success-ink); }

.ou-file__meter {
  display: block;
  height: 3px;
  margin-top: 6px;
  border-radius: var(--kernol-radius-full);
  background: var(--kernol-grey-200);
  overflow: hidden;
}

/* The finished bar goes away rather than sitting there full: a completed upload
   should read as a file, not as a progress bar that happens to be at 100%. */
.ou-file[data-state="uploaded"] .ou-file__meter,
.ou-file[data-state="error"] .ou-file__meter { display: none; }

.ou-file__bar {
  display: block;
  height: 100%;
  width: 0;
  border-radius: inherit;
  background: var(--kernol-accent);
  transition: width .12s linear;
}

.ou-file__action {
  font-size: var(--kernol-text-12);
  font-weight: 500;
  color: var(--kernol-accent);
  background: none;
  border: 0;
  padding: var(--kernol-space-1) var(--kernol-space-2);
  border-radius: var(--kernol-radius-xs);
  cursor: pointer;
}

.ou-file__action:hover { background: var(--kernol-selected); }
.ou-file__action:focus-visible { outline: none; box-shadow: var(--kernol-focus-ring); }

.ou-file__remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px; height: 28px;
  border: 0;
  border-radius: var(--kernol-radius-xs);
  background: none;
  color: var(--kernol-text-muted);
  cursor: pointer;
  flex: none;
}

.ou-file__remove svg { width: 16px; height: 16px; }

/* Everything inside these buttons is decoration — an icon, and a label that only a
   screen reader reads. The button is what was clicked, whichever pixel the pointer
   landed on.
   
   Worth the two lines: `.sr-only` is `position: absolute`, and inside a centred flex
   container an absolutely positioned child's static position is the container's own
   centre — so the one-pixel, visually clipped label sits directly under the middle of
   the button, which is exactly where a pointer lands. */
.ou-file__remove > *,
.ou-file__action > *,
.ou-attach > svg { pointer-events: none; }
.ou-file__remove:hover { background: var(--kernol-grey-100); color: var(--kernol-danger-ink); }
.ou-file__remove:focus-visible { outline: none; box-shadow: var(--kernol-focus-ring); }

/* ── Messages ─────────────────────────────────────────────────────────────── */

/* Announced as well as shown: the file that was refused was refused after the
   picker closed, so somebody using a screen reader has no other way to know. */
.ou-error {
  margin: var(--kernol-space-2) 0 0;
  font-size: var(--kernol-text-12);
  font-weight: 500;
  color: var(--kernol-danger-ink);
}

.ou-error[hidden] { display: none; }

.ou-note {
  margin: var(--kernol-space-1) 0 0;
  font-size: var(--kernol-text-11);
  color: var(--kernol-text-muted);
}

/* ── Tone: the client portal ─────────────────────────────────────────────── */

.ou--calm .ou-zone {
  padding: var(--kernol-space-8) var(--kernol-space-5);
  border-radius: var(--kernol-radius-lg);
  border-color: var(--kernol-border);
  background: var(--kernol-surface);
}

.ou--calm .ou-file { border-radius: var(--kernol-radius); padding: var(--kernol-space-3); }
.ou--calm .ou-file__remove { width: 34px; height: 34px; }

/* ── Tone: one line, in a composer or a settings row ─────────────────────── */

.ou--compact .ou-zone {
  flex-direction: row;
  gap: var(--kernol-space-3);
  padding: var(--kernol-space-3) var(--kernol-space-4);
  text-align: left;
  justify-content: flex-start;
}

.ou--compact .ou-zone__hint { margin-left: auto; text-align: right; }

@media (max-width: 480px) {
  .ou--compact .ou-zone { flex-direction: column; text-align: center; }
  .ou--compact .ou-zone__hint { margin-left: 0; text-align: center; }
}

/* ── An attachment that is already saved (shared/_attachment_row) ─────────── */

.ou-saved {
  list-style: none;
  margin: var(--kernol-space-2) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--kernol-space-1);
}

.ou-saved:empty { margin: 0; }

.ou-saved__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--kernol-space-3);
  padding: var(--kernol-space-2) var(--kernol-space-2) var(--kernol-space-2) 0;
}

/* The thumbnail and the filename are one hit area, not two: a saved file's whole
   tile opens it. The explicit "View" / "Download" links beside it stay, because a
   large silent target is not a label — somebody has to be able to read what will
   happen before they click. */
.ou-file__open {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr);
  align-items: center;
  gap: var(--kernol-space-3);
  min-width: 0;
  padding: var(--kernol-space-1);
  border-radius: var(--kernol-radius-sm);
  text-decoration: none;
  color: inherit;
}

a.ou-file__open:hover { background: var(--kernol-hover-strong); }
a.ou-file__open:hover .ou-file__name { text-decoration: underline; }
a.ou-file__open:focus-visible { outline: none; box-shadow: var(--kernol-focus-ring); }
.ou-file__open--static { cursor: default; }

.ou-saved__row + .ou-saved__row { border-top: 1px solid var(--kernol-border-subtle); }

.ou-saved__actions { display: flex; align-items: center; gap: var(--kernol-space-2); }

.ou-saved__link {
  font-size: var(--kernol-text-12);
  font-weight: 500;
  color: var(--kernol-accent);
  text-decoration: none;
  padding: var(--kernol-space-1) var(--kernol-space-2);
  border-radius: var(--kernol-radius-xs);
}

.ou-saved__link:hover { background: var(--kernol-selected); text-decoration: underline; }
.ou-saved__link:focus-visible { outline: none; box-shadow: var(--kernol-focus-ring); }
.ou-saved__link--danger { color: var(--kernol-danger-ink); }
.ou-saved__link--danger:hover { background: var(--kernol-danger-soft); }

/* Somebody who has asked their operating system to stop animating things has
   asked for this too; the width still changes, it just stops sliding. */
@media (prefers-reduced-motion: reduce) {
  .ou-zone, .ou-file__bar { transition: none; }
}

/* A field the viewer may not change. No controller is attached (see the partial),
   so this is appearance only — the input itself is disabled in the markup. */
.ou.is-disabled .ou-zone {
  cursor: not-allowed;
  opacity: .6;
  border-style: solid;
  border-color: var(--kernol-border-subtle);
  background: var(--kernol-grey-50);
}

.ou.is-disabled .ou-zone:hover { border-color: var(--kernol-border-subtle); background: var(--kernol-grey-50); }

/* ── Variant: the composer's paperclip ───────────────────────────────────────
   A message composer is not a place for a dropzone — the thing somebody came to
   do is write — so the same control appears as a trigger instead. Everything
   below it (the file list, the progress, the errors) is identical, because it is
   the same component. Dragging still works, onto the trigger itself. */

.ou-attach {
  display: inline-flex;
  align-items: center;
  gap: var(--kernol-space-2);
  padding: var(--kernol-space-1) var(--kernol-space-2);
  border-radius: var(--kernol-radius-xs);
  font-size: var(--kernol-text-12);
  color: var(--kernol-text-secondary);
  cursor: pointer;
}

.ou-attach:hover { background: var(--kernol-grey-100); color: var(--kernol-text-body); }
.ou-attach:focus-within { outline: none; box-shadow: var(--kernol-focus-ring); }

.ou-attach__icon { color: var(--kernol-text-muted); flex: none; }
.ou-attach__label { font-weight: 500; }
.ou-attach__hint { color: var(--kernol-text-muted); font-size: var(--kernol-text-11); }

.ou-attach.is-dragging {
  outline: 1.5px dashed var(--kernol-accent);
  outline-offset: 3px;
  background: var(--kernol-selected);
  color: var(--kernol-info-ink);
}

@media (max-width: 560px) {
  .ou-attach__hint { display: none; }
}
