/* ==========================================================================
   STATIX CLOUD - the web app.

   The design is law and the tokens below are quoted from it exactly. The
   display is MONOCHROME: state is carried by BRIGHTNESS, never by hue.
   Bright is done or active, normal is settled, dim is queued. Red appears
   only where something is destructive or refused. There is no green.

   Geometry: six rows, top to bottom, that never change height and never
   reflow. Only their contents change. Rows 3, 4, 5 and 6 have a browse
   form and a transfer form, and uploading SWAPS them -- it does not open
   a panel, because there are no panels.
   ========================================================================== */

/* -------------------------------------------------------------- fonts --
   Self-hosted, SIL OFL, in priv/web/fonts. No request to Google or to any
   third party ever leaves this page.                                     */

@font-face {
  font-family: "IBM Plex Mono";
  src: url("/fonts/IBMPlexMono-Regular.woff2") format("woff2");
  font-weight: 400; font-style: normal; font-display: block;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("/fonts/IBMPlexMono-Medium.woff2") format("woff2");
  font-weight: 500; font-style: normal; font-display: block;
}
@font-face {
  font-family: "IBM Plex Mono";
  src: url("/fonts/IBMPlexMono-SemiBold.woff2") format("woff2");
  font-weight: 600; font-style: normal; font-display: block;
}

/* ------------------------------------------------------------- tokens -- */

:root {
  --phosphor:        #FFB454;   /* normal text                            */
  --phosphor-bright: #FFE6BE;   /* headings, selection, current crumb     */
  --phosphor-mid:    #DE9A41;   /* secondary: sizes, dates                */
  --phosphor-dim:    #A0702F;   /* tertiary: labels, queued               */
  --phosphor-faint:  #6A4A20;   /* rules, placeholders, leader dots       */
  --red:             #FF6A5D;   /* DELETE, CANCEL, a full disk            */
  --page:            #060402;
  --glow: 0 0 7px rgba(255, 180, 84, .28);

  --row-h: 28px;                /* the windowed list depends on this      */
  --gap: 14px;
}

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

* { box-sizing: border-box; }

/* The view swap is driven by the `hidden` attribute, and every container
   it is applied to also carries `display: flex` from a rule below. A bare
   class selector beats the user-agent's [hidden] { display: none }, so
   without this the browse and transfer forms of rows 3, 4 and 6 render on
   top of each other. */
[hidden] { display: none !important; }

html, body {
  height: 100%;
  margin: 0;
  background: var(--page);
  color: var(--phosphor);
  font-family: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo,
               Consolas, monospace;
  font-size: 15px;
  font-weight: 400;
  line-height: 1.35;
  text-shadow: var(--glow);
  /* Sizes and dates line up because the digits are the same width, not
     because anything was hand-tuned. */
  font-variant-numeric: tabular-nums;
  overflow: hidden;
}

/* The tube. One screen: no panels, no boxes, no borders around anything. */
.tube {
  position: relative;
  height: 100%;
  background:
    radial-gradient(ellipse at 50% 45%,
                    #241606 0%, #120B03 60%, #080502 100%);
}

.scanlines, .vignette {
  position: absolute; inset: 0;
  pointer-events: none;
  z-index: 50;
}

/* FIXED. There is no toggle. */
.scanlines {
  background: repeating-linear-gradient(180deg,
              transparent 0 2px, rgba(0, 0, 0, .13) 3px 4px);
}

/* vignette plus the centre bloom */
.vignette {
  background:
    radial-gradient(ellipse at 50% 45%,
                    rgba(255, 180, 84, .05) 0%,
                    rgba(255, 180, 84, 0) 45%),
    radial-gradient(ellipse at 50% 50%,
                    rgba(0, 0, 0, 0) 55%, rgba(0, 0, 0, .55) 100%);
}

.screen {
  position: relative;
  z-index: 10;
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: 0 22px;
}

/* -------------------------------------------------------------- rules --
   Thin rules separate the rows. That is the only division on the screen. */

.row-title, .row-disk, .row-context, .row-columns, .row-bottom {
  flex: 0 0 auto;
  border-bottom: 1px solid var(--phosphor-faint);
}
.row-bottom { border-bottom: none; border-top: 1px solid var(--phosphor-faint); }

/* Fixed heights: these rows never move, whatever is inside them. */
.row-title   { height: 46px; }
.row-disk    { height: 34px; }
.row-context { height: 34px; }
.row-columns { height: 24px; }
.row-bottom  { height: 38px; }

/* The two rows that have no view-swapped inner container lay their own
   contents out. They must NOT take height from here: their fixed heights
   are set above, and a `height: 100%` in this rule would override them
   and give each of them the whole screen. */
.row-title, .row-disk {
  display: flex;
  align-items: center;
  gap: var(--gap);
}

/* The swapped inner containers fill the row they sit in. */
.ctx, .cols, .bar {
  display: flex;
  align-items: center;
  gap: var(--gap);
  height: 100%;
  width: 100%;
}

/* ------------------------------------------------------- leader dots -- */

/* flex-basis 0, not auto: the dots are long enough to be a leader on a
   wide screen, and with an auto basis that content length would become
   the element's preferred width and squeeze every label beside it. */
.lead {
  flex: 1 1 0;
  min-width: 12px;
  overflow: hidden;
  white-space: nowrap;
  color: var(--phosphor-faint);
  user-select: none;
}
.lead::after {
  content: "........................................................................"
           "........................................................................"
           "........................................................................";
}

/* --------------------------------------------------------- 1  title  -- */

.title, .user, .label, .used, .counts, .summary, .xfer-summary, .xfer-rate {
  flex: 0 0 auto;
  white-space: nowrap;
}

.title  { font-size: 22px; font-weight: 600; color: var(--phosphor-bright);
          letter-spacing: .06em; }
.user   { color: var(--phosphor-mid); }

/* --------------------------------------------------------- 2  disk   -- */

.label  { font-size: 12px; color: var(--phosphor-dim); letter-spacing: .1em; }
.used   { color: var(--phosphor); }
.counts { color: var(--phosphor-mid); }

/* A segmented gauge: 40 cells, lit from the left. Brightness carries the
   reading; red is reserved for a disk that is actually full. */
.gauge {
  display: flex;
  gap: 2px;
  width: 210px;
  height: 11px;
  flex: 0 0 auto;
}
.gauge i {
  flex: 1 1 auto;
  background: var(--phosphor-faint);
  opacity: .55;
}
.gauge i.on   { background: var(--phosphor); opacity: 1;
                box-shadow: 0 0 5px rgba(255, 180, 84, .5); }
.gauge.full i.on { background: var(--red);
                   box-shadow: 0 0 5px rgba(255, 106, 93, .5); }

/* ------------------------------------------------------ 3  context   -- */

.crumbs { display: flex; align-items: center; gap: 6px; overflow: hidden;
          white-space: nowrap; }
.crumb  { color: var(--phosphor-mid); cursor: pointer; background: none;
          border: 0; padding: 0; font: inherit; text-shadow: inherit; }
.crumb:hover { color: var(--phosphor-bright); }
.crumb.here  { color: var(--phosphor-bright); cursor: default; }
.crumb-sep   { color: var(--phosphor-faint); }

.find {
  width: 230px;
  flex: 0 0 auto;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--phosphor-faint);
  color: var(--phosphor-bright);
  font: inherit;
  text-shadow: inherit;
  padding: 2px 0;
  outline: none;
}
.find::placeholder { color: var(--phosphor-faint); }
.find:focus { border-bottom-color: var(--phosphor); }

/* the transfer progress bar, in the context line */
.bar {
  position: relative;
  width: 260px;
  height: 11px;
  flex: 0 0 auto;
  background: transparent;
  border: 1px solid var(--phosphor-faint);
}
#ctx-transfer .bar { display: block; }
.bar > i {
  display: block;
  height: 100%;
  width: 0;
  background: var(--phosphor);
  box-shadow: 0 0 6px rgba(255, 180, 84, .45);
  transition: width .18s linear;
}
/* Enumerating a dropped folder has no total to report yet, so the bar
   sweeps instead of filling. It must not look like a hang. */
.bar.indeterminate > i {
  width: 34%;
  animation: sweep 1.1s ease-in-out infinite alternate;
  transition: none;
}
@keyframes sweep { from { margin-left: 0; } to { margin-left: 66%; } }

.xfer-summary { color: var(--phosphor); }
.xfer-rate    { color: var(--phosphor-mid); }

/* ------------------------------------------------------ 4  columns   -- */

.cols { font-size: 12px; color: var(--phosphor-dim); letter-spacing: .1em; }
.sortable { cursor: pointer; user-select: none; }
.sortable:hover { color: var(--phosphor); }
.sortable.active { color: var(--phosphor-bright); }
.sortable.active::after { content: " \25B2"; font-size: 9px; }
.sortable.active.desc::after { content: " \25BC"; }

/* One column geometry, shared by the header and every row, so they cannot
   drift apart. */
.c-name  { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis;
           white-space: nowrap; }
.c-size  { flex: 0 0 96px;  text-align: right; }
.c-date  { flex: 0 0 168px; text-align: right; }
.c-prog  { flex: 0 0 190px; }
.c-state { flex: 0 0 96px;  text-align: right; }

/* --------------------------------------------------------- 5  body   -- */

.row-body {
  flex: 1 1 auto;
  position: relative;
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: thin;
  scrollbar-color: var(--phosphor-faint) transparent;
}
.row-body::-webkit-scrollbar { width: 9px; }
.row-body::-webkit-scrollbar-thumb { background: var(--phosphor-faint); }

.spacer { width: 100%; }
.rows   { position: absolute; top: 0; left: 0; right: 0; }

.row {
  position: absolute;
  left: 0; right: 0;
  height: var(--row-h);
  display: flex;
  align-items: center;
  gap: var(--gap);
  font-size: 14px;
  padding: 0 2px;
  cursor: default;
  user-select: none;
}
.row .c-size, .row .c-date { color: var(--phosphor-mid); }
.row.dir .c-name { color: var(--phosphor-bright); }

/* Selection is INVERSE VIDEO: a solid phosphor field with dark text. */
.row.sel { background: var(--phosphor); color: #120B03; text-shadow: none; }
.row.sel .c-name, .row.sel .c-size, .row.sel .c-date { color: #120B03; }

.empty {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--phosphor-faint);
  letter-spacing: .12em;
  pointer-events: none;
}

/* transfer rows: state by brightness, never by hue */
.row .prog { flex: 0 0 190px; height: 9px; border: 1px solid var(--phosphor-faint); }
.row .prog > i { display: block; height: 100%; width: 0; background: var(--phosphor); }
.row.queued  { color: var(--phosphor-dim); }
.row.queued .c-name, .row.queued .c-state { color: var(--phosphor-dim); }
.row.active .c-name  { color: var(--phosphor); }
.row.done   .c-name, .row.done .c-state { color: var(--phosphor-bright); }
.row.done   .prog > i { background: var(--phosphor-bright); }
.row.failed .c-name, .row.failed .c-state { color: var(--red); }
.row.failed .prog > i { background: var(--red); }

/* ---------------------------------------------------- 6  bottom bar  -- */

.row-bottom .bar { width: 100%; height: 100%; border: 0; position: static; }
.summary { color: var(--phosphor-mid); font-size: 13px; }

.act {
  background: none;
  border: 0;
  padding: 2px 4px;
  font: inherit;
  font-size: 13px;
  letter-spacing: .1em;
  color: var(--phosphor);
  text-shadow: inherit;
  cursor: pointer;
  flex: 0 0 auto;
}
.act:hover  { color: var(--phosphor-bright); }
.act.danger { color: var(--red); }
.act.danger:hover { color: #FF9A90; }
.act.on     { color: var(--phosphor-bright); }
.act[disabled] { color: var(--phosphor-faint); cursor: default; }

/* ----------------------------------------------------------- overlays --
   Drawn on the same tube: the background dims, the text is centred, and
   the dismiss control is a dashed-bordered word.                        */

.overlay {
  position: absolute; inset: 0;
  z-index: 40;
  display: flex; align-items: center; justify-content: center;
  background: rgba(6, 4, 2, .82);
  text-align: center;
}
.overlay[hidden] { display: none; }
.ov-inner { max-width: 640px; padding: 0 30px; }
.ov-title {
  font-size: 22px; font-weight: 600; letter-spacing: .16em;
  color: var(--phosphor-bright);
  margin: 0 0 14px;
}
.ov-title.danger { color: var(--red); text-shadow: 0 0 7px rgba(255, 106, 93, .3); }
.ov-note { color: var(--phosphor-mid); margin: 0 0 22px; line-height: 1.6; }
.ov-actions { display: flex; gap: 18px; justify-content: center; }

.dismiss {
  background: none;
  border: 1px dashed var(--phosphor-faint);
  color: var(--phosphor);
  font: inherit; font-size: 13px; letter-spacing: .12em;
  text-shadow: inherit;
  padding: 6px 18px;
  cursor: pointer;
}
.dismiss:hover { border-color: var(--phosphor); color: var(--phosphor-bright); }

/* The web-login code, typed off a phone screen. Wide tracking and the
   tabular figures the rest of the screen already uses, so a Q and a 2
   cannot be mistaken for each other at a glance. */
.code-input {
  width: 8ch;
  text-align: center;
  text-transform: uppercase;
  letter-spacing: .28em;
  font-size: 17px;
  color: var(--phosphor-bright);
}
.dismiss.danger { color: var(--red); border-color: var(--red); }
.dismiss.danger:hover { color: #FF9A90; }

/* The drop overlay must not eat the dragleave events it depends on. */
#ov-drop { pointer-events: none; }
