/* core/static/core/css/contact.css
   Styling for the Contact page.

   Follows the Spelling Bee's form treatment (spelling_bee.css) rather than
   inventing a second one: the same field/label/check/counter structure, the
   same tokens, the same `:has(:invalid)` submit gate. Only two things differ,
   and both are because this form is prose rather than tiles — the inputs are
   full-width and set in the body face instead of monospace tiles, and the
   fields stack in one column rather than flowing in a row.

   Per the project rule, form state is CSS here, not JS: `required` and
   `pattern` on the inputs are the real gate, the fade and the status icon are
   selectors reading `:invalid` / `:valid`, and contact.js is limited to the
   character counter and the reply-time note — the two things CSS cannot count
   or swap. */

/* ── Intro ──────────────────────────────────────────────────────────────── */

/* Bold for legibility on the paper texture (the same finding as .ct-fineprint
   below), and the reply-time promise in the stamp red so it is the one thing
   a skimming reader takes from the paragraph. Owner's call, 2026-09-13. */
.ct-intro {
    font-weight: bold;
}

.ct-intro strong {
    color: var(--op-stamp);
}

/* ── Layout ─────────────────────────────────────────────────────────────── */

.ct-form {
    margin-bottom: 26px;
    /* Prose fields, so a readable measure rather than the full page width.
       Past ~46em a text input stops reading as a field and starts reading as a
       rule across the page. */
    max-width: 46em;
}

.ct-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-bottom: 20px;
}

.ct-field-label {
    font-weight: bold;
    font-size: 14px;
    color: var(--op-ink);
}

.ct-input-row {
    display: flex;
    align-items: center;
    gap: 8px;
    /* The status icon is positioned against this, inside the field's box. */
    position: relative;
}

/* ── Inputs ─────────────────────────────────────────────────────────────── */

.ct-input {
    font-family: inherit;
    font-size: 16px;
    color: var(--op-text);
    background-color: var(--op-panel);
    border: 2px solid var(--op-border);
    border-radius: 6px;
    padding: 10px 12px;
    /* border-box because this project has no global rule for it — same
       reasoning as .sb-input and .jb-input. */
    box-sizing: border-box;
    min-height: 44px;
    flex: 1 1 auto;
    /* Without this a long placeholder or a long option label sets the flex
       item's base width and overflows the row on narrow screens. */
    min-width: 0;
}

/* Room for the status icon sitting inside the right edge, so typed text
   never runs underneath it. The optional page select has no icon and keeps
   the plain padding. */
.ct-input-row:has(.ct-status) .ct-input {
    padding-right: 44px;
}

/* 16px is not a style choice: iOS Safari zooms the viewport on focus for any
   input below 16px, and the zoom is not undone on blur. Stated here rather
   than left implicit, because "make the form text a bit smaller" is an
   entirely reasonable-sounding change that breaks the page on a phone. */

.ct-input::placeholder {
    color: #837d66;
}

/* Focus in the stamp red rather than the green the Bee uses: on this page the
   green already means "this field is valid", and a focused-but-empty field
   drawn in green read as approval. :focus rather than :focus-visible, because
   the ring is part of the form's design here and a <select> clicked with the
   mouse does not reliably match :focus-visible in every engine. */
.ct-input:focus {
    outline: 3px solid var(--op-stamp);
    outline-offset: 2px;
    border-color: var(--op-stamp);
}

.ct-select {
    /* Native select styling varies wildly; leave the arrow to the platform and
       only match the box to the text inputs beside it. */
    cursor: pointer;
    text-transform: none;
}

/* The native arrow owns the right edge of a select, so its status icon sits
   just inside of it, and the padding clears both. */
.ct-input-row:has(.ct-status) .ct-select {
    padding-right: 72px;
}

.ct-input-row:has(.ct-select) .ct-status {
    right: 40px;
}

.ct-textarea {
    font-size: 16px;
    line-height: 1.6;
    resize: vertical;
    /* min-height rather than rows alone, so a browser that sizes rows tightly
       still gives enough room to see a paragraph. */
    min-height: 180px;
    width: 100%;
}

/* ── The status icon ────────────────────────────────────────────────────── */

/* One span per required field, inside the box's right edge. Its glyph is
   generated content chosen by the field's own validity: a red "!" in a pale
   red disc while the browser would refuse the field, a green check once it
   would accept it. So a blank form shows four "!" marks — that is the point:
   the fade on the Send button says something is missing, and these say what.
   The span itself is empty and aria-hidden; the per-field error text and the
   browser's own validation messages carry the meaning for assistive tech. */
.ct-status {
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-50%);
    width: 22px;
    height: 22px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
    line-height: 1;
    /* Clicks go through to the field beneath. */
    pointer-events: none;
    transition: background-color .15s ease, color .15s ease;
}

.ct-field:has(.ct-input:invalid) .ct-status {
    font-size: 15px;
    color: var(--op-stamp);
    background-color: rgba(139, 26, 26, .13);
    border: 1.5px solid rgba(139, 26, 26, .55);
    box-sizing: border-box;
}

.ct-field:has(.ct-input:invalid) .ct-status::before {
    content: "!";
}

.ct-field:has(.ct-input:valid) .ct-status {
    font-size: 22px;
    color: var(--op-green-deep);
}

.ct-field:has(.ct-input:valid) .ct-status::before {
    content: "\2713";
}

/* The textarea's icon sits in the top-right corner rather than vertically
   centred in a 180px box, where it would float beside the middle of the text. */
.ct-input-row-block .ct-status {
    top: 12px;
    transform: none;
}

/* ── Hints and the character counter ────────────────────────────────────── */

/* Ink and bold at 12-13px for the same reason recorded in spelling_bee.css:
   the muted and faint tokens were measured against --op-bg, not against the
   paper photograph actually behind them, where muted is 3.48:1. */
.ct-hint {
    font-size: 12px;
    font-weight: bold;
    color: var(--op-ink);
}

.ct-counter {
    font-size: 12px;
    font-weight: bold;
    color: var(--op-ink);
    align-self: flex-end;
}

/* "(minimum 20)" beside the count, shown only while the browser would still
   refuse the box — empty, or under the floor. Without this the Send button
   stayed faded over a seven-character message with nothing on the page saying
   why (found by the owner typing "Love it", 2026-09-13). Read from the
   textarea's validity so it cannot disagree with the gate; contact.js rewrites
   the count beside it and never touches this span. */
.ct-counter-min {
    display: none;
    color: var(--op-stamp);
}

.ct-field:has(.ct-textarea:invalid) .ct-counter-min {
    display: inline;
}

/* Approaching the limit. Set by contact.js, because CSS cannot count the
   characters in a value — the same division of labour as the Bee's letter
   counter. Amber rather than the error red: this is a warning that the ceiling
   is close, not a report that something is wrong. */
.ct-counter-near {
    color: var(--op-stamp-dark, #8a3324);
}

/* ── Messages ───────────────────────────────────────────────────────────── */

.ct-msg {
    margin: 8px 0 0;
    font-size: 13px;
    font-weight: 600;
    color: var(--op-ink);
    /* This project has no global border-box rule and these carry padding. */
    box-sizing: border-box;
}

/* REQUIRED. The rule above gives .ct-msg no `display`, but the banners below
   do — and a `display` declaration in our own CSS outranks the UA's
   `[hidden] { display: none }`. Every per-field error <p> ships with `hidden`
   for the JS-off / first-load state, so without this guard all five error
   messages would render at once on a blank form. Same trap as .word-panel and
   .more-info-link in style.css, both of which were live bugs before they got
   one of these. */
.ct-msg[hidden] {
    display: none;
}

.ct-msg-error {
    color: var(--op-stamp);
    font-weight: bold;
}

/* "Did you mean anna@gmail.com?" under the email field. The address is a real
   <button type="submit"> dressed as a link, because it has to resubmit the
   form with the correction and a link cannot POST. Kept visually distinct
   from the red error text: this is an offer, not a fault. */
.ct-suggest {
    color: var(--op-ink);
}

.ct-suggest-btn {
    font: inherit;
    font-weight: bold;
    color: var(--op-stamp);
    background: none;
    border: 0;
    padding: 0;
    text-decoration: underline;
    cursor: pointer;
}

.ct-suggest-btn:hover {
    color: var(--op-stamp-dark);
}

/* Page-level banners — the rate-limit refusal and the expired-token notice.
   They sit above the form rather than under a field, so they need a surface to
   read as a banner rather than as stray text. Given their own class instead of
   being selected positionally: a sibling selector here would silently stop
   matching the first time anything is inserted between the intro and the
   form. */
.ct-banner {
    padding: 12px 14px;
    border-radius: 6px;
    font-size: 14px;
    margin: 0 0 16px;
    max-width: 46em;
}

.ct-banner-error {
    color: var(--op-ink);
    background-color: rgba(176, 58, 46, .10);
    border-left: 4px solid var(--op-stamp);
}

.ct-banner-notice {
    color: var(--op-ink);
    background-color: rgba(90, 110, 70, .12);
    border-left: 4px solid var(--op-green-deep);
}

/* ── Submit ─────────────────────────────────────────────────────────────── */

.ct-form-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    align-items: center;
    margin-top: 24px;
}

.ct-btn-primary {
    font-family: inherit;
    font-size: 15px;
    font-weight: bold;
    padding: 13px 22px;
    border-radius: 6px;
    cursor: pointer;
    color: var(--op-panel);
    background-color: var(--op-stamp);
    border: 2px solid var(--op-stamp);
    transition: background-color .15s ease, transform .15s ease, box-shadow .15s ease, opacity .15s ease;
    box-sizing: border-box;
    min-height: 44px;
}

.ct-btn-primary:hover {
    background-color: var(--op-stamp-dark);
    border-color: var(--op-stamp-dark);
}

/* The gate. Faded exactly when the browser will refuse to submit, so the fade
   reports a restriction that already exists rather than decorating a JS check.

   No `pointer-events: none`, for the reason recorded on .jb-btn-reset and
   .sb-btn-primary: an element that takes no pointer events shows no cursor
   either, and `not-allowed` is the one thing telling a mouse user why the
   click did nothing. Native validation blocks the submit regardless. */
.ct-form:has(.ct-input:invalid) .ct-btn-primary {
    opacity: .45;
    cursor: not-allowed;
}

.ct-form:has(.ct-input:invalid) .ct-btn-primary:hover {
    background-color: var(--op-stamp);
    border-color: var(--op-stamp);
    transform: none;
    box-shadow: none;
}

/* ── The honeypot ───────────────────────────────────────────────────────── */

/* Hidden here rather than with type="hidden", because bots skip hidden inputs
   and fill visible-in-the-DOM ones — which is the entire trick. Positioned off
   canvas rather than `display: none` for the same reason: some crawlers check
   computed style and skip anything display-none.

   Keep the field itself out of the tab order (tabindex="-1" in the template)
   and out of the accessibility tree (aria-hidden on the wrapper), so this is
   invisible to keyboard and screen-reader users too rather than merely
   off-screen for sighted ones. */
.ct-hp {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* ── Fine print ─────────────────────────────────────────────────────────── */

/* Bold, and that is legibility rather than emphasis. This sits directly on the
   paper photograph, which is mottled brown rather than a flat surface, so thin
   13px strokes lose against the grain in the lighter patches — the same
   finding recorded on .sb-counter, where --op-text-muted measured 3.48:1
   against the paper it is actually on rather than against --op-bg. Ink plus
   full bold is the fix this project already uses for small text on the
   texture; don't reach for a lighter weight or a muted token here.
   The <a> inherits the weight, which is most of what makes it readable too. */
.ct-fineprint {
    margin-top: 20px;
    font-size: 13px;
    line-height: 1.6;
    font-weight: bold;
    color: var(--op-ink);
}
