/*
 * Diario - one phosphor colour on black, and nothing else.
 *
 * Every visible thing in the app is the same hue at a different opacity.
 * That single constraint is what makes it read as a terminal rather than
 * as a website pretending to be one.
 */

:root {
  --p: #33ff70;            /* the phosphor; themes below swap only this */
  --bg: #04060a;
  --glow: 0.55;
  --font-scale: 1;         /* :fontsize multiplies this; theme eink starts higher */
  --contrast: 1;           /* :contrast multiplies how opaque the dim/faint/ghost/wash tiers are */

  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas,
          "Liberation Mono", "DejaVu Sans Mono", monospace;
  --pad: clamp(1.25rem, 5vw, 5rem);
  --lh: 1.62;
}

body[data-theme="matrix"] { --p: #00ff41; }
body[data-theme="green"]  { --p: #33ff70; }
body[data-theme="amber"]  { --p: #ffb000; }
body[data-theme="orange"] { --p: #ff7a18; }
body[data-theme="ice"]    { --p: #7fd7ff; }
body[data-theme="bone"]   { --p: #d8d4c8; }

/* e-ink: plain black on white, no glow, bigger default text - everything
   else about the theme mechanism (one hue, at varying opacity) still works
   unchanged because --p and --bg are the only things it swaps. */
body[data-theme="eink"] { --p: #000000; --bg: #ffffff; --glow: 0; --font-scale: 1.35; }

/* No animation, no glow, no scanlines: an e-ink panel redraws a fade or a
   blur as a smear across several refreshes instead of showing it, so this
   theme gets none of them, not just slower ones. */
body[data-theme="eink"] #crt { display: none; }
body[data-theme="eink"],
body[data-theme="eink"] *,
body[data-theme="eink"] *::before,
body[data-theme="eink"] *::after {
  text-shadow: none !important;
  transition: none !important;
  animation: none !important;
}

/* Fallback for engines without color-mix: still legible, just flatter. */
@supports not (color: color-mix(in srgb, red 50%, transparent)) {
  body {
    --p-dim: var(--p); --p-faint: var(--p);
    --p-ghost: var(--p); --p-wash: transparent;
  }
  .u-dim, .u-faint, .u-ghost { opacity: 0.6; }
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  /* Dynamic viewport units, so the on-screen keyboard does not push the
     writing surface off the bottom of a phone. */
  height: 100dvh;
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--p);
  font-family: var(--mono);
  font-size: calc(clamp(15px, 1.05vw + 11px, 18px) * var(--font-scale));
  line-height: var(--lh);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body {
  /* Declared here, not on :root, so var(--p) below resolves against this
     element's own --p - which a body[data-theme="x"] rule may have
     overridden. A custom property's var() references are substituted using
     the element that declares it, then inherited already-substituted; from
     :root they would have stayed green under every theme. */
  /* Percentages are how opaque each tier is against the page background -
     color-mix clamps them to [0%, 100%], so turning contrast up eventually
     flattens dim/faint/ghost/wash into one solid --p and turning it down
     fades them toward invisible, in that order, proportionally. */
  --p-dim: color-mix(in srgb, var(--p) calc(62% * var(--contrast)), transparent);
  --p-faint: color-mix(in srgb, var(--p) calc(34% * var(--contrast)), transparent);
  --p-ghost: color-mix(in srgb, var(--p) calc(15% * var(--contrast)), transparent);
  --p-wash: color-mix(in srgb, var(--p) calc(7% * var(--contrast)), transparent);

  text-shadow: 0 0 calc(var(--glow) * 7px) color-mix(in srgb, var(--p) 45%, transparent);
}

::selection { background: var(--p-dim); color: var(--bg); text-shadow: none; }

/* Scrollbars, in phosphor. */
* { scrollbar-width: thin; scrollbar-color: var(--p-ghost) transparent; }
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-thumb { background: var(--p-ghost); }
*::-webkit-scrollbar-track { background: transparent; }

.u-dim   { color: var(--p-dim); }
.u-faint { color: var(--p-faint); }
.u-ghost { color: var(--p-ghost); }
.hidden  { display: none !important; }

/* ------------------------------------------------------------ CRT skin */

#crt {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 80;
  background:
    /* scanlines */
    repeating-linear-gradient(
      to bottom,
      color-mix(in srgb, #000 22%, transparent) 0 1px,
      transparent 1px 3px
    ),
    /* corner vignette, so the glass feels curved */
    radial-gradient(
      120% 100% at 50% 50%,
      transparent 55%,
      color-mix(in srgb, #000 55%, transparent) 100%
    );
  opacity: 0.55;
}

body.no-crt #crt { display: none; }

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

#stage {
  position: absolute;
  inset: 0;
  display: flex;
  justify-content: center;
  overflow: hidden;
}

#editor {
  width: 100%;
  max-width: 82ch;
  height: 100%;
  padding: var(--pad) var(--pad) 45vh;
  border: 0;
  outline: 0;
  resize: none;
  background: transparent;
  color: inherit;
  font: inherit;
  line-height: var(--lh);
  caret-color: var(--p);
  text-shadow: inherit;
  overflow-y: auto;
  scrollbar-width: none;
  tab-size: 4;
}

#editor::-webkit-scrollbar { width: 0; }
#editor::placeholder { color: var(--p-ghost); }

/* A fat block caret, like a real terminal. */
@supports (caret-shape: block) {
  #editor { caret-shape: block; }
}

/* ---------------------------------------------------------------- HUD
 * One line at the bottom. It exists so you can find your way back, and it
 * gets out of the way the moment you start typing - which is most of the
 * time, which is the point.
 */

#hud {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;
  display: flex;
  gap: 1.5ch;
  align-items: baseline;
  padding: 0.55rem var(--pad);
  font-size: 0.82em;
  color: var(--p-faint);
  background: linear-gradient(to top, var(--bg) 55%, transparent);
  transition: opacity 220ms ease, transform 220ms ease;
  pointer-events: none;
  user-select: none;
  cursor: pointer;
  white-space: nowrap;
  overflow: hidden;
}

body.writing #hud {
  opacity: 0;
  transform: translateY(0.4rem);
}

/* Pressable only while it is actually visible, so it never swallows a tap
   meant for the last line of an entry. */
body:not(.writing) #hud { pointer-events: auto; }

#hud-title { color: var(--p-dim); overflow: hidden; text-overflow: ellipsis; }
#hud-right { margin-left: auto; }
#hud .sep { color: var(--p-ghost); }
#hud .dirty { color: var(--p); }

/* ------------------------------------------------------------ overlays */

.overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  padding: var(--pad);
  /* Opaque fallback first: if color-mix is unsupported the declaration is
     dropped, and an overlay with no background would sit transparently on
     top of the editor text. */
  background: var(--bg);
  background: color-mix(in srgb, var(--bg) 96%, transparent);
  backdrop-filter: blur(2px);
  overflow: hidden;
}

.overlay-inner {
  width: 100%;
  max-width: 92ch;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
}

/* ---------------------------------------------------------- prompt row */

.prompt-row {
  display: flex;
  align-items: baseline;
  gap: 1ch;
  border-bottom: 1px solid var(--p-ghost);
  padding-bottom: 0.5rem;
}

.prompt-row .sigil { color: var(--p); flex: none; }

.prompt-row input {
  flex: 1;
  min-width: 0;
  border: 0;
  outline: 0;
  background: transparent;
  color: var(--p);
  font: inherit;
  text-shadow: inherit;
  caret-color: var(--p);
}

.prompt-row input::placeholder { color: var(--p-ghost); }

.hint {
  margin: 0.6rem 0 0;
  font-size: 0.82em;
  color: var(--p-faint);
  min-height: 1.4em;
}

.hint b { color: var(--p-dim); font-weight: normal; }

/* --------------------------------------------------------------- lists */

.rows {
  list-style: none;
  margin: 1.1rem 0 0;
  padding: 0;
  overflow-y: auto;
  min-height: 0;
  flex: 1;
}

.row {
  display: flex;
  gap: 1.5ch;
  align-items: baseline;
  padding: 0.3rem 0.75ch;
  cursor: pointer;
  border-left: 2px solid transparent;
  white-space: nowrap;
}

.row:hover { background: var(--p-wash); }

.row.on {
  border-left-color: var(--p);
  background: var(--p-wash);
}

.row .r-date { color: var(--p-faint); flex: none; font-size: 0.88em; }
.row .r-title { color: var(--p); overflow: hidden; text-overflow: ellipsis; }
.row .r-snip { color: var(--p-faint); overflow: hidden; text-overflow: ellipsis; flex: 1; }
.row .r-tags { color: var(--p-dim); flex: none; font-size: 0.85em; }
.row .r-mark { flex: none; color: var(--p-dim); width: 2ch; }
.row.gone .r-title { text-decoration: line-through; color: var(--p-faint); }
.row mark { background: var(--p-dim); color: var(--bg); text-shadow: none; }

.empty { color: var(--p-faint); padding: 1.5rem 0.75ch; }

/* --------------------------------------------------------------- login */

#lock .overlay-inner {
  max-width: 56ch;
  justify-content: center;
  flex: 1;
}

.brand {
  font-size: 1.05em;
  letter-spacing: 0.42em;
  color: var(--p);
  margin-bottom: 0.2rem;
}

.brand-sub {
  color: var(--p-faint);
  font-size: 0.82em;
  margin-bottom: 2.2rem;
}

.field { margin-bottom: 1.3rem; }

.field label {
  display: block;
  font-size: 0.78em;
  color: var(--p-faint);
  margin-bottom: 0.35rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

.field input {
  width: 100%;
  border: 0;
  border-bottom: 1px solid var(--p-ghost);
  outline: 0;
  padding: 0.35rem 0;
  background: transparent;
  color: var(--p);
  font: inherit;
  text-shadow: inherit;
  caret-color: var(--p);
  letter-spacing: 0.08em;
}

.field input:focus { border-bottom-color: var(--p); }

.msg {
  min-height: 1.5em;
  font-size: 0.85em;
  color: var(--p-dim);
  margin-top: 0.6rem;
  transition: opacity 0.4s ease;
}

.msg.bad { color: var(--p); }
.msg.bad::before { content: "!! "; }

/* The hint is shown only while its link is held down; this is it going as
   the link is released. releaseHint() in app.js clears the text afterwards,
   so if the transition is skipped the text still goes. Only ever applied to
   the hint, never to an error. */
.msg.fading { opacity: 0; }

button {
  border: 1px solid var(--p-faint);
  background: transparent;
  color: var(--p);
  font: inherit;
  text-shadow: inherit;
  padding: 0.4rem 2ch;
  cursor: pointer;
  letter-spacing: 0.14em;
}

button:hover:not(:disabled) { background: var(--p-wash); border-color: var(--p); }
button:disabled { opacity: 0.4; cursor: default; }
button:focus-visible, .row:focus-visible { outline: 1px solid var(--p); outline-offset: 2px; }

/* Blinking block cursor, for the moments when nothing else is on screen. */
.caret::after {
  content: "\2588";
  animation: blink 1.05s steps(1) infinite;
  color: var(--p);
}

@keyframes blink { 50% { opacity: 0; } }

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

#toast {
  position: fixed;
  left: 50%;
  bottom: 2.5rem;
  transform: translateX(-50%);
  z-index: 90;
  padding: 0.35rem 2ch;
  font-size: 0.82em;
  color: var(--p);
  border: 1px solid var(--p-ghost);
  background: var(--bg);
  opacity: 0;
  transition: opacity 180ms ease;
  pointer-events: none;
  max-width: 80vw;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

#toast.show { opacity: 1; }

/* ---------------------------------------------------------- reader view */

#reader .body {
  overflow-y: auto;
  min-height: 0;
  flex: 1;
  white-space: pre-wrap;
  word-break: break-word;
  margin-top: 1.2rem;
  padding-bottom: 4rem;
}

#reader .meta {
  color: var(--p-faint);
  font-size: 0.82em;
  border-bottom: 1px solid var(--p-ghost);
  padding-bottom: 0.5rem;
}

/* :files - the one place an attachment is ever drawn or played. The CRT
   overlay still passes over it, which is the look; :crt turns that off for
   a clean view. */
#reader .body.gallery { white-space: normal; }

#reader .gallery figure { margin: 0 0 2.4rem; }

#reader .gallery figcaption {
  font-size: 0.82em;
  margin-bottom: 0.6rem;
}

#reader .gallery .img-slot {
  max-width: 100%;
  border: 1px solid var(--p-ghost);
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 3rem;
}

#reader .gallery .img-slot img {
  display: block;
  width: 100%;
  height: 100%;
  max-height: 78vh;
  object-fit: contain;
}

#reader .gallery .img-slot.media-slot { border: 0; justify-content: flex-start; }

#reader .gallery audio { width: min(100%, 40rem); }

#reader .gallery video { display: block; max-width: 100%; max-height: 78vh; }

#reader .gallery .file-save {
  font: inherit;
  font-size: 0.9em;
  color: var(--p);
  background: transparent;
  border: 1px solid var(--p-faint);
  padding: 0 1ch;
  margin-left: 1ch;
  cursor: pointer;
}

#reader .gallery .file-save:hover,
#reader .gallery .file-save:focus-visible { border-color: var(--p); }

/* --------------------------------------------------------------- setup */

.setup-wrap {
  max-width: 74ch;
  margin: 0 auto;
  padding: var(--pad);
  height: 100%;
  overflow-y: auto;
}

.step { margin-bottom: 2.6rem; }

.step h2 {
  font-size: 0.82em;
  font-weight: normal;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--p-faint);
  margin: 0 0 0.9rem;
  border-bottom: 1px solid var(--p-ghost);
  padding-bottom: 0.4rem;
}

.step p { color: var(--p-dim); margin: 0 0 0.9rem; }

.secret {
  font-size: 1.15em;
  letter-spacing: 0.22em;
  word-break: break-all;
  padding: 0.9rem 1.5ch;
  border: 1px dashed var(--p-faint);
  color: var(--p);
  user-select: all;
}

.codes {
  list-style: none;
  padding: 0.9rem 1.5ch;
  margin: 0;
  border: 1px dashed var(--p-faint);
  columns: 2;
  column-gap: 3ch;
}

.codes li { letter-spacing: 0.14em; break-inside: avoid; }

.checks { list-style: none; padding: 0; margin: 0; }
.checks li { color: var(--p-dim); }
.checks li::before { content: "[ok] "; color: var(--p-faint); }
.checks li.no { color: var(--p); }
.checks li.no::before { content: "[--] "; }

.warn {
  border-left: 2px solid var(--p);
  padding: 0.6rem 1.5ch;
  color: var(--p-dim);
  margin: 1.2rem 0;
  background: var(--p-wash);
}

.bar { height: 2px; background: var(--p-ghost); margin-top: 0.5rem; }
.bar i { display: block; height: 100%; width: 0; background: var(--p); transition: width 200ms; }

kbd {
  font: inherit;
  color: var(--p);
  border: 1px solid var(--p-ghost);
  padding: 0 0.6ch;
}

a { color: var(--p); }

/* --------------------------------------------------------- preferences */

@media (prefers-reduced-motion: reduce) {
  *, *::after, *::before { animation: none !important; transition: none !important; }
}

@media (max-width: 600px) {
  :root { --pad: 1.1rem; }
  #hud { font-size: 0.75em; gap: 1ch; }
  .codes { columns: 1; }
}

@media print { #crt, #hud, .overlay { display: none !important; } }

/* ------------------------------------------------------------- touch
 * A phone has no Escape key. The console gains a close control; the
 * writing surface gains nothing.
 */

#con-close, #rd-close {
  display: none;
  position: absolute;
  top: var(--pad);
  right: var(--pad);
  font-size: 0.8em;
  padding: 0.3rem 1.4ch;
  z-index: 2;
}

body.touch #con-close,
body.touch #rd-close { display: block; }

body.touch .prompt-row { padding-right: 9ch; }

/* Touch targets want room; a 44px minimum is the usual figure. */
body.touch .row { padding-top: 0.62rem; padding-bottom: 0.62rem; }
body.touch #hud { padding-top: 0.8rem; padding-bottom: 0.8rem; }

/* "remember this device" on the lock screen */
.remember {
  display: flex;
  align-items: center;
  gap: 1ch;
  margin: -0.4rem 0 1.3rem;
  font-size: 0.82em;
  color: var(--p-faint);
  cursor: pointer;
  user-select: none;
}

.remember input { accent-color: var(--p); cursor: pointer; }
.remember:hover { color: var(--p-dim); }

/* The password hint's link. Held rather than clicked, so suppress the
   long-press callout and the text selection a hold would otherwise start. */
.hint-link {
  -webkit-touch-callout: none;
  user-select: none;
  -webkit-user-select: none;
  touch-action: manipulation;
  display: block;
  margin: 0.4rem 0 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 0.78em;
  letter-spacing: 0.08em;
  color: var(--p-ghost);
  text-shadow: none;
  cursor: pointer;
}
.hint-link:hover, .hint-link:focus { color: var(--p-dim); background: none; outline: 0; }

/* ---------------------------------------------------------- idle tiers
 * The screensaver sits above everything except the soft lock, which sits
 * above it. Neither is security - see :tiers - but both must genuinely
 * cover the writing rather than dim it.
 */

#saver {
  position: fixed;
  inset: 0;
  z-index: 55;
  background: var(--bg);
  overflow: hidden;
}

/* Transparent, so the screensaver keeps running behind the pin prompt -
   that difference is what tells the two tiers apart at a glance. The panel
   itself is opaque enough to stay readable over the animation. */
#softlock {
  z-index: 75;
  background: transparent;
  backdrop-filter: none;
  align-items: center;
  justify-content: center;
}

#softlock .soft-inner {
  max-width: 44ch;
  flex: 0 0 auto;
  padding: 2rem 2.5ch;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  border: 1px solid var(--p-ghost);
}

@supports not (color: color-mix(in srgb, red 50%, transparent)) {
  #softlock .soft-inner { background: var(--bg); }
}

#softlock .field input {
  letter-spacing: 0.5em;
  font-size: 1.2em;
}

/* The lock screen shows the screensaver behind it too. */
#lock {
  background: transparent;
  backdrop-filter: none;
  align-items: center;
  justify-content: center;
}

#lock .overlay-inner {
  flex: 0 0 auto;
  padding: 2.2rem 3ch;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  border: 1px solid var(--p-ghost);
}

@supports not (color: color-mix(in srgb, red 50%, transparent)) {
  #lock .overlay-inner { background: var(--bg); }
}

/* A lock prompt sitting on screen all night announces that there is
   something here worth locking. It fades out a few seconds after the lock
   settles and comes back on any key or movement, so an untouched machine
   shows nothing but the screensaver. Slow out, quick back. */
#lock .overlay-inner,
#softlock .soft-inner {
  transition: opacity 0.3s ease;
}

#lock.veiled .overlay-inner,
#softlock.veiled .soft-inner {
  opacity: 0;
  pointer-events: none;
  transition: opacity 1.6s ease;
}

@media (prefers-reduced-motion: reduce) {
  #lock .overlay-inner,
  #softlock .soft-inner,
  #lock.veiled .overlay-inner,
  #softlock.veiled .soft-inner {
    transition: none;
  }
}

/* The import file picker, shown inside the console. */
#import-file {
  font: inherit;
  color: var(--p);
  background: transparent;
  border: 1px dashed var(--p-faint);
  padding: 0.8rem 1.5ch;
  width: 100%;
  cursor: pointer;
}

#import-file::file-selector-button {
  font: inherit;
  color: var(--p);
  background: transparent;
  border: 1px solid var(--p-faint);
  padding: 0.3rem 1.5ch;
  margin-right: 1.5ch;
  cursor: pointer;
}

#import-slot button {
  font: inherit;
  letter-spacing: 0.12em;
  color: var(--bg);
  background: var(--p);
  border: 1px solid var(--p);
  padding: 0.6rem 3ch;
  margin: 0.8rem 1.5ch 0 0;
  cursor: pointer;
}

/* Overwriting is the one that can lose writing. It does not get to look
   like the default. */
#import-slot button.danger {
  color: var(--p);
  background: transparent;
  border-style: dashed;
}

#import-slot button:disabled {
  color: var(--p-faint);
  background: transparent;
  border: 1px solid var(--p-faint);
  cursor: default;
}

/* The second-factor setup code. Deliberately black on white rather than
   phosphor on black: phone cameras are unreliable at reading an inverted
   code, and this is the one thing on the page that must scan first time. */
.qr {
  width: 100%;
  max-width: 15rem;
  margin: 1.2rem 0;
  padding: 0.6rem;
  background: #fff;
  border: 1px solid var(--p-faint);
}

.qr svg {
  display: block;
  width: 100%;
  height: auto;
}
