:root {
    color-scheme: dark;
    --bg: #1e2126;
    --panel: #262a31;
    --border: #3a3f47;
    --border-strong: #7a818c;
    --text: #e4e7eb;
    --accent: #1a7f37;
    /* Text and glyphs on an accent fill. */
    --accent-ink: #111;
    /* The code wells: the editor's pre and code, the output pane, a dialog input. */
    --code-bg: #14161a;
    /* The [code] regions in the output pane. Warm amber, distinct from the
       green accent so the two are not read as the same kind of thing. */
    --code-ink: #e9bd5e;
    /* A copy failure and a rejected link URL. */
    --danger: #e5534b;
    --muted: #9aa1ab;
    --mono: "JetBrains Mono", "Fira Code", Consolas, monospace;
}

/*
 * The dark palette is the default, and the light one is the same set of
 * roles for a reader whose system asks for light. Both are declared here so
 * nothing below this block names a colour that has to change with them.
 */
@media (prefers-color-scheme: light) {
    :root {
        color-scheme: light;
        --bg: #f6f7f9;
        --panel: #ffffff;
        --border: #dcdfe4;
        --border-strong: #9aa1ab;
        --text: #1c1f24;
        --accent: #1a7f37;
        --accent-ink: #ffffff;
        --code-bg: #f0f2f5;
        --code-ink: #8a5a00;
        --danger: #b3261e;
        --muted: #5b636d;
    }

    .dropdown-menu {
        box-shadow: 0 6px 16px rgb(0 0 0 / 18%);
    }
}

* {
    box-sizing: border-box;
}

/*
 * The chrome is not text to read or copy: the page header and its subtext,
 * the formatting toolbar and the menus it opens, the output pane's head, the
 * pane switcher, and the dialog headings and buttons. Without this, a drag
 * that starts on a label paints a selection highlight over the control being
 * pressed, which reads as a stuck state. The editor and the output keep the
 * default, since selecting them is how the entry leaves the app.
 *
 * -webkit- is for Safari before 16.4, which only knows the prefixed form.
 */
header,
.toolbar,
.dropdown-menu,
.output-head,
.view-tabs,
.dialog-actions,
.link-form h2,
.shortcut-form h2 {
    -webkit-user-select: none;
    user-select: none;
}

body {
    margin: 0;
    font-family:
        system-ui,
        -apple-system,
        "Segoe UI",
        sans-serif;
    background: var(--bg);
    color: var(--text);
    height: 100vh;
    height: 100dvh;
    display: flex;
    flex-direction: column;
}

header {
    padding: 12px 20px;
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
    row-gap: 8px;
}

header h1 {
    margin: 0;
    font-size: 17px;
    font-weight: 600;
}

header span {
    color: var(--muted);
    font-size: 13px;
}

/*
 * The actions keep to the right edge and drop to their own line once the
 * title and subtitle have taken the width, rather than squeezing them.
 */
.header-actions {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-left: auto;
}

/*
 * A refusal to store the entry is the one thing in the header the user has
 * to act on, so it carries the danger colour rather than the muted grey.
 */
.save-state {
    color: var(--danger);
    font-size: 13px;
}

.header-actions button {
    padding: 4px 10px;
    border: 1px solid var(--border-strong);
    border-radius: 4px;
    background: transparent;
    color: var(--text);
    font-family: inherit;
    font-size: 13px;
    cursor: pointer;
    transition:
        border-color 120ms ease,
        color 120ms ease;
}

.header-actions button:hover {
    border-color: var(--accent);
    color: var(--accent);
}

main {
    flex: 1;
    display: grid;
    grid-template-columns: minmax(220px, var(--editor-w, 1fr)) 6px minmax(220px, 1fr);
    min-height: 0;
}

.pane {
    display: flex;
    flex-direction: column;
    min-height: 0;
    /*
     * A column is 1fr, and 1fr's automatic minimum is the item's min-content
     * width: one unbreakable line in either pane would otherwise widen the
     * column and push the whole layout off screen. Zeroed, the panes keep
     * their half and the long line scrolls inside its own well instead.
     */
    min-width: 0;
}

/*
 * The divider is the only separator between the two panes. It is a wide,
 * invisible grab strip whose centre line is the visual boundary, so it reads
 * as one thin line but gives the pointer a target worth catching.
 */
.split-divider {
    position: relative;
    cursor: col-resize;
    background: var(--bg);
    touch-action: none;
    /* Fades away in step with the column collapse, and the delayed
       visibility keeps it out of the tab ring once it has gone. */
    transition:
        opacity 240ms ease,
        visibility 240ms ease;
}

.split-divider::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 50%;
    width: 1px;
    transform: translateX(-50%);
    background: var(--border);
}

.split-divider:hover::after,
.split-divider.is-dragging::after {
    width: 2px;
    background: var(--accent);
}

/*
 * Collapsing the output is a desktop gesture. The narrow layout already has
 * its own pane switcher, so the divider and the collapse controls stay out
 * of it and the collapse class is scoped to the wide layout only.
 *
 * The class holds the final state and fades the pane. The column change that
 * moves the divider is animated from JavaScript with a matching duration, so
 * the fade and the slide leave and return together. visibility is delayed
 * rather than instant: on the way out it flips at the end, once the fade has
 * finished, and on the way back it flips at once so the pane is reachable the
 * moment it starts to reappear.
 */
@media (min-width: 801px) {
    main.output-collapsed {
        grid-template-columns: minmax(220px, 1fr) 0px 0px;
    }

    main.output-collapsed #outputPane,
    main.output-collapsed .split-divider {
        opacity: 0;
        visibility: hidden;
        overflow: hidden;
        pointer-events: none;
    }
}

@media (max-width: 800px) {
    .split-divider,
    #hideOutputBtn,
    #showOutputBtn {
        display: none;
    }
}

.toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--border);
    background: var(--panel);
}

.toolbar button {
    min-width: 30px;
    padding: 4px 9px;
    border: 1px solid var(--border-strong);
    border-radius: 4px;
    background: transparent;
    color: var(--text);
    font-family: inherit;
    font-size: 13px;
    cursor: pointer;
    transition:
        background-color 120ms ease,
        border-color 120ms ease,
        color 120ms ease,
        box-shadow 120ms ease;
}

.toolbar button:hover {
    border-color: var(--accent);
    color: var(--accent);
}

/*
 * Clear, Restore, Undo and Redo are the controls whose state the user cannot
 * infer from the caret: nothing to clear, nothing to restore, nothing to undo.
 * They stay muted rather than hoverable.
 */
.toolbar button:disabled {
    border-color: var(--border);
    color: var(--muted);
    cursor: default;
}

.toolbar button:disabled:hover {
    border-color: var(--border);
    color: var(--muted);
}

.toolbar button[aria-pressed="true"] {
    border-color: var(--accent);
    /* The accent at a lower alpha, taken from the accent itself so a colour
       change reaches the pressed states too. */
    background: rgb(from var(--accent) r g b / 0.18);
    color: var(--accent);
    box-shadow: inset 0 0 0 1px var(--accent);
}

.toolbar button[aria-pressed="true"]:hover {
    background: rgb(from var(--accent) r g b / 0.25);
}

.toolbar .sep {
    width: 1px;
    margin: 2px 4px;
    background: var(--border);
}

/*
 * Sits between the toolbar and the editor, where a paste that lost something
 * is closest to the work. The accent edge marks it as a report rather than a
 * warning about the entry itself.
 */
.notice {
    margin: 0;
    padding: 8px 12px;
    border-bottom: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    background: var(--panel);
    color: var(--text);
    font-size: 13px;
}

.dropdown {
    position: relative;
}

.dropdown-toggle {
    display: flex;
    gap: 4px;
    align-items: center;
    justify-content: center;
}

/*
 * Drawn with borders rather than the "▾" glyph. The glyph's ink sits below the
 * centre of its line box (the font reserves descent space the triangle never
 * uses), so it read low in the button on every platform. A border triangle is
 * a plain 5x4 box, which flex centres on both axes, and it follows the text
 * colour through currentColor, so the muted and accent states above still hold.
 */
.dropdown-toggle::after {
    width: 0;
    height: 0;
    border-top: 4px solid currentColor;
    border-right: 2.5px solid transparent;
    border-left: 2.5px solid transparent;
    color: var(--muted);
    content: "";
}

/*
 * The label already names the level in use, so the toggle carries the accent
 * like a pressed format button while the caret is on a heading.
 */
.dropdown-toggle.is-active {
    border-color: var(--accent);
    color: var(--accent);
}

.dropdown-menu {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    z-index: 10;
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 100%;
    padding: 4px;
    border: 1px solid var(--border-strong);
    border-radius: 4px;
    background: var(--panel);
    box-shadow: 0 6px 16px rgb(0 0 0 / 45%);
}

/* The rule above outranks the user agent's display:none for [hidden]. */
.dropdown-menu[hidden] {
    display: none;
}

.dropdown-menu button {
    min-width: 0;
    text-align: left;
}

.dropdown-menu button[aria-checked="true"] {
    border-color: var(--accent);
    background: rgb(from var(--accent) r g b / 0.18);
    color: var(--accent);
}

#editor {
    position: relative;
    flex: 1;
    overflow-y: auto;
    padding: 16px 20px;
    outline: none;
    font-size: 14px;
    line-height: 1.55;
    tab-size: 4;
    white-space: pre-wrap;
    /*
     * A long unbroken run, a URL or a base64 blob, has no soft wrap
     * opportunity inside it, so without this its width becomes the pane's
     * minimum and the whole two-column layout is pushed off screen. The
     * output pane already breaks anywhere; the editor has to agree.
     */
    overflow-wrap: anywhere;
}

/*
 * Positioned so the placeholder occupies the caret's line without adding a
 * box that would push the first paragraph down.
 */
#editor.is-empty::before {
    /* The hint and what a screen reader announces are the same attribute. */
    content: attr(aria-placeholder);
    position: absolute;
    color: var(--muted);
    pointer-events: none;
    /* Matches the output pane's placeholder so both empty states read alike. */
    font-family: var(--mono);
    font-size: 13px;
}

/*
 * The placeholder sits at the content edge, so a first child carrying a
 * default top margin would otherwise start a line below it.
 */
#editor > :first-child {
    margin-top: 0;
}

#editor:focus {
    background: rgb(from var(--accent) r g b / 0.02);
    outline: 1px solid var(--accent);
    outline-offset: -1px;
}

#editor blockquote {
    margin: 8px 0;
    padding: 4px 12px;
    border-left: 3px solid var(--accent);
    color: var(--muted);
}

#editor pre {
    overflow-x: auto;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--code-bg);
    font-family: var(--mono);
    font-size: 13px;
}

#editor code {
    padding: 1px 5px;
    border: 1px solid var(--border);
    border-radius: 4px;
    background: var(--code-bg);
    font-family: var(--mono);
    font-size: 13px;
    font-weight: 700;
}

#editor pre code {
    padding: 0;
    border: none;
    background: none;
    font-weight: 400;
}

#editor a {
    color: var(--accent);
}

.output-head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--border);
    background: var(--panel);
    color: var(--muted);
    font-size: 13px;
}

/*
 * The feed size sits in the pane's bottom corner, out of the line of sight
 * while typing: small, dimmed, and out of the code well's flow. The box is
 * packed onto its own text, so the hover that raises it covers the numbers
 * and nothing else. An over-limit entry is the exception, since a paste that
 * gets cut is worth interrupting for.
 *
 * The row is always in the layout and only the text fades in, so the well
 * above it keeps its height and the first keystroke does not move anything.
 */
.output-stats {
    /* Fit-content plus the auto margin parks the text against the pane's
       right edge, at the same inset the code well uses. The vertical margin
       is the row's spacing, so the box itself is only ever the text. */
    width: fit-content;
    margin: 4px 20px 6px auto;
    padding: 0;
    color: var(--muted);
    font-size: 11px;
    text-align: right;
    /* Digits keep one width, so the corner does not shuffle as the entry grows. */
    font-variant-numeric: tabular-nums;
    /*
     * An empty box has no line to give it height, which would leave the row
     * shorter until the first digit lands and move the well above it. One
     * line's height is held instead, filled or not.
     */
    line-height: 15px;
    min-height: 1lh;
    opacity: 0;
    transition: opacity 160ms ease;
}

/* An empty entry has nothing to count, so the digits simply stay invisible. */
.output-stats:not(:empty) {
    opacity: 0.5;
}

.output-stats:hover {
    opacity: 1;
}

.output-stats.is-over {
    color: var(--danger);
    font-weight: 600;
    opacity: 1;
}

.output-actions {
    display: flex;
    gap: 6px;
    margin-left: auto;
}

.output-actions button {
    padding: 5px 12px;
    border: 1px solid transparent;
    border-radius: 4px;
    font-family: inherit;
    font-size: 13px;
    cursor: pointer;
}

/*
 * Copy is a split control: the two halves share the accent fill and read as
 * one button. The caret half opens the menu holding the one preference that
 * changes what a copy does. The toggle is scoped through .copy-dropdown so it
 * outranks the panel and toolbar button rules, which would otherwise square
 * its corners and give it their own border and background.
 */
.copy-dropdown {
    display: flex;
}

/*
 * The flex row above outranks the user agent's display:none for [hidden], so
 * the toolbar copy control would sit beside the output one instead of
 * replacing it. This keeps exactly one Copy on screen: in the output head
 * while the pane is open, and in the toolbar once it is collapsed.
 */
.copy-dropdown[hidden] {
    display: none;
}

#copyBtn,
#toolbarCopyBtn {
    border-radius: 4px 0 0 4px;
}

.copy-dropdown .copy-toggle {
    padding: 5px 8px;
    border: none;
    border-left: 1px solid rgb(0 0 0 / 25%);
    /* Matches the divider on the left so the content box, and with it the
       centred caret, sits on the button's true centre rather than half a pixel
       to the right. Invisible, and the divider still reads as one line. */
    border-right: 1px solid transparent;
    border-radius: 0 4px 4px 0;
    background: var(--accent);
    color: var(--accent-ink);
}

.copy-dropdown .copy-toggle::after {
    color: inherit;
}

.copy-dropdown .copy-toggle:hover {
    border-left-color: rgb(0 0 0 / 25%);
    color: var(--accent-ink);
    filter: brightness(1.1);
}

/* The caret mirrors the Copy half, so the split stays one control in every state. */
#copyBtn:disabled + .copy-toggle,
#copyBtn:disabled + .copy-toggle:hover,
#toolbarCopyBtn:disabled + .copy-toggle,
#toolbarCopyBtn:disabled + .copy-toggle:hover {
    border-left-color: transparent;
    background: transparent;
    box-shadow: inset 0 0 0 1px var(--border);
    color: var(--muted);
    filter: none;
}

#copyBtn.error + .copy-toggle,
#toolbarCopyBtn.error + .copy-toggle {
    background: var(--danger);
    color: #fff;
}

/* Hide is secondary, so it takes the outlined treatment the toolbar buttons use
   rather than falling back to the user agent's light button fill. */
#hideOutputBtn {
    border-color: var(--border-strong);
    background: transparent;
    color: var(--text);
}

#hideOutputBtn:hover {
    border-color: var(--accent);
    color: var(--accent);
}

/*
 * The menu hangs off a right-aligned control, so it opens leftward from the
 * button's edge. Its single item carries its own styling rather than borrowing
 * the toolbar buttons' rules, since the menu's other home is the output pane.
 */
.copy-menu {
    left: auto;
    right: 0;
    /* Sized by its one item rather than a fixed 12rem, which left the panel
       about twice the width of the Copy control it hangs from. max-content is
       what does the sizing: an absolutely positioned box shrinks to fit its
       containing block, and the control here is narrower than the item, so
       without it the label wraps onto four lines. The item's text ("Clear
       after copy") is the floor, so the panel stays wider than the button and
       cannot be made exactly the same size. */
    width: max-content;
}

.copy-menu button {
    display: flex;
    gap: 6px;
    align-items: center;
    padding: 5px 10px;
    border: 1px solid transparent;
    border-radius: 4px;
    background: transparent;
    color: var(--text);
    font-family: inherit;
    font-size: 13px;
    text-align: left;
    cursor: pointer;
}

/* The checkmark is the state, so the item itself is not filled when checked. */
.copy-menu button[aria-checked="true"] {
    border-color: transparent;
    background: transparent;
    color: var(--text);
}

.copy-menu button::before {
    width: 1em;
    color: var(--accent);
    content: "✓";
    visibility: hidden;
}

.copy-menu button[aria-checked="true"]::before {
    visibility: visible;
}

.copy-menu button:hover {
    background: rgb(from var(--accent) r g b / 0.18);
    color: var(--accent);
}

#copyBtn,
#toolbarCopyBtn {
    /* Holds the resting label and the "Copied!" flash at one width.
       "Copy failed" is wider than this, so that one flash grows the
       button and shifts the caret half beside it. */
    min-width: 4.5rem;
    border: none;
    background: var(--accent);
    color: var(--accent-ink);
    font-weight: 600;
}

#copyBtn:hover,
#toolbarCopyBtn:hover {
    filter: brightness(1.1);
}

#copyBtn:disabled,
#copyBtn:disabled:hover,
#toolbarCopyBtn:disabled,
#toolbarCopyBtn:disabled:hover {
    background: transparent;
    box-shadow: inset 0 0 0 1px var(--border);
    color: var(--muted);
    cursor: default;
    filter: none;
}

#copyBtn.error,
#toolbarCopyBtn.error {
    background: var(--danger);
    color: #fff;
}

/*
 * The pane carries the well's colour, so the code and the strip the stats
 * footer sits on read as one surface. The pane header keeps its own colour.
 */
#outputPane {
    background: var(--code-bg);
    /* The same fade and delayed visibility the divider uses, so the pane and
       its divider leave and return together. */
    transition:
        opacity 240ms ease,
        visibility 240ms ease;
}

#output {
    flex: 1;
    overflow: auto;
    margin: 0;
    padding: 16px 20px;
    background: var(--code-bg);
    font-family: var(--mono);
    font-size: 13px;
    line-height: 1.5;
    /* The editor inserts real tabs in code blocks; match their width here so
       the two panes agree on how far a tab reaches. */
    tab-size: 4;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/*
 * The hint is a pseudo-element, so the pane still reads as empty text: Copy
 * has nothing to take and the stats footer has no digits to draw.
 */
#output.is-empty::before {
    content: attr(data-empty);
    color: var(--muted);
}

/* Just the delimiters and what they hold, so the rest of the entry reads as
   plain prose. */
#output .code-region {
    color: var(--code-ink);
}

/* ---------- View tabs ---------- */

/*
 * The tabs exist for the narrow layout and are hidden everywhere else, where
 * both panes are on screen at once and there is nothing to choose between.
 */
.view-tabs {
    display: none;
    gap: 4px;
    padding: 8px 12px;
    border-bottom: 1px solid var(--border);
    background: var(--panel);
}

.view-tabs button {
    flex: 1;
    padding: 5px 12px;
    border: 1px solid var(--border-strong);
    border-radius: 4px;
    background: transparent;
    color: var(--text);
    font-family: inherit;
    font-size: 13px;
    cursor: pointer;
}

.view-tabs button[aria-current="true"] {
    border-color: var(--accent);
    background: rgb(from var(--accent) r g b / 0.18);
    color: var(--accent);
}

/*
 * One pane at a time. A phone keyboard leaves too little of a 50/50 split to
 * show either pane, so the other one is hidden rather than shrunk.
 */
@media (max-width: 800px) {
    .view-tabs {
        display: flex;
    }

    main {
        grid-template-columns: 1fr;
        grid-template-rows: 1fr;
    }

    main[data-view="editor"] #outputPane,
    main[data-view="output"] #editorPane {
        display: none;
    }

    .pane {
        border-right: none;
        border-bottom: none;
    }
}

/* ---------- Dialogs ---------- */

/*
 * The dialog box itself stays transparent and unpadded, so every click that
 * lands on it rather than on the form is a click on the backdrop.
 */
.link-dialog,
.shortcut-dialog {
    padding: 0;
    border: none;
    background: transparent;
}

.link-dialog::backdrop,
.shortcut-dialog::backdrop {
    background: rgb(0 0 0 / 55%);
}

.link-form,
.shortcut-form {
    display: flex;
    flex-direction: column;
    gap: 6px;
    width: 22rem;
    max-width: 100%;
    padding: 16px;
    border: 1px solid var(--border-strong);
    border-radius: 6px;
    background: var(--panel);
    box-shadow: 0 12px 32px rgb(0 0 0 / 50%);
}

/*
 * The URL row is a direct child of the form, so the form's stretch reaches
 * it. The text row is nested, and its input would otherwise fall back to the
 * intrinsic width of an input box, so it stretches its own children.
 */
.link-text {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

/* The rule above outranks the user agent's display:none for [hidden]. */
.link-text[hidden] {
    display: none;
}

.link-form h2,
.shortcut-form h2 {
    margin: 0 0 6px;
    font-size: 15px;
}

.link-form label {
    color: var(--muted);
    font-size: 12px;
}

.link-form input {
    padding: 6px 8px;
    border: 1px solid var(--border-strong);
    border-radius: 4px;
    background: var(--code-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 13px;
}

/*
 * The border colour alone is what hover does, so focus adds a ring: a
 * keyboard user has to be able to see where the typing will land.
 */
.link-form input:focus {
    border-color: var(--accent);
    outline: 2px solid var(--accent);
    outline-offset: 1px;
}

.link-error {
    margin: 0;
    color: var(--danger);
    font-size: 12px;
}

.dialog-actions {
    display: flex;
    justify-content: flex-end;
    gap: 6px;
    margin-top: 8px;
}

.dialog-actions button {
    padding: 5px 12px;
    border: 1px solid var(--border-strong);
    border-radius: 4px;
    background: transparent;
    color: var(--text);
    font-family: inherit;
    font-size: 13px;
    cursor: pointer;
}

.dialog-actions button:hover {
    border-color: var(--accent);
    color: var(--accent);
}

.dialog-actions .primary {
    border-color: var(--accent);
    background: var(--accent);
    color: var(--accent-ink);
    font-weight: 600;
}

.dialog-actions .primary:hover {
    color: var(--accent-ink);
    filter: brightness(1.1);
}

/*
 * Remove link is destructive and sits apart from Cancel and Insert, which
 * stay together on the right.
 */
.dialog-actions .danger {
    margin-right: auto;
    border-color: var(--danger);
    color: var(--danger);
}

.dialog-actions .danger:hover {
    border-color: var(--danger);
    background: rgb(from var(--danger) r g b / 0.12);
    color: var(--danger);
}

/* The rule above outranks the user agent's display:none for [hidden]. */
.dialog-actions button[hidden] {
    display: none;
}

/* ---------- Keyboard shortcut reference ---------- */

.shortcut-form {
    /* Wider than the link dialog: a chord and its name sit side by side. */
    width: 26rem;
}

.shortcut-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin: 0;
    padding: 0;
    list-style: none;
}

/* A fixed first column keeps the chords in one line down the list. */
.shortcut-list li {
    display: grid;
    grid-template-columns: 7.5rem 1fr;
    gap: 12px;
    align-items: baseline;
}

.shortcut-keys {
    color: var(--accent);
    font-family: var(--mono);
    font-size: 12px;
    white-space: nowrap;
}

.shortcut-list li span + span {
    font-size: 13px;
}

.shortcut-note {
    margin: 6px 0 0;
    color: var(--muted);
    font-size: 12px;
}
