/* editor.css — the code editor component built by editor.js.

   The component is three boxes:

     .fe            the frame: border, background, focus ring
       .fe-gutter     line numbers, clipped and translated with the content
       .fe-body       the stack
         .fe-hl         the painted layer, <pre><code>, clipped
         .fe-input      the textarea, on top, its own text transparent
       .fe-note       the "highlighting is off" pill

   Every colour comes from tokens.css; this file writes none of its own. The
   rules that matter are marked: the overlay only works while the textarea is
   transparent and the two layers agree about metrics, so those few
   declarations defend themselves against page CSS that styles the textarea
   directly (app.css styles .editor, and #output by id, which outranks a class
   selector).

   The one number written down here that also lives in editor.js is 640px: the
   gutter is hidden below it. */

.fe {
  position: relative;
  display: flex;
  align-items: stretch;
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-raised);
  /* The frame clips the layers, so neither of them needs a radius of its own. */
  overflow: hidden;

  --fe-digits: 2;
  /* Selection has to sit over painted text and leave it readable, so it is a
     wash rather than a fill. */
  --fe-sel: rgba(125, 125, 160, 0.28);
}

@supports (color: color-mix(in srgb, red 50%, transparent)) {
  .fe { --fe-sel: color-mix(in srgb, var(--accent) 26%, transparent); }
}

.fe.is-readonly { background: var(--bg-sunken); }

/* The ring belongs to the whole component, not to the textarea inside it —
   otherwise the gutter reads as a separate control sitting next to a focused
   box. The textarea's own outline is removed a few rules down. */
.fe:focus-within {
  border-color: var(--accent);
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* ---------- gutter ---------- */

.fe-gutter {
  position: relative;
  flex: 0 0 auto;
  /* The ch unit has to mean a character of the code, so the gutter carries the
     code's font as well — editor.js overwrites both from the textarea. */
  width: calc(var(--fe-digits, 2) * 1ch + var(--space-3) * 2);
  overflow: hidden;
  border-right: 1px solid var(--line);
  background: var(--bg-sunken);
  font-family: var(--font-mono);
}

.fe-gutter-lines {
  /* Absolutely positioned, and that is the point: a thousand line numbers in
     the flow would stretch the flex row and drag the textarea's height with
     them. Out of flow, the numbers can be as long as the file and the
     component is still as tall as the textarea. Vertical padding and every
     font property come from editor.js, off the textarea's computed style, so
     the numbers sit on the code's baselines. */
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  padding-inline: var(--space-3);
  color: var(--fg-faint);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  text-align: right;
  white-space: pre;
  -webkit-user-select: none;
  user-select: none;
  pointer-events: none;
}

/* ---------- the two layers ---------- */

.fe-body {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
}

.fe-hl {
  position: absolute;
  inset: 0;
  z-index: 0;
  margin: 0;
  overflow: hidden;   /* it never scrolls; editor.js translates it instead */
  pointer-events: none;
}

.fe-code {
  display: block;
  color: var(--syn-plain, var(--fg));
  font-family: var(--font-mono);
  white-space: pre;
  word-break: normal;
  overflow-wrap: normal;
  /* Ligatures and kerning are the classic way for two layers in the same font
     to end up a fraction of a pixel apart. Neither layer gets them. */
  font-variant-ligatures: none;
  font-kerning: none;
  text-rendering: optimizeSpeed;
}

.fe .fe-input {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  margin: 0;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  /* The two that the whole illusion rests on. An opaque background hides the
     painted layer and visible text doubles it, so these outrank whatever the
     page says about this textarea. */
  background: transparent !important;
  color: transparent !important;
  caret-color: var(--accent);
  font-variant-ligatures: none;
  font-kerning: none;
  text-rendering: optimizeSpeed;
  white-space: pre;
  overflow: auto;
  resize: vertical;
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}

.fe .fe-input:focus,
.fe .fe-input:focus-visible {
  outline: none;   /* the ring is on .fe */
}

/* Transparent text would take the placeholder with it in some engines, so it
   is coloured explicitly — both properties, because WebKit prefers the second. */
.fe .fe-input::placeholder {
  color: var(--fg-faint);
  -webkit-text-fill-color: var(--fg-faint);
  opacity: 1;
}

/* Code must not wrap, but the placeholder should, or it is cut off on a phone. */
.fe .fe-input:placeholder-shown { white-space: pre-wrap; }

.fe .fe-input::selection { background: var(--fe-sel); }

/* ---------- degraded mode ---------- */

/* Over the size threshold editor.js stops tokenising and the textarea becomes
   an ordinary one: real text, no painted layer underneath. */
.fe.is-plain .fe-input { color: var(--fg) !important; }
.fe.is-plain .fe-hl { display: none; }

/* ---------- withheld: too large to draw at all ---------- */

/* display: none, not visibility or opacity. A textarea holding five megabytes
   is laid out whatever its box looks like, and that layout is the cost being
   refused here. The value never moves: the page still formats, copies and
   downloads it in full. */
.fe.is-withheld .fe-input,
.fe.is-withheld .fe-hl,
.fe.is-withheld .fe-gutter,
.fe.is-withheld .fe-note { display: none; }

.fe-panel {
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  padding: var(--space-5);
  text-align: center;
}

/* min-height is written by editor.js from the height the textarea had a moment
   before it was hidden, so swapping in the panel never moves the page. */
.fe.is-withheld .fe-panel { display: flex; }

.fe-panel-title {
  margin: 0;
  color: var(--fg);
  font-family: var(--font-ui);
  font-size: 15px;
  font-weight: 600;
  line-height: 1.4;
}

.fe-panel-note {
  max-width: 52ch;
  margin: 0;
  color: var(--fg-dim);
  font-family: var(--font-ui);
  font-size: 13px;
  line-height: 1.6;
}

.fe-show {
  padding: 8px 14px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-raised);
  color: var(--fg-dim);
  font-family: var(--font-ui);
  font-size: 13px;
  line-height: 1.2;
  cursor: pointer;
}

.fe-show:hover { background: var(--bg-sunken); color: var(--fg); }

.fe-show:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.fe-note {
  position: absolute;
  right: var(--space-3);
  bottom: var(--space-3);
  z-index: 2;
  margin: 0;
  padding: 3px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--bg-sunken);
  color: var(--fg-faint);
  font-family: var(--font-ui);
  font-size: 11px;
  line-height: 1.5;
  white-space: nowrap;
  pointer-events: none;
}

/* ---------- syntax ---------- */

.fe-code .sy-cm { color: var(--syn-comment, var(--fg-faint)); font-style: italic; }
.fe-code .sy-st { color: var(--syn-string, var(--ok)); }
.fe-code .sy-nu { color: var(--syn-number, var(--warn)); }
.fe-code .sy-kw { color: var(--syn-keyword, var(--accent)); }
.fe-code .sy-nm { color: var(--syn-name, var(--fg)); }
.fe-code .sy-pr { color: var(--syn-prop, var(--accent)); }
.fe-code .sy-pu { color: var(--syn-punct, var(--fg-faint)); }
.fe-code .sy-tg { color: var(--syn-tag, var(--err)); }
.fe-code .sy-at { color: var(--syn-attr, var(--warn)); }
.fe-code .sy-vl { color: var(--syn-value, var(--fg-dim)); }

/* ---------- motion ---------- */

@media (prefers-reduced-motion: no-preference) {
  .fe {
    transition:
      border-color 140ms var(--ease-out, cubic-bezier(0.2, 0.8, 0.2, 1)),
      background 140ms var(--ease-out, cubic-bezier(0.2, 0.8, 0.2, 1));
  }

  .fe-show {
    transition:
      background 140ms var(--ease-out, cubic-bezier(0.2, 0.8, 0.2, 1)),
      color 140ms var(--ease-out, cubic-bezier(0.2, 0.8, 0.2, 1)),
      border-color 140ms var(--ease-out, cubic-bezier(0.2, 0.8, 0.2, 1));
  }
}

/* ---------- narrow screens ---------- */

@media (max-width: 640px) {
  /* Twelve characters of gutter is a tenth of the screen on a phone, and the
     line number is not what anyone came for. */
  .fe-gutter { display: none; }
  .fe-note { font-size: 10px; }
  .fe-panel { padding: var(--space-4); gap: var(--space-2); }
  .fe-panel-title { font-size: 14px; }
  .fe-panel-note { font-size: 12px; }
}
