/*
 * Propshaft links every file in app/assets/stylesheets, A to Z, so this one comes first
 * and sets the order of the layers: later layers win. Colours come from theme.css and
 * fonts from fonts.css; use their variables rather than writing colours in here.
 */

@layer reset, theme, global, layout, components, utilities, states;

@layer global {
  html {
    background: var(--bg);
  }

  body {
    background: var(--bg);
    color: var(--fg);
    font-family: "Monaspace Argon", ui-monospace, Menlo, Consolas, monospace;
    /* At least 16px: iOS Safari zooms the page in when you focus a smaller input. */
    font-size: clamp(1rem, 0.8rem + 0.3vw, 1.0625rem);
    font-weight: 400;
    line-height: 1.55;
    /* calt is Monaspace's texture healing; the stylistic sets are its code ligatures. */
    font-feature-settings: "calt" 1, "liga" 1, "ss01" 1, "ss02" 1, "ss03" 1, "ss04" 1, "ss05" 1, "ss06" 1, "ss07" 1, "ss08" 1;
  }

  * {
    line-height: inherit;
  }

  ::selection {
    background: var(--selection);
    color: var(--fg-strong);
  }

  pre {
    font: inherit;
  }

  /* Lists in output (.who, .theme, help) are just lines, as on a terminal: no bullets. */
  ul {
    padding-left: 0;
    list-style: none;
  }

  /* Room for the numbers ("1."), as in .kick's list. */
  ol {
    padding-left: 3ch;
  }
}

@layer layout {
  .terminal {
    /* The boot screen covers it (see home/_boot). */
    position: relative;
    display: flex;
    flex-direction: column;
    /* --viewport-height is the part not under a phone's keyboard (see viewport_controller). */
    height: var(--viewport-height, 100dvh);
    overflow: hidden;
  }

  .terminal--body {
    flex: 1;
    overflow-x: hidden;
    overflow-y: auto;
    /* Scrolling past either end bounces the terminal, not the whole page. */
    overscroll-behavior: contain;
    overflow-wrap: anywhere;
    /* A centred column on wide screens, with the scrollbar still at the window's edge. */
    padding-block: var(--gutter);
    padding-inline: max(var(--gutter), calc((100% - var(--measure)) / 2));
    scrollbar-width: thin;
    scrollbar-color: var(--fg-faint) transparent;
  }

  /* Safari, which doesn't have scrollbar-color. */
  .terminal--body::-webkit-scrollbar { width: 8px; }
  .terminal--body::-webkit-scrollbar-track { background: transparent; }
  .terminal--body::-webkit-scrollbar-thumb { background: var(--fg-faint); border-radius: 4px; }
}

@layer components {
  .terminal--input {
    display: flex;
    align-items: baseline;
  }

  .terminal--input-form {
    flex: 1;
  }

  .terminal--input-field {
    width: 100%;
    padding: 0;
    border: none;
    outline: none;
    background: transparent;
    color: var(--fg);
    caret-color: var(--caret);
  }

  @supports (caret-shape: block) {
    .terminal--input-field {
      caret-shape: block;
    }
  }

  /* A prompt that's a textarea, one line that wraps as it gets long and grows to fit
     (see line_controller, which sets its height where field-sizing isn't supported). */
  textarea.terminal--input-field:not(.editor--field) {
    display: block;
    resize: none;
    field-sizing: content;
    min-height: 1lh;
    overflow: hidden;
  }

  /* A block cursor where you'd type, while there's no caret: where there's a mouse, while
     the prompt doesn't have focus; on a touch screen, while the keyboard's closed, as a
     prompt can have focus without it (each new one is focused by script, which doesn't
     open a phone's keyboard; see focus_controller), and it would look like any other
     text. Only while it's empty (the field's placeholder is a space, so it's shown when
     empty), and not once it's been sent (see inert_controller). */
  .terminal--input-form {
    position: relative;
  }

  @media not (pointer: coarse) {
    .terminal--input-form:not([inert], [data-sent], :focus-within):has(.terminal--input-field:placeholder-shown)::before {
      content: "";
    }
  }

  @media (pointer: coarse) {
    .terminal:not(.terminal--keyboard) .terminal--input-form:not([inert], [data-sent]):has(.terminal--input-field:placeholder-shown)::before {
      content: "";
    }
  }

  .terminal--input-form::before {
    position: absolute;
    left: 0;
    top: 50%;
    translate: 0 -50%;
    width: 1ch;
    height: 1.2em;
    pointer-events: none;
    background: var(--caret);
    animation: cursor-blink 1s step-end infinite;
  }

  /* Where the browser can't draw its caret as a block (Safari), it's a line, like the
     caret, so the cursor doesn't change shape once you start typing. */
  @supports not (caret-shape: block) {
    .terminal--input-form::before {
      width: 2px;
    }
  }

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

  /* The prompt, like guest@benjaminb.dev:~$ (see home/_prompt_label). */
  .prompt {
    margin-right: 1ch;
    white-space: nowrap;
    font-weight: 500;
  }

  .prompt--user { color: var(--prompt-user); }
  .prompt--host { color: var(--prompt-host); }
  .prompt--path { color: var(--prompt-path); }
  .prompt--talker { color: var(--prompt-talker); }
  .prompt--punctuation { color: var(--fg-muted); }

  /* The talker's invisible field, which briefly takes focus so iOS rereads the prompt's
     autocorrect settings (see autocorrect_controller). It has to be focusable, so it's
     shrunk and see-through rather than hidden, and fixed on screen so focusing it
     doesn't scroll. 16px, or iOS zooms in on it. */
  .terminal--decoy {
    position: fixed;
    bottom: 0;
    left: 0;
    width: 1px;
    height: 1px;
    padding: 0;
    border: 0;
    opacity: 0;
    font-size: 16px;
    pointer-events: none;
  }

  /* A phone's left with little room to type beside guest@benjaminb.dev:~$, so it gets ~$. */
  @media (max-width: 30rem) {
    .prompt--wide-only {
      display: none;
    }
  }

  /* Commands and files in the output that run when tapped or clicked (see
     CommandsHelper#runnable_tag). A touch screen can't hover, so there they're always
     underlined, to show they do something: fainter in lists (help, ls), where a column
     of underlines gets busy, than in a sentence. */
  .runnable {
    --underline: color-mix(in srgb, currentColor 55%, transparent);
    padding: 0;
    border: none;
    background: none;
    color: inherit;
    text-align: inherit;
    cursor: pointer;
    text-decoration: underline dotted var(--underline);
    text-underline-offset: 0.25em;
  }

  :is(li, .listing, .long-listing) > .runnable {
    --underline: color-mix(in srgb, currentColor 25%, transparent);
  }

  @media (hover: hover) {
    .runnable {
      text-decoration-color: transparent;
    }

    .runnable:hover {
      text-decoration-color: currentColor;
    }
  }

  .runnable:focus-visible {
    outline: 1px dashed var(--fg-faint);
    outline-offset: 2px;
  }

  /* A fingertip needs more than a line of text to aim at (about 44px, Apple says): each
     one reaches half a line beyond its text, without moving anything, and help's
     columns are a little further apart, with each command taking its whole cell. */
  @media (pointer: coarse) {
    .runnable {
      position: relative;
    }

    .runnable::after {
      content: "";
      position: absolute;
      inset: -0.5em -0.5ch;
    }

    .columns {
      row-gap: 0.25em;
    }

    .columns > li > .runnable {
      width: 100%;
    }
  }

  /* At the bottom of a touch screen, just above the keyboard when it's open: what you
     could be typing, and the history (see suggestions_controller), plus, while the
     keyboard's closed (see viewport_controller), a key to open it. Always there, even
     when empty, so the terminal doesn't jump about as it comes and goes. */
  .suggestions {
    display: none;
  }

  @media (pointer: coarse) {
    .suggestions {
      display: flex;
      align-items: center;
      gap: 1ch;
      min-height: 2.75rem;
      padding-inline: var(--gutter);
      border-top: 1px solid var(--selection);
    }

    /* Nothing to suggest while writing (see talker/_prompt), and the room's better used. */
    .terminal--keyboard:has(.editor--field:focus) .suggestions {
      display: none;
    }

    .terminal--keyboard .suggestions--keyboard {
      display: none;
    }
  }

  .suggestions--list {
    display: flex;
    flex: 1;
    gap: 1ch;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
  }

  .suggestions--key,
  .suggestions--item {
    flex: none;
    min-height: 2.25rem;
    padding: 0 1.25ch;
    border: 1px solid var(--fg-faint);
    border-radius: 4px;
    background: none;
    white-space: nowrap;
    cursor: pointer;
  }

  .suggestions--key {
    color: var(--fg);
  }

  .suggestions--item {
    color: var(--command);
  }

  .suggestions--key[hidden] {
    display: none;
  }

  .terminal--welcome {
    margin-bottom: 1.5em;
  }

  /* What a command printed (see home/execute_prompt and talker/_prompt), with a line's
     gap after it, so each prompt reads as one block with its own output. Talker chat
     lines aren't results, so they stay close together. */
  .terminal--result {
    margin-bottom: 1em;
  }

  /* Paragraphs in a result get a little space, but a heading stays with what it heads. */
  .terminal--result > * + * {
    margin-top: 0.4em;
  }

  .terminal--result > .heading + * {
    margin-top: 0;
  }

  /* A command you've sent, bold and bright, so it stands out from what it printed when
     you scroll back (see inert_controller). */
  .terminal--input-form:is([inert], [data-sent]) .terminal--input-field {
    color: var(--fg-strong);
    font-weight: 600;
  }

  /* In the talker, the prompt something was said at, once it's come back as a message
     (see talker_room_controller's fold), as that says it again. Only once it's inert:
     hiding it while it has focus would close a phone's keyboard. */
  .terminal--input--echoed:has(> .terminal--input-form[inert]) {
    display: none;
  }

  /* Lists of words, like `ls`: spaced apart, wrapping onto more lines as needed. */
  .listing {
    display: flex;
    flex-wrap: wrap;
    column-gap: 2ch;
  }

  /* Words in columns that line up, filling across, as many as fit: like NUTS's help,
     which listed commands in columns 10 characters wide. That's room for the longest
     command name, .unmuzzle, with the gap on top; three fit on a phone. */
  .columns {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(10ch, max-content));
    column-gap: 1ch;
  }

  .file--document { color: var(--file-document); }
  .file--code { color: var(--file-code); }
  .file--image { color: var(--file-image); }
  .file--archive { color: var(--file-archive); }
  .file--media { color: var(--file-media); }

  .file--directory {
    color: var(--file-directory);
    font-weight: 600;
  }

  /* ls -l: one row per file, in columns. Phones only get the mode, time and name, as
     all of it won't fit. */
  .long-listing {
    display: grid;
    grid-template-columns: repeat(6, max-content) minmax(0, 1fr);
    column-gap: 1ch;
  }

  .long-listing--size {
    justify-self: end;
  }

  @media (max-width: 30rem) {
    .long-listing {
      grid-template-columns: repeat(2, max-content) minmax(0, 1fr);
    }

    .long-listing--extra,
    .long-listing--size {
      display: none;
    }
  }

  /* Writing something, like a profile (see talker/_prompt): what it is, then a textarea
     that grows with what's in it, marked by a rule down its side, and save and cancel
     buttons, with their keys wherever there's a keyboard to press them on. */
  .terminal--input--editor {
    flex-direction: column;
    align-items: stretch;
  }

  .editor--field {
    display: block;
    resize: none;
    field-sizing: content;
    min-height: 4lh;
    margin-top: 0.25em;
    padding-left: 1ch;
    border-left: 1px solid var(--fg-faint);
  }

  .editor--actions {
    display: flex;
    flex-wrap: wrap;
    column-gap: 3ch;
    margin-top: 0.25em;
  }

  .editor--key {
    font: inherit;
    color: var(--fg-muted);
  }

  /* Lines and characters, out of how many there can be: over the limit, as an error. */
  .editor--count {
    margin-left: auto;
    color: var(--fg-muted);
  }

  .editor--over {
    color: var(--error);
  }

  /* A phone or tablet with nothing but its touch screen. */
  @media (pointer: coarse) and (not (any-pointer: fine)) {
    .editor--key {
      display: none;
    }
  }

  /* Once it's sent: what was written stays, as it was typed rather than bold like a sent
     command, and the buttons go. */
  .editor:is([inert], [data-sent]) .editor--field {
    min-height: 0;
    color: var(--fg);
    font-weight: inherit;
  }

  .editor:is([inert], [data-sent]) .editor--actions {
    display: none;
  }

  /* A profile, with its lines and blank lines as they were written. */
  .profile {
    white-space: pre-wrap;
  }

  /* Labelled values, like .examine's, the labels in a column of their own. */
  .fields {
    display: grid;
    grid-template-columns: max-content minmax(0, 1fr);
    column-gap: 1ch;
  }

  .fields dt {
    color: var(--fg-muted);
  }

  /* neofetch: its logo beside what it says. */
  .neofetch {
    display: flex;
    flex-wrap: wrap;
    align-items: start;
    gap: 1em 3ch;
  }

  .neofetch--logo {
    --columns: 8;
  }

  /* On a phone, still beside the rest (above it, it's a smudge), which wraps instead. */
  @media (max-width: 30rem) {
    .neofetch {
      flex-wrap: nowrap;
      gap: 1em 2ch;
    }

    .neofetch > * {
      min-width: 0;
    }
  }

  .neofetch--key {
    color: var(--magenta);
    font-weight: 600;
  }

  /* Two rows of blocks, close enough to touch, like neofetch's. */
  .neofetch--colours {
    margin-top: 1em;
    line-height: 1.15;
  }

  /* The visitor counter in the welcome, like an old hit counter: a box per digit. */
  .odometer {
    display: inline-flex;
    gap: 2px;
  }

  .odometer > span {
    padding-inline: 0.2ch;
    border-radius: 2px;
    background: var(--selection);
    color: var(--fg-strong);
  }

  /* What `cat` shows: keep the file's own line breaks and spacing. */
  .terminal--file {
    white-space: pre-wrap;
  }

  /* ASCII art titles, shrunk to fit narrow screens: --columns is how wide the art is,
     and each character is 0.62em wide. Block characters only join up at a line
     height around the font's own (1.145). */
  .banner {
    --columns: 74;
    font-size: min(1em, calc((100vw - 2 * var(--gutter)) / (var(--columns) * 0.62)));
    line-height: 1.15;
    white-space: pre;
    width: fit-content;
    color: transparent;
    background-image: linear-gradient(90deg, var(--magenta), var(--blue), var(--cyan), var(--blue), var(--magenta));
    background-size: 200% 100%;
    background-clip: text;
    -webkit-background-clip: text;
    animation: banner-shimmer 12s linear infinite;
  }

  .banner--talker {
    --columns: 64;
    background-image: linear-gradient(90deg, var(--red), var(--orange), var(--yellow), var(--orange), var(--red));
  }

  @keyframes banner-shimmer {
    to { background-position: -200% 0; }
  }

  .banner + .tagline {
    margin-top: 0.5em;
  }

  .tagline {
    font-family: "Monaspace Radon", "Monaspace Argon", monospace;
    font-style: oblique 11deg;
    color: var(--fg-muted);
  }

  .heading {
    font-family: "Monaspace Xenon", "Monaspace Argon", monospace;
    font-weight: 600;
    color: var(--fg-strong);
  }

  /* Talker lines (see talker/_broadcast). */
  .username {
    font-weight: 600;
  }

  .username--0 { color: var(--username-0); }
  .username--1 { color: var(--username-1); }
  .username--2 { color: var(--username-2); }
  .username--3 { color: var(--username-3); }
  .username--4 { color: var(--username-4); }
  .username--5 { color: var(--username-5); }
  .username--6 { color: var(--username-6); }
  .username--7 { color: var(--username-7); }

  /* Talker messages, by Talker::MessageType. */
  .talker--action,
  .talker--notice,
  .talker--status {
    font-family: "Monaspace Radon", "Monaspace Argon", monospace;
  }

  .talker--action {
    font-style: oblique 11deg;
  }

  /* A status is a notice in someone's own words (like .afk's message). */
  .talker--notice,
  .talker--status {
    color: var(--fg-muted);
  }

  .talker--notice::before,
  .talker--status::before {
    content: "» ";
    color: var(--fg-faint);
  }

  .talker--private {
    color: var(--whisper);
  }

  /* The boot screen, over the whole terminal until it's done (see boot_controller). */
  .boot {
    position: absolute;
    inset: 0;
    overflow: hidden;
    padding-block: var(--gutter);
    padding-inline: max(var(--gutter), calc((100% - var(--measure)) / 2));
    background: var(--bg);
    transition: opacity 300ms ease-out;
    /* Out of the way by itself if its JavaScript never runs. */
    animation: boot-give-up 0s 8s forwards;
  }

  @keyframes boot-give-up {
    to { visibility: hidden; }
  }

  /* Keeps the spaces in [  OK  ]. */
  .boot > p {
    white-space: pre-wrap;
  }

  .boot--done {
    opacity: 0;
  }

  .boot--title {
    color: var(--fg-strong);
    font-weight: 600;
    margin-bottom: 0.5em;
  }

  .boot--ok {
    color: var(--green);
  }

  /* shutdown and reboot switch the terminal off like a CRT (see power_off_controller):
     the picture squashes into a bright line, which shrinks to a dot and fades away. */
  .terminal--off {
    animation: crt-squash 250ms ease-in forwards;
  }

  @keyframes crt-squash {
    to {
      scale: 1 0.004;
      opacity: 0;
      filter: brightness(3);
    }
  }

  .power-line {
    position: fixed;
    inset-inline: 0;
    top: 50%;
    height: 2px;
    pointer-events: none;
    background: var(--fg-strong);
    box-shadow: 0 0 12px 2px var(--fg);
    opacity: 0;
    animation: crt-line 900ms ease-in forwards;
  }

  @keyframes crt-line {
    20% { opacity: 1; scale: 1 1; }
    60% { opacity: 1; scale: 0.004 1; }
    to { opacity: 0; scale: 0.004 1; }
  }

  .power-button {
    position: fixed;
    inset-inline: 0;
    bottom: 30%;
    margin-inline: auto;
    width: fit-content;
    padding: 0.5em 1ch;
    border: none;
    background: none;
    color: var(--fg-muted);
    font: inherit;
    cursor: pointer;
    animation: power-button-in 600ms ease-out;
  }

  @keyframes power-button-in {
    from { opacity: 0; }
  }

  .power-button:focus-visible {
    outline: 1px dashed var(--fg-faint);
  }
}

@layer utilities {
  .command { color: var(--command); }
  .highlight { color: var(--highlight); }
  .muted { color: var(--fg-muted); }

  /* Keeps things together on one line, like a word and the full stop after it. */
  .nowrap { white-space: nowrap; }

  .error {
    color: var(--error);
    font-weight: 600;
  }

  /* Colour codes in what people type (see the Printcodes model): ~F? is a colour, ~C?
     its bright version, ~B? a background. */
  .printcode--fk { color: var(--black); }
  .printcode--fr { color: var(--red); }
  .printcode--fg { color: var(--green); }
  .printcode--fy { color: var(--yellow); }
  .printcode--fb { color: var(--blue); }
  .printcode--fm { color: var(--magenta); }
  .printcode--ft { color: var(--cyan); }
  .printcode--fw { color: var(--white); }

  .printcode--ck { color: var(--bright-black); }
  .printcode--cr { color: var(--bright-red); }
  .printcode--cg { color: var(--bright-green); }
  .printcode--cy { color: var(--bright-yellow); }
  .printcode--cb { color: var(--bright-blue); }
  .printcode--cm { color: var(--bright-magenta); }
  .printcode--ct { color: var(--bright-cyan); }
  .printcode--cw { color: var(--bright-white); }

  .printcode--bk { background: var(--bg-black); }
  .printcode--br { background: var(--bg-red); }
  .printcode--bg { background: var(--bg-green); }
  .printcode--by { background: var(--bg-yellow); }
  .printcode--bb { background: var(--bg-blue); }
  .printcode--bm { background: var(--bg-magenta); }
  .printcode--bt { background: var(--bg-cyan); }
  .printcode--bw { background: var(--bg-white); }

  .printcode--bold { font-weight: 700; }
  .printcode--underline { text-decoration: underline; }
  .printcode--blink { animation: printcode-blink 1s step-end infinite; }

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

/* The CRT the phosphor themes are on: text glows in its own colour, with scanlines and
   a darker edge over the top (which clicks and selecting go straight through), and a
   faint refresh bar rolling down the screen. */
@layer components {
  :root:is([data-theme="amber"], [data-theme="green"]) {
    .terminal {
      position: relative;
      text-shadow: 0 0 1px currentColor, 0 0 6px color-mix(in srgb, currentColor 45%, transparent);
    }

    .terminal--input-field {
      text-shadow: inherit;
    }

    .banner {
      filter: drop-shadow(0 0 6px color-mix(in srgb, var(--fg) 45%, transparent));
    }

    /* New output lands bright, with a wider glow, and settles as the phosphor cools.
       Only here: on the other themes output just appears, as in any terminal. */
    .terminal--entry {
      animation: phosphor-flash 400ms ease-out;
    }

    .terminal::after {
      content: "";
      position: absolute;
      inset: 0;
      z-index: 1;
      pointer-events: none;
      background:
        repeating-linear-gradient(to bottom, transparent 0 2px, rgb(0 0 0 / 0.22) 2px 3px),
        radial-gradient(ellipse at center, transparent 55%, rgb(0 0 0 / 0.55) 100%);
    }

    .terminal::before {
      content: "";
      position: absolute;
      inset-inline: 0;
      top: 0;
      height: 20vh;
      z-index: 1;
      pointer-events: none;
      background: linear-gradient(to bottom, transparent, color-mix(in srgb, var(--fg) 6%, transparent), transparent);
      animation: crt-refresh 8s linear infinite;
    }
  }

  @keyframes crt-refresh {
    from { translate: 0 -20vh; }
    to { translate: 0 100vh; }
  }

  @keyframes phosphor-flash {
    from {
      filter: brightness(1.6);
      text-shadow: 0 0 1px currentColor, 0 0 14px currentColor;
    }
  }
}

@media (prefers-reduced-motion: reduce) {
  .printcode--blink,
  .terminal--input-form::before,
  .banner,
  .terminal--entry,
  .power-button {
    animation: none;
  }

  .terminal::before,
  .power-line,
  .boot {
    display: none;
  }

  /* Just off, with no collapsing picture. */
  .terminal--off {
    animation: none;
    opacity: 0;
  }
}
