/* Building blocks shared by pages: buttons, form fields, tags, messages, dialogs. */

/* --- Buttons --------------------------------------------------------------
 * .button alone is the main action; modifiers make it quieter.
 */

.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    min-height: 2.75rem;
    padding: 0 1.125rem;
    border: 1px solid transparent;
    border-radius: var(--radius-control);
    background: var(--moss);
    color: #fff;
    font-size: 0.875rem;
    font-weight: 600;
    line-height: 1.2;
    cursor: pointer;
    transition: background-color 0.15s, border-color 0.15s;
}

.button:hover {
    background: var(--moss-dark);
    color: #fff;
    text-decoration: none;
}

/* Not to be pressed now: nothing reacts to the pointer either. */
.button:disabled {
    opacity: 0.6;
    pointer-events: none;
}

/* Busy with what it was pressed for: the spinner (see [aria-busy]) stands where the icon would, the gap keeps it away from the text. */
.button[aria-busy='true']::before {
    margin-right: 0;
}

.button-large {
    min-height: 3rem;
    font-size: 0.9375rem;
}

.button-secondary {
    border-color: var(--thread-strong);
    background: #fff;
    color: var(--ink);
}

.button-secondary:hover {
    border-color: var(--bark-light);
    background: var(--paper);
    color: var(--ink);
}

.button-ghost {
    padding: 0 0.875rem;
    background: transparent;
    color: var(--moss);
}

.button-ghost:hover {
    background: var(--moss-mist);
    color: var(--moss-deep);
}

.button-danger {
    color: var(--ember);
}

.button-danger:hover {
    background: var(--ember-mist);
    color: var(--ember);
}

/* A button that is only an icon (with a hidden label). */
.icon-button {
    display: inline-flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    padding: 0;
    border: 0;
    border-radius: var(--radius-small);
    background: transparent;
    color: var(--bark);
    cursor: pointer;
}

.icon-button:hover {
    background: rgb(42 36 31 / 0.06);
    color: var(--ink);
}

.icon-button-small {
    width: 2rem;
    height: 2rem;
}

/* --- Forms ---------------------------------------------------------------- */

form {
    margin: 0;
}

.field {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

label {
    color: var(--ink-soft);
    font-size: 0.8125rem;
    font-weight: 600;
}

/* A label with something next to it, e.g. a link helping with the field. */
.field-label {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    font-size: 0.8125rem;
}

input:not([type='checkbox'], [type='radio'], [type='file'], [type='hidden']),
select,
textarea {
    width: 100%;
    min-height: 2.75rem;
    padding: 0.5rem 0.875rem;
    border: 1px solid var(--thread-strong);
    border-radius: var(--radius-control);
    background: #fff;
    transition: border-color 0.15s, box-shadow 0.15s;
}

input:not([type='checkbox'], [type='radio'], [type='file'], [type='hidden']):focus,
select:focus,
textarea:focus {
    border-color: var(--moss);
    /* Kept (transparent) for modes that replace colours and drop shadows. */
    outline: 2px solid transparent;
    box-shadow: var(--focus-ring);
}

[aria-invalid='true']:not(:focus) {
    border-color: var(--ember) !important;
}

.form-error {
    color: var(--ember);
    font-size: 0.8125rem;
    font-weight: 500;
}

.help-text {
    color: var(--bark);
    font-size: 0.8125rem;
}

/* --- Tags ------------------------------------------------------------------
 * A short fact about something, e.g. the format of a file.
 */

.tag {
    display: inline-flex;
    align-items: center;
    height: 1.5rem;
    padding: 0 0.5rem;
    border: 1px solid var(--thread);
    border-radius: 5px;
    background: var(--paper);
    color: var(--bark);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.04em;
}

.tag-round {
    height: 1.75rem;
    padding: 0 0.625rem;
    border-radius: 999px;
    background: transparent;
    font-size: 0.75rem;
}

/* A filled, neutral one: the kind of a device, the state of a book (e.g. still being processed, with a spinner). */
.tag-filled {
    gap: 0.375rem;
    border-color: transparent;
    background: var(--linen);
    color: var(--ink-soft);
    letter-spacing: 0;
}

/* The gap already keeps the spinner (see [aria-busy]) away from the text. */
.tag-filled::before {
    margin-right: 0;
}

/* --- Split buttons and menus -------------------------------------------------
 * A .split-button is a button with a toggle next to it, opening a menu of other choices: a <details class="menu">,
 * which opens without JavaScript (assets/js/menu.js only closes it when the user clicks elsewhere).
 */

.split-button {
    position: relative;
    display: flex;
    align-items: stretch;
}

.split-button > form {
    display: flex;
    flex: 1;
    min-width: 0;
}

.split-button > form .button {
    width: 100%;
    border-radius: var(--radius-control) 0 0 var(--radius-control);
}

/* The <summary> of the menu, looking like the other half of the button. */
.split-button-toggle {
    display: inline-flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    min-height: 2.75rem;
    padding: 0;
    border: 1px solid transparent;
    border-left-color: rgb(255 255 255 / 0.28);
    border-radius: 0 var(--radius-control) var(--radius-control) 0;
    background: var(--moss);
    color: #fff;
    list-style: none;
    cursor: pointer;
}

.split-button-toggle::-webkit-details-marker {
    display: none;
}

.split-button-toggle:hover,
.menu[open] > .split-button-toggle {
    background: var(--moss-dark);
}

.menu-panel {
    position: absolute;
    top: calc(100% + 0.375rem);
    right: 0;
    left: 0;
    z-index: 20;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 0.375rem;
    border: 1px solid var(--thread);
    border-radius: var(--radius-card);
    background: var(--paper);
    box-shadow: 0 16px 40px rgb(42 36 31 / 0.18);
}

.menu-item {
    display: flex;
    gap: 0.625rem;
    align-items: center;
    width: 100%;
    min-height: 3rem;
    padding: 0.625rem;
    border: 0;
    border-radius: var(--radius-small);
    background: transparent;
    color: var(--ink);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.menu-item:hover {
    background: var(--linen);
}

/* Not to be pressed now (the device doesn't take the book): the entry says why. */
.menu-item:disabled {
    opacity: 0.6;
    cursor: default;
}

.menu-item:disabled:hover {
    background: transparent;
}

.menu-item-icon {
    display: inline-flex;
    flex-shrink: 0;
    color: var(--moss);
}

.menu-item-icon-idle {
    color: var(--bark-light);
}

.menu-item-text {
    display: flex;
    flex: 1;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

.menu-item-name {
    display: flex;
    gap: 0.375rem;
    align-items: center;
    overflow: hidden;
    font-size: 0.875rem;
    font-weight: 600;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.menu-footer {
    display: flex;
    align-items: center;
    min-height: 2.5rem;
    margin-top: 2px;
    padding: 0.625rem;
    border-top: 1px solid var(--thread);
    font-size: 0.8125rem;
    font-weight: 600;
}

/* The star of the default device: a filled one. */
.default-mark {
    display: inline-flex;
    flex-shrink: 0;
    color: var(--honey);
}

.default-mark .icon {
    fill: currentColor;
}

/* --- Messages --------------------------------------------------------------- */

/* Something a page says inline, in the flow of its content. */
.flash {
    padding: 0.75rem 1rem;
    border-radius: var(--radius-control);
    background: var(--moss-mist);
    color: var(--moss-deep);
    font-size: 0.875rem;
    font-weight: 500;
}

.flash[role='alert'] {
    background: var(--ember-mist);
    color: var(--ember-deep);
}

/*
 * What the previous action ended with, shown over the page at its bottom (templates/_toasts.html.twig): the page
 * itself doesn't move. toast.js takes the toasts away after a while, or at once when the user closes them.
 */
.toasts {
    position: fixed;
    right: 0;
    bottom: 1.75rem;
    left: 0;
    z-index: 50;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
    padding: 0 1rem;
    pointer-events: none;
}

.toast {
    display: flex;
    gap: 0.75rem;
    align-items: center;
    width: 100%;
    max-width: 35rem;
    padding: 0.75rem 0.75rem 0.75rem 1rem;
    border-radius: 0.875rem;
    background: var(--ink);
    color: var(--parchment);
    font-size: 0.875rem;
    font-weight: 600;
    box-shadow: 0 18px 44px rgb(42 36 31 / 0.32);
    pointer-events: auto;
    animation: toast-in 0.25s ease-out;
}

/* A green tick for what went well, an ember cross for what went wrong. */
.toast-icon {
    display: inline-flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    width: 1.875rem;
    height: 1.875rem;
    border-radius: 50%;
    background: var(--moss);
    color: #fff;
}

.toast-error .toast-icon {
    background: var(--ember);
}

.toast-text {
    flex: 1;
    min-width: 0;
    overflow-wrap: anywhere;
}

.toast-close {
    display: inline-flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: var(--radius-small);
    color: rgb(245 240 232 / 0.7);
}

.toast-close:hover {
    background: rgb(255 255 255 / 0.1);
    color: var(--parchment);
    text-decoration: none;
}

/* Slides up into view; no fading, which would make the toast invisible to the browser for a moment. */
@keyframes toast-in {
    from {
        transform: translateY(0.75rem);
    }
}

@media (prefers-reduced-motion: reduce) {
    .toast {
        animation: none;
    }
}

/* Something is going on: a spinner before the text saying what. */
[aria-busy='true']::before {
    content: '';
    display: inline-block;
    width: 0.875em;
    height: 0.875em;
    margin-right: 0.5em;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    vertical-align: -0.125em;
    animation: spin 0.8s linear infinite;
}

/* The same, as an icon of its own: an arc running around a faint ring. */
.spinner {
    stroke: var(--moss);
    stroke-width: 2.5;
    stroke-linecap: round;
    fill: none;
    animation: spin 0.9s linear infinite;
}

.spinner-track {
    stroke: var(--thread);
}

@keyframes spin {
    to {
        transform: rotate(360deg);
    }
}

@media (prefers-reduced-motion: reduce) {
    [aria-busy='true']::before,
    .spinner {
        animation-duration: 3s;
    }
}

/* --- Dialogs ---------------------------------------------------------------
 * <dialog class="modal"> covers the page with a veil and holds a .modal-box. Links to "#id-of-the-dialog"
 * open it: app.js makes it a real modal dialog (focus kept inside, Esc closes it); without JavaScript
 * the same link shows it thanks to :target, and .is-open shows a dialog the server wants to be open.
 */

dialog.modal {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: none;
    width: 100%;
    max-width: none;
    height: 100%;
    max-height: none;
    margin: 0;
    padding: 1.5rem;
    overflow: auto;
    border: 0;
    background: rgb(42 36 31 / 0.48);
    color: inherit;
}

dialog.modal[open],
html:not(.js) dialog.modal:target,
html:not(.js) dialog.modal.is-open {
    display: flex;
}

/* The dialog itself is the veil. */
dialog.modal::backdrop {
    background: transparent;
}

/* The page behind an open dialog stays where it was. */
html:has(dialog.modal[open]) {
    overflow: hidden;
}

.modal-box {
    display: flex;
    flex-direction: column;
    gap: 1.375rem;
    width: 100%;
    max-width: 40rem;
    /* Centred, and still reachable by scrolling when taller than the window. */
    margin: auto;
    padding: 1.75rem;
    border-radius: var(--radius-modal);
    background: var(--paper);
    box-shadow: 0 24px 60px rgb(42 36 31 / 0.28);
}

.modal-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
}

.modal-header h2 {
    margin-bottom: 0.25rem;
    font-size: 1.75rem;
}

.modal-header p {
    color: var(--bark);
    font-size: 0.875rem;
}

.modal-footer {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.625rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--thread);
}

@media (max-width: 40rem) {
    dialog.modal {
        padding: 0.75rem;
    }

    .modal-box {
        padding: 1.25rem;
    }
}
