/* ===================================================================
   Folio — application styles
   Tokens live in variables.css. Nothing here should define a colour.

   The stylesheets used to be pulled in with @import from this file,
   which forced the browser to download this one before it even learned
   the other three existed. They're linked from the HTML in the same
   order instead, so all four fetch at once.

   A second :root block also used to sit here, after those imports,
   redefining the whole palette plus the spacing and radius scales with
   the old values. Being later in the cascade, it won — variables.css
   was decorative. It's gone; every name it declared is defined there.
   =================================================================== */

/* ===================================
   Global Styles
   =================================== */

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

html {
    font-size: 16px;
    scroll-behavior: smooth;
}

body {
    font-family: var(--font-body);
    color: var(--ink);
    background: var(--paper);
    line-height: var(--line-height-normal);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    min-height: 100dvh;
    overflow-x: hidden;
}

/* Paper grain.
   This was a four-stop gradient cycling through pink on a 15s loop — a
   moving background nobody asked to watch. Paper doesn't move, but it
   isn't perfectly smooth either, so a fixed layer of fine noise sits
   over the whole page at almost no opacity. You won't see it. You'd
   notice if it went. */
body::before {
    content: '';
    position: fixed;
    inset: 0;
    z-index: -2;
    pointer-events: none;
    opacity: 0.035;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

[data-theme="dark"] body::before {
    opacity: 0.05;
}

/* ===================================
   Typography
   =================================== */

/* Display type wants negative tracking and less leading; at 40px the
   default 1.2 and 0 tracking read as loose and unset. */
h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-display);
    font-weight: 600;
    line-height: var(--line-height-tight);
    letter-spacing: var(--tracking-tight);
    color: var(--ink);
    text-wrap: balance;
}

h1 { font-size: var(--font-size-4xl); letter-spacing: var(--tracking-display); }
h2 { font-size: var(--font-size-3xl); }
h3 { font-size: var(--font-size-xl);  }
h4 { font-size: var(--font-size-lg);  letter-spacing: var(--tracking-normal); }

p {
    margin-bottom: var(--spacing-sm);
    text-wrap: pretty;
}

/* ─── none of that reaches the sheet, and the fix is not here ──────
   The document is drawn on this same page out of ordinary h1, h2, p
   and li, so every rule above lands on it too unless
   css/document.css has declared the same property. Two of them had
   not been, and both moved lines; phase 5 found it by asking
   build.html and tests/ats/harness.html for the same document and
   getting two different page cuts back.

   The guard that closed it used to sit here. It does not any more,
   because a guard here is a list somebody has to remember to add to:
   it can only patch the app into agreeing with the harness, one
   named property at a time. css/document.css declares the document
   for itself instead, and every surface that draws the document
   loads that file. See the block there, and
   tests/css-parity.test.mjs, which fails on one differing computed
   property across all sixty documents.

   So: a new global rule above needs nothing added here. If it lands
   on the sheet and should not, document.css is where it is answered,
   and the test says so before it ships. */

/* Numbers, dates and identifiers get the mono stamp and line up in
   columns instead of drifting with proportional digits. */
time, .tabular, [data-numeric] {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-feature-settings: 'tnum';
}

a {
    color: var(--pen);
    text-decoration: none;
    transition: color var(--transition-fast);
}

a:hover {
    color: var(--pen-deep);
}

a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
    border-radius: var(--radius-sm);
}

/* ===================================
   Icons
   =================================== */

/* Phosphor ships as a font, so glyphs inherit colour and scale with the
   text around them. One weight (regular) across the whole app, which is
   what Font Awesome's mixed solid/regular set never gave us. */
.ph {
    font-size: 1.15em;
    line-height: 1;
    vertical-align: -0.1em;
}

/* The only rotation on the page, and only while something is saving. */
.icon-spin {
    animation: icon-spin 900ms linear infinite;
}

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

/* ===================================
   Layout Components
   =================================== */

.app-container {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

/* Header */
.app-header {
    background: var(--glass-bg);
    border-bottom: 1px solid var(--glass-border);
    box-shadow: var(--shadow-md);
    position: sticky;
    top: 0;
    z-index: 100;
}

/* One row that cannot get wider than the window it is in.
   Everything here is either fixed-size or allowed to shrink: the wordmark
   truncates, the save state truncates, and the three controls on the right
   are 44px each and never shrink. There is no wrap and no scroll, because
   both of those are ways of saying the row did not fit. */
.header-content {
    max-width: 1200px;
    margin: 0 auto;
    /* Side padding matches .progress-container and .main-content so the
       wordmark starts on the same line as the page under it. */
    padding: var(--spacing-xs) var(--spacing-lg);
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
    min-height: var(--header-height);
}

.logo {
    display: inline-flex;
    align-items: center;
    gap: var(--spacing-xs);
    min-width: 0;
    min-height: 44px;
    text-decoration: none;
    border-radius: var(--radius-md);
}

.logo:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
}

.logo-mark {
    width: 26px;
    height: 26px;
    flex-shrink: 0;
    display: block;
}

/* The wordmark carries the brand on its own, no gradient fill, no
   clipping tricks. Tight tracking so it reads as one object. */
.logo-word {
    font-family: var(--font-display);
    font-size: 1.25rem;
    font-weight: 600;
    letter-spacing: var(--tracking-tight);
    color: var(--ink);
    line-height: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

@keyframes pulse {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.02); }
}

/* The save state is the last thing before the controls, and the auto margin
   is what pushes the whole right-hand group over. Nothing else in the row
   needs a rule about where it goes. */
.autosave-indicator {
    margin-inline-start: auto;
    min-width: 0;
}

.autosave-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.header-actions {
    display: flex;
    align-items: center;
    gap: var(--spacing-2xs);
    flex: 0 0 auto;
}

/* ─── the menu ────────────────────────────────────────────────────────
   Import, theme, feedback and clear used to be four separate buttons in
   the row. None of them is something you reach for while typing, and all
   four together were most of the 543px. */
.header-more {
    position: relative;
}

.header-menu {
    position: absolute;
    top: calc(100% + var(--spacing-2xs));
    inset-inline-end: 0;
    z-index: var(--z-dropdown);
    min-width: 12.5rem;
    /* Anchored to the right edge of a header that is already inside the
       page padding, so this can never be the thing that widens the page. */
    max-width: calc(100vw - var(--spacing-xl));
    padding: var(--spacing-2xs);
    background: var(--paper-raised);
    border: 1px solid var(--rule);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
}

.header-menu[hidden] {
    display: none;
}

.header-menu-item {
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
    width: 100%;
    min-height: 44px;
    padding: 0 var(--spacing-sm);
    background: none;
    border: none;
    border-radius: var(--radius-md);
    font-family: inherit;
    font-size: var(--font-size-sm);
    color: var(--ink-2);
    text-align: start;
    text-decoration: none;
    cursor: pointer;
    transition: background-color var(--transition-fast), color var(--transition-fast);
}

.header-menu-item:hover {
    background: var(--surface);
    color: var(--ink);
}

.header-menu-item i {
    color: var(--ink-soft);
}

.header-menu-item:hover i {
    color: var(--ink-2);
}

/* Clearing everything you have typed is the one thing in here you cannot
   undo, so it is the one thing that is not ink-coloured, and it sits below
   a rule rather than next to the others. */
.header-menu-item-stop,
.header-menu-item-stop i {
    color: var(--stop);
}

.header-menu-item-stop:hover,
.header-menu-item-stop:hover i {
    background: var(--stop-wash);
    color: var(--stop-deep);
}

.header-menu-rule {
    height: 1px;
    margin: var(--spacing-2xs) 0;
    background: var(--rule);
}

/* The theme item names what pressing it does, not what the page already
   is, so the label and the glyph both swap with the theme. */
.theme-when-dark {
    display: none;
}

[data-theme="dark"] .theme-when-light {
    display: none;
}

[data-theme="dark"] .theme-when-dark {
    display: inline;
}

[data-theme="dark"] i.theme-when-dark {
    display: inline-block;
}

/* Progress Bar */
.progress-container {
    max-width: 1200px;
    margin: 0 auto;
    padding: var(--spacing-lg) var(--spacing-lg) 0;
    width: 100%;
}

.progress-bar {
    height: 8px;
    background: var(--bg-tertiary);
    border-radius: 999px;
    overflow: hidden;
    margin-bottom: var(--spacing-md);
    box-shadow: var(--shadow-well);
}

.progress-fill {
    height: 100%;
    background: var(--primary-gradient);
    border-radius: 999px;
    transition: width var(--transition-slow);
    position: relative;
    overflow: hidden;
}

.progress-steps {
    display: flex;
    justify-content: space-between;
    gap: var(--spacing-xs);
    flex-wrap: wrap;
}

/* These are clickable, and the floor is on the element rather than on
   whatever happens to be inside it. It read 34px the moment R3 shortened
   the step titles: ten long names like "Professional Summary" wrapped to
   two lines and carried the height by accident, and seven short ones do
   not. A target that is only big enough because the words in it are long
   is not sized, it is lucky. */
.progress-step {
    flex: 1;
    min-width: 80px;
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: var(--spacing-xs);
    font-size: 0.75rem;
    color: var(--text-light);
    font-weight: 500;
    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    position: relative;
}

.progress-step.clickable {
    cursor: pointer;
}

.progress-step.clickable:hover {
    color: var(--primary-color);
    transform: translateY(-2px);
}

.progress-step.active {
    color: var(--primary-color);
    font-weight: 600;
}

.progress-step.completed {
    color: var(--success-color);
}

.progress-step.completed::after {
    content: '✓';
    position: absolute;
    top: -8px;
    right: 5px;
    font-size: 0.7rem;
    color: var(--success-color);
}

/* Main Content */
.main-content {
    flex: 1;
    max-width: 1200px;
    margin: 0 auto;
    padding: var(--spacing-lg);
    width: 100%;
}

.form-container {
    background: var(--glass-bg);
    border-radius: var(--radius-xl);
    padding: var(--spacing-xl);
    box-shadow: var(--shadow-xl), 0 0 0 1px rgba(var(--pen-rgb), 0.1);
    border: 1px solid var(--glass-border);
    animation: fadeInUp var(--transition-slow);
}

@keyframes fadeInUp {
    from {
        opacity: 0;
        transform: translateY(20px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* Footer */
.app-footer {
    background: var(--glass-bg);
    border-top: 1px solid var(--glass-border);
    padding: var(--spacing-lg) var(--spacing-md);
    text-align: center;
    margin-top: var(--spacing-xl);
}

.app-footer p {
    margin: var(--spacing-xs) auto;
    max-width: 46rem;
    font-size: 0.875rem;
    color: var(--text-secondary);
}

/* Was set in --success-color, which made a permanent line of green text
   the second loudest thing on the page after the accent. Nothing has
   succeeded here; it is a standing fact about where your data lives, so
   it reads as one of the three footer lines rather than as a result. The
   status tokens are for status. The shield stays, because that is the
   part you recognise before you read the sentence.

   --ink-2 and not --ink-soft: soft on paper measures 4.47:1, which is
   under the floor R1 spent a phase getting everything above. */
.privacy-notice {
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: var(--spacing-xs);
    color: var(--ink-2);
}

.privacy-notice i {
    color: var(--ok);
}

.built-by {
    margin-top: 0.5rem;
}

.built-by a {
    color: var(--primary-color);
    text-decoration: none;
    font-weight: 600;
    transition: color var(--transition-fast);
}

.built-by a:hover {
    color: var(--primary-dark);
    text-decoration: underline;
}

.btn-icon {
    background: transparent;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: var(--spacing-xs) var(--spacing-sm);
    cursor: pointer;
    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    font-size: 1rem;
}

.btn-icon:hover {
    transform: scale(1.1) rotate(5deg);
    border-color: var(--primary-color);
    color: var(--primary-color);
    box-shadow: 0 2px 8px rgba(var(--pen-rgb), 0.2);
}

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

.btn-icon.btn-danger:hover {
    background: var(--error-color);
    color: var(--on-pen);
    border-color: var(--error-color);
}

.btn-icon:disabled {
    opacity: 0.4;
    cursor: not-allowed;
    pointer-events: none;
}

.modal-actions {
    display: flex;
    gap: var(--spacing-sm);
    justify-content: flex-end;
    margin-top: var(--spacing-md);
}

.built-by {
    font-size: 0.9375rem;
    color: var(--text-primary);
    margin-bottom: var(--spacing-xs);
    text-align: center;
}

/* The one link in the footer, and 105 x 16 before this. Height only, no
   side padding: padding here opens a visible gap after "Built by" and the
   name is already 120px wide, which is well over the floor on that axis. */
.built-by a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    color: var(--primary-color);
    font-weight: 600;
    text-decoration: none;
    transition: color var(--transition-fast);
}

.built-by a:hover {
    color: var(--primary-dark);
    text-decoration: underline;
}

/* Success Modal */
.modal-success {
    max-width: 600px;
}

.modal-close-btn {
    text-align: right;
    padding: var(--spacing-md) var(--spacing-md) 0;
}

.success-body {
    text-align: center;
    padding: var(--spacing-lg) var(--spacing-xl);
}

.success-icon {
    font-size: 5rem;
    color: var(--success-color);
    margin-bottom: var(--spacing-md);
    animation: scaleInBounce 0.5s ease-out;
}

@keyframes scaleInBounce {
    0% {
        transform: scale(0);
        opacity: 0;
    }
    50% {
        transform: scale(1.1);
    }
    100% {
        transform: scale(1);
        opacity: 1;
    }
}

.success-title {
    font-size: 1.5rem;
    color: var(--text-primary);
    margin-bottom: var(--spacing-sm);
}

.success-message {
    font-size: 1rem;
    color: var(--text-secondary);
    margin-bottom: var(--spacing-lg);
}

.success-actions {
    margin: var(--spacing-lg) 0;
}

.feedback-btn {
    font-size: 1rem;
    padding: var(--spacing-md) var(--spacing-xl);
}

.creator-credit {
    margin-top: var(--spacing-xl);
    padding-top: var(--spacing-lg);
    border-top: 1px solid var(--border-color);
}

.creator-credit p {
    font-size: 0.9375rem;
    color: var(--text-primary);
    margin-bottom: var(--spacing-sm);
}

.creator-credit a {
    color: var(--primary-color);
    text-decoration: none;
    transition: color var(--transition-fast);
}

.creator-credit a:hover {
    color: var(--primary-dark);
}

.linktree-link {
    display: inline-flex;
    align-items: center;
    gap: var(--spacing-xs);
    font-size: 0.875rem;
    color: var(--text-secondary) !important;
    margin-top: var(--spacing-xs);
}

.linktree-link:hover {
    color: var(--primary-color) !important;
}

.auto-dismiss-text {
    font-size: 0.8125rem;
    color: var(--text-light);
    margin-top: var(--spacing-lg);
    margin-bottom: 0;
}

.auto-dismiss-text i {
    color: var(--warning-color);
}

.auto-dismiss-text span {
    font-weight: 600;
    color: var(--primary-color);
}

/* ===================================
   Form Elements
   =================================== */

.form-step {
    animation: slideIn var(--transition-base);
}

@keyframes slideIn {
    from {
        opacity: 0;
        transform: translateX(20px);
    }
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

.step-header {
    margin-bottom: var(--spacing-lg);
    text-align: center;
}

.step-title {
    font-size: 1.75rem;
    margin-bottom: var(--spacing-xs);
    color: var(--text-primary);
}

.step-description {
    color: var(--text-secondary);
    font-size: 1rem;
}

.form-group {
    margin-bottom: var(--spacing-md);
}

.form-group label {
    display: block;
    margin-bottom: var(--spacing-xs);
    font-weight: 500;
    color: var(--text-primary);
    font-size: 0.875rem;
}

.label-required::after {
    content: ' *';
    color: var(--error-color);
}

/* Fields sit on the recessed surface with a hairline, not on the page
   background behind a 2px outline. The old version gave the field no fill
   of its own, so all the work fell to the border, and any tint in the
   border colour became the loudest thing on screen. 16px on the font size
   because anything smaller makes iOS zoom the page on focus. */
.form-group input[type="text"],
.form-group input[type="email"],
.form-group input[type="tel"],
.form-group input[type="url"],
.form-group input[type="number"],
.form-group select,
.form-group textarea {
    width: 100%;
    padding: 0.7rem 0.9rem;
    border: 1px solid var(--rule);
    border-radius: var(--radius-md);
    font-size: var(--font-size-md);
    font-family: inherit;
    transition: border-color var(--transition-fast),
                background-color var(--transition-fast),
                box-shadow var(--transition-fast);
    background-color: var(--surface);
    color: var(--ink);
}

.form-group input::placeholder,
.form-group textarea::placeholder {
    color: var(--ink-faint);
}

.form-group input:hover,
.form-group select:hover,
.form-group textarea:hover {
    border-color: var(--ink-faint);
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
    outline: none;
    border-color: var(--pen);
    background-color: var(--paper-raised);
    box-shadow: var(--focus-ring);
}

/* Validation States */
.form-group input.valid,
.form-group select.valid,
.form-group textarea.valid {
    border-color: var(--ok);
    background-image: var(--valid-tick);
    background-repeat: no-repeat;
    background-position: right 0.7rem center;
    background-size: 1.1rem;
    padding-right: 2.5rem;
}

.form-group input.invalid,
.form-group select.invalid,
.form-group textarea.invalid {
    border-color: var(--error-color);
}

.error-message {
    display: block;
    margin-top: var(--spacing-xs);
    font-size: 0.8125rem;
    color: var(--error-color);
    animation: shake 0.3s;
}

@keyframes shake {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-5px); }
    75% { transform: translateX(5px); }
}

.help-text {
    display: block;
    margin-top: var(--spacing-xs);
    font-size: 0.8125rem;
    color: var(--text-secondary);
}

/* Character Counter */
.char-counter {
    text-align: right;
    font-size: 0.8125rem;
    color: var(--text-light);
    margin-top: var(--spacing-xs);
}

.char-counter.warning {
    color: var(--warning-color);
    font-weight: 500;
}

.char-counter.error {
    color: var(--error-color);
    font-weight: 500;
}

/* Keyword Analyzer Styles */
.keyword-input-section {
    margin-bottom: var(--spacing-lg);
}

.keyword-input-section .form-group {
    margin-bottom: var(--spacing-md);
}

.keyword-input-section label {
    display: block;
    margin-bottom: var(--spacing-xs);
    font-weight: 500;
    color: var(--text-primary);
}

.keyword-input-section textarea {
    width: 100%;
    border: 2px solid var(--border-color);
    border-radius: var(--radius-md);
    padding: var(--spacing-sm);
    font-family: inherit;
    font-size: 0.9375rem;
    transition: border-color var(--transition-fast);
}

.keyword-input-section textarea:focus {
    outline: none;
    border-color: var(--primary-color);
    box-shadow: 0 0 0 3px rgba(var(--pen-rgb), 0.1);
}

.keyword-input-section .btn {
    width: 100%;
    font-size: 1rem;
}

/* Textarea */
textarea {
    resize: vertical;
    min-height: 100px;
    line-height: 1.5;
}

/* Select
   A data URI can't read a custom property, so the two glyphs below are
   declared as variables and swapped wholesale for dark. They were both
   hardcoded to colours from the old palette: a cool #6b7280 chevron
   against warm neutrals, and a #10b981 tick that was never our green. */
:root {
    --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%2378736A'%3E%3Cpath fill-rule='evenodd' d='M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z' clip-rule='evenodd'/%3E%3C/svg%3E");
    --valid-tick: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%232F7D4F'%3E%3Cpath fill-rule='evenodd' d='M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z' clip-rule='evenodd'/%3E%3C/svg%3E");
}

[data-theme="dark"] {
    --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%23928D83'%3E%3Cpath fill-rule='evenodd' d='M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z' clip-rule='evenodd'/%3E%3C/svg%3E");
    --valid-tick: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%2362B884'%3E%3Cpath fill-rule='evenodd' d='M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z' clip-rule='evenodd'/%3E%3C/svg%3E");
}

select {
    cursor: pointer;
    appearance: none;
    background-image: var(--select-arrow);
    background-repeat: no-repeat;
    background-position: right 0.7rem center;
    background-size: 1.1rem;
    padding-right: 2.5rem;
}

/* ===================================================================
   The floor under anything you tap
   44px, which is Apple's HIG number and where WCAG 2.2 AAA also lands.

   R1 counted seventeen kinds of target under it at 390px and fourteen at
   1440 (D-52). Nine of the seventeen were the form's own inputs and
   selects, sitting at 42 and 40 because the padding happened to add up to
   that and nothing was holding a floor.

   This is declared on the controls rather than inside the seven different
   parents they happen to live in, because a floor that has to be
   remembered per-container is how nine of them got under it. The two that
   nobody would have found by reading .form-group are named below: the
   month and year selects sit in .date-fields, which is inside a bare div,
   and #resumeHeadline sits in another one.

   Checkboxes and radios are exempt and stay 18px: the finger lands on the
   label, and the label is what tests/interface.test.mjs measures. Their
   labels are given the floor instead, further down.
   =================================================================== */

.app-container input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
.app-container select,
.modal input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
.modal select {
    min-height: 44px;
}

/* ===================================
   Buttons
   =================================== */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--spacing-xs);
    padding: 0.75rem 1.5rem;
    border: none;
    border-radius: var(--radius-md);
    font-size: 0.9375rem;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    transition: all var(--transition-fast);
    text-decoration: none;
    min-height: 44px;
    min-width: 44px;
}

.btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
}

.btn-primary {
    background: var(--primary-gradient);
    color: var(--on-pen);
    box-shadow: var(--shadow-md);
    position: relative;
    overflow: hidden;
}

.btn-primary::before {
    content: '';
    position: absolute;
    top: 0;
    left: -100%;
    width: 100%;
    height: 100%;
    background: linear-gradient(90deg, transparent, var(--sheen), transparent);
    transition: left 0.5s;
}

.btn-primary:hover::before {
    left: 100%;
}

.btn-primary:hover:not(:disabled) {
    transform: translateY(-2px);
    box-shadow: 0 8px 20px rgba(var(--pen-rgb), 0.4),
                0 4px 10px rgba(var(--pen-rgb), 0.3);
}

.btn-primary:active:not(:disabled) {
    transform: translateY(0);
}

.btn-secondary {
    background: transparent;
    color: var(--text-primary);
    border: 2px solid var(--border-color);
    position: relative;
    overflow: hidden;
}

.btn-secondary::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: var(--primary-gradient);
    opacity: 0;
    transition: opacity var(--transition-fast);
    z-index: -1;
}

.btn-secondary:hover:not(:disabled) {
    border-color: var(--primary-color);
    color: var(--on-pen);
}

.btn-secondary:hover:not(:disabled)::before {
    opacity: 1;
}

.btn-danger {
    background: var(--error-color);
    color: var(--on-pen);
}

.btn-danger:hover:not(:disabled) {
    background: var(--stop-deep);
}

/* Small is a type size and a padding, not a smaller thing to hit. 36px was
   under the floor everywhere .btn-sm was used, including the two buttons in
   every repeating section header. */
.btn-sm {
    padding: 0.5rem 1rem;
    font-size: 0.8125rem;
    min-height: 44px;
    min-width: 44px;
}

.btn-lg {
    padding: 1rem 2rem;
    font-size: 1rem;
}

.btn-icon {
    width: 44px;
    height: 44px;
    border-radius: var(--radius-md);
    border: 2px solid var(--border-color);
    background: var(--bg-primary);
    color: var(--text-secondary);
    cursor: pointer;
    transition: all var(--transition-fast);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.125rem;
}

.btn-icon:hover {
    border-color: var(--primary-color);
    color: var(--primary-color);
    transform: translateY(-2px);
}

/* Form Navigation */
.form-navigation {
    display: flex;
    justify-content: space-between;
    gap: var(--spacing-md);
    margin-top: var(--spacing-xl);
    padding-top: var(--spacing-lg);
    border-top: 1px solid var(--border-color);
}

.form-navigation .btn {
    flex: 1;
    max-width: 200px;
}

/* Responsive Design */
/* .divider-text was six rules and .profile-type-card.selected was one, and
   nothing in build.html, index.html or js/ named either. Deleted in R4
   (D-61 Q3).

   The first mattered more than a tidy: .divider-text span carried
   text-transform: uppercase, which is the third rule to do that while dead.
   .badge went in R2 and .divider-with-text in R3, and the note beside
   stringsRenderedInAllCaps said those were both of them. So that count read
   zero because a third dead rule was not being rendered through rather than
   because the rule is kept - which is the same sentence, twice already, and
   the note is corrected now.

   The second is what R3's sweep missed: it removed the five sibling
   .profile-type-card rules and left this one, because the question it asked
   was "what did my work kill" and not "what is dead". R4 asked the wider one
   over every class in every stylesheet; 58 more are named nowhere outside
   the utility library, and they are listed in PR4-report.md section 4
   rather than deleted, because that is a sweep and not this phase. */

/* Repeating Sections */
.repeating-section {
    margin-bottom: var(--spacing-lg);
}

.repeating-item {
    background: var(--bg-primary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: var(--spacing-lg);
    margin-bottom: var(--spacing-md);
    position: relative;
    animation: fadeInUp var(--transition-base);
}

.repeating-item-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: var(--spacing-md);
    padding-bottom: var(--spacing-sm);
    border-bottom: 1px solid var(--border-color);
}

.repeating-item-title {
    font-size: 1rem;
    font-weight: 600;
    color: var(--text-primary);
}

.btn-delete {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    background: transparent;
    border: 2px solid var(--error-color);
    color: var(--error-color);
    padding: 0 1rem;
    border-radius: var(--radius-sm);
    font-size: 0.875rem;
    cursor: pointer;
    transition: all var(--transition-fast);
    font-weight: 500;
}

.btn-delete:hover {
    background: var(--error-color);
    color: var(--on-pen);
}

.btn-add-more {
    width: 100%;
    margin-top: var(--spacing-md);
    background: transparent;
    border: 2px dashed var(--border-color);
    color: var(--text-secondary);
    padding: var(--spacing-md);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: all var(--transition-fast);
    font-weight: 500;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--spacing-xs);
}

.btn-add-more:hover {
    border-color: var(--primary-color);
    color: var(--primary-color);
    background: var(--surface);
}

/* Date Range Fields */
.date-range {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--spacing-lg);
    align-items: start;   /* stop the shorter half stretching to match */
}

/* Each half was a tinted card with its own border, hover shadow and an
   all-caps label, wrapping two dropdowns that needed none of it. The
   cards also stretched to match each other, so the left one carried a
   block of dead space the height of the "currently work here" checkbox.
   A label and two fields, like every other row on the form. */
.date-range > div {
    min-width: 0;
}

.date-range label {
    display: block;
    margin-bottom: var(--spacing-xs);
    font-weight: 500;
    font-size: var(--font-size-sm);
    color: var(--ink);
}

.date-fields {
    display: grid;
    grid-template-columns: 1.5fr 1fr;
    gap: var(--spacing-sm);
}

/* Month and year inherit everything from the shared field rules above.
   This block used to re-declare its own 2px border and page-coloured
   background, which is how the date row ended up looking different from
   every other field on the form. Only the parts that genuinely differ
   stay: dates are numerals, so they get the mono face and tabular
   figures, and the year column lines up. */
.date-fields select {
    padding: 0.7rem 0.75rem;
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    cursor: pointer;
}

/* Current Checkbox */
/* A checkbox is a checkbox. It does not need a tinted panel and a border
   of its own to be findable, and the panel is what forced the start-date
   column to stretch alongside it. */
.current-checkbox {
    display: flex;
    align-items: center;
    gap: var(--spacing-xs);
    margin-top: var(--spacing-sm);
    cursor: pointer;
}

.current-checkbox input[type="checkbox"] {
    width: 20px;
    height: 20px;
    cursor: pointer;
    accent-color: var(--primary-color);
}

.current-checkbox label {
    display: flex;
    align-items: center;
    min-height: 44px;
    margin: 0;
    cursor: pointer;
    font-size: var(--font-size-sm);
    font-weight: 400;
    color: var(--ink-2);
}

/* ===================================
   Modals
   =================================== */

.modal {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: var(--scrim);
    z-index: 1000;
    animation: fadeIn var(--transition-base);
}

.modal.active {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--spacing-md);
}

@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* dvh, not vh. On a phone vh is the height with the browser chrome
   retracted, so a panel at 90vh is taller than the glass whenever the
   address bar is showing, which is most of the time. */
.modal-content {
    background: var(--bg-primary);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-2xl),
                0 10px 25px rgba(var(--pen-rgb), 0.2);
    border: 1px solid rgba(var(--pen-rgb), 0.2);
    max-width: 500px;
    width: 100%;
    max-height: 90dvh;
    overflow-y: auto;
    animation: scaleIn var(--transition-base);
}

.modal-content.modal-large {
    max-width: 800px;
}

.modal-content.modal-small {
    max-width: 400px;
}

@keyframes scaleIn {
    from {
        opacity: 0;
        transform: scale(0.9);
    }
    to {
        opacity: 1;
        transform: scale(1);
    }
}

.modal-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: var(--spacing-lg);
    border-bottom: 1px solid var(--border-color);
}

.modal-header h2 {
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
    font-size: 1.25rem;
}

/* Was 32 declared and 29 measured, and it is the way out of every modal
   in the app including the one you export from. */
.modal-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    border: none;
    border-radius: var(--radius-md);
    background: transparent;
    font-size: 1.5rem;
    color: var(--text-secondary);
    cursor: pointer;
    transition: color var(--transition-fast), background-color var(--transition-fast);
    line-height: 1;
}

.modal-close:hover {
    background: var(--surface);
    color: var(--text-primary);
}

.modal-body {
    padding: var(--spacing-lg);
}

.modal-actions {
    display: flex;
    gap: var(--spacing-sm);
    margin-top: var(--spacing-lg);
}

.modal-actions .btn {
    flex: 1;
}

/* Download Options */
.download-options {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--spacing-md);
    margin-bottom: var(--spacing-lg);
}

.download-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--spacing-sm);
    padding: var(--spacing-lg);
    background: var(--bg-secondary);
    border: 2px solid var(--border-color);
    border-radius: var(--radius-lg);
    cursor: pointer;
    transition: all var(--transition-base);
    text-align: center;
}

.download-btn:hover {
    border-color: var(--primary-color);
    transform: translateY(-4px);
    box-shadow: var(--shadow-lg);
}

.download-btn i {
    font-size: 2.5rem;
    color: var(--primary-color);
}

.download-btn span {
    font-weight: 600;
    color: var(--text-primary);
}

.download-btn small {
    font-size: 0.8125rem;
    color: var(--text-secondary);
}

/* ─── locked, and saying so ───────────────────────────────────────────
   The button stays a button: same size, same position, still pressable,
   still focusable. Pressing it opens the payment screen rather than doing
   nothing, so the lock reads as a price and not as a fault. Disabled was
   the other option and it is the one that makes a product look broken. */
.download-btn.locked {
    border-style: dashed;
    background: var(--surface);
}

.download-btn.locked i {
    color: var(--ink-soft);
}

.download-btn .lock-tag {
    display: inline-flex;
    align-items: center;
    gap: var(--spacing-2xs);
    font-family: var(--font-body);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-medium);
    letter-spacing: var(--tracking-label);
    color: var(--ink-2);
}

.download-btn .lock-tag i {
    font-size: var(--font-size-sm);
    color: var(--ink-2);
}

/* ─── what the lock costs, said once above the five ───────────────── */
.paywall-note {
    margin-bottom: var(--spacing-lg);
    padding: var(--spacing-md);
    border: 1px solid var(--rule);
    border-radius: var(--radius-lg);
    background: var(--surface);
}

.paywall-note.paywall-open {
    border-color: var(--ok);
    background: var(--ok-wash);
}

/* Not a flex row. It was, and the icon, the bold half and the rest of the
   sentence became three flex items: "Downloads are locked." broke over two
   lines with the sentence it belongs to sitting beside it. One paragraph,
   one text flow, and the icon is inline in it. */
.paywall-line {
    margin: 0;
    font-size: var(--font-size-base);
    line-height: var(--line-height-normal);
    color: var(--ink-2);
}

.paywall-line strong {
    color: var(--ink);
}

.paywall-line i {
    margin-inline-end: var(--spacing-2xs);
    color: var(--ink-2);
}

.paywall-note.paywall-open .paywall-line,
.paywall-note.paywall-open .paywall-line i {
    color: var(--ok);
}

.paywall-sub {
    margin-top: var(--spacing-xs);
}

.paywall-note .btn {
    margin-top: var(--spacing-md);
}

/* ─── the payment screen ──────────────────────────────────────────────
   Three steps, in the order they happen, because the rail is manual: the
   transfer goes out, a person sees it, a code comes back. Nothing here
   talks to a server. Saying so on the screen is what stops the wait from
   reading as a failure. */
.pay-lede {
    margin-bottom: var(--spacing-lg);
    font-size: var(--font-size-lg);
    line-height: var(--line-height-normal);
    color: var(--ink);
}

.pay-steps {
    margin: 0 0 var(--spacing-lg);
    padding-inline-start: var(--spacing-lg);
    display: flex;
    flex-direction: column;
    gap: var(--spacing-md);
    font-size: var(--font-size-base);
    line-height: var(--line-height-relaxed);
    color: var(--ink-2);
}

/* A thumb has to be able to hit it, and inside a sentence a link is as tall
   as the type. So it carries its own floor rather than relying on the line
   box it happens to sit in. */
.pay-steps a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    color: var(--pen);
    font-weight: var(--font-weight-medium);
}

.pay-handle {
    display: inline-block;
    padding: var(--spacing-2xs) var(--spacing-xs);
    border: 1px solid var(--rule);
    border-radius: var(--radius-sm);
    background: var(--paper-raised);
    font-family: var(--font-mono);
    font-size: var(--font-size-sm);
    color: var(--ink);
    overflow-wrap: anywhere;
}

.pay-steps .btn {
    margin-inline-start: var(--spacing-xs);
    vertical-align: middle;
}

.pay-block {
    padding-top: var(--spacing-md);
    margin-top: var(--spacing-md);
    border-top: 1px solid var(--rule);
}

.pay-block label {
    display: block;
    margin-bottom: var(--spacing-2xs);
    font-family: var(--font-display);
    font-weight: var(--font-weight-semibold);
    font-size: var(--font-size-base);
    color: var(--ink);
}

.pay-hint {
    margin-bottom: var(--spacing-xs);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-normal);
    color: var(--ink-2);
}

.pay-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-xs);
    align-items: stretch;
}

.pay-row .form-input {
    flex: 1 1 14rem;
}

.pay-code-input {
    font-family: var(--font-mono);
    letter-spacing: var(--tracking-label);
}

.pay-state {
    margin-top: var(--spacing-xs);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-normal);
}

.pay-state.pay-waiting {
    color: var(--ink-2);
}

.pay-state.pay-problem {
    color: var(--stop);
}

.pay-state.pay-open {
    color: var(--ok);
}

/* Template picker.
   Replaced four colour swatches. The names carry the choice, because what
   separates these six is typography and rhythm, and a swatch cannot show
   either. Picking one redraws the preview, so the answer is on screen
   before anything is downloaded. */
.template-picker {
    padding-top: var(--spacing-lg);
    margin-top: var(--spacing-lg);
    border-top: 1px solid var(--rule);
}

.template-picker-label {
    margin-bottom: var(--spacing-sm);
    font-family: var(--font-display);
    font-weight: 600;
    font-size: var(--font-size-base);
    color: var(--ink);
}

.template-options {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-xs);
}

.template-btn {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding: 0 var(--spacing-md);
    background: none;
    border: 1px solid var(--rule);
    border-radius: var(--radius-md);
    font-family: var(--font-display);
    font-size: var(--font-size-sm);
    color: var(--ink-2);
    cursor: pointer;
    transition: border-color var(--transition-fast), color var(--transition-fast);
}

.template-btn:hover { border-color: var(--ink-faint); color: var(--ink); }

.template-btn[aria-checked="true"] {
    border-color: var(--pen);
    color: var(--pen);
    font-weight: 500;
}

.template-note {
    margin-top: var(--spacing-sm);
    font-size: var(--font-size-sm);
    color: var(--ink-soft);
    min-height: 2.6em;      /* holds its ground so the modal does not jump */
}

/* --ink-faint was here and it is a placeholder colour: 2.41:1 light and
   3.02:1 dark, on the sentence that tells you which template suits you. Same
   defect and the same two numbers as .preview-meta a thousand lines down,
   which was moved to --ink-2 before R4 while these two were left. R1's
   contrast sweep was one pass by hand and nothing guarded it afterwards;
   contrastPairsUnder45 found this one. */
.template-note .template-suits {
    display: block;
    color: var(--ink-soft);
    font-size: var(--font-size-xs);
}

/* ATS Results */
.ats-score {
    text-align: center;
    margin-bottom: var(--spacing-lg);
}

.score-circle {
    width: 150px;
    height: 150px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 auto var(--spacing-md);
    font-size: 2.5rem;
    font-weight: 700;
    position: relative;
}

.score-circle.excellent {
    background: var(--ok);
    color: var(--on-pen);
}

.score-circle.good {
    background: var(--pen);
    color: var(--on-pen);
}

.score-circle.fair {
    background: var(--warn);
    color: var(--on-warn);
}

.score-circle.poor {
    background: var(--stop);
    color: var(--on-pen);
}

.ats-checks {
    margin-top: var(--spacing-lg);
}

/* ─── the two result panels, in classes ──────────────────────────
   Everything below held the same declarations as style="..." in
   js/atschecker.js and js/keywordanalyzer.js until R4. They were sixteen
   of the twenty attributes that kept 'unsafe-inline' in style-src, and
   under the tightened policy they are dropped rather than applied: the
   panels lose their spacing and their status colours and nothing says so.
   Same values, so the panels look the same; a different place, so the
   policy can close. */

.ats-checks h3 {
    margin-bottom: var(--spacing-md);
}

.ats-advice {
    margin-top: var(--spacing-xl);
    padding: var(--spacing-md);
    background: var(--bg-secondary);
    border-radius: var(--radius-md);
}

.ats-advice ul {
    margin: var(--spacing-xs) 0 0 var(--spacing-lg);
    font-size: var(--font-size-sm);
}

.ats-check-item {
    display: flex;
    align-items: start;
    gap: var(--spacing-sm);
    padding: var(--spacing-md);
    background: var(--bg-secondary);
    border-radius: var(--radius-md);
    margin-bottom: var(--spacing-sm);
}

.ats-check-item.pass {
    border-left: 4px solid var(--success-color);
}

.ats-check-item.fail {
    border-left: 4px solid var(--error-color);
}

.ats-check-item i {
    font-size: 1.25rem;
    margin-top: 0.125rem;
}

.ats-check-item.pass i {
    color: var(--success-color);
}

.ats-check-item.fail i {
    color: var(--error-color);
}

.ats-check-content {
    flex: 1;
}

.ats-check-content h4 {
    font-size: 0.9375rem;
    margin-bottom: 0.25rem;
}

.ats-check-content p {
    font-size: 0.8125rem;
    color: var(--text-secondary);
    margin: 0;
}

/* Keyword Results */
.keyword-match-score {
    text-align: center;
    padding: var(--spacing-lg);
    background: var(--bg-secondary);
    border-radius: var(--radius-lg);
    margin-bottom: var(--spacing-lg);
}

.match-percentage {
    font-size: 3rem;
    font-weight: 700;
    background: var(--primary-gradient);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

.kw-score-label {
    color: var(--text-secondary);
    margin: 0;
}

.kw-score-note {
    font-size: var(--font-size-sm);
    margin-top: var(--spacing-xs);
}

.kw-group {
    margin-top: var(--spacing-xl);
}

.kw-group-title {
    margin-bottom: var(--spacing-md);
}

.kw-group-title.is-matched { color: var(--success-color); }
.kw-group-title.is-missing { color: var(--error-color); }

.kw-empty {
    color: var(--text-secondary);
}

.kw-advice {
    margin-top: var(--spacing-xl);
    padding: var(--spacing-lg);
    background: var(--bg-secondary);
    border-radius: var(--radius-md);
}

.kw-advice-list {
    margin: var(--spacing-xs) 0 0 var(--spacing-lg);
    font-size: var(--font-size-sm);
    color: var(--text-secondary);
}

.kw-advice-list li {
    margin-bottom: var(--spacing-xs);
}

.keywords-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: var(--spacing-sm);
    margin-top: var(--spacing-lg);
}

.keyword-tag {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--spacing-sm);
    background: var(--bg-secondary);
    border-radius: var(--radius-sm);
    font-size: 0.875rem;
}

.keyword-tag.matched {
    background: var(--ok-wash);
    border: 1px solid var(--success-color);
    color: var(--success-color);
}

.keyword-tag.missing {
    background: var(--stop-wash);
    border: 1px solid var(--error-color);
    color: var(--error-color);
}

.keyword-tag-plain {
    background: var(--bg-primary);
    border: 1px solid var(--border-color);
}

.keyword-count {
    font-weight: 600;
    margin-left: var(--spacing-xs);
}

/* ===================================
   Responsive Design
   =================================== */

@media (max-width: 1024px) {
    .header-content,
    .progress-container {
        padding-inline: var(--spacing-md);
    }

    .main-content {
        padding: var(--spacing-md);
    }

    .form-container {
        padding: var(--spacing-lg);
    }
}

@media (max-width: 768px) {
    html {
        font-size: 15px;
    }
    
    /* 56 rather than 64, and the toast reads this to sit under the header
       rather than over it. */
    :root {
        --header-height: 56px;
    }

    .header-content {
        padding: var(--spacing-2xs) var(--spacing-md);
        gap: var(--spacing-xs);
    }

    /* Icon and word, no chip. The border and tinted fill were reading as a
       third button next to two real ones. */
    .autosave-indicator {
        padding: 0;
        border: none;
        background: none;
        font-size: var(--font-size-xs);
    }

    .autosave-indicator.saved,
    .autosave-indicator.saving,
    .autosave-indicator.unsaved {
        border: none;
        background: none;
    }

    .progress-container {
        padding: var(--spacing-md) var(--spacing-md) 0;
    }
    
    .progress-steps {
        display: none;
    }

    /* One left edge down the whole phone: header, progress and content all
       start here. They were on 16, 16 and 12 before. */
    .main-content {
        padding: var(--spacing-md);
    }

    .form-container {
        padding: var(--spacing-md);
        border-radius: var(--radius-lg);
    }
    
    .step-title {
        font-size: 1.5rem;
    }
    
    .form-navigation {
        flex-direction: column-reverse;
    }
    
    .form-navigation .btn {
        max-width: 100%;
    }
    
    .date-range {
        grid-template-columns: 1fr;
    }
    
.download-options {
        grid-template-columns: 1fr;
        gap: var(--spacing-xs);
    }

    /* Five export options as tall centred cards is 5 x 150px of scrolling
       on a phone, so the last two are below the fold of the modal and the
       template picker under them is invisible. As rows they are all on
       screen at once, which is what a chooser is for. */
    .download-btn {
        display: grid;
        grid-template-columns: auto 1fr;
        column-gap: var(--spacing-md);
        align-items: center;
        padding: var(--spacing-sm) var(--spacing-md);
        text-align: start;
    }

    .download-btn i {
        grid-row: 1 / span 2;
        font-size: 1.75rem;
    }

    .download-btn:hover {
        transform: none;
    }

    .modal.active {
        padding: var(--spacing-sm);
    }

    .modal-content {
        max-height: calc(100dvh - var(--spacing-lg));
    }

    /* Whatever is in the body, the way out stays where you can reach it. */
    .modal-header {
        position: sticky;
        top: 0;
        z-index: 1;
        padding: var(--spacing-sm) var(--spacing-sm) var(--spacing-sm) var(--spacing-md);
        background: var(--bg-primary);
    }

    .modal-body {
        padding: var(--spacing-md);
    }

    .keywords-grid {
        grid-template-columns: 1fr;
    }
}

@media (max-width: 480px) {
    .btn {
        padding: 0.625rem 1rem;
        font-size: 0.875rem;
    }

    /* Month and year stack rather than share 170px between them. */
    .date-fields {
        grid-template-columns: 1fr;
    }
}

.hidden { display: none !important; }
.visible { display: block !important; }

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

/* Print Styles */

/* The print lock (phase L). The five downloads ask Paywall.unlocked()
   inside runExport; the print path never passed through it, so a person
   who had not paid pressed a button labelled "Print or save as PDF" and
   received the document (PL-report.md, D-83's columns re-measured).

   These rules are inert until <html> carries .print-locked, and that
   class is written by paintPrintLock() in js/formhandler.js - re-read
   inside beforeprint, because expiry is not an event and print time is
   the only moment the status can be re-asked on the paths that have no
   button (Ctrl+P, the browser menu).

   They live here and not in css/document.css on purpose (PL-lock §3.1):
   the hide wins on specificity, not on order. html.print-locked
   #docPrintRoot carries an ID against document.css's class-only
   .doc-print-root reveal, so with both marked !important the hide wins
   from either file, and the protected stylesheet stays untouched. That
   claim is not this comment: tests/print-lock.test.mjs deletes this rule
   in a control run and watches the document come back. */
#printLockNotice { display: none; }

@media print {
    body::before {
        display: none;
    }

    .app-header,
    .progress-container,
    .form-navigation,
    .app-footer {
        display: none;
    }

    html.print-locked #docPrintRoot { display: none !important; }

    /* The measure host is a visibility:hidden absolute box that keeps the
       last measured flow (js/docview.js measure() writes, nothing clears),
       and a box that paints nothing still counts in pagination. Unlocked
       it hides behind the document, which is taller; locked, the notice
       is shorter than it, and it fed a blank second sheet out of the
       printer. Scoped to the lock on purpose: the unlocked print path
       must not move in this phase, and this selector cannot reach it. */
    html.print-locked #docMeasure { display: none !important; }

    html.print-locked #printLockNotice {
        display: block;
        font-family: var(--font-body);
        color: var(--ink);
        max-width: 150mm;
        padding: 28mm 24mm;
        font-size: 12pt;
        line-height: 1.65;
    }

    #printLockNotice h1 {
        font-family: var(--font-display);
        font-size: 17pt;
        font-weight: 600;
        margin: 0 0 9mm;
    }

    #printLockNotice p {
        margin: 0 0 6mm;
    }
}

/* ===================================================================
   The workbench
   Form on the left, the document on the right. The single biggest gap
   in the old build was that you filled in ten steps and downloaded a
   file you had never seen.
   =================================================================== */

.workbench {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--spacing-xl);
    align-items: start;
}

.preview-panel {
    display: none;
}

.preview-head {
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
    padding-bottom: var(--spacing-xs);
    border-bottom: 1px solid var(--rule);
    margin-bottom: var(--spacing-md);
}

.preview-title {
    font-family: var(--font-display);
    font-weight: 600;
    font-size: var(--font-size-base);
    color: var(--ink);
}

/* --ink-faint is a placeholder colour and this is not a placeholder: on a
   phone the drawer bar is the only thing telling you how many pages your CV
   runs to. It measured 2.41:1 light and 3.02:1 dark. The mono face and the
   12px size are what make it secondary, not the ink. */
.preview-meta {
    font-size: var(--font-size-xs);
    color: var(--ink-2);
    margin-inline-start: auto;
}

.preview-close {
    display: none;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    flex: 0 0 auto;
    background: none;
    border: none;
    border-radius: var(--radius-md);
    /* Was --ink-soft, which is 4.47:1 on the page and under the floor R1
       cleared. This is the way out of the drawer, so it is not decoration. */
    color: var(--ink-2);
    cursor: pointer;
    font-size: var(--font-size-xl);
    line-height: 1;
    padding: 0;
}

.preview-close:hover { background: var(--surface); color: var(--ink); }

/* The document inside is laid out at 210mm and scaled by transform to
   whatever width this column happens to be, so the panel only has to
   supply a width. See css/document.css. */
.preview-stage {
    min-width: 0;
}

.preview-print {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--spacing-2xs);
    min-height: 44px;
    background: none;
    border: 1px solid var(--rule);
    border-radius: var(--radius-md);
    padding: 0 var(--spacing-sm);
    font-family: inherit;
    font-size: var(--font-size-xs);
    color: var(--ink-2);
    cursor: pointer;
}

.preview-print:hover { border-color: var(--ink-faint); color: var(--ink); }

.preview-print:disabled {
    color: var(--ink-faint);
    border-color: var(--rule-soft);
    cursor: not-allowed;
}

/* ─── The advice ─────────────────────────────────────────────────
   What the tool says when the document will not fit. Phase 5.

   Styled as an indicator and not as an error, which is the same
   decision the words in js/preview.js are making. Nothing has gone
   wrong: the CV is longer than it needs to be and the tool has a
   number. So no red, no amber, no warning triangle, none of the
   --stop or --warn tokens. It is a note in the accent, pinned to the
   document by a hairline down its left edge, and it reads at the
   weight of a margin annotation rather than of an alarm.

   It sits above the sheet rather than over it. An overlay would be
   the tool interrupting to talk about your writing, and this is a
   thing you glance at while you carry on. */
.advice {
    border-inline-start: 2px solid var(--pen);
    background: var(--pen-wash);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    padding: var(--spacing-sm) var(--spacing-md);
    margin-bottom: var(--spacing-md);
}

.advice-line {
    margin: 0 0 var(--spacing-2xs);
    font-size: var(--font-size-sm);
    line-height: var(--line-height-normal);
    color: var(--ink-2);
}

.advice-line:first-child { color: var(--ink); }
.advice-line:last-of-type { margin-bottom: var(--spacing-sm); }

/* Every number in this interface is set in the mono face. Here that
   is doing a second job as well: the count is the argument the panel
   is making, and it should be findable without reading the sentence
   round it. */
.advice-figure {
    font-family: var(--font-mono);
    font-weight: var(--font-weight-medium);
    font-size: 0.95em;
    color: var(--pen-deep);
}

.advice-acts {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-xs);
}

.advice-btn {
    background: var(--paper-raised);
    border: 1px solid var(--rule);
    border-radius: var(--radius-sm);
    padding: 0.3rem 0.7rem;
    font-family: inherit;
    font-size: var(--font-size-xs);
    color: var(--ink-2);
    cursor: pointer;
    transition: border-color var(--transition-fast), color var(--transition-fast);
}

.advice-btn:hover { border-color: var(--pen-light); color: var(--ink); }
.advice-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.advice-btn[aria-pressed="true"] {
    border-color: var(--pen);
    color: var(--pen-deep);
}

/* "Leave it" is a real answer and not a way of getting rid of a
   warning, so it is the same button as the other two with less
   contrast, rather than a dismissal cross in a corner.

   Almost no side padding, because the three of them are wider than
   this column and the third always wraps onto a line of its own,
   where a button's worth of padding would set it in from the left
   edge of the two above and read as a mistake. */
.advice-btn-quiet {
    background: none;
    border-color: transparent;
    color: var(--ink-soft);
    padding-inline: 0.15rem;
}

.advice-btn-quiet:hover {
    border-color: transparent;
    color: var(--ink-2);
    text-decoration: underline;
}

.advice-detail { margin-top: var(--spacing-sm); }

.advice-cuts-lead {
    margin: 0 0 var(--spacing-xs);
    font-size: var(--font-size-xs);
    color: var(--ink-soft);
}

.advice-cuts {
    margin: 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: var(--spacing-xs);
}

.advice-cuts li {
    padding-top: var(--spacing-xs);
    border-top: 1px solid var(--rule-soft);
}

/* The point in the user's own words, clamped rather than cut with an
   ellipsis in JS: the panel is showing you where something is, and
   the whole of it is on the sheet beside this. */
.advice-cut-text {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-size: var(--font-size-xs);
    line-height: var(--line-height-snug);
    color: var(--ink-2);
}

.advice-cut-meta {
    display: flex;
    justify-content: space-between;
    gap: var(--spacing-xs);
    margin-top: 0.15rem;
    font-size: var(--font-size-xs);
    color: var(--ink-soft);
}

.advice-cut-where {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.advice-cut-lines {
    font-family: var(--font-mono);
    color: var(--pen-deep);
    white-space: nowrap;
}

/* The mark on the sheet itself.
   Scoped to #previewSheet and nowhere else. Print reads
   #docPrintRoot, the PDF is built from the model, and neither can
   reach this rule, so a mark the tool made can never leave as part of
   the document.

   The colour comes from the document's own accent rather than the
   interface's. Paper is paper in either theme, so --pen-wash would
   put a dark navy block behind black text the moment the interface
   went dark; --doc-pen is inside the palette css/document.css
   re-scopes for the sheet, and mixing it into white gives a tint that
   belongs to the page it is drawn on. */
#previewSheet .doc-entry li.is-cut {
    background: color-mix(in srgb, var(--doc-pen) 9%, var(--doc-paper));
    box-shadow: -2px 0 0 0 var(--doc-pen);
}

#previewSheet .doc-entry li.is-cut::marker { color: var(--doc-pen); }

/* Below the split-view width the panel is a closed drawer, so a
   document that will not fit would go unmentioned until you opened
   it. One dot on the button that opens it. */
.preview-toggle.has-advice::after {
    content: "";
    width: 0.45rem;
    height: 0.45rem;
    border-radius: var(--radius-full);
    background: var(--pen-light);
}

/* Before there is anything to draw, the panel says what will appear here
   rather than presenting an empty rectangle as a finished page. */
.doc-placeholder {
    display: grid;
    place-items: center;
    align-content: center;
    gap: var(--spacing-2xs);
    aspect-ratio: 1 / 1.414;
    padding: 0 12%;
    text-align: center;
    color: var(--ink-soft);
    border: 1px dashed var(--rule);
    border-radius: var(--radius-xs);
}

.doc-placeholder i {
    font-size: 2rem;
    color: var(--ink-faint);
}

.doc-placeholder p { margin: 0; font-size: var(--font-size-sm); }

/* Same move as .template-suits, and the same reason. This is the sentence a
   first time visitor reads in the preview panel before there is anything to
   preview, so it is copy and not a placeholder. */
.doc-placeholder-note {
    color: var(--ink-soft);
    font-size: var(--font-size-xs) !important;
}

/* Shown only when the panel is a drawer rather than a column, which makes
   this the only way anyone on a phone sees the document the whole product
   exists to produce. It spent two phases pinned 271 to 310px below the
   bottom of the screen (D-54).

   env() so it clears the home indicator on an iPhone and the gesture bar
   on Android; the fallback is what every other browser reads. */
.preview-toggle {
    position: fixed;
    right: var(--spacing-md);
    bottom: calc(var(--spacing-md) + env(safe-area-inset-bottom, 0px));
    z-index: var(--z-fixed);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--spacing-xs);
    min-height: 44px;
    padding: 0.6rem 1.1rem;
    background: var(--ink);
    color: var(--paper);
    border: none;
    border-radius: var(--radius-full);
    font-family: var(--font-display);
    font-size: var(--font-size-base);
    font-weight: 500;
    cursor: pointer;
    box-shadow: var(--shadow-lg);
}

.preview-toggle:active { transform: translateY(1px); }

/* Not on the screen that already draws the document. It had nothing to
   offer there and it was sitting on the corner of "Match it to a job ad" -
   R2's collision check compares fixed against fixed and could not see a
   fixed thing over a button (D-61). */
body[data-step="finish"] .preview-toggle { display: none; }

/* ─── Split view ─────────────────────────────────────────────────
   Only once there is genuinely room for both. Below this the sheet
   would be too small to read, which is worse than not showing it. */
@media (min-width: 1180px) {
    .workbench {
        grid-template-columns: minmax(0, 1fr) 23rem;
        gap: var(--spacing-2xl);
    }

    .preview-panel {
        display: block;
        position: sticky;
        top: calc(var(--header-height) + var(--spacing-md));
        max-height: calc(100dvh - var(--header-height) - var(--spacing-xl));
        overflow-y: auto;
        overscroll-behavior: contain;
    }

    .preview-toggle { display: none; }
}

/* ─── Drawer ─────────────────────────────────────────────────────
   Below the split-view width the panel opens over the form instead of
   squeezing beside it, and on a phone this is the document: not an
   accessory to the form, the thing the form is for.

   It was already fixed to inset: 0, which sounds right and was not,
   because inset: 0 is the layout viewport and the layout viewport was
   558px wide on a 390px phone. The sheet drew at 558 and the glass
   showed the middle of it, so the left edge of your own CV was off the
   screen with no way to reach it. The header fix is what puts it back;
   what is here is the panel behaving like a screen rather than a box.

   dvh, not vh: vh on a phone is the height with the address bar hidden,
   so the bar of a full-height panel measured in vh sits under it.

   Whitespace-only lines inside the padding are on purpose - the sheet is
   scaled to the panel's width by css/document.css, so the padding here
   is the only thing setting how big the document draws. */
@media (max-width: 1179px) {
    .preview-panel.is-open {
        display: flex;
        flex-direction: column;
        position: fixed;
        inset: 0;
        z-index: var(--z-modal);
        height: 100dvh;
        background: var(--paper);
        padding: 0;
        overflow-y: auto;
        overscroll-behavior: contain;
    }

    /* Stays put while the document scrolls under it, so the way out is
       always where you left it. */
    .preview-panel.is-open .preview-head {
        position: sticky;
        top: 0;
        z-index: 1;
        align-items: center;
        gap: var(--spacing-sm);
        margin: 0;
        padding: var(--spacing-xs) var(--spacing-md);
        padding-inline-end: var(--spacing-xs);
        background: var(--paper);
    }

    .preview-panel.is-open .preview-close { display: inline-flex; }
    .preview-panel.is-open .preview-meta { margin-inline-start: 0; margin-inline-end: auto; }

    .preview-panel.is-open .advice { margin: var(--spacing-md) var(--spacing-md) 0; }

    .preview-panel.is-open .preview-stage {
        padding: var(--spacing-md);
        padding-bottom: calc(var(--spacing-xl) + env(safe-area-inset-bottom, 0px));
    }

    /* The pill floats over the end of the page, so the page ends above
       where it floats. */
    .app-footer {
        padding-bottom: calc(var(--spacing-xl) + 44px + env(safe-area-inset-bottom, 0px));
    }
}

/* ===================================================================
   R3 - the seven screens

   Everything below draws a screen the form rebuilt in R3. There are no
   style attributes in any of it: 28 of the 48 the ratchet counted lived
   in the step render functions this replaces, which is why that count
   drops to 20 and why the rest of the way to zero is R4's.
   =================================================================== */

/* --- the import line -------------------------------------------------
   What is left of a whole step. It is drawn only on an empty form: once
   there is a name in the file, importing over the top is something you go
   and ask for from the header menu rather than something offered beside
   your own typing. */
.import-line {
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
    width: 100%;
    min-height: 44px;
    margin-bottom: var(--spacing-lg);
    padding: var(--spacing-sm) var(--spacing-md);
    border: 1px solid var(--rule);
    border-radius: var(--radius-md);
    background: var(--surface);
    font: inherit;
    color: var(--ink);
    text-align: start;
    cursor: pointer;
    transition: border-color var(--transition-fast), transform var(--transition-fast);
}

.import-line:hover { border-color: var(--pen); }
.import-line:active { transform: translateY(1px); }

.import-line > i {
    flex: 0 0 auto;
    font-size: 1.25rem;
    color: var(--pen);
}

.import-line-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.import-line-text small {
    color: var(--ink-2);
    font-size: var(--font-size-xs);
}

.import-line-go {
    flex: 0 0 auto;
    margin-inline-start: auto;
    color: var(--ink-2);
}

/* --- two short fields on one line ---------------------------------- */
.form-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--spacing-md);
}

.form-row .form-group { margin-bottom: 0; }

@media (max-width: 480px) {
    .form-row { grid-template-columns: 1fr; gap: 0; }
    .form-row .form-group { margin-bottom: var(--spacing-md); }
}

/* Fields that belong together, named rather than divided by a bare rule,
   so the grouping is heard as well as seen. */
.field-set {
    margin: var(--spacing-lg) 0 0;
    padding: 0;
    border: none;
    border-top: 1px solid var(--rule);
}

.field-set-legend {
    padding-inline-end: var(--spacing-sm);
    color: var(--ink-2);
    font-size: var(--font-size-sm);
}

/* An input with a button on its end. */
.field-with-action {
    display: flex;
    align-items: stretch;
    gap: var(--spacing-xs);
}

.field-with-action input { flex: 1 1 auto; min-width: 0; }
.field-action { flex: 0 0 auto; min-height: 44px; white-space: nowrap; }

.field-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-xs);
    margin-top: var(--spacing-sm);
}

.field-actions .btn { flex: 1 1 auto; min-height: 44px; }

/* --- advice, folded --------------------------------------------------
   Six tips and fifteen verbs sat open above the summary box, and four
   achievement examples open under the experience list. Advice before the
   first word is a screen you scroll past. */
.hint {
    margin-top: var(--spacing-lg);
    border: 1px solid var(--rule);
    border-radius: var(--radius-md);
    background: var(--surface);
}

.hint-summary {
    display: flex;
    align-items: center;
    min-height: 44px;
    padding: 0 var(--spacing-md);
    color: var(--ink-2);
    font-size: var(--font-size-sm);
    cursor: pointer;
}

.hint[open] .hint-summary { border-bottom: 1px solid var(--rule); }

.hint-list {
    margin: 0;
    padding: var(--spacing-md) var(--spacing-md) var(--spacing-md) var(--spacing-xl);
    color: var(--ink-2);
    font-size: var(--font-size-sm);
}

.hint-list li { margin-bottom: var(--spacing-2xs); }

.hint-lead {
    margin: 0;
    padding: var(--spacing-md) var(--spacing-md) var(--spacing-2xs);
    color: var(--ink-2);
    font-size: var(--font-size-xs);
}

.hint-words {
    margin: 0;
    padding: 0 var(--spacing-md) var(--spacing-md);
    color: var(--ink-2);
    font-size: var(--font-size-sm);
}

/* --- an empty list, said properly ------------------------------------
   Experience and Education had nothing at all here: a heading and an Add
   button on an otherwise blank screen. The three lists that did have a
   line said "No projects added yet. Click Add Project to get started",
   which describes the button sitting next to it. */
.blank {
    display: grid;
    place-items: center;
    gap: var(--spacing-2xs);
    padding: var(--spacing-xl) var(--spacing-lg);
    border: 1px dashed var(--rule);
    border-radius: var(--radius-md);
    text-align: center;
}

.blank > i {
    font-size: 1.75rem;
    color: var(--ink-faint);
}

.blank-lead {
    margin: 0;
    color: var(--ink);
    font-weight: var(--font-weight-medium);
}

.blank-note {
    max-width: 44ch;
    margin: 0;
    color: var(--ink-2);
    font-size: var(--font-size-sm);
}

/* --- skills ----------------------------------------------------------
   This screen measured 12,654px at 390. */
.skill-search { position: relative; margin-bottom: var(--spacing-lg); }

.skill-suggestions {
    position: absolute;
    z-index: var(--z-dropdown);
    inset-inline: 0;
    max-height: 17rem;
    margin: var(--spacing-2xs) 0 0;
    padding: 0;
    overflow-y: auto;
    list-style: none;
    border: 1px solid var(--rule);
    border-radius: var(--radius-md);
    background: var(--paper);
    box-shadow: var(--shadow-lg);
}

.skill-suggestion {
    display: flex;
    align-items: center;
    min-height: 44px;
    padding: 0 var(--spacing-md);
    cursor: pointer;
}

.skill-suggestion + .skill-suggestion { border-top: 1px solid var(--rule); }
.skill-suggestion:hover { background: var(--surface); }
.skill-suggestion-new { color: var(--ink-2); }
.skill-suggestion-new strong { color: var(--ink); }

.chosen-lead {
    display: flex;
    align-items: center;
    gap: var(--spacing-xs);
    margin: 0 0 var(--spacing-sm);
    color: var(--ink-2);
    font-size: var(--font-size-sm);
}

.chosen-count {
    padding: 2px var(--spacing-xs);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--ink);
    font-family: var(--font-mono);
}

.chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-xs);
}

.chip {
    display: inline-flex;
    align-items: center;
    gap: var(--spacing-2xs);
    min-height: 44px;
    padding: 0 var(--spacing-sm);
    border: 1px solid var(--pen);
    border-radius: var(--radius-full);
    background: var(--paper);
    color: var(--ink);
    font: inherit;
    font-size: var(--font-size-sm);
    cursor: pointer;
    transition: background var(--transition-fast), transform var(--transition-fast);
}

.chip:hover { background: var(--surface); }
.chip:active { transform: translateY(1px); }
.chip i { color: var(--ink-2); }

.chip-add {
    border-color: var(--rule);
    color: var(--ink-2);
}

.chip-add:hover { border-color: var(--pen); color: var(--ink); }
.chips-quiet { padding: 0 var(--spacing-md) var(--spacing-md); }

/* --- everything else -------------------------------------------------
   Six lists on one screen. An empty one is a row with a zero on it, which
   is what makes room for awards, publications and volunteering without
   handing anybody three more screens to walk past. */
.extras {
    border: 1px solid var(--rule);
    border-radius: var(--radius-md);
    overflow: hidden;
}

.extra + .extra { border-top: 1px solid var(--rule); }

.extra-head {
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
    min-height: 52px;
    padding: 0 var(--spacing-md);
    cursor: pointer;
}

.extra-head:hover { background: var(--surface); }
.extra-title { font-weight: var(--font-weight-medium); }

.extra-count {
    margin-inline-start: auto;
    min-width: 2ch;
    font-family: var(--font-mono);
    text-align: end;
    color: var(--ink);
}

.extra-count.is-none { color: var(--ink-2); }

.extra-body { padding: 0 var(--spacing-md) var(--spacing-md); }

.extra-note {
    margin: 0 0 var(--spacing-sm);
    color: var(--ink-2);
    font-size: var(--font-size-sm);
}

.extra-items {
    margin: 0 0 var(--spacing-sm);
    padding: 0;
    list-style: none;
}

.extra-item {
    display: flex;
    align-items: center;
    gap: var(--spacing-sm);
    min-height: 52px;
    padding: var(--spacing-2xs) 0;
    border-top: 1px solid var(--rule);
}

.extra-item-text {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}

.extra-item-actions {
    flex: 0 0 auto;
    display: flex;
    gap: var(--spacing-2xs);
}

.btn-icon-stop { color: var(--stop); }

.btn-add-quiet { margin-top: 0; }

/* --- the finish screen -----------------------------------------------
   The document, drawn by DocView into a second container. Not a summary
   of it: a retyped summary is what disagreed with the file three times in
   this project. */
.finish-sheet {
    margin-bottom: var(--spacing-sm);
    min-height: 4rem;
}

.finish-meta {
    margin: 0 0 var(--spacing-lg);
    color: var(--ink-2);
    font-size: var(--font-size-sm);
}

.finish-meta .tabular {
    font-family: var(--font-mono);
    color: var(--ink);
}

.finish-actions {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-xs);
}

.finish-actions .btn { min-height: 48px; }

@media (min-width: 640px) {
    .finish-actions { flex-direction: row; flex-wrap: wrap; }
    .finish-actions .btn { flex: 1 1 12rem; }
}

/* What a dialog says when a field it needs is empty. It names the field,
   because "Please fill in required fields" on a five-field dialog is a
   riddle with the answer withheld. */
.dialog-error {
    margin: 0 0 var(--spacing-sm);
    color: var(--stop);
    font-size: var(--font-size-sm);
}
