/* addshit.com - dark by default, mobile first, no framework. */

:root {
    --bg: #0d0d0d;
    --bg-raised: #161616;
    --border: #262626;
    --accent: #f5a623;
    --text: #ffffff;
    --muted: #8b8b8b;
    --danger: #ff5c5c;
    --mono: 'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    --measure: 68ch;   /* comfortable line length for prose */
    --sans: 'Archivo', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font-family: var(--sans);
    font-size: 16px;
    line-height: 1.6;
}

a {
    color: var(--accent);
    text-underline-offset: 2px;
    transition: color 150ms ease, text-underline-offset 150ms ease;
}

a:hover { color: #ffb63d; }

/* Content links nudge their underline down rather than gaining or losing
   one, so the line of text never reflows. */
main a:not(.btn):hover,
.footer a:hover { text-underline-offset: 3px; }

code { font-family: var(--mono); }

/* Inline code in prose: reads as data without spending the accent on it. */
p > code,
li > code {
    padding: 1px 5px;
    border-radius: 4px;
    background: var(--bg-raised);
    border: 1px solid var(--border);
    font-size: 0.875em;
}

.wrap {
    width: 100%;
    max-width: 1040px;
    margin: 0 auto;
    padding: 0 20px;
}

.muted { color: var(--muted); }
.small { font-size: 13px; }

/* Top bar ---------------------------------------------------------- */

.topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px 16px;
    max-width: 1040px;
    margin: 0 auto;
    padding: 20px;
}

.logo {
    font-family: var(--mono);
    font-weight: 700;
    text-decoration: none;
    color: var(--text);
    letter-spacing: -0.5px;
}

/* a:hover is more specific than .logo, so without this the whole wordmark
   picks up the link hover colour and add/.com go orange with it. The accent
   belongs to "shit" and the dot, and to nothing else. */
.logo:hover { color: var(--text); }

.logo .dot { color: var(--accent); }

/* Hover scramble. logo.js rebuilds the wordmark into one span per character
   and wraps the four in the middle, so nothing here needs the markup to
   change. Every character is a monospace cell either way, which is why the
   word cannot change width while it churns and the topbar cannot reflow. */
.logo .ch { display: inline-block; }

/* The accent half of the wordmark. Always this colour, scramble or no
   scramble, JavaScript or none: it is the brand, not a hover state. */
.logo .shit {
    position: relative;
    display: inline-block;
    color: var(--accent);
    transition: text-shadow 200ms ease;
}

/* The payoff is the glow and the swell now that the colour is permanent. */
.logo.is-done .shit {
    text-shadow: 0 0 14px rgba(245, 166, 35, 0.55);
    animation: logo-pop 340ms cubic-bezier(0.3, 1.6, 0.5, 1);
}

@keyframes logo-pop {
    0%   { transform: scale(1); }
    45%  { transform: scale(1.16); }
    100% { transform: scale(1); }
}

.topbar nav a {
    margin-left: 14px;
    font-size: 13px;
    text-decoration: none;
    color: var(--muted);
}

.topbar nav a:hover { color: var(--accent); }

.topbar nav a { transition: color 150ms ease; }

/* Hero ------------------------------------------------------------- */

.hero { padding: 32px 20px 8px; }

h1 {
    font-size: 34px;
    line-height: 1.1;
    letter-spacing: -1.2px;
    font-weight: 700;
    margin: 0 0 14px;
}

h2 {
    font-size: 19px;
    letter-spacing: -0.4px;
    margin: 0 0 10px;
}

/* Brighter than .muted: this is the one line that has to be read. */
.sub {
    margin: 0;
    max-width: 46ch;
    font-size: 17px;
    color: #c9c9c9;
}

.hint {
    font-size: 13px;
    color: var(--muted);
}

/* Prose measure ---------------------------------------------------- */

/* The container is wide so code and captured data have room. Paragraphs
   would become hard to scan at that width, so they stay capped. */
main p,
main li,
.sample figcaption,
.footer p {
    max-width: var(--measure);
}

/* Steps ------------------------------------------------------------ */

.steps {
    list-style: none;
    display: grid;
    grid-template-columns: 1fr;
    gap: 28px;
    margin: 32px 0 36px;
    padding: 0;
}

/* Grid cells set their own width, so the prose cap must not apply here. */
.steps li,
.steps p {
    max-width: none;
}

.steps li {
    padding-top: 14px;
    border-top: 1px solid var(--border);
}

.step-n {
    display: block;
    margin-bottom: 6px;
    font-family: var(--mono);
    font-size: 12px;
    letter-spacing: 0.5px;
    color: var(--accent);
}

.steps h3 {
    margin: 0 0 6px;
    font-size: 16px;
    letter-spacing: -0.2px;
}

.steps p {
    margin: 0;
    font-size: 15px;
    color: var(--muted);
}

.step-extra {
    margin: 36px 0 8px;
    font-size: 16px;
    letter-spacing: -0.2px;
}

@media (min-width: 760px) {
    .steps {
        grid-template-columns: repeat(3, 1fr);
        gap: 32px;
    }
}

/* Sample capture --------------------------------------------------- */

.sample {
    margin: 28px 0 0;
}

.sample figcaption {
    margin-bottom: 10px;
    font-size: 13px;
    color: var(--muted);
}

.sample-card {
    padding: 14px 16px;
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: 8px;
}

.sample-head {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.sample-meta {
    min-width: 0;
    font-family: var(--mono);
    font-size: 13px;
    color: var(--muted);
    overflow-wrap: anywhere;
}

.sample-body {
    margin: 12px 0 0;
    padding: 12px;
    border-radius: 6px;
    background: var(--bg);
    border: 1px solid var(--border);
    font-size: 13px;
    overflow-x: auto;
}

.sample-body code { white-space: pre; }

/* Webhook catcher + dumpster --------------------------------------- */

.catcher { margin-top: 28px; }

/* First section on its own page now, so it sits where .catcher does. */
.dumpster { margin-top: 28px; }

/* The pointer at the foot of each tool's page to the other tool. Separated
   by a rule so it reads as a footnote and not as another feature of the page
   you are on. */
.sibling {
    margin-top: 72px;
    padding-top: 28px;
    border-top: 1px solid var(--border);
}

.sibling p { max-width: var(--measure); }
.sibling .btn { margin-top: 6px; }

.label {
    display: block;
    font-family: var(--mono);
    font-size: 13px;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: var(--muted);
    margin-bottom: 8px;
}

textarea {
    width: 100%;
    min-height: 180px;
    padding: 14px;
    background: var(--bg-raised);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 6px;
    font-family: var(--mono);
    font-size: 14px;
    line-height: 1.5;
    resize: vertical;
}

textarea:focus {
    outline: none;
    border-color: var(--accent);
}

.controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 14px;
    margin-top: 12px;
}

.keep {
    font-size: 13px;
    color: var(--muted);
    display: flex;
    align-items: center;
    gap: 9px;
    cursor: pointer;
    user-select: none;
}

/* appearance:none turns the real input into the box, so it stays the
   focusable, clickable control and no wrapper markup is needed. The tick is
   ::after, two borders rotated 45 degrees, drawn on by animating its size
   from nothing. */
.keep input {
    appearance: none;
    -webkit-appearance: none;
    flex: none;
    width: 18px;
    height: 18px;
    margin: 0;
    border: 1px solid var(--border);
    border-radius: 4px;
    background-color: var(--bg-raised);
    /* Centres ::after without positioning maths. */
    display: inline-grid;
    place-content: center;
    cursor: pointer;
    transition: border-color 200ms ease, background-color 200ms ease;
}

.keep input::after {
    content: '';
    width: 0;
    height: 0;
    opacity: 0;
    border-left: 2px solid var(--accent);
    border-bottom: 2px solid var(--accent);
    transform: rotate(-45deg) translate(1px, -1px);
    /* Short leg first, then the long one, so it reads as a stroke rather
       than a box appearing. */
    transition: width 140ms ease 80ms,
                height 120ms ease 50ms,
                opacity 60ms linear 50ms;
}

.keep input:checked {
    border-color: var(--accent);
    background-color: rgba(245, 166, 35, 0.10);
}

.keep input:checked::after {
    width: 9px;
    height: 5px;
    opacity: 1;
}

.keep:hover input:not(:checked) { border-color: var(--muted); }

/* Registering the property is what lets it interpolate. Without this the
   ripple would jump straight to full size instead of growing. */
@property --ripple {
    syntax: '<percentage>';
    inherits: false;
    initial-value: 0%;
}

.btn {
    display: inline-block;
    padding: 12px 18px;
    border: 1px solid var(--accent);
    border-radius: 6px;
    color: var(--accent);
    font-family: var(--mono);
    font-size: 14px;
    font-weight: 700;
    text-decoration: none;
    cursor: pointer;

    /* Hover grows a soft circle from wherever the pointer crossed the edge.
       --mx / --my are written by JS on mouseenter; without it they stay at
       the centre, which is also what keyboard focus gets.

       The circle lives on the element's own background-image rather than a
       pseudo-element: a ::before would need z-index:-1 to sit under the
       label, which would also put it under the button's own background and
       hide it entirely on the filled primary.

       Rest is -(--ripple-feather), not 0. At 0 the transparent stop still
       sits at calc(0% + feather), leaving a soft blob parked at the origin
       after the pointer leaves.

       Only background and shadow animate, so a hovered button can never
       reflow its row or shift under the cursor. */
    --mx: 50%;
    --my: 50%;
    --ripple-feather: 22%;
    --ripple: calc(-1 * var(--ripple-feather));

    background-color: transparent;
    background-image: radial-gradient(circle at var(--mx) var(--my),
        rgba(245, 166, 35, 0.20) var(--ripple),
        transparent calc(var(--ripple) + var(--ripple-feather)));
    box-shadow: 0 0 0 0 rgba(245, 166, 35, 0);

    transition: --ripple 520ms cubic-bezier(0.33, 0.9, 0.28, 1),
                box-shadow 520ms cubic-bezier(0.33, 0.9, 0.28, 1),
                background-color 200ms ease,
                border-color 200ms ease,
                color 200ms ease,
                opacity 200ms ease,
                filter 120ms ease;
}

/* :not(:disabled) throughout, so a busy button stops responding to the
   pointer entirely instead of lighting up while it is refusing clicks. */
.btn:hover:not(:disabled) {
    --ripple: 170%;
    box-shadow: 0 0 0 6px rgba(245, 166, 35, 0.14);
}

/* Press feedback without movement: the button dims for as long as it is held. */
.btn:active:not(:disabled) { filter: brightness(0.92); }

/* Disabling is instant and silent, so a fast request cannot make the button
   blink. The dimming belongs to .is-busy, which JS only applies once the
   request has been running long enough to be worth showing. */
.btn:disabled { cursor: not-allowed; }
.btn.is-busy  { opacity: 0.55; }

.btn-primary {
    background-color: var(--accent);
    color: #0d0d0d;
    /* Same ripple, lighter, so it reads against the filled background. */
    background-image: radial-gradient(circle at var(--mx) var(--my),
        rgba(255, 255, 255, 0.30) var(--ripple),
        transparent calc(var(--ripple) + var(--ripple-feather)));
}

.btn-primary:hover:not(:disabled) { box-shadow: 0 0 0 6px rgba(245, 166, 35, 0.24); }

.btn-ghost {
    border-color: var(--border);
    color: var(--muted);
    padding: 10px 14px;
    /* Wide enough for "Copied" so swapping the label after a click cannot
       resize the button and shove the URL beside it. */
    min-width: calc(6ch + 30px);
    background-image: radial-gradient(circle at var(--mx) var(--my),
        rgba(255, 255, 255, 0.08) var(--ripple),
        transparent calc(var(--ripple) + var(--ripple-feather)));
}

.btn-ghost:hover:not(:disabled) {
    border-color: var(--muted);
    color: var(--text);
    box-shadow: 0 0 0 6px rgba(255, 255, 255, 0.06);
}

/* Result ----------------------------------------------------------- */

.result {
    margin-top: 18px;
    padding: 14px;
    background: var(--bg-raised);
    border: 1px solid var(--accent);
    border-radius: 6px;
}

/* The two URLs a new webhook hands you. They do different jobs and get
   confused for each other, so each is labelled and separated rather than one
   being a link and the other a sentence. */
.hook-pair + .hook-pair {
    margin-top: 16px;
    padding-top: 16px;
    border-top: 1px solid var(--border);
}

.hook-label {
    display: block;
    margin-bottom: 8px;
    font-family: var(--mono);
    font-size: 12px;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    color: var(--accent);
}

.hook-note { margin: 8px 0 0; }

.result-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
}

.result-url {
    font-family: var(--mono);
    flex: 1 1 240px;
    word-break: break-all;
    font-size: 14px;
}

.error {
    margin-top: 14px;
    padding: 12px 14px;
    border: 1px solid var(--danger);
    border-radius: 6px;
    color: var(--danger);
    font-size: 14px;
}

#api { margin-top: 72px; }

/* Code blocks ------------------------------------------------------ */

.code {
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-left: 3px solid var(--accent);
    border-radius: 6px;
    padding: 14px;
    font-size: 13px;
    overflow-x: auto;
}

.code code { white-space: pre; }

.facts {
    list-style: none;
    padding: 0;
    color: var(--muted);
    font-size: 14px;
}

.facts li { padding: 4px 0; }

.facts code { color: var(--text); }

/* Footer ----------------------------------------------------------- */


.footer {
    margin: 48px auto;
    padding-top: 20px;
    border-top: 1px solid var(--border);
    font-size: 13px;
}

.footer code { color: var(--text); }

/* 404 -------------------------------------------------------------- */

.page-404 {
    display: flex;
    align-items: center;
    min-height: 100vh;
    text-align: center;
}

.err-code {
    font-size: 72px;
    font-weight: 700;
    color: var(--accent);
    line-height: 1;
}

/* Paragraphs are capped at the prose measure, which on a centred page leaves
   the box itself parked against the left edge with its text centred inside.
   The cap is right, the box needs centring too. */
.page-404 p { margin-left: auto; margin-right: auto; }

.page-404 .code { text-align: left; }

/* One row, evenly spaced, wrapping on a narrow screen rather than letting
   whitespace between inline-blocks decide the gaps. */
.btn-row {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 12px;
    margin-top: 26px;
}

/* Wider screens ---------------------------------------------------- */

@media (min-width: 640px) {
    h1 { font-size: 46px; }
    .topbar nav a { margin-left: 18px; font-size: 14px; }
    .hero { padding-top: 56px; }
}

@media (min-width: 1000px) {
    h1 { font-size: 52px; }
}

/* Webhook dashboard ------------------------------------------------ */

.token-form {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 20px;
}

.token-form input {
    flex: 1 1 260px;
    min-width: 0;
    padding: 10px 12px;
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: 6px;
    color: var(--text);
    font-family: var(--mono);
    font-size: 14px;
}

.abyss-title {
    margin-bottom: 24px;
}

.endpoint {
    margin-bottom: 20px;
}

.abyss-bar {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    padding: 12px 0;
    border-top: 1px solid var(--border);
    border-bottom: 1px solid var(--border);
}

.abyss-bar .spacer { flex: 1 1 auto; }

.status {
    font-family: var(--mono);
    font-size: 13px;
    color: var(--muted);
}

.status-ok::before,
.status-bad::before {
    content: '';
    display: inline-block;
    width: 8px;
    height: 8px;
    margin-right: 8px;
    border-radius: 50%;
    vertical-align: middle;
}

.status-ok { color: var(--accent); }
.status-ok::before { background: var(--accent); }
.status-bad { color: var(--danger); }
.status-bad::before { background: var(--danger); }

.events { margin-top: 24px; }

.waiting {
    padding: 40px 0;
    text-align: center;
}

.event {
    margin-bottom: 16px;
    padding: 14px 16px;
    background: var(--bg-raised);
    border: 1px solid var(--border);
    border-radius: 8px;
}

.event-head {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.method {
    font-family: var(--mono);
    padding: 2px 8px;
    border: 1px solid var(--accent);
    border-radius: 4px;
    color: var(--accent);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.5px;
}

.method-get    { border-color: var(--muted); color: var(--muted); }
.method-delete { border-color: var(--danger); color: var(--danger); }

.event-time { font-family: var(--mono); font-size: 13px; }

.event-meta {
    font-family: var(--mono);
    /* Long content types wrap instead of stretching the card. */
    min-width: 0;
    overflow-wrap: anywhere;
}

.badge {
    font-family: var(--mono);
    margin-left: auto;
    padding: 2px 8px;
    border-radius: 4px;
    font-size: 12px;
}

.badge-ok  { font-family: var(--mono); background: rgba(245, 166, 35, 0.15); color: var(--accent); }
.badge-bad { background: rgba(255, 92, 92, 0.15); color: var(--danger); }

.qs {
    font-family: var(--mono);
    margin: 8px 0 0;
    overflow-wrap: anywhere;
}

.event-section {
    margin-top: 12px;
    border-top: 1px solid var(--border);
    padding-top: 10px;
}

.event-section > summary {
    cursor: pointer;
    font-size: 13px;
    color: var(--muted);
}

.kv {
    font-family: var(--mono);
    width: 100%;
    margin-top: 10px;
    border-collapse: collapse;
    font-size: 13px;
    table-layout: fixed;
}

.kv th,
.kv td {
    padding: 4px 8px 4px 0;
    text-align: left;
    vertical-align: top;
    overflow-wrap: anywhere;
}

.kv th {
    width: 34%;
    font-weight: 400;
    color: var(--muted);
}

/* 34% was tuned for the old 760px container. Once there is room, pin the
   label column so values sit next to their names instead of a wide gutter. */
@media (min-width: 640px) {
    .kv th { width: 220px; }
}

.body-block { margin-top: 10px; }

.body-pre {
    max-height: 420px;
    overflow: auto;
}

/* Beats the .code code rule above, which is deliberately pre for the static
   curl examples. A captured payload is one long line and has to wrap. */
.body-pre code {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.raw-link {
    display: inline-block;
    font-size: 13px;
}

.forward-block { margin-top: 10px; }

.forward-error { color: var(--danger); }

/* Motion and focus -------------------------------------------------- */

/* Mock response options -------------------------------------------- */

.mock-row {
    display: grid;
    gap: 16px;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    max-width: 620px;
    margin-top: 18px;
}

/* The arrow is the browser's, but the rest has to match the textarea above
   it or the two read as different controls. */
.mock-row select {
    appearance: none;
    font-family: var(--mono);
    font-size: 14px;
    padding-right: 34px;
    background-image:
        linear-gradient(45deg, transparent 50%, var(--muted) 50%),
        linear-gradient(135deg, var(--muted) 50%, transparent 50%);
    background-position: calc(100% - 17px) 50%, calc(100% - 12px) 50%;
    background-size: 5px 5px, 5px 5px;
    background-repeat: no-repeat;
    cursor: pointer;
}

.mock-row select:focus { border-color: var(--accent); }

/* Contact form ----------------------------------------------------- */

.contact { margin-top: 28px; }

.form {
    max-width: 620px;
    display: grid;
    gap: 20px;
}

.field label {
    display: block;
    margin-bottom: 7px;
    font-family: var(--mono);
    font-size: 12px;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    color: var(--muted);
}

.field .optional {
    text-transform: none;
    letter-spacing: 0;
    color: #5f5f5f;
}

.field input,
.field select,
.field textarea {
    width: 100%;
    padding: 11px 13px;
    background: var(--bg-raised);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 6px;
    font-family: var(--sans);
    font-size: 15px;
    line-height: 1.5;
    transition: border-color 150ms ease, box-shadow 150ms ease;
}

/* The message is the one field people paste logs into, so it gets the mono
   face and the room the dumpster textarea has. */
.field textarea {
    min-height: 170px;
    font-family: var(--mono);
    font-size: 14px;
    resize: vertical;
}

.field input::placeholder,
.field textarea::placeholder { color: #5f5f5f; }

.field input:focus,
.field select:focus,
.field textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 4px rgba(245, 166, 35, 0.14);
}

.field input.is-invalid,
.field textarea.is-invalid {
    border-color: var(--danger);
    box-shadow: none;
}

.field-error {
    margin: 7px 0 0;
    color: var(--danger);
    font-size: 13px;
}

/* Error on the left, counter on the right, and the row keeps its height
   whether or not the error is showing. */
.field-foot {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
}

.field-foot .field-error { flex: 1 1 auto; }

.counter {
    flex: 0 0 auto;
    margin-top: 7px;
    font-family: var(--mono);
    font-size: 12px;
    color: #5f5f5f;
}

.counter.is-full { color: var(--danger); }

/* Off screen rather than display:none: a bot that skips hidden inputs still
   sees this one, which is the entire point. */
.hp {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

.notice {
    margin-top: 18px;
    padding: 12px 14px;
    border: 1px solid var(--accent);
    border-radius: 6px;
    background: rgba(245, 166, 35, 0.08);
    color: var(--text);
    font-size: 14px;
}

.contact .facts { margin-top: 34px; }

.topbar nav a[aria-current="page"] { color: var(--text); }

/* Hover affordances are pointer-only, so give the keyboard an equivalent.
   :focus-visible keeps the ring off mouse clicks. */
.btn:focus-visible,
a:focus-visible,
summary:focus-visible,
.keep input:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 4px;
}

@media (prefers-reduced-motion: reduce) {
    a,
    .btn,
    .topbar nav a,
    .keep input,
    .keep input::after,
    .field input,
    .field textarea,
    .logo .shit {
        transition: none;
    }

    .logo.is-done .shit { animation: none; }
}
