*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

:root {
    color-scheme: light;
    --bg: #ffffff;
    --surface: rgba(0, 0, 0, 0.05);
    --surface-strong: rgba(0, 0, 0, 0.09);
    --text: #111111;
    --muted: #626262;
    --border: rgba(0, 0, 0, 0.09);
    --accent: #111111;
    --accent-hover: #2a2a2a;
    --accent-text: #f4f0e8;
    --success: #2e7d52;
    --success-ring: rgba(46, 125, 82, 0.16);
    --success-surface: rgba(46, 125, 82, 0.08);
    --error: #b24c4c;
    --services: rgba(17, 17, 17, 0.64);
    --services-separator: rgba(17, 17, 17, 0.34);
    --shimmer: rgba(17, 17, 17, 0.98), rgba(17, 17, 17, 0.78) 16%, rgba(17, 17, 17, 0.26) 34%, rgba(17, 17, 17, 0.08) 48%, transparent 64%;

    --radius: 0.5rem;
    --space-page: 1.25rem;
    --control-height: 3rem;
    --control-padding-x: 1rem;
    --layout-max-width: 26.25rem;
    --ring-width: 0.1875rem;
    --motion-fast: 180ms;
    --motion-base: 220ms;
    --motion-progress: 400ms;
    --motion-loop: 1100ms;
    --ease: ease;
    --ease-emphasis: cubic-bezier(0.3, 0, 0.6, 1);
}

@media (prefers-color-scheme: dark) {
    :root {
        color-scheme: dark;
        --bg: #000000;
        --surface: rgba(255, 255, 255, 0.06);
        --surface-strong: rgba(255, 255, 255, 0.1);
        --text: #eeeeee;
        --muted: #a3a3a3;
        --border: rgba(255, 255, 255, 0.09);
        --accent: #eeeeee;
        --accent-hover: #ffffff;
        --accent-text: #111111;
        --success: #6b9e7a;
        --success-ring: rgba(107, 158, 122, 0.18);
        --success-surface: rgba(107, 158, 122, 0.1);
        --error: #b86464;
        --services: rgba(238, 238, 238, 0.68);
        --services-separator: rgba(238, 238, 238, 0.36);
        --shimmer: rgba(255, 255, 255, 0.96), rgba(255, 255, 255, 0.78) 16%, rgba(255, 255, 255, 0.24) 34%, rgba(255, 255, 255, 0.06) 48%, transparent 64%;
    }
}

html {
    background: var(--bg);
    overscroll-behavior: none;
}

body {
    min-height: 100dvh;
    display: flex;
    justify-content: center;
    align-items: center;
    overflow-x: hidden;
    padding:
        calc(var(--space-page) + env(safe-area-inset-top, 0px))
        calc(var(--space-page) + env(safe-area-inset-right, 0px))
        calc(var(--space-page) + env(safe-area-inset-bottom, 0px))
        calc(var(--space-page) + env(safe-area-inset-left, 0px));
    background: var(--bg);
    color: var(--text);
    font-family: system-ui, sans-serif;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

main {
    --gap: 0.375rem;
    display: flex;
    flex-direction: column;
    gap: var(--gap);
    width: 100%;
    max-width: var(--layout-max-width);
    text-align: center;
}

h1 {
    align-self: center;
    font-size: 1.5rem;
    font-weight: 600;
    letter-spacing: -0.02em;
    line-height: 1;
}

.sprite {
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
}

.icon {
    display: block;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* A panel that folds away: the grid row goes from 1fr to 0fr, so no height is
   hard-coded and nothing needs a timer. Closed, it stays out of the focus order
   and of the accessibility tree, and cancels the flex gap it would still take. */
.collapsible {
    display: grid;
    grid-template-rows: 1fr;
    transition:
        grid-template-rows var(--motion-base) var(--ease),
        opacity var(--motion-base) var(--ease),
        margin-top var(--motion-base) var(--ease),
        visibility 0s;
}

.collapsible > div {
    min-height: 0;
    overflow: hidden;
}

.collapsible[data-open="false"] {
    grid-template-rows: 0fr;
    margin-top: calc(var(--gap) * -1);
    opacity: 0;
    visibility: hidden;
    transition-delay: 0s, 0s, 0s, var(--motion-base);
}

/* ---- Supported services ---- */

.service-list {
    position: relative;
    display: inline-block;
    color: var(--services);
    font-size: 0.92rem;
    font-weight: 500;
    letter-spacing: 0.015em;
}

.service-separator {
    color: var(--services-separator);
}

/* The same words again, painted by a light that sweeps across them. */
.service-shimmer {
    position: absolute;
    inset: 0;
    z-index: 1;
    background-image: radial-gradient(ellipse 20% 120% at center, var(--shimmer));
    background-repeat: no-repeat;
    background-size: 240% 100%;
    background-clip: text;
    -webkit-background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
    opacity: 0.72;
    pointer-events: none;
    animation: shimmer 3.5s linear infinite;
}

/* Folded away it is invisible: stop painting it. */
.collapsible[data-open="false"] .service-shimmer {
    animation-play-state: paused;
}

@keyframes shimmer {
    from { background-position: 110% 50%; }
    to { background-position: -10% 50%; }
}

/* ---- Form ---- */

form {
    --gap: 0.75rem;
    display: flex;
    flex-direction: column;
    gap: var(--gap);
    width: 100%;
    margin-top: calc(var(--gap) / 2);
}

.input-row {
    display: flex;
    gap: 0.5rem;
}

input {
    flex: 1;
    min-width: 0;
    height: var(--control-height);
    padding: 0.75rem var(--control-padding-x);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--text);
    font: inherit;
    font-size: 1rem; /* 16px: below that, iOS zooms the page when the field is focused */
    outline: none;
    transition: border-color var(--motion-fast) var(--ease), box-shadow var(--motion-fast) var(--ease), background-color var(--motion-fast) var(--ease);
}

input::placeholder {
    color: var(--muted);
}

input:focus-visible {
    outline: 2px solid var(--text);
    outline-offset: -2px;
}

input[aria-invalid="true"] {
    border-color: var(--error);
}

.input-row.is-pasted input {
    background: var(--success-surface);
}

.input-row.is-pasted input:not(:focus-visible) {
    border-color: var(--success);
    box-shadow: 0 0 0 var(--ring-width) var(--success-ring);
}

/* Buttons: each kind styles itself; nothing here is a global button rule. */
button {
    font: inherit;
    cursor: pointer;
}

button:focus-visible {
    outline: 2px solid var(--text);
    outline-offset: -2px;
}

.paste {
    position: relative;
    flex: none;
    width: var(--control-height);
    height: var(--control-height);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--muted);
    transition: color var(--motion-fast) var(--ease), border-color var(--motion-fast) var(--ease), background-color var(--motion-fast) var(--ease), box-shadow var(--motion-fast) var(--ease);
}

.paste .icon {
    position: absolute;
    inset: 0;
    margin: auto;
    width: 18px;
    height: 18px;
    stroke-width: 2;
    transition: opacity var(--motion-fast) var(--ease), transform var(--motion-fast) var(--ease);
}

.paste .icon-tick,
.paste.is-confirmed .icon-clipboard {
    opacity: 0;
    transform: scale(0.85);
}

.paste.is-confirmed .icon-tick {
    opacity: 1;
    transform: none;
}

.paste.is-confirmed {
    border-color: var(--success);
    background: var(--success-surface);
    box-shadow: 0 0 0 var(--ring-width) var(--success-ring);
    color: var(--success);
}

/* ---- Choice of what to download ---- */

.control {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
    text-align: left;
    transition: opacity var(--motion-base) var(--ease);
}

.control-label {
    font-size: 0.9rem;
}

.options {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: 0.5rem;
}

.option {
    min-height: var(--control-height);
    padding-inline: 0.625rem;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--muted);
    white-space: nowrap;
    transition: border-color var(--motion-fast) var(--ease), background-color var(--motion-fast) var(--ease), color var(--motion-fast) var(--ease);
}

.option[aria-pressed="true"] {
    border-color: var(--accent);
    background: var(--accent);
    color: var(--accent-text);
    font-weight: 600;
}

.option[aria-pressed="true"]:focus-visible {
    outline-color: var(--accent-text);
}

form[aria-busy="true"] .control {
    opacity: 0.45;
    pointer-events: none;
}

/* ---- The download button ----
   Its data-mode says what it is doing: idle, indeterminate (the server works),
   determinate (bytes are flowing, --download-progress is 0..1) or complete.
   The fill rises behind the label, and a second copy of the label, in the
   opposite colour, shows through exactly where the fill is. */

#btn {
    position: relative;
    isolation: isolate;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: var(--control-height);
    padding: 0.75rem var(--control-padding-x);
    overflow: hidden;
    border: 1px solid var(--accent);
    border-radius: var(--radius);
    background: var(--accent);
    color: var(--accent-text);
    font-size: 1rem;
    font-weight: 500;
    transition: background-color var(--motion-base) var(--ease), border-color var(--motion-base) var(--ease), color var(--motion-base) var(--ease), transform var(--motion-fast) var(--ease);
}

#btn:focus-visible {
    outline-color: var(--accent-text);
}

#btn:disabled {
    border-color: var(--border);
    background: var(--surface-strong);
    color: var(--muted);
    cursor: default;
}

#btn:not([data-mode="idle"]) {
    cursor: progress;
}

#btn[data-mode="idle"]:not(:disabled):active {
    transform: translateY(1px);
}

@media (hover: hover) and (pointer: fine) {
    #btn[data-mode="idle"]:not(:disabled):hover {
        border-color: var(--accent-hover);
        background: var(--accent-hover);
    }

    .paste:hover,
    .option:hover {
        border-color: var(--text);
        background: var(--surface-strong);
        color: var(--text);
    }

    .option[aria-pressed="true"]:hover {
        border-color: var(--accent-hover);
        background: var(--accent-hover);
        color: var(--accent-text);
    }
}

.btn-progress {
    position: absolute;
    inset: 0;
    z-index: 0;
    width: calc(var(--download-progress, 0) * 100%);
    background: var(--bg);
    opacity: 0;
    transition: width var(--motion-progress) linear, opacity var(--motion-fast) var(--ease);
}

#btn:not([data-mode="idle"]) .btn-progress {
    opacity: 1;
}

#btn[data-mode="indeterminate"] .btn-progress {
    width: 32%;
    opacity: 0.18;
    transition: none;
    animation: sweep 900ms linear infinite;
}

@keyframes sweep {
    from { transform: translateX(-110%); }
    to { transform: translateX(340%); }
}

.btn-content {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
}

.btn-main {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.625rem;
    line-height: 1;
}

.btn-label {
    white-space: nowrap;
}

.btn-content-fill {
    position: absolute;
    inset: 0;
    clip-path: inset(0 calc(100% - var(--download-progress, 0) * 100%) 0 0);
    color: var(--accent);
    opacity: 0;
    pointer-events: none;
    transition: clip-path var(--motion-progress) linear, opacity var(--motion-fast) var(--ease);
}

#btn:not([data-mode="idle"]) .btn-content-fill {
    opacity: 1;
}

#btn[data-mode="indeterminate"] .btn-content-fill {
    opacity: 0;
}

/* Room for the percentage only while one is shown, or an empty slot would push
   "Preparando" off-centre. */
.btn-percent {
    width: 0;
    overflow: hidden;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    text-align: left;
    white-space: nowrap;
    opacity: 0;
    transition: opacity var(--motion-fast) var(--ease), width var(--motion-fast) var(--ease), margin-left var(--motion-fast) var(--ease);
}

#btn[data-mode="determinate"] .btn-percent {
    width: 4.5ch;
    margin-left: 0.375rem;
    opacity: 1;
}

.btn-icon {
    flex: none;
    display: block;
    width: 1em;
    height: 1em;
    transform-box: view-box;
    transform-origin: center;
}

.btn-icon path {
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    shape-rendering: geometricPrecision;
}

.btn-arrow,
.btn-tray {
    transform-box: fill-box;
    transform-origin: center bottom;
    transition: opacity var(--motion-fast) var(--ease);
}

.btn-arrow path,
.btn-tray {
    vector-effect: non-scaling-stroke;
}

.btn-check {
    /* No non-scaling-stroke here: it breaks the pathLength-based dash draw. */
    opacity: 0;
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
    transition: opacity var(--motion-fast) var(--ease), stroke-dashoffset var(--motion-base) var(--ease-emphasis);
}

/* Idle: one nudge on hover hints at the action. */
@media (hover: hover) and (pointer: fine) {
    #btn[data-mode="idle"]:not(:disabled):hover .btn-arrow {
        animation: nudge 520ms var(--ease-emphasis);
    }
}

@keyframes nudge {
    0%, 100% { transform: translateY(0); }
    45% { transform: translateY(2px); }
}

/* The server is preparing the file: the arrow hovers in place, waiting. */
#btn[data-mode="indeterminate"] .btn-arrow {
    animation: wait var(--motion-loop) ease-in-out infinite alternate;
}

@keyframes wait {
    from { transform: translateY(-1.5px); }
    to { transform: translateY(1.5px); }
}

/* Bytes are flowing: arrows fall into the tray, which gives as each one lands. */
#btn[data-mode="determinate"] .btn-arrow {
    animation: drop var(--motion-loop) cubic-bezier(0.5, 0, 0.9, 0.6) infinite;
}

#btn[data-mode="determinate"] .btn-tray {
    animation: catch var(--motion-loop) var(--ease) infinite;
}

@keyframes drop {
    from { transform: translateY(-11px); }
    to { transform: translateY(11px); }
}

@keyframes catch {
    0%, 62%, 100% { transform: scaleX(1); }
    76% { transform: scaleX(0.78); }
}

/* Done: the arrow and tray give way to a check that draws itself. */
#btn[data-mode="complete"] .btn-arrow,
#btn[data-mode="complete"] .btn-tray {
    opacity: 0;
}

#btn[data-mode="complete"] .btn-check {
    opacity: 1;
    stroke-dashoffset: 0;
}

/* ---- Status line ---- */

#status {
    min-height: 1.4em;
    margin-top: calc(var(--gap) / -2);
    color: var(--muted);
    font-size: 0.9rem;
    line-height: 1.4;
}

#status.success #statusText {
    color: var(--success);
}

#status.error #statusText {
    color: var(--error);
}

/* The picker's header carries the hint and the count, so while it is open the
   status line only appears for an error. */
form.is-picking #status:not(.error) {
    display: none;
}

/* ---- Small and short screens ---- */

@media (max-width: 30rem) {
    :root {
        --space-page: 1rem;
    }
}

@media (max-height: 36rem) {
    body {
        align-items: flex-start;
    }

    main {
        margin-block: auto;
    }
}

@media (forced-colors: active) {
    input:focus-visible,
    button:focus-visible {
        outline: 3px solid Highlight;
        outline-offset: -3px;
    }

    .option[aria-pressed="true"] {
        border: 2px solid Highlight;
    }
}

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        transition-delay: 0s !important;
    }

    /* A looping animation parked on its last frame would be wrong, so these stay still. */
    .service-shimmer {
        background-image: none;
    }

    .btn-arrow,
    .btn-tray {
        animation: none !important;
    }

    #btn[data-mode="indeterminate"] .btn-progress {
        width: 100%;
        opacity: 0.12;
        animation: none;
    }
}
